Failed Conditions
Pull Request — master (#275)
by Ramiro
02:42
created

Cluster.updateIcon   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
nc 4
nop 0
dl 0
loc 24
rs 8.5125
c 1
b 0
f 0
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 Gmaps MarkerClusterer for Google Maps v3
8
 * @version version 1.2.0
9
 * @author Luke Mahe
10
 * @author Sebastian Hösl <[email protected]>
11
 * @fileoverview
12
 * The library creates and manages per-zoom-level clusters for large amounts of
13
 * markers.
14
 * This is a v3 implementation of the v2 MarkerClusterer
15
 * {@link http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/}
16
 * ></a>.
17
 */
18
19
/**
20
 * @license
21
 *
22
 * Licensed under the Apache License, Version 2.0 (the "License");
23
 * you may not use this file except in compliance with the License.
24
 * You may obtain a copy of the License at
25
 *
26
 *     http://www.apache.org/licenses/LICENSE-2.0
27
 *
28
 * Unless required by applicable law or agreed to in writing, software
29
 * distributed under the License is distributed on an "AS IS" BASIS,
30
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31
 * See the License for the specific language governing permissions and
32
 * limitations under the License.
33
 */
34
35
/**
36
 * A Marker Clusterer that clusters markers.
37
 *
38
 * @param {google.maps.Map} map The Google map to attach to.
39
 * @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to
40
 *   the cluster.
41
 * @param {Object=} opt_options support the following options:
42
 *     'gridSize': (number) The grid size of a cluster in pixels.
43
 *     'maxZoom': (number) The maximum zoom level that a marker can be part of a
44
 *                cluster.
45
 *     'zoomOnClick': (boolean) Whether the default behaviour of clicking on a
46
 *                    cluster is to zoom into it.
47
 *     'averageCenter': (boolean) Whether the center of each cluster should be
48
 *                      the average of all markers in the cluster.
49
 *     'minimumClusterSize': (number) The minimum number of markers to be in a
50
 *                           cluster before the markers are hidden and a count
51
 *                           is shown.
52
 *     'ignoreHiddenMarkers': (boolean) Whether to ignore markers that are not
53
 *                            visible or count and cluster them anyway 
54
 *     'styles': (Array) An array of objects with these 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
 *       'cssClass': (string) One or more CSS class for styling this marker.
64
 *     'onMouseoverCluster': (function) The event handler used for onmouseover
65
 *                           each cluster
66
 *     'onMouseoutCluster': (function) The event handler used for onmouseout
67
 *                          each cluster
68
 * @constructor
69
 * @extends google.maps.OverlayView
70
 */
71
function MarkerClusterer(map, opt_markers, opt_options) {
72
    // MarkerClusterer implements google.maps.OverlayView interface. We use the
73
    // extend function to extend MarkerClusterer with google.maps.OverlayView
74
    // because it might not always be available when the code is defined so we
75
    // look for it at the last possible moment. If it doesn't exist now then
76
    // there is no point going ahead :)
77
    this.extend(MarkerClusterer, google.maps.OverlayView);
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
78
    this.map_ = map;
79
80
    /**
81
     * @type {Array.<google.maps.Marker>}
82
     * @private
83
     */
84
    this.markers_ = [];
85
86
    /**
87
     *  @type {Array.<Cluster>}
88
     */
89
    this.clusters_ = [];
90
91
    /**
92
     * @type {Object} holding information about every markers cluster
93
     */
94
    this.markersCluster_ = {};
95
96
    /**
97
     * @type {Number} Unique markers ID
98
     */
99
    this.markersUniqueID = 1;
100
101
    this.sizes = [53, 56, 66, 78, 90];
102
103
    /**
104
     * @private
105
     */
106
    this.styles_ = [];
107
108
    /**
109
     * @private
110
     */
111
    this.cssClass_ = '';
112
113
    /**
114
     * @private
115
     * @type {string} Set a default Cluster Class 
116
     */
117
    this.cssDefaultClass_ = 'cluster';
118
119
    /**
120
     * @private
121
     */
122
    this.setIndex_ = 0;
123
124
    /**
125
     * @type {boolean}
126
     * @private
127
     */
128
    this.ready_ = false;
129
130
    var options = opt_options || {};
131
132
    /**
133
     * @type {number}
134
     * @private
135
     */
136
    this.gridSize_ = (options['gridSize'] !== undefined) ? options['gridSize'] : 60;
137
138
    /**
139
     * @private
140
     */
141
    this.minClusterSize_ = options['minimumClusterSize'] || 2;
142
143
    /**
144
     * @type {boolean}
145
     * @private
146
     */
147
    this.ignoreHiddenMarkers_ = options['ignoreHiddenMarkers'] || false;
148
149
    /**
150
     * @type {?number}
151
     * @private
152
     */
153
    this.maxZoom_ = options['maxZoom'] || null;
154
155
    this.styles_ = options['styles'] || [];
156
157
    this.cssClass_ = options['cssClass'] || null;
158
159
    /**
160
     * @type {string}
161
     * @private
162
     */
163
    this.imagePath_ = options['imagePath'] ||
164
        this.MARKER_CLUSTER_IMAGE_PATH_;
165
166
    /**
167
     * @type {string}
168
     * @private
169
     */
170
    this.imageExtension_ = options['imageExtension'] ||
171
        this.MARKER_CLUSTER_IMAGE_EXTENSION_;
172
173
    /**
174
     * @type {boolean}
175
     * @private
176
     */
177
    this.zoomOnClick_ = true;
178
179
    if (options['zoomOnClick'] != undefined) {
0 ignored issues
show
Best Practice introduced by
Comparing options."zoomOnClick" to undefined using the != operator is not safe. Consider using !== instead.
Loading history...
180
        this.zoomOnClick_ = options['zoomOnClick'];
181
    }
182
183
    /**
184
     * @type {boolean}
185
     * @private
186
     */
187
    this.averageCenter_ = false;
188
189
    if (options['averageCenter'] != undefined) {
190
        this.averageCenter_ = options['averageCenter'];
191
    }
192
193
    /**
194
     * @type {function}
195
     * @private
196
     */
197
    this.onMouseoverCluster_ = options['onMouseoverCluster'];
198
199
    /**
200
     * @type {function}
201
     * @private
202
     */
203
    this.onMouseoutCluster_ = options['onMouseoutCluster'];
204
205
    this.setupStyles_();
206
207
    this.setMap(map);
208
209
    /**
210
     * @type {number}
211
     * @private
212
     */
213
    this.prevZoom_ = this.map_.getZoom();
214
215
    // Add the map event listeners
216
    var that = this;
217
    google.maps.event.addListener(this.map_, 'zoom_changed', function() {
218
        var zoom = that.map_.getZoom();
219
220
        if (that.prevZoom_ != zoom) {
221
            that.prevZoom_ = zoom;
222
            that.resetViewport();
223
        }
224
    });
225
226
    google.maps.event.addListener(this.map_, 'idle', function() {
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
227
        that.redraw();
228
    });
229
230
    // Finally, add the markers
231
    if (opt_markers && opt_markers.length) {
232
        this.addMarkers(opt_markers, false);
233
    }
234
}
235
236
237
/**
238
 * The marker cluster image path.
239
 *
240
 * @type {string}
241
 * @private
242
 */
243
MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = 'https://raw.githubusercontent.com/gmaps-marker-clusterer/gmaps-marker-clusterer/master/images/m';
244
245
246
/**
247
 * The marker cluster image path.
248
 *
249
 * @type {string}
250
 * @private
251
 */
252
MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png';
253
254
255
/**
256
 * Extends a objects prototype by anothers.
257
 *
258
 * @param {Object} obj1 The object to be extended.
259
 * @param {Object} obj2 The object to extend with.
260
 * @return {Object} The new extended object.
261
 * @ignore
262
 */
263
MarkerClusterer.prototype.extend = function(obj1, obj2) {
264
    return (function(object) {
265
        for (var property in object.prototype) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
266
            this.prototype[property] = object.prototype[property];
267
        }
268
        return this;
269
    }).apply(obj1, [obj2]);
270
};
271
272
273
/**
274
 * Implementaion of the interface method.
275
 * @ignore
276
 */
