{"action":"created","comment":{"url":"https://api.github.com/repos/hailexiao/Rentr/pulls/comments/42048875","id":42048875,"diff_hunk":"@@ -0,0 +1,1290 @@\n+// ==ClosureCompiler==\n+// @compilation_level ADVANCED_OPTIMIZATIONS\n+// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3_3.js\n+// ==/ClosureCompiler==\n+\n+/**\n+ * @name MarkerClusterer for Google Maps v3\n+ * @version version 1.0\n+ * @author Luke Mahe\n+ * @fileoverview\n+ * The library creates and manages per-zoom-level clusters for large amounts of\n+ * markers.\n+ * <br/>\n+ * This is a v3 implementation of the\n+ * <a href=\"http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/\"\n+ * >v2 MarkerClusterer</a>.\n+ */\n+\n+/**\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+\n+/**\n+ * A Marker Clusterer that clusters markers.\n+ *\n+ * @param {google.maps.Map} map The Google map to attach to.\n+ * @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to\n+ *   the cluster.\n+ * @param {Object=} opt_options support the following options:\n+ *     'gridSize': (number) The grid size of a cluster in pixels.\n+ *     'maxZoom': (number) The maximum zoom level that a marker can be part of a\n+ *                cluster.\n+ *     'zoomOnClick': (boolean) Whether the default behaviour of clicking on a\n+ *                    cluster is to zoom into it.\n+ *     'averageCenter': (boolean) Wether the center of each cluster should be\n+ *                      the average of all markers in the cluster.\n+ *     'minimumClusterSize': (number) The minimum number of markers to be in a\n+ *                           cluster before the markers are hidden and a count\n+ *                           is shown.\n+ *     'styles': (object) An object that has style properties:\n+ *       'url': (string) The image url.\n+ *       'height': (number) The image height.\n+ *       'width': (number) The image width.\n+ *       'anchor': (Array) The anchor position of the label text.\n+ *       'textColor': (string) The text color.\n+ *       'textSize': (number) The text size.\n+ *       'backgroundPosition': (string) The position of the backgound x, y.\n+ * @constructor\n+ * @extends google.maps.OverlayView\n+ */\n+function MarkerClusterer(map, opt_markers, opt_options) {\n+  // MarkerClusterer implements google.maps.OverlayView interface. We use the\n+  // extend function to extend MarkerClusterer with google.maps.OverlayView\n+  // because it might not always be available when the code is defined so we\n+  // look for it at the last possible moment. If it doesn't exist now then\n+  // there is no point going ahead :)\n+  this.extend(MarkerClusterer, google.maps.OverlayView);\n+  this.map_ = map;\n+\n+  /**\n+   * @type {Array.<google.maps.Marker>}\n+   * @private\n+   */\n+  this.markers_ = [];\n+\n+  /**\n+   *  @type {Array.<Cluster>}\n+   */\n+  this.clusters_ = [];\n+\n+  this.sizes = [53, 56, 66, 78, 90];\n+\n+  /**\n+   * @private\n+   */\n+  this.styles_ = [];\n+\n+  /**\n+   * @type {boolean}\n+   * @private\n+   */\n+  this.ready_ = false;\n+\n+  var options = opt_options || {};\n+\n+  /**\n+   * @type {number}\n+   * @private\n+   */\n+  this.gridSize_ = options['gridSize'] || 60;\n+\n+  /**\n+   * @private\n+   */\n+  this.minClusterSize_ = options['minimumClusterSize'] || 2;\n+\n+\n+  /**\n+   * @type {?number}\n+   * @private\n+   */\n+  this.maxZoom_ = options['maxZoom'] || null;\n+\n+  this.styles_ = options['styles'] || [];\n+\n+  /**\n+   * @type {string}\n+   * @private\n+   */\n+  this.imagePath_ = options['imagePath'] ||\n+      this.MARKER_CLUSTER_IMAGE_PATH_;\n+\n+  /**\n+   * @type {string}\n+   * @private\n+   */\n+  this.imageExtension_ = options['imageExtension'] ||\n+      this.MARKER_CLUSTER_IMAGE_EXTENSION_;\n+\n+  /**\n+   * @type {boolean}\n+   * @private\n+   */\n+  this.zoomOnClick_ = true;\n+\n+  if (options['zoomOnClick'] != undefined) {\n+    this.zoomOnClick_ = options['zoomOnClick'];\n+  }\n+\n+  /**\n+   * @type {boolean}\n+   * @private\n+   */\n+  this.averageCenter_ = false;\n+\n+  if (options['averageCenter'] != undefined) {\n+    this.averageCenter_ = options['averageCenter'];\n+  }\n+\n+  this.setupStyles_();\n+\n+  this.setMap(map);\n+\n+  /**\n+   * @type {number}\n+   * @private\n+   */\n+  this.prevZoom_ = this.map_.getZoom();\n+\n+  // Add the map event listeners\n+  var that = this;\n+  google.maps.event.addListener(this.map_, 'zoom_changed', function() {\n+    var zoom = that.map_.getZoom();\n+\n+    if (that.prevZoom_ != zoom) {\n+      that.prevZoom_ = zoom;\n+      that.resetViewport();\n+    }\n+  });\n+\n+  google.maps.event.addListener(this.map_, 'idle', function() {\n+    that.redraw();\n+  });\n+\n+  // Finally, add the markers\n+  if (opt_markers && opt_markers.length) {\n+    this.addMarkers(opt_markers, false);\n+  }\n+}\n+\n+\n+/**\n+ * The marker cluster image path.\n+ *\n+ * @type {string}\n+ * @private\n+ */\n+MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ =\n+    'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' +\n+    'images/m';\n+\n+\n+/**\n+ * The marker cluster image path.\n+ *\n+ * @type {string}\n+ * @private\n+ */\n+MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png';\n+\n+\n+/**\n+ * Extends a objects prototype by anothers.\n+ *\n+ * @param {Object} obj1 The object to be extended.\n+ * @param {Object} obj2 The object to extend with.\n+ * @return {Object} The new extended object.\n+ * @ignore\n+ */\n+MarkerClusterer.prototype.extend = function(obj1, obj2) {\n+  return (function(object) {\n+    for (var property in object.prototype) {\n+      this.prototype[property] = object.prototype[property];\n+    }\n+    return this;\n+  }).apply(obj1, [obj2]);\n+};\n+\n+\n+/**\n+ * Implementaion of the interface method.\n+ * @ignore\n+ */\n+MarkerClusterer.prototype.onAdd = function() {\n+  this.setReady_(true);\n+};\n+\n+/**\n+ * Implementaion of the interface method.\n+ * @ignore\n+ */\n+MarkerClusterer.prototype.draw = function() {};\n+\n+/**\n+ * Sets up the styles object.\n+ *\n+ * @private\n+ */\n+MarkerClusterer.prototype.setupStyles_ = function() {\n+  if (this.styles_.length) {\n+    return;\n+  }\n+\n+  for (var i = 0, size; size = this.sizes[i]; i++) {\n+    this.styles_.push({\n+      url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_,\n+      height: size,\n+      width: size\n+    });\n+  }\n+};\n+\n+/**\n+ *  Fit the map to the bounds of the markers in the clusterer.\n+ */\n+MarkerClusterer.prototype.fitMapToMarkers = function() {\n+  var markers = this.getMarkers();\n+  var bounds = new google.maps.LatLngBounds();\n+  for (var i = 0, marker; marker = markers[i]; i++) {\n+    bounds.extend(marker.getPosition());\n+  }\n+\n+  this.map_.fitBounds(bounds);\n+};\n+\n+\n+/**\n+ *  Sets the styles.\n+ *\n+ *  @param {Object} styles The style to set.\n+ */\n+MarkerClusterer.prototype.setStyles = function(styles) {\n+  this.styles_ = styles;\n+};\n+\n+\n+/**\n+ *  Gets the styles.\n+ *\n+ *  @return {Object} The styles object.\n+ */\n+MarkerClusterer.prototype.getStyles = function() {\n+  return this.styles_;\n+};\n+\n+\n+/**\n+ * Whether zoom on click is set.\n+ *\n+ * @return {boolean} True if zoomOnClick_ is set.\n+ */\n+MarkerClusterer.prototype.isZoomOnClick = function() {\n+  return this.zoomOnClick_;\n+};\n+\n+/**\n+ * Whether average center is set.\n+ *\n+ * @return {boolean} True if averageCenter_ is set.\n+ */\n+MarkerClusterer.prototype.isAverageCenter = function() {\n+  return this.averageCenter_;\n+};\n+\n+\n+/**\n+ *  Returns the array of markers in the clusterer.\n+ *\n+ *  @return {Array.<google.maps.Marker>} The markers.\n+ */\n+MarkerClusterer.prototype.getMarkers = function() {\n+  return this.markers_;\n+};\n+\n+\n+/**\n+ *  Returns the number of markers in the clusterer\n+ *\n+ *  @return {Number} The number of markers.\n+ */\n+MarkerClusterer.prototype.getTotalMarkers = function() {\n+  return this.markers_.length;\n+};\n+\n+\n+/**\n+ *  Sets the max zoom for the clusterer.\n+ *\n+ *  @param {number} maxZoom The max zoom level.\n+ */\n+MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {\n+  this.maxZoom_ = maxZoom;\n+};\n+\n+\n+/**\n+ *  Gets the max zoom for the clusterer.\n+ *\n+ *  @return {number} The max zoom level.\n+ */\n+MarkerClusterer.prototype.getMaxZoom = function() {\n+  return this.maxZoom_;\n+};\n+\n+\n+/**\n+ *  The function for calculating the cluster icon image.\n+ *\n+ *  @param {Array.<google.maps.Marker>} markers The markers in the clusterer.\n+ *  @param {number} numStyles The number of styles available.\n+ *  @return {Object} A object properties: 'text' (string) and 'index' (number).\n+ *  @private\n+ */\n+MarkerClusterer.prototype.calculator_ = function(markers, numStyles) {\n+  var index = 0;\n+  var count = markers.length;\n+  var dv = count;\n+  while (dv !== 0) {\n+    dv = parseInt(dv / 10, 10);\n+    index++;\n+  }\n+\n+  index = Math.min(index, numStyles);\n+  return {\n+    text: count,\n+    index: index\n+  };\n+};\n+\n+\n+/**\n+ * Set the calculator function.\n+ *\n+ * @param {function(Array, number)} calculator The function to set as the\n+ *     calculator. The function should return a object properties:\n+ *     'text' (string) and 'index' (number).\n+ *\n+ */\n+MarkerClusterer.prototype.setCalculator = function(calculator) {\n+  this.calculator_ = calculator;\n+};\n+\n+\n+/**\n+ * Get the calculator function.\n+ *\n+ * @return {function(Array, number)} the calculator function.\n+ */\n+MarkerClusterer.prototype.getCalculator = function() {\n+  return this.calculator_;\n+};\n+\n+\n+/**\n+ * Add an array of markers to the clusterer.\n+ *\n+ * @param {Array.<google.maps.Marker>} markers The markers to add.\n+ * @param {boolean=} opt_nodraw Whether to redraw the clusters.\n+ */\n+MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) {\n+  for (var i = 0, marker; marker = markers[i]; i++) {\n+    this.pushMarkerTo_(marker);\n+  }\n+  if (!opt_nodraw) {\n+    this.redraw();\n+  }\n+};\n+\n+\n+/**\n+ * Pushes a marker to the clusterer.\n+ *\n+ * @param {google.maps.Marker} marker The marker to add.\n+ * @private\n+ */\n+MarkerClusterer.prototype.pushMarkerTo_ = function(marker) {\n+  marker.isAdded = false;\n+  if (marker['draggable']) {\n+    // If the marker is draggable add a listener so we update the clusters on\n+    // the drag end.\n+    var that = this;\n+    google.maps.event.addListener(marker, 'dragend', function() {\n+      marker.isAdded = false;\n+      that.repaint();\n+    });\n+  }\n+  this.markers_.push(marker);\n+};\n+\n+\n+/**\n+ * Adds a marker to the clusterer and redraws if needed.\n+ *\n+ * @param {google.maps.Marker} marker The marker to add.\n+ * @param {boolean=} opt_nodraw Whether to redraw the clusters.\n+ */\n+MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) {\n+  this.pushMarkerTo_(marker);\n+  if (!opt_nodraw) {\n+    this.redraw();\n+  }\n+};\n+\n+\n+/**\n+ * Removes a marker and returns true if removed, false if not\n+ *\n+ * @param {google.maps.Marker} marker The marker to remove\n+ * @return {boolean} Whether the marker was removed or not\n+ * @private\n+ */\n+MarkerClusterer.prototype.removeMarker_ = function(marker) {\n+  var index = -1;\n+  if (this.markers_.indexOf) {\n+    index = this.markers_.indexOf(marker);\n+  } else {\n+    for (var i = 0, m; m = this.markers_[i]; i++) {\n+      if (m == marker) {\n+        index = i;\n+        break;\n+      }\n+    }\n+  }\n+\n+  if (index == -1) {\n+    // Marker is not in our list of markers.\n+    return false;\n+  }\n+\n+  marker.setMap(null);\n+\n+  this.markers_.splice(index, 1);\n+\n+  return true;\n+};\n+\n+\n+/**\n+ * Remove a marker from the cluster.\n+ *\n+ * @param {google.maps.Marker} marker The marker to remove.\n+ * @param {boolean=} opt_nodraw Optional boolean to force no redraw.\n+ * @return {boolean} True if the marker was removed.\n+ */\n+MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) {\n+  var removed = this.removeMarker_(marker);\n+\n+  if (!opt_nodraw && removed) {\n+    this.resetViewport();\n+    this.redraw();\n+    return true;\n+  } else {\n+   return false;\n+  }\n+};\n+\n+\n+/**\n+ * Removes an array of markers from the cluster.\n+ *\n+ * @param {Array.<google.maps.Marker>} markers The markers to remove.\n+ * @param {boolean=} opt_nodraw Optional boolean to force no redraw.\n+ */\n+MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) {\n+  var removed = false;\n+\n+  for (var i = 0, marker; marker = markers[i]; i++) {\n+    var r = this.removeMarker_(marker);\n+    removed = removed || r;\n+  }\n+\n+  if (!opt_nodraw && removed) {\n+    this.resetViewport();\n+    this.redraw();\n+    return true;\n+  }\n+};\n+\n+\n+/**\n+ * Sets the clusterer's ready state.\n+ *\n+ * @param {boolean} ready The state.\n+ * @private\n+ */\n+MarkerClusterer.prototype.setReady_ = function(ready) {\n+  if (!this.ready_) {\n+    this.ready_ = ready;\n+    this.createClusters_();\n+  }\n+};\n+\n+\n+/**\n+ * Returns the number of clusters in the clusterer.\n+ *\n+ * @return {number} The number of clusters.\n+ */\n+MarkerClusterer.prototype.getTotalClusters = function() {\n+  return this.clusters_.length;\n+};\n+\n+\n+/**\n+ * Returns the google map that the clusterer is associated with.\n+ *\n+ * @return {google.maps.Map} The map.\n+ */\n+MarkerClusterer.prototype.getMap = function() {\n+  return this.map_;\n+};\n+\n+\n+/**\n+ * Sets the google map that the clusterer is associated with.\n+ *\n+ * @param {google.maps.Map} map The map.\n+ */\n+MarkerClusterer.prototype.setMap = function(map) {\n+  this.map_ = map;\n+};\n+\n+\n+/**\n+ * Returns the size of the grid.\n+ *\n+ * @return {number} The grid size.\n+ */\n+MarkerClusterer.prototype.getGridSize = function() {\n+  return this.gridSize_;\n+};\n+\n+\n+/**\n+ * Sets the size of the grid.\n+ *\n+ * @param {number} size The grid size.\n+ */\n+MarkerClusterer.prototype.setGridSize = function(size) {\n+  this.gridSize_ = size;\n+};\n+\n+\n+/**\n+ * Returns the min cluster size.\n+ *\n+ * @return {number} The grid size.\n+ */\n+MarkerClusterer.prototype.getMinClusterSize = function() {\n+  return this.minClusterSize_;\n+};\n+\n+/**\n+ * Sets the min cluster size.\n+ *\n+ * @param {number} size The grid size.\n+ */\n+MarkerClusterer.prototype.setMinClusterSize = function(size) {\n+  this.minClusterSize_ = size;\n+};\n+\n+\n+/**\n+ * Extends a bounds object by the grid size.\n+ *\n+ * @param {google.maps.LatLngBounds} bounds The bounds to extend.\n+ * @return {google.maps.LatLngBounds} The extended bounds.\n+ */\n+MarkerClusterer.prototype.getExtendedBounds = function(bounds) {\n+  var projection = this.getProjection();\n+\n+  // Turn the bounds into latlng.\n+  var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),\n+      bounds.getNorthEast().lng());\n+  var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),\n+      bounds.getSouthWest().lng());\n+\n+  // Convert the points to pixels and the extend out by the grid size.\n+  var trPix = projection.fromLatLngToDivPixel(tr);\n+  trPix.x += this.gridSize_;\n+  trPix.y -= this.gridSize_;\n+\n+  var blPix = projection.fromLatLngToDivPixel(bl);\n+  blPix.x -= this.gridSize_;\n+  blPix.y += this.gridSize_;\n+\n+  // Convert the pixel points back to LatLng\n+  var ne = projection.fromDivPixelToLatLng(trPix);\n+  var sw = projection.fromDivPixelToLatLng(blPix);\n+\n+  // Extend the bounds to contain the new bounds.\n+  bounds.extend(ne);\n+  bounds.extend(sw);\n+\n+  return bounds;\n+};\n+\n+\n+/**\n+ * Determins if a marker is contained in a bounds.\n+ *\n+ * @param {google.maps.Marker} marker The marker to check.\n+ * @param {google.maps.LatLngBounds} bounds The bounds to check against.\n+ * @return {boolean} True if the marker is in the bounds.\n+ * @private\n+ */\n+MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) {\n+  return bounds.contains(marker.getPosition());\n+};\n+\n+\n+/**\n+ * Clears all clusters and markers from the clusterer.\n+ */\n+MarkerClusterer.prototype.clearMarkers = function() {\n+  this.resetViewport(true);\n+\n+  // Set the markers a empty array.\n+  this.markers_ = [];\n+};\n+\n+\n+/**\n+ * Clears all existing clusters and recreates them.\n+ * @param {boolean} opt_hide To also hide the marker.\n+ */\n+MarkerClusterer.prototype.resetViewport = function(opt_hide) {\n+  // Remove all the clusters\n+  for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {\n+    cluster.remove();\n+  }\n+\n+  // Reset the markers to not be added and to be invisible.\n+  for (var i = 0, marker; marker = this.markers_[i]; i++) {\n+    marker.isAdded = false;\n+    if (opt_hide) {\n+      marker.setMap(null);\n+    }\n+  }\n+\n+  this.clusters_ = [];\n+};\n+\n+/**\n+ *\n+ */\n+MarkerClusterer.prototype.repaint = function() {\n+  var oldClusters = this.clusters_.slice();\n+  this.clusters_.length = 0;\n+  this.resetViewport();\n+  this.redraw();\n+\n+  // Remove the old clusters.\n+  // Do it in a timeout so the other clusters have been drawn first.\n+  window.setTimeout(function() {\n+    for (var i = 0, cluster; cluster = oldClusters[i]; i++) {\n+      cluster.remove();\n+    }\n+  }, 0);\n+};\n+\n+\n+/**\n+ * Redraws the clusters.\n+ */\n+MarkerClusterer.prototype.redraw = function() {\n+  this.createClusters_();\n+};\n+\n+\n+/**\n+ * Calculates the distance between two latlng locations in km.\n+ * @see http://www.movable-type.co.uk/scripts/latlong.html\n+ *\n+ * @param {google.maps.LatLng} p1 The first lat lng point.\n+ * @param {google.maps.LatLng} p2 The second lat lng point.\n+ * @return {number} The distance between the two points in km.\n+ * @private\n+*/\n+MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) {\n+  if (!p1 || !p2) {\n+    return 0;\n+  }\n+\n+  var R = 6371; // Radius of the Earth in km\n+  var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;\n+  var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;\n+  var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +\n+    Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *\n+    Math.sin(dLon / 2) * Math.sin(dLon / 2);\n+  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n+  var d = R * c;\n+  return d;\n+};\n+\n+\n+/**\n+ * Add a marker to a cluster, or creates a new cluster.\n+ *\n+ * @param {google.maps.Marker} marker The marker to add.\n+ * @private\n+ */\n+MarkerClusterer.prototype.addToClosestCluster_ = function(marker) {\n+  var distance = 40000; // Some large number\n+  var clusterToAddTo = null;\n+  var pos = marker.getPosition();\n+  for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {\n+    var center = cluster.getCenter();\n+    if (center) {\n+      var d = this.distanceBetweenPoints_(center, marker.getPosition());\n+      if (d < distance) {\n+        distance = d;\n+        clusterToAddTo = cluster;\n+      }\n+    }\n+  }\n+\n+  if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {\n+    clusterToAddTo.addMarker(marker);\n+  } else {\n+    var cluster = new Cluster(this);\n+    cluster.addMarker(marker);\n+    this.clusters_.push(cluster);\n+  }\n+};\n+\n+\n+/**\n+ * Creates the clusters.\n+ *\n+ * @private\n+ */\n+MarkerClusterer.prototype.createClusters_ = function() {\n+  if (!this.ready_) {\n+    return;\n+  }\n+\n+  // Get our current map view bounds.\n+  // Create a new bounds object so we don't affect the map.\n+  var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),\n+      this.map_.getBounds().getNorthEast());\n+  var bounds = this.getExtendedBounds(mapBounds);\n+\n+  for (var i = 0, marker; marker = this.markers_[i]; i++) {\n+    if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {\n+      this.addToClosestCluster_(marker);\n+    }\n+  }\n+};\n+\n+\n+/**\n+ * A cluster that contains markers.\n+ *\n+ * @param {MarkerClusterer} markerClusterer The markerclusterer that this\n+ *     cluster is associated with.\n+ * @constructor\n+ * @ignore\n+ */\n+function Cluster(markerClusterer) {\n+  this.markerClusterer_ = markerClusterer;\n+  this.map_ = markerClusterer.getMap();\n+  this.gridSize_ = markerClusterer.getGridSize();\n+  this.minClusterSize_ = markerClusterer.getMinClusterSize();\n+  this.averageCenter_ = markerClusterer.isAverageCenter();\n+  this.center_ = null;\n+  this.markers_ = [];\n+  this.bounds_ = null;\n+  this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(),\n+      markerClusterer.getGridSize());\n+}\n+\n+/**\n+ * Determins if a marker is already added to the cluster.\n+ *\n+ * @param {google.maps.Marker} marker The marker to check.\n+ * @return {boolean} True if the marker is already added.\n+ */\n+Cluster.prototype.isMarkerAlreadyAdded = function(marker) {\n+  if (this.markers_.indexOf) {\n+    return this.markers_.indexOf(marker) != -1;\n+  } else {\n+    for (var i = 0, m; m = this.markers_[i]; i++) {\n+      if (m == marker) {\n+        return true;\n+      }\n+    }\n+  }\n+  return false;\n+};\n+\n+\n+/**\n+ * Add a marker the cluster.\n+ *\n+ * @param {google.maps.Marker} marker The marker to add.\n+ * @return {boolean} True if the marker was added.\n+ */\n+Cluster.prototype.addMarker = function(marker) {\n+  if (this.isMarkerAlreadyAdded(marker)) {\n+    return false;\n+  }\n+\n+  if (!this.center_) {\n+    this.center_ = marker.getPosition();\n+    this.calculateBounds_();\n+  } else {\n+    if (this.averageCenter_) {\n+      var l = this.markers_.length + 1;\n+      var lat = (this.center_.lat() * (l-1) + marker.getPosition().lat()) / l;\n+      var lng = (this.center_.lng() * (l-1) + marker.getPosition().lng()) / l;\n+      this.center_ = new google.maps.LatLng(lat, lng);\n+      this.calculateBounds_();\n+    }\n+  }\n+\n+  marker.isAdded = true;\n+  this.markers_.push(marker);\n+\n+  var len = this.markers_.length;\n+  if (len < this.minClusterSize_ && marker.getMap() != this.map_) {\n+    // Min cluster size not reached so show the marker.\n+    marker.setMap(this.map_);\n+  }\n+\n+  if (len == this.minClusterSize_) {\n+    // Hide the markers that were showing.\n+    for (var i = 0; i < len; i++) {\n+      this.markers_[i].setMap(null);\n+    }\n+  }\n+\n+  if (len >= this.minClusterSize_) {\n+    marker.setMap(null);\n+  }\n+\n+  this.updateIcon();\n+  return true;\n+};\n+\n+\n+/**\n+ * Returns the marker clusterer that the cluster is associated with.\n+ *\n+ * @return {MarkerClusterer} The associated marker clusterer.\n+ */\n+Cluster.prototype.getMarkerClusterer = function() {\n+  return this.markerClusterer_;\n+};\n+\n+\n+/**\n+ * Returns the bounds of the cluster.\n+ *\n+ * @return {google.maps.LatLngBounds} the cluster bounds.\n+ */\n+Cluster.prototype.getBounds = function() {\n+  var bounds = new google.maps.LatLngBounds(this.center_, this.center_);\n+  var markers = this.getMarkers();\n+  for (var i = 0, marker; marker = markers[i]; i++) {\n+    bounds.extend(marker.getPosition());\n+  }\n+  return bounds;\n+};\n+\n+\n+/**\n+ * Removes the cluster\n+ */\n+Cluster.prototype.remove = function() {\n+  this.clusterIcon_.remove();\n+  this.markers_.length = 0;\n+  delete this.markers_;\n+};\n+\n+\n+/**\n+ * Returns the center of the cluster.\n+ *\n+ * @return {number} The cluster center.\n+ */\n+Cluster.prototype.getSize = function() {\n+  return this.markers_.length;\n+};\n+\n+\n+/**\n+ * Returns the center of the cluster.\n+ *\n+ * @return {Array.<google.maps.Marker>} The cluster center.\n+ */\n+Cluster.prototype.getMarkers = function() {\n+  return this.markers_;\n+};\n+\n+\n+/**\n+ * Returns the center of the cluster.\n+ *\n+ * @return {google.maps.LatLng} The cluster center.\n+ */\n+Cluster.prototype.getCenter = function() {\n+  return this.center_;\n+};\n+\n+\n+/**\n+ * Calculated the extended bounds of the cluster with the grid.\n+ *\n+ * @private\n+ */\n+Cluster.prototype.calculateBounds_ = function() {\n+  var bounds = new google.maps.LatLngBounds(this.center_, this.center_);\n+  this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);\n+};\n+\n+\n+/**\n+ * Determines if a marker lies in the clusters bounds.\n+ *\n+ * @param {google.maps.Marker} marker The marker to check.\n+ * @return {boolean} True if the marker lies in the bounds.\n+ */\n+Cluster.prototype.isMarkerInClusterBounds = function(marker) {\n+  return this.bounds_.contains(marker.getPosition());\n+};\n+\n+\n+/**\n+ * Returns the map that the cluster is associated with.\n+ *\n+ * @return {google.maps.Map} The map.\n+ */\n+Cluster.prototype.getMap = function() {\n+  return this.map_;\n+};\n+\n+\n+/**\n+ * Updates the cluster icon\n+ */\n+Cluster.prototype.updateIcon = function() {\n+  var zoom = this.map_.getZoom();\n+  var mz = this.markerClusterer_.getMaxZoom();\n+\n+  if (mz && zoom > mz) {\n+    // The zoom is greater than our max zoom so show all the markers in cluster.\n+    for (var i = 0, marker; marker = this.markers_[i]; i++) {","path":"app/assets/javascripts/markerclusterer.js","position":989,"original_position":989,"commit_id":"558a92d9626a015d1a4fc5311669446a86088d83","original_commit_id":"558a92d9626a015d1a4fc5311669446a86088d83","user":{"login":"houndci","id":6697940,"avatar_url":"https://avatars.githubusercontent.com/u/6697940?v=3","gravatar_id":"","url":"https://api.github.com/users/houndci","html_url":"https://github.com/houndci","followers_url":"https://api.github.com/users/houndci/followers","following_url":"https://api.github.com/users/houndci/following{/other_user}","gists_url":"https://api.github.com/users/houndci/gists{/gist_id}","starred_url":"https://api.github.com/users/houndci/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/houndci/subscriptions","organizations_url":"https://api.github.com/users/houndci/orgs","repos_url":"https://api.github.com/users/houndci/repos","events_url":"https://api.github.com/users/houndci/events{/privacy}","received_events_url":"https://api.github.com/users/houndci/received_events","type":"User","site_admin":false},"body":"Expected a conditional expression and instead saw an assignment.","created_at":"2015-10-14T20:33:12Z","updated_at":"2015-10-14T20:33:12Z","html_url":"https://github.com/hailexiao/Rentr/pull/8#discussion_r42048875","pull_request_url":"https://api.github.com/repos/hailexiao/Rentr/pulls/8","_links":{"self":{"href":"https://api.github.com/repos/hailexiao/Rentr/pulls/comments/42048875"},"html":{"href":"https://github.com/hailexiao/Rentr/pull/8#discussion_r42048875"},"pull_request":{"href":"https://api.github.com/repos/hailexiao/Rentr/pulls/8"}}},"pull_request":{"url":"https://api.github.com/repos/hailexiao/Rentr/pulls/8","id":47711080,"html_url":"https://github.com/hailexiao/Rentr/pull/8","diff_url":"https://github.com/hailexiao/Rentr/pull/8.diff","patch_url":"https://github.com/hailexiao/Rentr/pull/8.patch","issue_url":"https://api.github.com/repos/hailexiao/Rentr/issues/8","number":8,"state":"open","locked":false,"title":"Utility visualization","user":{"login":"hailexiao","id":10236227,"avatar_url":"https://avatars.githubusercontent.com/u/10236227?v=3","gravatar_id":"","url":"https://api.github.com/users/hailexiao","html_url":"https://github.com/hailexiao","followers_url":"https://api.github.com/users/hailexiao/followers","following_url":"https://api.github.com/users/hailexiao/following{/other_user}","gists_url":"https://api.github.com/users/hailexiao/gists{/gist_id}","starred_url":"https://api.github.com/users/hailexiao/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hailexiao/subscriptions","organizations_url":"https://api.github.com/users/hailexiao/orgs","repos_url":"https://api.github.com/users/hailexiao/repos","events_url":"https://api.github.com/users/hailexiao/events{/privacy}","received_events_url":"https://api.github.com/users/hailexiao/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-10-14T20:33:04Z","updated_at":"2015-10-14T20:33:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/hailexiao/Rentr/pulls/8/commits","review_comments_url":"https://api.github.com/repos/hailexiao/Rentr/pulls/8/comments","review_comment_url":"https://api.github.com/repos/hailexiao/Rentr/pulls/comments{/number}","comments_url":"https://api.github.com/repos/hailexiao/Rentr/issues/8/comments","statuses_url":"https://api.github.com/repos/hailexiao/Rentr/statuses/558a92d9626a015d1a4fc5311669446a86088d83","head":{"label":"hailexiao:utility-visualization","ref":"utility-visualization","sha":"558a92d9626a015d1a4fc5311669446a86088d83","user":{"login":"hailexiao","id":10236227,"avatar_url":"https://avatars.githubusercontent.com/u/10236227?v=3","gravatar_id":"","url":"https://api.github.com/users/hailexiao","html_url":"https://github.com/hailexiao","followers_url":"https://api.github.com/users/hailexiao/followers","following_url":"https://api.github.com/users/hailexiao/following{/other_user}","gists_url":"https://api.github.com/users/hailexiao/gists{/gist_id}","starred_url":"https://api.github.com/users/hailexiao/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hailexiao/subscriptions","organizations_url":"https://api.github.com/users/hailexiao/orgs","repos_url":"https://api.github.com/users/hailexiao/repos","events_url":"https://api.github.com/users/hailexiao/events{/privacy}","received_events_url":"https://api.github.com/users/hailexiao/received_events","type":"User","site_admin":false},"repo":{"id":43692086,"name":"Rentr","full_name":"hailexiao/Rentr","owner":{"login":"hailexiao","id":10236227,"avatar_url":"https://avatars.githubusercontent.com/u/10236227?v=3","gravatar_id":"","url":"https://api.github.com/users/hailexiao","html_url":"https://github.com/hailexiao","followers_url":"https://api.github.com/users/hailexiao/followers","following_url":"https://api.github.com/users/hailexiao/following{/other_user}","gists_url":"https://api.github.com/users/hailexiao/gists{/gist_id}","starred_url":"https://api.github.com/users/hailexiao/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hailexiao/subscriptions","organizations_url":"https://api.github.com/users/hailexiao/orgs","repos_url":"https://api.github.com/users/hailexiao/repos","events_url":"https://api.github.com/users/hailexiao/events{/privacy}","received_events_url":"https://api.github.com/users/hailexiao/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/hailexiao/Rentr","description":"A web application for analyzing the total cost of rental housing","fork":false,"url":"https://api.github.com/repos/hailexiao/Rentr","forks_url":"https://api.github.com/repos/hailexiao/Rentr/forks","keys_url":"https://api.github.com/repos/hailexiao/Rentr/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hailexiao/Rentr/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hailexiao/Rentr/teams","hooks_url":"https://api.github.com/repos/hailexiao/Rentr/hooks","issue_events_url":"https://api.github.com/repos/hailexiao/Rentr/issues/events{/number}","events_url":"https://api.github.com/repos/hailexiao/Rentr/events","assignees_url":"https://api.github.com/repos/hailexiao/Rentr/assignees{/user}","branches_url":"https://api.github.com/repos/hailexiao/Rentr/branches{/branch}","tags_url":"https://api.github.com/repos/hailexiao/Rentr/tags","blobs_url":"https://api.github.com/repos/hailexiao/Rentr/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hailexiao/Rentr/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hailexiao/Rentr/git/refs{/sha}","trees_url":"https://api.github.com/repos/hailexiao/Rentr/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hailexiao/Rentr/statuses/{sha}","languages_url":"https://api.github.com/repos/hailexiao/Rentr/languages","stargazers_url":"https://api.github.com/repos/hailexiao/Rentr/stargazers","contributors_url":"https://api.github.com/repos/hailexiao/Rentr/contributors","subscribers_url":"https://api.github.com/repos/hailexiao/Rentr/subscribers","subscription_url":"https://api.github.com/repos/hailexiao/Rentr/subscription","commits_url":"https://api.github.com/repos/hailexiao/Rentr/commits{/sha}","git_commits_url":"https://api.github.com/repos/hailexiao/Rentr/git/commits{/sha}","comments_url":"https://api.github.com/repos/hailexiao/Rentr/comments{/number}","issue_comment_url":"https://api.github.com/repos/hailexiao/Rentr/issues/comments{/number}","contents_url":"https://api.github.com/repos/hailexiao/Rentr/contents/{+path}","compare_url":"https://api.github.com/repos/hailexiao/Rentr/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hailexiao/Rentr/merges","archive_url":"https://api.github.com/repos/hailexiao/Rentr/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hailexiao/Rentr/downloads","issues_url":"https://api.github.com/repos/hailexiao/Rentr/issues{/number}","pulls_url":"https://api.github.com/repos/hailexiao/Rentr/pulls{/number}","milestones_url":"https://api.github.com/repos/hailexiao/Rentr/milestones{/number}","notifications_url":"https://api.github.com/repos/hailexiao/Rentr/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hailexiao/Rentr/labels{/name}","releases_url":"https://api.github.com/repos/hailexiao/Rentr/releases{/id}","created_at":"2015-10-05T15:12:31Z","updated_at":"2015-10-05T15:13:51Z","pushed_at":"2015-10-14T20:33:05Z","git_url":"git://github.com/hailexiao/Rentr.git","ssh_url":"git@github.com:hailexiao/Rentr.git","clone_url":"https://github.com/hailexiao/Rentr.git","svn_url":"https://github.com/hailexiao/Rentr","homepage":null,"size":784,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"hailexiao:master","ref":"master","sha":"48fc4a2313b1941eb68b7cca712f3a2f84c8fe08","user":{"login":"hailexiao","id":10236227,"avatar_url":"https://avatars.githubusercontent.com/u/10236227?v=3","gravatar_id":"","url":"https://api.github.com/users/hailexiao","html_url":"https://github.com/hailexiao","followers_url":"https://api.github.com/users/hailexiao/followers","following_url":"https://api.github.com/users/hailexiao/following{/other_user}","gists_url":"https://api.github.com/users/hailexiao/gists{/gist_id}","starred_url":"https://api.github.com/users/hailexiao/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hailexiao/subscriptions","organizations_url":"https://api.github.com/users/hailexiao/orgs","repos_url":"https://api.github.com/users/hailexiao/repos","events_url":"https://api.github.com/users/hailexiao/events{/privacy}","received_events_url":"https://api.github.com/users/hailexiao/received_events","type":"User","site_admin":false},"repo":{"id":43692086,"name":"Rentr","full_name":"hailexiao/Rentr","owner":{"login":"hailexiao","id":10236227,"avatar_url":"https://avatars.githubusercontent.com/u/10236227?v=3","gravatar_id":"","url":"https://api.github.com/users/hailexiao","html_url":"https://github.com/hailexiao","followers_url":"https://api.github.com/users/hailexiao/followers","following_url":"https://api.github.com/users/hailexiao/following{/other_user}","gists_url":"https://api.github.com/users/hailexiao/gists{/gist_id}","starred_url":"https://api.github.com/users/hailexiao/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hailexiao/subscriptions","organizations_url":"https://api.github.com/users/hailexiao/orgs","repos_url":"https://api.github.com/users/hailexiao/repos","events_url":"https://api.github.com/users/hailexiao/events{/privacy}","received_events_url":"https://api.github.com/users/hailexiao/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/hailexiao/Rentr","description":"A web application for analyzing the total cost of rental housing","fork":false,"url":"https://api.github.com/repos/hailexiao/Rentr","forks_url":"https://api.github.com/repos/hailexiao/Rentr/forks","keys_url":"https://api.github.com/repos/hailexiao/Rentr/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hailexiao/Rentr/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hailexiao/Rentr/teams","hooks_url":"https://api.github.com/repos/hailexiao/Rentr/hooks","issue_events_url":"https://api.github.com/repos/hailexiao/Rentr/issues/events{/number}","events_url":"https://api.github.com/repos/hailexiao/Rentr/events","assignees_url":"https://api.github.com/repos/hailexiao/Rentr/assignees{/user}","branches_url":"https://api.github.com/repos/hailexiao/Rentr/branches{/branch}","tags_url":"https://api.github.com/repos/hailexiao/Rentr/tags","blobs_url":"https://api.github.com/repos/hailexiao/Rentr/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hailexiao/Rentr/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hailexiao/Rentr/git/refs{/sha}","trees_url":"https://api.github.com/repos/hailexiao/Rentr/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hailexiao/Rentr/statuses/{sha}","languages_url":"https://api.github.com/repos/hailexiao/Rentr/languages","stargazers_url":"https://api.github.com/repos/hailexiao/Rentr/stargazers","contributors_url":"https://api.github.com/repos/hailexiao/Rentr/contributors","subscribers_url":"https://api.github.com/repos/hailexiao/Rentr/subscribers","subscription_url":"https://api.github.com/repos/hailexiao/Rentr/subscription","commits_url":"https://api.github.com/repos/hailexiao/Rentr/commits{/sha}","git_commits_url":"https://api.github.com/repos/hailexiao/Rentr/git/commits{/sha}","comments_url":"https://api.github.com/repos/hailexiao/Rentr/comments{/number}","issue_comment_url":"https://api.github.com/repos/hailexiao/Rentr/issues/comments{/number}","contents_url":"https://api.github.com/repos/hailexiao/Rentr/contents/{+path}","compare_url":"https://api.github.com/repos/hailexiao/Rentr/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hailexiao/Rentr/merges","archive_url":"https://api.github.com/repos/hailexiao/Rentr/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hailexiao/Rentr/downloads","issues_url":"https://api.github.com/repos/hailexiao/Rentr/issues{/number}","pulls_url":"https://api.github.com/repos/hailexiao/Rentr/pulls{/number}","milestones_url":"https://api.github.com/repos/hailexiao/Rentr/milestones{/number}","notifications_url":"https://api.github.com/repos/hailexiao/Rentr/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hailexiao/Rentr/labels{/name}","releases_url":"https://api.github.com/repos/hailexiao/Rentr/releases{/id}","created_at":"2015-10-05T15:12:31Z","updated_at":"2015-10-05T15:13:51Z","pushed_at":"2015-10-14T20:33:05Z","git_url":"git://github.com/hailexiao/Rentr.git","ssh_url":"git@github.com:hailexiao/Rentr.git","clone_url":"https://github.com/hailexiao/Rentr.git","svn_url":"https://github.com/hailexiao/Rentr","homepage":null,"size":784,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/hailexiao/Rentr/pulls/8"},"html":{"href":"https://github.com/hailexiao/Rentr/pull/8"},"issue":{"href":"https://api.github.com/repos/hailexiao/Rentr/issues/8"},"comments":{"href":"https://api.github.com/repos/hailexiao/Rentr/issues/8/comments"},"review_comments":{"href":"https://api.github.com/repos/hailexiao/Rentr/pulls/8/comments"},"review_comment":{"href":"https://api.github.com/repos/hailexiao/Rentr/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/hailexiao/Rentr/pulls/8/commits"},"statuses":{"href":"https://api.github.com/repos/hailexiao/Rentr/statuses/558a92d9626a015d1a4fc5311669446a86088d83"}}}}