277
MarkerClusterer.prototype.onAdd = function() {
278
    this.setReady_(true);
279
};
280
281
/**
282
 * Implementaion of the interface method.
283
 * @ignore
284
 */
285
MarkerClusterer.prototype.draw = function() {};
286
287
/**
288
 * Sets up the styles object.
289
 *
290
 * @private
291
 */
292
MarkerClusterer.prototype.setupStyles_ = function() {
293
    if (this.styles_.length) {
294
        return;
295
    }
296
297
    for (var i = 0, size; size = this.sizes[i]; i++) {
298
        var url = '';
299
        if (typeof this.imagePath_ === 'function') {
300
            url = this.imagePath_(i, size);
301
        } else {
302
            url = this.imagePath_ + (i + 1) + '.' + this.imageExtension_;
303
        }
304
        this.styles_.push({
305
            url: url,
306
            height: size,
307
            width: size
308
        });
309
    }
310
};
311
312
/**
313
 *  Fit the map to the bounds of the markers in the clusterer.
314
 */
315
MarkerClusterer.prototype.fitMapToMarkers = function() {
316
    var markers = this.getMarkers();
317
    var bounds = new google.maps.LatLngBounds();
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
318
    for (var i = 0, marker; marker = markers[i]; i++) {
319
        bounds.extend(marker.getPosition());
320
    }
321
322
    this.map_.fitBounds(bounds);
323
};
324
325
326
/**
327
 *  Sets the styles.
328
 *
329
 *  @param {Object} styles The style to set.
330
 */
331
MarkerClusterer.prototype.setStyles = function(styles) {
332
    this.styles_ = styles;
333
};
334
335
336
/**
337
 *  Gets the styles.
338
 *
339
 *  @return {Object} The styles object.
340
 */
341
MarkerClusterer.prototype.getStyles = function() {
342
    return this.styles_;
343
};
344
345
346
/**
347
 * Whether zoom on click is set.
348
 *
349
 * @return {boolean} True if zoomOnClick_ is set.
350
 */
351
MarkerClusterer.prototype.isZoomOnClick = function() {
352
    return this.zoomOnClick_;
353
};
354
355
/**
356
 * Whether average center is set.
357
 *
358
 * @return {boolean} True if averageCenter_ is set.
359
 */
360
MarkerClusterer.prototype.isAverageCenter = function() {
361
    return this.averageCenter_;
362
};
363
364
365
/**
366
 *  Returns the array of markers in the clusterer.
367
 *
368
 *  @return {Array.<google.maps.Marker>} The markers.
369
 */
370
MarkerClusterer.prototype.getMarkers = function() {
371
    return this.markers_;
372
};
373
374
375
/**
376
 *  Returns the number of markers in the clusterer
377
 *
378
 *  @return {Number} The number of markers.
379
 */
380
MarkerClusterer.prototype.getTotalMarkers = function() {
381
    return this.markers_.length;
382
};
383
384
385
/**
386
 *  Sets the max zoom for the clusterer.
387
 *
388
 *  @param {number} maxZoom The max zoom level.
389
 */
390
MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
391
    this.maxZoom_ = maxZoom;
392
};
393
394
395
/**
396
 *  Gets the max zoom for the clusterer.
397
 *
398
 *  @return {number} The max zoom level.
399
 */
400
MarkerClusterer.prototype.getMaxZoom = function() {
401
    return this.maxZoom_;
402
};
403
404
/**
405
 * Gets marker's cluster object based on given marker
406
 * 
407
 * @param  {google.maps.Marker} marker
408
 * 
409
 * @return {Cluster}
410
 */
411
MarkerClusterer.prototype.getMarkersCluster = function(marker) {
412
    return this.clusters_[this.markersCluster_[marker.uniqueID]];
413
};
414
415
/**
416
 *  The function for calculating the cluster icon image.
417
 *
418
 *  @param {Array.<google.maps.Marker>} markers The markers in the clusterer.
419
 *  @param {number} numStyles The number of styles available.
420
 *  @return {Object} A object properties: 'text' (string) and 'index' (number).
421
 *  @private
422
 */
423
MarkerClusterer.prototype.calculator_ = function(markers, numStyles) {
424
    var index = 0;
425
    var count = markers.length;
426
    var dv = count;
427
    while (dv !== 0) {
428
        dv = parseInt(dv / 10, 10);
429
        index++;
430
    }
431
432
    index = Math.min(index, numStyles);
433
434
    return {
435
        text: count,
436
        index: index
437
    };
438
};
439
440
441
/**
442
 * Set the calculator function.
443
 *
444
 * @param {function(Array, number)} calculator The function to set as the
445
 *     calculator. The function should return a object properties:
446
 *     'text' (string) and 'index' (number).
447
 *
448
 */
449
MarkerClusterer.prototype.setCalculator = function(calculator) {
450
    this.calculator_ = calculator;
451
};
452
453
454
/**
455
 * Get the calculator function.
456
 *
457
 * @return {function(Array, number)} the calculator function.
458
 */
459
MarkerClusterer.prototype.getCalculator = function() {
460
    return this.calculator_;
461
};
462
463
464
/**
465
 * Add an array of markers to the clusterer.
466
 *
467
 * @param {Array.<google.maps.Marker>} markers The markers to add.
468
 * @param {boolean=} opt_nodraw Whether to redraw the clusters.
469
 */
470
MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) {
471
    for (var i = 0, marker; marker = markers[i]; i++) {
472
        this.pushMarkerTo_(marker);
473
    }
474
    if (!opt_nodraw) {
475
        this.redraw();
476
    }
477
};
478
479
480
/**
481
 * Pushes a marker to the clusterer.
482
 *
483
 * @param {google.maps.Marker} marker The marker to add.
484
 * @private
485
 */
486
MarkerClusterer.prototype.pushMarkerTo_ = function(marker) {
487
    marker.isAdded = false;
488
    if (marker['draggable']) {
489
        // If the marker is draggable add a listener so we update the clusters on
490
        // the drag end.
491
        var that = this;
492
        google.maps.event.addListener(marker, 'dragend', function() {
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
493
            marker.isAdded = false;
494
            that.repaint();
495
        });
496
    }
497
    marker.uniqueID = this.markersUniqueID;
498
    this.markersUniqueID++;
499
    this.markers_.push(marker);
500
};
501
502
503
/**
504
 * Adds a marker to the clusterer and redraws if needed.
505
 *
506
 * @param {google.maps.Marker} marker The marker to add.
507
 * @param {boolean=} opt_nodraw Whether to redraw the clusters.
508
 */
509
MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) {
510
    this.pushMarkerTo_(marker);
511
    if (!opt_nodraw) {
512
        this.redraw();
513
    }
514
};
515
516
517
/**
518
 * Removes a marker and returns true if removed, false if not
519
 *
520
 * @param {google.maps.Marker} marker The marker to remove
521
 * @return {boolean} Whether the marker was removed or not
522
 * @private
523
 */
524
MarkerClusterer.prototype.removeMarker_ = function(marker) {
525
    var index = -1;
526
    if (this.markers_.indexOf) {
527
        index = this.markers_.indexOf(marker);
528
    } else {
529
        for (var i = 0, m; m = this.markers_[i]; i++) {
530
            if (m == marker) {
531
                index = i;
532
                break;
533
            }
534
        }
535
    }
536
537
    if (index == -1) {
538
        // Marker is not in our list of markers.
539
        return false;
540
    }
541
542
    marker.setMap(null);
543
	
544
    this.markers_.splice(index, 1);
545
	if (this.markersCluster_[marker.uniqueID]) {
546
		this.clusters_[this.markersCluster_[marker.uniqueID]].removeMarker(marker);
547
	}
548
    delete this.markersCluster_[marker.uniqueID];
549
550
    return true;
551
};
552
553
554
/**
555
 * Remove a marker from the cluster.
556
 *
557
 * @param {google.maps.Marker} marker The marker to remove.
558
 * @return {boolean} True if the marker was removed.
559
 */
560
MarkerClusterer.prototype.removeMarker = function(marker) {
561
	return this.removeMarker_(marker);
562
};
563
564
565
/**
566
 * Removes an array of markers from the cluster.
567
 *
568
 * @param {Array.<google.maps.Marker>} markers The markers to remove.
569
 */
570
MarkerClusterer.prototype.removeMarkers = function(markers) {
571
    var removed = false;
572
573
    for (var i = markers.length; i >= 0; i--) {
574
        var marker = markers[i];
575
        var r = this.removeMarker_(marker);
576
        removed = removed || r;
577
    }
578
579
	return removed;
580
};
581
582
583
/**
584
 * Sets the clusterer's ready state.
585
 *
586
 * @param {boolean} ready The state.
587
 * @private
588
 */
589
MarkerClusterer.prototype.setReady_ = function(ready) {
590
    if (!this.ready_) {
591
        this.ready_ = ready;
592
        this.createClusters_();
593
    }
594
};
595
596
597
/**
598
 * Returns the number of clusters in the clusterer.
599
 *
600
 * @return {number} The number of clusters.
601
 */
602
MarkerClusterer.prototype.getTotalClusters = function() {
603
    return this.clusters_.length;
604
};
605
606
607
/**
608
 * Returns the google map that the clusterer is associated with.
609
 *
610
 * @return {google.maps.Map} The map.
611
 */
612
MarkerClusterer.prototype.getMap = function() {
613
    return this.map_;
614
};
615
616
617
/**
618
 * Sets the google map that the clusterer is associated with.
619
 *
620
 * @param {google.maps.Map} map The map.
621
 */
622
MarkerClusterer.prototype.setMap = function(map) {
623
    this.map_ = map;
624
};
625
626
627
/**
628
 * Returns the size of the grid.
629
 *
630
 * @return {number} The grid size.
631
 */
632
MarkerClusterer.prototype.getGridSize = function() {
633
    return this.gridSize_;
634
};
635
636
637
/**
638
 * Sets the size of the grid.
639
 *
640
 * @param {number} size The grid size.
641
 */
642
MarkerClusterer.prototype.setGridSize = function(size) {
643
    this.gridSize_ = size;
644
};
645
646
647
/**
648
 * Returns the min cluster size.
649
 *
650
 * @return {number} The grid size.
651
 */
652
MarkerClusterer.prototype.getMinClusterSize = function() {
653
    return this.minClusterSize_;
654
};
655
656
/**
657
 * Sets the min cluster size.
658
 *
659
 * @param {number} size The grid size.
660
 */
661
MarkerClusterer.prototype.setMinClusterSize = function(size) {
662
    this.minClusterSize_ = size;
663
};
664
665
666
/**
667
 * Extends a bounds object by the grid size.
668
 *
669
 * @param {google.maps.LatLngBounds} bounds The bounds to extend.
670
 * @return {google.maps.LatLngBounds} The extended bounds.
671
 */
672
MarkerClusterer.prototype.getExtendedBounds = function(bounds) {
673
    var projection = this.getProjection();
674
675
    // Turn the bounds into latlng.
676
    var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
677
        bounds.getNorthEast().lng());
678
    var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
679
        bounds.getSouthWest().lng());
680
681
    // Convert the points to pixels and the extend out by the grid size.
682
    var trPix = projection.fromLatLngToDivPixel(tr);
683
    trPix.x += this.gridSize_;
684
    trPix.y -= this.gridSize_;
685
686
    var blPix = projection.fromLatLngToDivPixel(bl);
687
    blPix.x -= this.gridSize_;
688
    blPix.y += this.gridSize_;
689
690
    // Convert the pixel points back to LatLng
691
    var ne = projection.fromDivPixelToLatLng(trPix);
692
    var sw = projection.fromDivPixelToLatLng(blPix);
693
694
    // Extend the bounds to contain the new bounds.
695
    bounds.extend(ne);
696
    bounds.extend(sw);
697
698
    return bounds;
699
};
700
701
702
/**
703
 * Determins if a marker is contained in a bounds.
704
 *
705
 * @param {google.maps.Marker} marker The marker to check.
706
 * @param {google.maps.LatLngBounds} bounds The bounds to check against.
707
 * @return {boolean} True if the marker is in the bounds.
708
 * @private
709
 */
710
MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) {
711
    return bounds.contains(marker.getPosition());
712
};
713
714
715
/**
716
 * Clears all clusters and markers from the clusterer.
717
 */
718
MarkerClusterer.prototype.clearMarkers = function() {
719
    this.resetViewport(true);
720
721
    // Set the markers a empty array.
722
    this.markers_ = [];
723
    this.markersCluster_ = {};
724
    this.markersUniqueID = 1;
725
};
726
727
728
/**
729
 * Clears all existing clusters and recreates them.
730
 * @param {boolean} opt_hide To also hide the marker.
731
 */
732
MarkerClusterer.prototype.resetViewport = function(opt_hide) {
733
    var oldClusters = this.clusters_.slice();
734
735
    // Remove the old clusters.
736
    // Do it in a timeout so the other clusters have been drawn first.
737
    window.setTimeout(function() {
738
        for (var i = 0, cluster; cluster = oldClusters[i]; i++) {
739
            cluster.remove();
740
        }
741
    }, 0);
742
743
    // Reset the markers to not be added and to be invisible.
744
    for (var i = 0, marker; marker = this.markers_[i]; i++) {
745
        marker.isAdded = false;
746
        if (opt_hide) {
747
            marker.setMap(null);
748
        }
749
    }
750
751
    this.clusters_ = [];
752
    this.markersCluster_ = {};
753
    this.markersUniqueID = 1;
754
};
755
756
/**
757
 *
758
 */
759
MarkerClusterer.prototype.repaint = function() {
760
    this.clusters_.length = 0;
761
    this.resetViewport();
762
    this.redraw();
763
764
};
765
766
767
/**
768
 * Redraws the clusters.
769
 */
770
MarkerClusterer.prototype.redraw = function() {
771
    this.createClusters_();
772
};
773
774
MarkerClusterer.prototype.toRad_ = function (deg) {
775
   return deg * Math.PI / 180;
776
}
777
778
/**
779
 * Calculates the distance between two latlng locations in km.
780
 * @see http://www.movable-type.co.uk/scripts/latlong.html
781
 *
782
 * @param {google.maps.LatLng} p1 The first lat lng point.
783
 * @param {google.maps.LatLng} p2 The second lat lng point.
784
 * @return {number} The distance between the two points in km.
785
 * @private
786
 */
787
MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) {
788
    if (!p1 || !p2) {
789
        return 0;
790
    }
791
792
	var aLat = this.toRad_(p2.lat());
793
	var bLat = this.toRad_(p1.lat());
794
	var dLat2 = (bLat - aLat) * 0.5;
795
	var dLon2 = this.toRad_(p2.lng() - p1.lng()) * 0.5;
796
	var sindLat = Math.sin(dLat2);
797
	var sindLon = Math.sin(dLon2);
798
	var x = sindLat * sindLat + Math.cos(aLat) * Math.cos(bLat) * sindLon * sindLon;
799
	return 6371 * 2 * Math.asin(Math.sqrt(x));
800
};
801
802
803
/**
804
 * Add a marker to a cluster, or creates a new cluster.
805
 *
806
 * @param {google.maps.Marker} marker The marker to add.
807
 * @private
808
 */
809
MarkerClusterer.prototype.addToClosestCluster_ = function(marker) {
810
    var distance = 40000; // Some large number
811
    var clusterToAddTo = null;
812
    var pos = marker.getPosition();
0 ignored issues
show
Unused Code introduced by
The variable pos seems to be never used. Consider removing it.
Loading history...
813
    var clusterIndex = null;
814
    for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {
815
        var center = cluster.getCenter();
816
        if (center) {
817
            var d = this.distanceBetweenPoints_(center, marker.getPosition());
818
            if (d < distance) {
819
                distance = d;
820
                clusterToAddTo = cluster;
821
                clusterIndex = i;
822
            }
823
        }
824
    }
825
826
    if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {
827
        clusterToAddTo.addMarker(marker);
828
    } else {
829
        var cluster = new Cluster(this);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable cluster already seems to be declared on line 814. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
830
        cluster.addMarker(marker);
831
        this.clusters_.push(cluster);
832
        clusterIndex = this.clusters_.length - 1;
833
    }
834
835
    if (marker.isAdded) {
836
        this.markersCluster_[marker.uniqueID] = clusterIndex;
837
    }
838
};
839
840
841
/**
842
 * Creates the clusters.
843
 *
844
 * @private
845
 */
846
MarkerClusterer.prototype.createClusters_ = function() {
847
    if (!this.ready_) {
848
        return;
849
    }
850
851
    // Get our current map view bounds.
852
    // Create a new bounds object so we don't affect the map.
853
    var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
854
        this.map_.getBounds().getNorthEast());
855
    var bounds = this.getExtendedBounds(mapBounds);
856
857
    for (var i = 0, marker; marker = this.markers_[i]; i++) {
858
        if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds) && (!this.ignoreHiddenMarkers_ || marker.getVisible())) {
859
            this.addToClosestCluster_(marker);
860
        }
861
    }
862
};
863
864
865
/**
866
 * A cluster that contains markers.
867
 *
868
 * @param {MarkerClusterer} markerClusterer The markerclusterer that this
869
 *     cluster is associated with.
870
 * @constructor
871
 * @ignore
872
 */
873
function Cluster(markerClusterer) {
874
    this.markerClusterer_ = markerClusterer;
875
    this.map_ = markerClusterer.getMap();
876
    this.gridSize_ = markerClusterer.getGridSize();
877
    this.minClusterSize_ = markerClusterer.getMinClusterSize();
878
    this.averageCenter_ = markerClusterer.isAverageCenter();
879
    this.center_ = null;
880
    this.markers_ = [];
881
    this.bounds_ = null;
882
    this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(),
883
        markerClusterer.getGridSize());
884
}
885
886
/**
887
 * Returns marker index in this.markers_ arry
888
 *
889
 * @param {google.maps.Marker} marker The marker to check.
890
 * @return {boolean} Position in the array or -1 if not found
891
 */
892
Cluster.prototype.getMarkerIndex = function(marker) {
893
    if (this.markers_.indexOf) {
894
        return this.markers_.indexOf(marker);
895
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
896
        for (var i = 0, m; m = this.markers_[i]; i++) {
897
            if (m == marker) {
898
                return i;
899
            }
900
        }
901
    }
902
    return -1;
903
};
904
905
906
/**
907
 * Add a marker the cluster.
908
 *
909
 * @param {google.maps.Marker} marker The marker to add.
910
 * @return {boolean} True if the marker was added.
911
 */
912
Cluster.prototype.addMarker = function(marker) {
913
    if (this.getMarkerIndex(marker) != -1) {
914
        return false;
915
    }
916
917
    if (!this.center_) {
918
        this.center_ = marker.getPosition();
919
        this.calculateBounds_();
920
    } else {
921
        if (this.averageCenter_) {
922
            var l = this.markers_.length + 1;
923
            var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;
924
            var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;
925
            this.center_ = new google.maps.LatLng(lat, lng);
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
926
            this.calculateBounds_();
927
        }
928
    }
929
930
    marker.isAdded = true;
931
    this.markers_.push(marker);
932
933
    var len = this.markers_.length;
934
    if (len < this.minClusterSize_ && marker.getMap() != this.map_) {
935
        // Min cluster size not reached so show the marker.
936
        marker.setMap(this.map_);
937
    }
938
939
    if (len == this.minClusterSize_) {
940
        // Hide the markers that were showing.
941
        for (var i = 0; i < len; i++) {
942
            this.markers_[i].setMap(null);
943
        }
944
    }
945
946
    if (len >= this.minClusterSize_) {
947
        marker.setMap(null);
948
    }
949
950
    this.updateIcon();
951
    return true;
952
};
953
954
/**
955
 * Removes a marker from the cluster.
956
 *
957
 * @param {google.maps.Marker} marker The marker to remove.
958
 * @return {boolean} True if the marker was removed.
959
 */
960
Cluster.prototype.removeMarker = function(marker) {
961
	var index = this.getMarkerIndex(marker);
962
    if (index === -1) {
963
        return false;
964
    }
965
966
    this.markers_.splice(index, 1);
967
968
    var len = this.markers_.length;
969
    if (len < this.minClusterSize_) {
970
        for (var i = 0; i < len; i++) {
971
            this.markers_[i].setMap(this.map_);
972
        }
973
    }
974
975
    this.updateIcon();
976
    return true;
977
};
978
979
/**
980
 * Returns the marker clusterer that the cluster is associated with.
981
 *
982
 * @return {MarkerClusterer} The associated marker clusterer.
983
 */
984
Cluster.prototype.getMarkerClusterer = function() {
985
    return this.markerClusterer_;
986
};
987
988
989
/**
990
 * Returns the bounds of the cluster.
991
 *
992
 * @return {google.maps.LatLngBounds} the cluster bounds.
993
 */
994
Cluster.prototype.getBounds = function() {
995
    var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
996
    var markers = this.getMarkers();
997
    for (var i = 0, marker; marker = markers[i]; i++) {
998
        bounds.extend(marker.getPosition());
999
    }
1000
    return bounds;
1001
};
1002
1003
1004
/**
1005
 * Removes the cluster
1006
 */
1007
Cluster.prototype.remove = function() {
1008
    this.clusterIcon_.remove();
1009
    this.markers_.length = 0;
1010
    delete this.markers_;
1011
};
1012
1013
1014
/**
1015
 * Returns the center of the cluster.
1016
 *
1017
 * @return {number} The cluster center.
1018
 */
1019
Cluster.prototype.getSize = function() {
1020
    return this.markers_.length;
1021
};
1022
1023
1024
/**
1025
 * Returns the center of the cluster.
1026
 *
1027
 * @return {Array.<google.maps.Marker>} The cluster center.
1028
 */
1029
Cluster.prototype.getMarkers = function() {
1030
    return this.markers_;
1031
};
1032
1033
1034
/**
1035
 * Returns the center of the cluster.
1036
 *
1037
 * @return {google.maps.LatLng} The cluster center.
1038
 */
1039
Cluster.prototype.getCenter = function() {
1040
    return this.center_;
1041
};
1042
1043
1044
/**
1045
 * Calculated the extended bounds of the cluster with the grid.
1046
 *
1047
 * @private
1048
 */
1049
Cluster.prototype.calculateBounds_ = function() {
1050
    var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1051
    this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
1052
};
1053
1054
1055
/**
1056
 * Determines if a marker lies in the clusters bounds.
1057
 *
1058
 * @param {google.maps.Marker} marker The marker to check.
1059
 * @return {boolean} True if the marker lies in the bounds.
1060
 */
1061
Cluster.prototype.isMarkerInClusterBounds = function(marker) {
1062
    return this.bounds_.contains(marker.getPosition());
1063
};
1064
1065
1066
/**
1067
 * Returns the map that the cluster is associated with.
1068
 *
1069
 * @return {google.maps.Map} The map.
1070
 */
1071
Cluster.prototype.getMap = function() {
1072
    return this.map_;
1073
};
1074
1075
1076
/**
1077
 * Updates the cluster icon
1078
 */
1079
Cluster.prototype.updateIcon = function() {
1080
    var zoom = this.map_.getZoom();
1081
    var mz = this.markerClusterer_.getMaxZoom();
1082
1083
    if (mz && zoom > mz) {
1084
        // The zoom is greater than our max zoom so show all the markers in cluster.
1085
        for (var i = 0, marker; marker = this.markers_[i]; i++) {
1086
            marker.setMap(this.map_);
1087
        }
1088
        return;
1089
    }
1090
1091
    if (this.markers_.length < this.minClusterSize_) {
1092
        // Min cluster size not yet reached.
1093
        this.clusterIcon_.hide();
1094
        return;
1095
    }
1096
1097
    var numStyles = this.markerClusterer_.getStyles().length;
1098
    var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
1099
    this.clusterIcon_.setCenter(this.center_);
1100
    this.clusterIcon_.setSums(sums);
1101
    this.clusterIcon_.show();
1102
};
1103
1104
1105
/**
1106
 * A cluster icon
1107
 *
1108
 * @param {Cluster} cluster The cluster to be associated with.
1109
 * @param {Object} styles An object that has style properties:
1110
 *     'url': (string) The image url.
1111
 *     'height': (number) The image height.
1112
 *     'width': (number) The image width.
1113
 *     'anchor': (Array) The anchor position of the label text.
1114
 *     'textColor': (string) The text color.
1115
 *     'textSize': (number) The text size.
1116
 *     'backgroundPosition: (string) The background postition x, y.
1117
 * @param {number=} opt_padding Optional padding to apply to the cluster icon.
1118
 * @constructor
1119
 * @extends google.maps.OverlayView
1120
 * @ignore
1121
 */
1122
function ClusterIcon(cluster, styles, opt_padding) {
1123
    cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1124
1125
    this.styles_ = styles;
1126
    this.padding_ = opt_padding || 0;
1127
    this.cluster_ = cluster;
1128
    this.center_ = null;
1129
    this.map_ = cluster.getMap();
1130
    this.div_ = null;
1131
    this.sums_ = null;
1132
    this.visible_ = false;
1133
1134
    this.setMap(this.map_);
1135
}
1136
1137
/**
1138
 * Triggers the clusterclick event and zoom's if the option is set.
1139
 *
1140
 * @param {google.maps.MouseEvent} event The event to propagate
1141
 */
1142
ClusterIcon.prototype.triggerClusterClick = function(event) {
1143
    var markerClusterer = this.cluster_.getMarkerClusterer();
1144
1145
    // Trigger the clusterclick event.
1146
    google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_, event);
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1147
1148
    if (markerClusterer.isZoomOnClick()) {
1149
        // Zoom into the cluster.
1150
        this.map_.fitBounds(this.cluster_.getBounds());
1151
    }
1152
};
1153
1154
/**
1155
 * Triggers the clustermouseover event.
1156
 * @param {google.maps.MouseEvent} event The event to propagate
1157
 */
1158
ClusterIcon.prototype.triggerClusterMouseover = function(event) {
1159
    var markerClusterer = this.cluster_.getMarkerClusterer();
1160
1161
    // Trigger the clustermouseover event.
1162
    google.maps.event.trigger(markerClusterer, 'clustermouseover', this.cluster_, event);
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1163
1164
    if (typeof this.cluster_.markerClusterer_.onMouseoverCluster_ === 'function') {
1165
        this.cluster_.markerClusterer_.onMouseoverCluster_(this, event);
1166
    }
1167
};
1168
1169
/**
1170
 * Triggers the clustermouseout event.
1171
 * @param {google.maps.MouseEvent} event The event to propagate
1172
 */
1173
ClusterIcon.prototype.triggerClusterMouseout = function(event) {
1174
    var markerClusterer = this.cluster_.getMarkerClusterer();
1175
1176
    // Trigger the clustermouseout event.
1177
    google.maps.event.trigger(markerClusterer, 'clustermouseout', this.cluster_, event);
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1178
1179
    if (typeof this.cluster_.markerClusterer_.onMouseoutCluster_ === 'function') {
1180
        this.cluster_.markerClusterer_.onMouseoutCluster_(this, event);
1181
    }
1182
};
1183
1184
/**
1185
 * Adding the cluster icon to the dom.
1186
 * @ignore
1187
 */
1188
ClusterIcon.prototype.onAdd = function() {
1189
    // Creating DOM Element only if visible, otherwise an empty div will be rendered.
1190
    if (this.visible_) {
1191
        this.div_ = document.createElement('DIV');
1192
1193
        var pos = this.getPosFromLatLng_(this.center_);
1194
1195
        this.div_.style.cssText = this.createCss(pos);
1196
        this.div_.innerHTML = this.sums_.text;
1197
1198
        var markerClusterer = this.cluster_.getMarkerClusterer();
1199
1200
        if (markerClusterer.cssClass_) {
1201
            this.div_.className = markerClusterer.cssClass_ + ' ' + markerClusterer.cssDefaultClass_ + this.setIndex_;
1202
        } else {
1203
            this.div_.className = markerClusterer.cssDefaultClass_ + this.setIndex_;
1204
        }
1205
1206
        var panes = this.getPanes();
1207
1208
        panes.overlayMouseTarget.appendChild(this.div_);
1209
1210
        var that = this;
1211
        var isDragging = false;
1212
        var isMouseDown = false;
1213
1214
        google.maps.event.addDomListener(this.div_, 'click', function(event) {
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1215
            // Only perform click when not preceded by a drag
1216
            if (!isDragging) {
1217
                that.triggerClusterClick(event);
1218
            }
1219
        });
1220
1221
        google.maps.event.addDomListener(this.div_, 'mousedown', function() {
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1222
            isDragging = false;
1223
            isMouseDown = true;
1224
        });
1225
1226
        google.maps.event.addDomListener(this.div_, 'mouseup', function() {
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1227
            isDragging = false;
1228
            isMouseDown = false;
1229
        });
1230
1231
        google.maps.event.addDomListener(this.div_, 'mousemove', function() {
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1232
            if (isMouseDown) {
1233
                isDragging = true;
1234
            }
1235
        });
1236
1237
        google.maps.event.addDomListener(this.div_, 'mouseover', function(event) {
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1238
            that.triggerClusterMouseover(event);
1239
        });
1240
1241
        google.maps.event.addDomListener(this.div_, 'mouseout', function(event) {
0 ignored issues
show
Bug introduced by
The variable google seems to be never declared. If this is a global, consider adding a /** global: google */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1242
            that.triggerClusterMouseout(event);
1243
        });
1244
    }
1245
};
1246
1247
/**
1248
 * Returns the position to place the div dending on the latlng.
1249
 *
1250
 * @param {google.maps.LatLng} latlng The position in latlng.
1251
 * @return {google.maps.Point} The position in pixels.
1252
 * @private
1253
 */
1254
ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) {
1255
    var pos = this.getProjection().fromLatLngToDivPixel(latlng);
1256
1257
    if (typeof this.iconAnchor_ === 'object' && this.iconAnchor_.length === 2) {
1258
        pos.x -= this.iconAnchor_[0];
1259
        pos.y -= this.iconAnchor_[1];
1260
    } else {
1261
        pos.x -= parseInt(this.width_ / 2, 10);
1262
        pos.y -= parseInt(this.height_ / 2, 10);
1263
    }
1264
    return pos;
1265
};
1266
1267
1268
/**
1269
 * Draw the icon.
1270
 * @ignore
1271
 */
1272
ClusterIcon.prototype.draw = function() {
1273
    if (this.visible_) {
1274
        var pos = this.getPosFromLatLng_(this.center_);
1275
		if (this.div_) {
1276
			this.div_.style.top = pos.y + 'px';
1277
			this.div_.style.left = pos.x + 'px';
1278
		}
1279
    }
1280
};
1281
1282
1283
/**
1284
 * Hide the icon.
1285
 */
1286
ClusterIcon.prototype.hide = function() {
1287
    if (this.div_) {
1288
        this.div_.style.display = 'none';
1289
    }
1290
    this.visible_ = false;
1291
};
1292
1293
1294
/**
1295
 * Position and show the icon.
1296
 */
1297
ClusterIcon.prototype.show = function() {
1298
    if (this.div_) {
1299
        var pos = this.getPosFromLatLng_(this.center_);
1300
        this.div_.style.cssText = this.createCss(pos);
1301
        this.div_.style.display = '';
1302
    }
1303
    this.visible_ = true;
1304
};
1305
1306
1307
/**
1308
 * Remove the icon from the map
1309
 */
1310
ClusterIcon.prototype.remove = function() {
1311
    this.setMap(null);
1312
};
1313
1314
1315
/**
1316
 * Implementation of the onRemove interface.
1317
 * @ignore
1318
 */
1319
ClusterIcon.prototype.onRemove = function() {
1320
    if (this.div_ && this.div_.parentNode) {
1321
        this.hide();
1322
        this.div_.parentNode.removeChild(this.div_);
1323
        this.div_ = null;
1324
    }
1325
};
1326
1327
1328
/**
1329
 * Set the sums of the icon.
1330
 *
1331
 * @param {Object} sums The sums containing:
1332
 *   'text': (string) The text to display in the icon.
1333
 *   'index': (number) The style index of the icon.
1334
 */
1335
ClusterIcon.prototype.setSums = function(sums) {
1336
    this.sums_ = sums;
1337
    this.text_ = sums.text;
1338
    this.index_ = sums.index;
1339
    if (this.div_) {
1340
        this.div_.innerHTML = sums.text;
1341
    }
1342
1343
    this.useStyle();
1344
};
1345
1346
1347
/**
1348
 * Sets the icon to the the styles.
1349
 */
1350
ClusterIcon.prototype.useStyle = function() {
1351
    var index = Math.max(0, this.sums_.index - 1);
1352
    index = Math.min(this.styles_.length - 1, index);
1353
    var style = this.styles_[index];
1354
    this.url_ = style['url'];
1355
    this.height_ = style['height'];
1356
    this.width_ = style['width'];
1357
    this.textColor_ = style['textColor'];
1358
    this.anchor_ = style['anchor'];
1359
    this.textSize_ = style['textSize'];
1360
    this.backgroundPosition_ = style['backgroundPosition'];
1361
    this.iconAnchor_ = style['iconAnchor'];
1362
    this.setIndex_ = index;
1363
};
1364
1365
1366
/**
1367
 * Sets the center of the icon.
1368
 *
1369
 * @param {google.maps.LatLng} center The latlng to set as the center.
1370
 */
1371
ClusterIcon.prototype.setCenter = function(center) {
1372
    this.center_ = center;
1373
};
1374
1375
1376
/**
1377
 * Create the css text based on the position of the icon.
1378
 *
1379
 * @param {google.maps.Point} pos The position.
1380
 * @return {string} The css style text.
1381
 */
1382
ClusterIcon.prototype.createCss = function(pos) {
1383
    var style = [];
1384
    var markerClusterer = this.cluster_.getMarkerClusterer();
1385
1386
    if (!markerClusterer.cssClass_) {
1387
        style.push('background-image:url(' + this.url_ + ');');
1388
        var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0';
1389
        style.push('background-position:' + backgroundPosition + ';');
1390
1391
        if (typeof this.anchor_ === 'object') {
1392
            if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 &&
1393
                this.anchor_[0] < this.height_) {
1394
                style.push('height:' + (this.height_ - this.anchor_[0]) +
1395
                    'px; padding-top:' + this.anchor_[0] + 'px;');
1396
            } else if (typeof this.anchor_[0] === 'number' && this.anchor_[0] < 0 &&
1397
                -this.anchor_[0] < this.height_) {
1398
                style.push('height:' + this.height_ + 'px; line-height:' + (this.height_ + this.anchor_[0]) +
1399
                    'px;');
1400
            } else {
1401
                style.push('height:' + this.height_ + 'px; line-height:' + this.height_ +
1402
                    'px;');
1403
            }
1404
            if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 &&
1405
                this.anchor_[1] < this.width_) {
1406
                style.push('width:' + (this.width_ - this.anchor_[1]) +
1407
                    'px; padding-left:' + this.anchor_[1] + 'px;');
1408
            } else {
1409
                style.push('width:' + this.width_ + 'px; text-align:center;');
1410
            }
1411
        } else {
1412
            style.push('height:' + this.height_ + 'px; line-height:' +
1413
                this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;');
1414
        }
1415
1416
        var txtColor = this.textColor_ ? this.textColor_ : 'black';
1417
        var txtSize = this.textSize_ ? this.textSize_ : 11;
1418
1419
        style.push('cursor:pointer; top:' + pos.y + 'px; left:' +
1420
            pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' +
1421
            txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
1422
1423
    } else {
1424
        style.push('top:' + pos.y + 'px; left:' + pos.x + 'px;');
1425
    }
1426
1427
    return style.join('');
1428
};
1429
1430
1431
// Export Symbols for Closure
1432
// If you are not going to compile with closure then you can remove the
1433
// code below.
1434
window['MarkerClusterer'] = MarkerClusterer;
1435
MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker;
1436
MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers;
1437
MarkerClusterer.prototype['clearMarkers'] =
1438
    MarkerClusterer.prototype.clearMarkers;
1439
MarkerClusterer.prototype['fitMapToMarkers'] =
1440
    MarkerClusterer.prototype.fitMapToMarkers;
1441
MarkerClusterer.prototype['getCalculator'] =
1442
    MarkerClusterer.prototype.getCalculator;
1443
MarkerClusterer.prototype['getGridSize'] =
1444
    MarkerClusterer.prototype.getGridSize;
1445
MarkerClusterer.prototype['getExtendedBounds'] =
1446
    MarkerClusterer.prototype.getExtendedBounds;
1447
MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap;
1448
MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers;
1449
MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom;
1450
MarkerClusterer.prototype['getMarkersCluster'] = MarkerClusterer.prototype.getMarkersCluster;
1451
MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles;
1452
MarkerClusterer.prototype['getTotalClusters'] =
1453
    MarkerClusterer.prototype.getTotalClusters;
1454
MarkerClusterer.prototype['getTotalMarkers'] =
1455
    MarkerClusterer.prototype.getTotalMarkers;
1456
MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw;
1457
MarkerClusterer.prototype['removeMarker'] =
1458
    MarkerClusterer.prototype.removeMarker;
1459
MarkerClusterer.prototype['removeMarkers'] =
1460
    MarkerClusterer.prototype.removeMarkers;
1461
MarkerClusterer.prototype['resetViewport'] =
1462
    MarkerClusterer.prototype.resetViewport;
1463
MarkerClusterer.prototype['repaint'] =
1464
    MarkerClusterer.prototype.repaint;
1465
MarkerClusterer.prototype['setCalculator'] =
1466
    MarkerClusterer.prototype.setCalculator;
1467
MarkerClusterer.prototype['setGridSize'] =
1468
    MarkerClusterer.prototype.setGridSize;
1469
MarkerClusterer.prototype['setMaxZoom'] =
1470
    MarkerClusterer.prototype.setMaxZoom;
1471
MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd;
1472
MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw;
1473
1474
Cluster.prototype['getCenter'] = Cluster.prototype.getCenter;
1475
Cluster.prototype['getSize'] = Cluster.prototype.getSize;
1476
Cluster.prototype['getMarkers'] = Cluster.prototype.getMarkers;
1477
1478
ClusterIcon.prototype['onAdd'] = ClusterIcon.prototype.onAdd;
1479
ClusterIcon.prototype['draw'] = ClusterIcon.prototype.draw;
1480
ClusterIcon.prototype['onRemove'] = ClusterIcon.prototype.onRemove;