pyc-website
main website for pyc inc.
git clone https://9o.is/git/pyc-website.git
angular-google-maps.min.js
(295405B)
1 /*! angular-google-maps 1.1.6 2014-06-28
2 * AngularJS directives for Google Maps
3 * git: https://github.com/nlaplante/angular-google-maps.git
4 */
5 /*
6 !
7 The MIT License
8
9 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in
19 all copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 THE SOFTWARE.
28
29 angular-google-maps
30 https://github.com/nlaplante/angular-google-maps
31
32 @authors
33 Nicolas Laplante - https://plus.google.com/108189012221374960701
34 Nicholas McCready - https://twitter.com/nmccready
35 */
36
37
38 (function() {
39 angular.module("google-maps.directives.api.utils", []);
40
41 angular.module("google-maps.directives.api.managers", []);
42
43 angular.module("google-maps.directives.api.models.child", ["google-maps.directives.api.utils"]);
44
45 angular.module("google-maps.directives.api.models.parent", ["google-maps.directives.api.managers", "google-maps.directives.api.models.child"]);
46
47 angular.module("google-maps.directives.api", ["google-maps.directives.api.models.parent"]);
48
49 angular.module("google-maps", ["google-maps.directives.api"]).factory("debounce", [
50 "$timeout", function($timeout) {
51 return function(fn) {
52 var nthCall;
53 nthCall = 0;
54 return function() {
55 var argz, later, that;
56 that = this;
57 argz = arguments;
58 nthCall++;
59 later = (function(version) {
60 return function() {
61 if (version === nthCall) {
62 return fn.apply(that, argz);
63 }
64 };
65 })(nthCall);
66 return $timeout(later, 0, true);
67 };
68 };
69 }
70 ]);
71
72 }).call(this);
73
74 (function() {
75 angular.element(document).ready(function() {
76 if (!(google || (typeof google !== "undefined" && google !== null ? google.maps : void 0) || (google.maps.InfoWindow != null))) {
77 return;
78 }
79 google.maps.InfoWindow.prototype._open = google.maps.InfoWindow.prototype.open;
80 google.maps.InfoWindow.prototype._close = google.maps.InfoWindow.prototype.close;
81 google.maps.InfoWindow.prototype._isOpen = false;
82 google.maps.InfoWindow.prototype.open = function(map, anchor) {
83 this._isOpen = true;
84 this._open(map, anchor);
85 };
86 google.maps.InfoWindow.prototype.close = function() {
87 this._isOpen = false;
88 this._close();
89 };
90 google.maps.InfoWindow.prototype.isOpen = function(val) {
91 if (val == null) {
92 val = void 0;
93 }
94 if (val == null) {
95 return this._isOpen;
96 } else {
97 return this._isOpen = val;
98 }
99 };
100 /*
101 Do the same for InfoBox
102 TODO: Clean this up so the logic is defined once, wait until develop becomes master as this will be easier
103 */
104
105 if (!window.InfoBox) {
106 return;
107 }
108 window.InfoBox.prototype._open = window.InfoBox.prototype.open;
109 window.InfoBox.prototype._close = window.InfoBox.prototype.close;
110 window.InfoBox.prototype._isOpen = false;
111 window.InfoBox.prototype.open = function(map, anchor) {
112 this._isOpen = true;
113 this._open(map, anchor);
114 };
115 window.InfoBox.prototype.close = function() {
116 this._isOpen = false;
117 this._close();
118 };
119 return window.InfoBox.prototype.isOpen = function(val) {
120 if (val == null) {
121 val = void 0;
122 }
123 if (val == null) {
124 return this._isOpen;
125 } else {
126 return this._isOpen = val;
127 }
128 };
129 });
130
131 }).call(this);
132
133 /*
134 Author Nick McCready
135 Intersection of Objects if the arrays have something in common each intersecting object will be returned
136 in an new array.
137 */
138
139
140 (function() {
141 _.intersectionObjects = function(array1, array2, comparison) {
142 var res,
143 _this = this;
144 if (comparison == null) {
145 comparison = void 0;
146 }
147 res = _.map(array1, function(obj1) {
148 return _.find(array2, function(obj2) {
149 if (comparison != null) {
150 return comparison(obj1, obj2);
151 } else {
152 return _.isEqual(obj1, obj2);
153 }
154 });
155 });
156 return _.filter(res, function(o) {
157 return o != null;
158 });
159 };
160
161 _.containsObject = _.includeObject = function(obj, target, comparison) {
162 var _this = this;
163 if (comparison == null) {
164 comparison = void 0;
165 }
166 if (obj === null) {
167 return false;
168 }
169 return _.any(obj, function(value) {
170 if (comparison != null) {
171 return comparison(value, target);
172 } else {
173 return _.isEqual(value, target);
174 }
175 });
176 };
177
178 _.differenceObjects = function(array1, array2, comparison) {
179 if (comparison == null) {
180 comparison = void 0;
181 }
182 return _.filter(array1, function(value) {
183 return !_.containsObject(array2, value);
184 });
185 };
186
187 _.withoutObjects = function(array, array2) {
188 return _.differenceObjects(array, array2);
189 };
190
191 _.indexOfObject = function(array, item, comparison, isSorted) {
192 var i, length;
193 if (array == null) {
194 return -1;
195 }
196 i = 0;
197 length = array.length;
198 if (isSorted) {
199 if (typeof isSorted === "number") {
200 i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
201 } else {
202 i = _.sortedIndex(array, item);
203 return (array[i] === item ? i : -1);
204 }
205 }
206 while (i < length) {
207 if (comparison != null) {
208 if (comparison(array[i], item)) {
209 return i;
210 }
211 } else {
212 if (_.isEqual(array[i], item)) {
213 return i;
214 }
215 }
216 i++;
217 }
218 return -1;
219 };
220
221 _["extends"] = function(arrayOfObjectsToCombine) {
222 return _.reduce(arrayOfObjectsToCombine, function(combined, toAdd) {
223 return _.extend(combined, toAdd);
224 }, {});
225 };
226
227 }).call(this);
228
229 /*
230 Author: Nicholas McCready & jfriend00
231 _async handles things asynchronous-like :), to allow the UI to be free'd to do other things
232 Code taken from http://stackoverflow.com/questions/10344498/best-way-to-iterate-over-an-array-without-blocking-the-ui
233
234 The design of any funcitonality of _async is to be like lodash/underscore and replicate it but call things
235 asynchronously underneath. Each should be sufficient for most things to be derrived from.
236
237 TODO: Handle Object iteration like underscore and lodash as well.. not that important right now
238 */
239
240
241 (function() {
242 var async;
243
244 async = {
245 each: function(array, callback, doneCallBack, pausedCallBack, chunk, index, pause) {
246 var doChunk;
247 if (chunk == null) {
248 chunk = 20;
249 }
250 if (index == null) {
251 index = 0;
252 }
253 if (pause == null) {
254 pause = 1;
255 }
256 if (!pause) {
257 throw "pause (delay) must be set from _async!";
258 return;
259 }
260 if (array === void 0 || (array != null ? array.length : void 0) <= 0) {
261 doneCallBack();
262 return;
263 }
264 doChunk = function() {
265 var cnt, i;
266 cnt = chunk;
267 i = index;
268 while (cnt-- && i < (array ? array.length : i + 1)) {
269 callback(array[i], i);
270 ++i;
271 }
272 if (array) {
273 if (i < array.length) {
274 index = i;
275 if (pausedCallBack != null) {
276 pausedCallBack();
277 }
278 return setTimeout(doChunk, pause);
279 } else {
280 if (doneCallBack) {
281 return doneCallBack();
282 }
283 }
284 }
285 };
286 return doChunk();
287 },
288 map: function(objs, iterator, doneCallBack, pausedCallBack, chunk) {
289 var results;
290 results = [];
291 if (objs == null) {
292 return results;
293 }
294 return _async.each(objs, function(o) {
295 return results.push(iterator(o));
296 }, function() {
297 return doneCallBack(results);
298 }, pausedCallBack, chunk);
299 }
300 };
301
302 window._async = async;
303
304 angular.module("google-maps.directives.api.utils").factory("async", function() {
305 return window._async;
306 });
307
308 }).call(this);
309
310 (function() {
311 var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
312
313 angular.module("google-maps.directives.api.utils").factory("BaseObject", function() {
314 var BaseObject, baseObjectKeywords;
315 baseObjectKeywords = ['extended', 'included'];
316 BaseObject = (function() {
317 function BaseObject() {}
318
319 BaseObject.extend = function(obj) {
320 var key, value, _ref;
321 for (key in obj) {
322 value = obj[key];
323 if (__indexOf.call(baseObjectKeywords, key) < 0) {
324 this[key] = value;
325 }
326 }
327 if ((_ref = obj.extended) != null) {
328 _ref.apply(this);
329 }
330 return this;
331 };
332
333 BaseObject.include = function(obj) {
334 var key, value, _ref;
335 for (key in obj) {
336 value = obj[key];
337 if (__indexOf.call(baseObjectKeywords, key) < 0) {
338 this.prototype[key] = value;
339 }
340 }
341 if ((_ref = obj.included) != null) {
342 _ref.apply(this);
343 }
344 return this;
345 };
346
347 return BaseObject;
348
349 })();
350 return BaseObject;
351 });
352
353 }).call(this);
354
355 /*
356 Useful function callbacks that should be defined at later time.
357 Mainly to be used for specs to verify creation / linking.
358
359 This is to lead a common design in notifying child stuff.
360 */
361
362
363 (function() {
364 angular.module("google-maps.directives.api.utils").factory("ChildEvents", function() {
365 return {
366 onChildCreation: function(child) {}
367 };
368 });
369
370 }).call(this);
371
372 (function() {
373 angular.module("google-maps.directives.api.utils").service("EventsHelper", [
374 "Logger", function($log) {
375 return {
376 setEvents: function(marker, scope, model) {
377 if (angular.isDefined(scope.events) && (scope.events != null) && angular.isObject(scope.events)) {
378 return _.compact(_.map(scope.events, function(eventHandler, eventName) {
379 if (scope.events.hasOwnProperty(eventName) && angular.isFunction(scope.events[eventName])) {
380 return google.maps.event.addListener(marker, eventName, function() {
381 return eventHandler.apply(scope, [marker, eventName, model, arguments]);
382 });
383 } else {
384 return $log.info("MarkerEventHelper: invalid event listener " + eventName);
385 }
386 }));
387 }
388 }
389 };
390 }
391 ]);
392
393 }).call(this);
394
395 (function() {
396 var __hasProp = {}.hasOwnProperty,
397 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
398
399 angular.module("google-maps.directives.api.utils").factory("FitHelper", [
400 "BaseObject", "Logger", function(BaseObject, $log) {
401 var FitHelper, _ref;
402 return FitHelper = (function(_super) {
403 __extends(FitHelper, _super);
404
405 function FitHelper() {
406 _ref = FitHelper.__super__.constructor.apply(this, arguments);
407 return _ref;
408 }
409
410 FitHelper.prototype.fit = function(gMarkers, gMap) {
411 var bounds, everSet,
412 _this = this;
413 if (gMap && gMarkers && gMarkers.length > 0) {
414 bounds = new google.maps.LatLngBounds();
415 everSet = false;
416 return _async.each(gMarkers, function(gMarker) {
417 if (gMarker) {
418 if (!everSet) {
419 everSet = true;
420 }
421 return bounds.extend(gMarker.getPosition());
422 }
423 }, function() {
424 if (everSet) {
425 return gMap.fitBounds(bounds);
426 }
427 });
428 }
429 };
430
431 return FitHelper;
432
433 })(BaseObject);
434 }
435 ]);
436
437 }).call(this);
438
439 (function() {
440 angular.module("google-maps.directives.api.utils").service("GmapUtil", [
441 "Logger", "$compile", function(Logger, $compile) {
442 var getCoords, validateCoords;
443 getCoords = function(value) {
444 if (Array.isArray(value) && value.length === 2) {
445 return new google.maps.LatLng(value[1], value[0]);
446 } else if (angular.isDefined(value.type) && value.type === "Point") {
447 return new google.maps.LatLng(value.coordinates[1], value.coordinates[0]);
448 } else {
449 return new google.maps.LatLng(value.latitude, value.longitude);
450 }
451 };
452 validateCoords = function(coords) {
453 if (angular.isUndefined(coords)) {
454 return false;
455 }
456 if (_.isArray(coords)) {
457 if (coords.length === 2) {
458 return true;
459 }
460 } else if ((coords != null) && (coords != null ? coords.type : void 0)) {
461 if (coords.type === "Point" && _.isArray(coords.coordinates) && coords.coordinates.length === 2) {
462 return true;
463 }
464 }
465 if (coords && angular.isDefined((coords != null ? coords.latitude : void 0) && angular.isDefined(coords != null ? coords.longitude : void 0))) {
466 return true;
467 }
468 return false;
469 };
470 return {
471 getLabelPositionPoint: function(anchor) {
472 var xPos, yPos;
473 if (anchor === void 0) {
474 return void 0;
475 }
476 anchor = /^([-\d\.]+)\s([-\d\.]+)$/.exec(anchor);
477 xPos = parseFloat(anchor[1]);
478 yPos = parseFloat(anchor[2]);
479 if ((xPos != null) && (yPos != null)) {
480 return new google.maps.Point(xPos, yPos);
481 }
482 },
483 createMarkerOptions: function(coords, icon, defaults, map) {
484 var opts;
485 if (map == null) {
486 map = void 0;
487 }
488 if (defaults == null) {
489 defaults = {};
490 }
491 opts = angular.extend({}, defaults, {
492 position: defaults.position != null ? defaults.position : getCoords(coords),
493 icon: defaults.icon != null ? defaults.icon : icon,
494 visible: defaults.visible != null ? defaults.visible : validateCoords(coords)
495 });
496 if (map != null) {
497 opts.map = map;
498 }
499 return opts;
500 },
501 createWindowOptions: function(gMarker, scope, content, defaults) {
502 if ((content != null) && (defaults != null) && ($compile != null)) {
503 return angular.extend({}, defaults, {
504 content: this.buildContent(scope, defaults, content),
505 position: defaults.position != null ? defaults.position : angular.isObject(gMarker) ? gMarker.getPosition() : getCoords(scope.coords)
506 });
507 } else {
508 if (!defaults) {
509 Logger.error("infoWindow defaults not defined");
510 if (!content) {
511 return Logger.error("infoWindow content not defined");
512 }
513 } else {
514 return defaults;
515 }
516 }
517 },
518 buildContent: function(scope, defaults, content) {
519 var parsed, ret;
520 if (defaults.content != null) {
521 ret = defaults.content;
522 } else {
523 if ($compile != null) {
524 parsed = $compile(content)(scope);
525 if (parsed.length > 0) {
526 ret = parsed[0];
527 }
528 } else {
529 ret = content;
530 }
531 }
532 return ret;
533 },
534 defaultDelay: 50,
535 isTrue: function(val) {
536 return angular.isDefined(val) && val !== null && val === true || val === "1" || val === "y" || val === "true";
537 },
538 isFalse: function(value) {
539 return ['false', 'FALSE', 0, 'n', 'N', 'no', 'NO'].indexOf(value) !== -1;
540 },
541 getCoords: getCoords,
542 validateCoords: validateCoords,
543 validatePath: function(path) {
544 var array, i, polygon, trackMaxVertices;
545 i = 0;
546 if (angular.isUndefined(path.type)) {
547 if (!Array.isArray(path) || path.length < 2) {
548 return false;
549 }
550 while (i < path.length) {
551 if (!((angular.isDefined(path[i].latitude) && angular.isDefined(path[i].longitude)) || (typeof path[i].lat === "function" && typeof path[i].lng === "function"))) {
552 return false;
553 }
554 i++;
555 }
556 return true;
557 } else {
558 if (angular.isUndefined(path.coordinates)) {
559 return false;
560 }
561 if (path.type === "Polygon") {
562 if (path.coordinates[0].length < 4) {
563 return false;
564 }
565 array = path.coordinates[0];
566 } else if (path.type === "MultiPolygon") {
567 trackMaxVertices = {
568 max: 0,
569 index: 0
570 };
571 _.forEach(path.coordinates, function(polygon, index) {
572 if (polygon[0].length > this.max) {
573 this.max = polygon[0].length;
574 return this.index = index;
575 }
576 }, trackMaxVertices);
577 polygon = path.coordinates[trackMaxVertices.index];
578 array = polygon[0];
579 if (array.length < 4) {
580 return false;
581 }
582 } else if (path.type === "LineString") {
583 if (path.coordinates.length < 2) {
584 return false;
585 }
586 array = path.coordinates;
587 } else {
588 return false;
589 }
590 while (i < array.length) {
591 if (array[i].length !== 2) {
592 return false;
593 }
594 i++;
595 }
596 return true;
597 }
598 },
599 convertPathPoints: function(path) {
600 var array, i, latlng, result, trackMaxVertices;
601 i = 0;
602 result = new google.maps.MVCArray();
603 if (angular.isUndefined(path.type)) {
604 while (i < path.length) {
605 latlng;
606 if (angular.isDefined(path[i].latitude) && angular.isDefined(path[i].longitude)) {
607 latlng = new google.maps.LatLng(path[i].latitude, path[i].longitude);
608 } else if (typeof path[i].lat === "function" && typeof path[i].lng === "function") {
609 latlng = path[i];
610 }
611 result.push(latlng);
612 i++;
613 }
614 } else {
615 array;
616 if (path.type === "Polygon") {
617 array = path.coordinates[0];
618 } else if (path.type === "MultiPolygon") {
619 trackMaxVertices = {
620 max: 0,
621 index: 0
622 };
623 _.forEach(path.coordinates, function(polygon, index) {
624 if (polygon[0].length > this.max) {
625 this.max = polygon[0].length;
626 return this.index = index;
627 }
628 }, trackMaxVertices);
629 array = path.coordinates[trackMaxVertices.index][0];
630 } else if (path.type === "LineString") {
631 array = path.coordinates;
632 }
633 while (i < array.length) {
634 result.push(new google.maps.LatLng(array[i][1], array[i][0]));
635 i++;
636 }
637 }
638 return result;
639 },
640 extendMapBounds: function(map, points) {
641 var bounds, i;
642 bounds = new google.maps.LatLngBounds();
643 i = 0;
644 while (i < points.length) {
645 bounds.extend(points.getAt(i));
646 i++;
647 }
648 return map.fitBounds(bounds);
649 }
650 };
651 }
652 ]);
653
654 }).call(this);
655
656 (function() {
657 var __hasProp = {}.hasOwnProperty,
658 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
659
660 angular.module("google-maps.directives.api.utils").factory("Linked", [
661 "BaseObject", function(BaseObject) {
662 var Linked;
663 Linked = (function(_super) {
664 __extends(Linked, _super);
665
666 function Linked(scope, element, attrs, ctrls) {
667 this.scope = scope;
668 this.element = element;
669 this.attrs = attrs;
670 this.ctrls = ctrls;
671 }
672
673 return Linked;
674
675 })(BaseObject);
676 return Linked;
677 }
678 ]);
679
680 }).call(this);
681
682 (function() {
683 angular.module("google-maps.directives.api.utils").service("Logger", [
684 "$log", function($log) {
685 return {
686 logger: $log,
687 doLog: false,
688 info: function(msg) {
689 if (this.doLog) {
690 if (this.logger != null) {
691 return this.logger.info(msg);
692 } else {
693 return console.info(msg);
694 }
695 }
696 },
697 error: function(msg) {
698 if (this.doLog) {
699 if (this.logger != null) {
700 return this.logger.error(msg);
701 } else {
702 return console.error(msg);
703 }
704 }
705 },
706 warn: function(msg) {
707 if (this.doLog) {
708 if (this.logger != null) {
709 return this.logger.warn(msg);
710 } else {
711 return console.warn(msg);
712 }
713 }
714 }
715 };
716 }
717 ]);
718
719 }).call(this);
720
721 (function() {
722 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
723 __hasProp = {}.hasOwnProperty,
724 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
725
726 angular.module("google-maps.directives.api.utils").factory("ModelKey", [
727 "BaseObject", function(BaseObject) {
728 var ModelKey;
729 return ModelKey = (function(_super) {
730 __extends(ModelKey, _super);
731
732 function ModelKey(scope) {
733 this.scope = scope;
734 this.setIdKey = __bind(this.setIdKey, this);
735 this.modelKeyComparison = __bind(this.modelKeyComparison, this);
736 ModelKey.__super__.constructor.call(this);
737 this.defaultIdKey = "id";
738 this.idKey = void 0;
739 }
740
741 ModelKey.prototype.evalModelHandle = function(model, modelKey) {
742 if (model === void 0) {
743 return void 0;
744 }
745 if (modelKey === 'self') {
746 return model;
747 } else {
748 return model[modelKey];
749 }
750 };
751
752 ModelKey.prototype.modelKeyComparison = function(model1, model2) {
753 var scope;
754 scope = this.scope.coords != null ? this.scope : this.parentScope;
755 if (scope == null) {
756 throw "No scope or parentScope set!";
757 }
758 return this.evalModelHandle(model1, scope.coords).latitude === this.evalModelHandle(model2, scope.coords).latitude && this.evalModelHandle(model1, scope.coords).longitude === this.evalModelHandle(model2, scope.coords).longitude;
759 };
760
761 ModelKey.prototype.setIdKey = function(scope) {
762 return this.idKey = scope.idKey != null ? scope.idKey : this.defaultIdKey;
763 };
764
765 return ModelKey;
766
767 })(BaseObject);
768 }
769 ]);
770
771 }).call(this);
772
773 (function() {
774 angular.module("google-maps.directives.api.utils").factory("ModelsWatcher", [
775 "Logger", function(Logger) {
776 return {
777 figureOutState: function(idKey, scope, childObjects, comparison, callBack) {
778 var adds, mappedScopeModelIds, removals,
779 _this = this;
780 adds = [];
781 mappedScopeModelIds = {};
782 removals = [];
783 return _async.each(scope.models, function(m) {
784 var child;
785 if (m[idKey] != null) {
786 mappedScopeModelIds[m[idKey]] = {};
787 if (childObjects[m[idKey]] == null) {
788 return adds.push(m);
789 } else {
790 child = childObjects[m[idKey]];
791 if (!comparison(m, child.model)) {
792 adds.push(m);
793 return removals.push(child.model);
794 }
795 }
796 } else {
797 return Logger.error("id missing for model " + (m.toString()) + ", can not use do comparison/insertion");
798 }
799 }, function() {
800 return _async.each(childObjects.values(), function(c) {
801 var id;
802 if (c == null) {
803 Logger.error("child undefined in ModelsWatcher.");
804 return;
805 }
806 if (c.model == null) {
807 Logger.error("child.model undefined in ModelsWatcher.");
808 return;
809 }
810 id = c.model[idKey];
811 if (mappedScopeModelIds[id] == null) {
812 return removals.push(c.model[idKey]);
813 }
814 }, function() {
815 return callBack({
816 adds: adds,
817 removals: removals
818 });
819 });
820 });
821 }
822 };
823 }
824 ]);
825
826 }).call(this);
827
828 /*
829 Simple Object Map with a lenght property to make it easy to track length/size
830 */
831
832
833 (function() {
834 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
835
836 angular.module("google-maps.directives.api.utils").factory("PropMap", function() {
837 var PropMap, propsToPop;
838 propsToPop = ['get', 'put', 'remove', 'values', 'keys', 'length'];
839 PropMap = (function() {
840 function PropMap() {
841 this.keys = __bind(this.keys, this);
842 this.values = __bind(this.values, this);
843 this.remove = __bind(this.remove, this);
844 this.put = __bind(this.put, this);
845 this.get = __bind(this.get, this);
846 this.length = 0;
847 }
848
849 PropMap.prototype.get = function(key) {
850 return this[key];
851 };
852
853 PropMap.prototype.put = function(key, value) {
854 if (this[key] == null) {
855 this.length++;
856 }
857 return this[key] = value;
858 };
859
860 PropMap.prototype.remove = function(key) {
861 delete this[key];
862 return this.length--;
863 };
864
865 PropMap.prototype.values = function() {
866 var all, keys,
867 _this = this;
868 all = [];
869 keys = _.keys(this);
870 _.each(keys, function(value) {
871 if (_.indexOf(propsToPop, value) === -1) {
872 return all.push(_this[value]);
873 }
874 });
875 return all;
876 };
877
878 PropMap.prototype.keys = function() {
879 var all, keys,
880 _this = this;
881 keys = _.keys(this);
882 all = [];
883 _.each(keys, function(prop) {
884 if (_.indexOf(propsToPop, prop) === -1) {
885 return all.push(prop);
886 }
887 });
888 return all;
889 };
890
891 return PropMap;
892
893 })();
894 return PropMap;
895 });
896
897 }).call(this);
898
899 (function() {
900 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
901 __hasProp = {}.hasOwnProperty,
902 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
903
904 angular.module("google-maps.directives.api.managers").factory("ClustererMarkerManager", [
905 "Logger", "FitHelper", function($log, FitHelper) {
906 var ClustererMarkerManager;
907 ClustererMarkerManager = (function(_super) {
908 __extends(ClustererMarkerManager, _super);
909
910 function ClustererMarkerManager(gMap, opt_markers, opt_options, opt_events) {
911 var self;
912 this.opt_events = opt_events;
913 this.fit = __bind(this.fit, this);
914 this.destroy = __bind(this.destroy, this);
915 this.clear = __bind(this.clear, this);
916 this.draw = __bind(this.draw, this);
917 this.removeMany = __bind(this.removeMany, this);
918 this.remove = __bind(this.remove, this);
919 this.addMany = __bind(this.addMany, this);
920 this.add = __bind(this.add, this);
921 ClustererMarkerManager.__super__.constructor.call(this);
922 self = this;
923 this.opt_options = opt_options;
924 if ((opt_options != null) && opt_markers === void 0) {
925 this.clusterer = new MarkerClusterer(gMap, void 0, opt_options);
926 } else if ((opt_options != null) && (opt_markers != null)) {
927 this.clusterer = new MarkerClusterer(gMap, opt_markers, opt_options);
928 } else {
929 this.clusterer = new MarkerClusterer(gMap);
930 }
931 this.attachEvents(this.opt_events, "opt_events");
932 this.clusterer.setIgnoreHidden(true);
933 this.noDrawOnSingleAddRemoves = true;
934 $log.info(this);
935 }
936
937 ClustererMarkerManager.prototype.add = function(gMarker) {
938 return this.clusterer.addMarker(gMarker, this.noDrawOnSingleAddRemoves);
939 };
940
941 ClustererMarkerManager.prototype.addMany = function(gMarkers) {
942 return this.clusterer.addMarkers(gMarkers);
943 };
944
945 ClustererMarkerManager.prototype.remove = function(gMarker) {
946 return this.clusterer.removeMarker(gMarker, this.noDrawOnSingleAddRemoves);
947 };
948
949 ClustererMarkerManager.prototype.removeMany = function(gMarkers) {
950 return this.clusterer.addMarkers(gMarkers);
951 };
952
953 ClustererMarkerManager.prototype.draw = function() {
954 return this.clusterer.repaint();
955 };
956
957 ClustererMarkerManager.prototype.clear = function() {
958 this.clusterer.clearMarkers();
959 return this.clusterer.repaint();
960 };
961
962 ClustererMarkerManager.prototype.attachEvents = function(options, optionsName) {
963 var eventHandler, eventName, _results;
964 if (angular.isDefined(options) && (options != null) && angular.isObject(options)) {
965 _results = [];
966 for (eventName in options) {
967 eventHandler = options[eventName];
968 if (options.hasOwnProperty(eventName) && angular.isFunction(options[eventName])) {
969 $log.info("" + optionsName + ": Attaching event: " + eventName + " to clusterer");
970 _results.push(google.maps.event.addListener(this.clusterer, eventName, options[eventName]));
971 } else {
972 _results.push(void 0);
973 }
974 }
975 return _results;
976 }
977 };
978
979 ClustererMarkerManager.prototype.clearEvents = function(options) {
980 var eventHandler, eventName, _results;
981 if (angular.isDefined(options) && (options != null) && angular.isObject(options)) {
982 _results = [];
983 for (eventName in options) {
984 eventHandler = options[eventName];
985 if (options.hasOwnProperty(eventName) && angular.isFunction(options[eventName])) {
986 $log.info("" + optionsName + ": Clearing event: " + eventName + " to clusterer");
987 _results.push(google.maps.event.clearListeners(this.clusterer, eventName));
988 } else {
989 _results.push(void 0);
990 }
991 }
992 return _results;
993 }
994 };
995
996 ClustererMarkerManager.prototype.destroy = function() {
997 this.clearEvents(this.opt_events);
998 this.clearEvents(this.opt_internal_events);
999 return this.clear();
1000 };
1001
1002 ClustererMarkerManager.prototype.fit = function() {
1003 return ClustererMarkerManager.__super__.fit.call(this, this.clusterer.getMarkers(), this.clusterer.getMap());
1004 };
1005
1006 return ClustererMarkerManager;
1007
1008 })(FitHelper);
1009 return ClustererMarkerManager;
1010 }
1011 ]);
1012
1013 }).call(this);
1014
1015 (function() {
1016 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
1017 __hasProp = {}.hasOwnProperty,
1018 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
1019
1020 angular.module("google-maps.directives.api.managers").factory("MarkerManager", [
1021 "Logger", "FitHelper", function(Logger, FitHelper) {
1022 var MarkerManager;
1023 MarkerManager = (function(_super) {
1024 __extends(MarkerManager, _super);
1025
1026 MarkerManager.include(FitHelper);
1027
1028 function MarkerManager(gMap, opt_markers, opt_options) {
1029 this.fit = __bind(this.fit, this);
1030 this.handleOptDraw = __bind(this.handleOptDraw, this);
1031 this.clear = __bind(this.clear, this);
1032 this.draw = __bind(this.draw, this);
1033 this.removeMany = __bind(this.removeMany, this);
1034 this.remove = __bind(this.remove, this);
1035 this.addMany = __bind(this.addMany, this);
1036 this.add = __bind(this.add, this);
1037 var self;
1038 MarkerManager.__super__.constructor.call(this);
1039 self = this;
1040 this.gMap = gMap;
1041 this.gMarkers = [];
1042 this.$log = Logger;
1043 this.$log.info(this);
1044 }
1045
1046 MarkerManager.prototype.add = function(gMarker, optDraw, redraw) {
1047 if (redraw == null) {
1048 redraw = true;
1049 }
1050 this.handleOptDraw(gMarker, optDraw, redraw);
1051 return this.gMarkers.push(gMarker);
1052 };
1053
1054 MarkerManager.prototype.addMany = function(gMarkers) {
1055 var gMarker, _i, _len, _results;
1056 _results = [];
1057 for (_i = 0, _len = gMarkers.length; _i < _len; _i++) {
1058 gMarker = gMarkers[_i];
1059 _results.push(this.add(gMarker));
1060 }
1061 return _results;
1062 };
1063
1064 MarkerManager.prototype.remove = function(gMarker, optDraw) {
1065 var index, tempIndex;
1066 this.handleOptDraw(gMarker, optDraw, false);
1067 if (!optDraw) {
1068 return;
1069 }
1070 index = void 0;
1071 if (this.gMarkers.indexOf != null) {
1072 index = this.gMarkers.indexOf(gMarker);
1073 } else {
1074 tempIndex = 0;
1075 _.find(this.gMarkers, function(marker) {
1076 tempIndex += 1;
1077 if (marker === gMarker) {
1078 index = tempIndex;
1079 }
1080 });
1081 }
1082 if (index != null) {
1083 return this.gMarkers.splice(index, 1);
1084 }
1085 };
1086
1087 MarkerManager.prototype.removeMany = function(gMarkers) {
1088 var _this = this;
1089 return this.gMarkers.forEach(function(marker) {
1090 return _this.remove(marker);
1091 });
1092 };
1093
1094 MarkerManager.prototype.draw = function() {
1095 var deletes,
1096 _this = this;
1097 deletes = [];
1098 this.gMarkers.forEach(function(gMarker) {
1099 if (!gMarker.isDrawn) {
1100 if (gMarker.doAdd) {
1101 gMarker.setMap(_this.gMap);
1102 return gMarker.isDrawn = true;
1103 } else {
1104 return deletes.push(gMarker);
1105 }
1106 }
1107 });
1108 return deletes.forEach(function(gMarker) {
1109 gMarker.isDrawn = false;
1110 return _this.remove(gMarker, true);
1111 });
1112 };
1113
1114 MarkerManager.prototype.clear = function() {
1115 var gMarker, _i, _len, _ref;
1116 _ref = this.gMarkers;
1117 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1118 gMarker = _ref[_i];
1119 gMarker.setMap(null);
1120 }
1121 delete this.gMarkers;
1122 return this.gMarkers = [];
1123 };
1124
1125 MarkerManager.prototype.handleOptDraw = function(gMarker, optDraw, doAdd) {
1126 if (optDraw === true) {
1127 if (doAdd) {
1128 gMarker.setMap(this.gMap);
1129 } else {
1130 gMarker.setMap(null);
1131 }
1132 return gMarker.isDrawn = true;
1133 } else {
1134 gMarker.isDrawn = false;
1135 return gMarker.doAdd = doAdd;
1136 }
1137 };
1138
1139 MarkerManager.prototype.fit = function() {
1140 return MarkerManager.__super__.fit.call(this, this.gMarkers, this.gMap);
1141 };
1142
1143 return MarkerManager;
1144
1145 })(FitHelper);
1146 return MarkerManager;
1147 }
1148 ]);
1149
1150 }).call(this);
1151
1152 (function() {
1153 angular.module("google-maps").factory("array-sync", [
1154 "add-events", function(mapEvents) {
1155 return function(mapArray, scope, pathEval, pathChangedFn) {
1156 var geojsonArray, geojsonHandlers, geojsonWatcher, isSetFromScope, legacyHandlers, legacyWatcher, mapArrayListener, scopePath, watchListener;
1157 isSetFromScope = false;
1158 scopePath = scope.$eval(pathEval);
1159 if (!scope["static"]) {
1160 legacyHandlers = {
1161 set_at: function(index) {
1162 var value;
1163 if (isSetFromScope) {
1164 return;
1165 }
1166 value = mapArray.getAt(index);
1167 if (!value) {
1168 return;
1169 }
1170 if (!value.lng || !value.lat) {
1171 return scopePath[index] = value;
1172 } else {
1173 scopePath[index].latitude = value.lat();
1174 return scopePath[index].longitude = value.lng();
1175 }
1176 },
1177 insert_at: function(index) {
1178 var value;
1179 if (isSetFromScope) {
1180 return;
1181 }
1182 value = mapArray.getAt(index);
1183 if (!value) {
1184 return;
1185 }
1186 if (!value.lng || !value.lat) {
1187 return scopePath.splice(index, 0, value);
1188 } else {
1189 return scopePath.splice(index, 0, {
1190 latitude: value.lat(),
1191 longitude: value.lng()
1192 });
1193 }
1194 },
1195 remove_at: function(index) {
1196 if (isSetFromScope) {
1197 return;
1198 }
1199 return scopePath.splice(index, 1);
1200 }
1201 };
1202 geojsonArray;
1203 if (scopePath.type === "Polygon") {
1204 geojsonArray = scopePath.coordinates[0];
1205 } else if (scopePath.type === "LineString") {
1206 geojsonArray = scopePath.coordinates;
1207 }
1208 geojsonHandlers = {
1209 set_at: function(index) {
1210 var value;
1211 if (isSetFromScope) {
1212 return;
1213 }
1214 value = mapArray.getAt(index);
1215 if (!value) {
1216 return;
1217 }
1218 if (!value.lng || !value.lat) {
1219 return;
1220 }
1221 geojsonArray[index][1] = value.lat();
1222 return geojsonArray[index][0] = value.lng();
1223 },
1224 insert_at: function(index) {
1225 var value;
1226 if (isSetFromScope) {
1227 return;
1228 }
1229 value = mapArray.getAt(index);
1230 if (!value) {
1231 return;
1232 }
1233 if (!value.lng || !value.lat) {
1234 return;
1235 }
1236 return geojsonArray.splice(index, 0, [value.lng(), value.lat()]);
1237 },
1238 remove_at: function(index) {
1239 if (isSetFromScope) {
1240 return;
1241 }
1242 return geojsonArray.splice(index, 1);
1243 }
1244 };
1245 mapArrayListener = mapEvents(mapArray, angular.isUndefined(scopePath.type) ? legacyHandlers : geojsonHandlers);
1246 }
1247 legacyWatcher = function(newPath) {
1248 var i, l, newLength, newValue, oldArray, oldLength, oldValue;
1249 isSetFromScope = true;
1250 oldArray = mapArray;
1251 if (newPath) {
1252 i = 0;
1253 oldLength = oldArray.getLength();
1254 newLength = newPath.length;
1255 l = Math.min(oldLength, newLength);
1256 newValue = void 0;
1257 while (i < l) {
1258 oldValue = oldArray.getAt(i);
1259 newValue = newPath[i];
1260 if (typeof newValue.equals === "function") {
1261 if (!newValue.equals(oldValue)) {
1262 oldArray.setAt(i, newValue);
1263 }
1264 } else {
1265 if ((oldValue.lat() !== newValue.latitude) || (oldValue.lng() !== newValue.longitude)) {
1266 oldArray.setAt(i, new google.maps.LatLng(newValue.latitude, newValue.longitude));
1267 }
1268 }
1269 i++;
1270 }
1271 while (i < newLength) {
1272 newValue = newPath[i];
1273 if (typeof newValue.lat === "function" && typeof newValue.lng === "function") {
1274 oldArray.push(newValue);
1275 } else {
1276 oldArray.push(new google.maps.LatLng(newValue.latitude, newValue.longitude));
1277 }
1278 i++;
1279 }
1280 while (i < oldLength) {
1281 oldArray.pop();
1282 i++;
1283 }
1284 }
1285 return isSetFromScope = false;
1286 };
1287 geojsonWatcher = function(newPath) {
1288 var array, i, l, newLength, newValue, oldArray, oldLength, oldValue;
1289 isSetFromScope = true;
1290 oldArray = mapArray;
1291 if (newPath) {
1292 array;
1293 if (scopePath.type === "Polygon") {
1294 array = newPath.coordinates[0];
1295 } else if (scopePath.type === "LineString") {
1296 array = newPath.coordinates;
1297 }
1298 i = 0;
1299 oldLength = oldArray.getLength();
1300 newLength = array.length;
1301 l = Math.min(oldLength, newLength);
1302 newValue = void 0;
1303 while (i < l) {
1304 oldValue = oldArray.getAt(i);
1305 newValue = array[i];
1306 if ((oldValue.lat() !== newValue[1]) || (oldValue.lng() !== newValue[0])) {
1307 oldArray.setAt(i, new google.maps.LatLng(newValue[1], newValue[0]));
1308 }
1309 i++;
1310 }
1311 while (i < newLength) {
1312 newValue = array[i];
1313 oldArray.push(new google.maps.LatLng(newValue[1], newValue[0]));
1314 i++;
1315 }
1316 while (i < oldLength) {
1317 oldArray.pop();
1318 i++;
1319 }
1320 }
1321 return isSetFromScope = false;
1322 };
1323 watchListener;
1324 if (!scope["static"]) {
1325 if (angular.isUndefined(scopePath.type)) {
1326 watchListener = scope.$watchCollection(pathEval, legacyWatcher);
1327 } else {
1328 watchListener = scope.$watch(pathEval, geojsonWatcher, true);
1329 }
1330 }
1331 return function() {
1332 if (mapArrayListener) {
1333 mapArrayListener();
1334 mapArrayListener = null;
1335 }
1336 if (watchListener) {
1337 watchListener();
1338 return watchListener = null;
1339 }
1340 };
1341 };
1342 }
1343 ]);
1344
1345 }).call(this);
1346
1347 (function() {
1348 angular.module("google-maps").factory("add-events", [
1349 "$timeout", function($timeout) {
1350 var addEvent, addEvents;
1351 addEvent = function(target, eventName, handler) {
1352 return google.maps.event.addListener(target, eventName, function() {
1353 handler.apply(this, arguments);
1354 return $timeout((function() {}), true);
1355 });
1356 };
1357 addEvents = function(target, eventName, handler) {
1358 var remove;
1359 if (handler) {
1360 return addEvent(target, eventName, handler);
1361 }
1362 remove = [];
1363 angular.forEach(eventName, function(_handler, key) {
1364 return remove.push(addEvent(target, key, _handler));
1365 });
1366 return function() {
1367 angular.forEach(remove, function(listener) {
1368 return google.maps.event.removeListener(listener);
1369 });
1370 return remove = null;
1371 };
1372 };
1373 return addEvents;
1374 }
1375 ]);
1376
1377 }).call(this);
1378
1379 (function() {
1380 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
1381 __hasProp = {}.hasOwnProperty,
1382 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
1383
1384 angular.module("google-maps.directives.api.models.child").factory("MarkerLabelChildModel", [
1385 "BaseObject", "GmapUtil", function(BaseObject, GmapUtil) {
1386 var MarkerLabelChildModel;
1387 MarkerLabelChildModel = (function(_super) {
1388 __extends(MarkerLabelChildModel, _super);
1389
1390 MarkerLabelChildModel.include(GmapUtil);
1391
1392 function MarkerLabelChildModel(gMarker, opt_options) {
1393 this.destroy = __bind(this.destroy, this);
1394 this.draw = __bind(this.draw, this);
1395 this.setPosition = __bind(this.setPosition, this);
1396 this.setZIndex = __bind(this.setZIndex, this);
1397 this.setVisible = __bind(this.setVisible, this);
1398 this.setAnchor = __bind(this.setAnchor, this);
1399 this.setMandatoryStyles = __bind(this.setMandatoryStyles, this);
1400 this.setStyles = __bind(this.setStyles, this);
1401 this.setContent = __bind(this.setContent, this);
1402 this.setTitle = __bind(this.setTitle, this);
1403 this.getSharedCross = __bind(this.getSharedCross, this);
1404 var self, _ref, _ref1;
1405 MarkerLabelChildModel.__super__.constructor.call(this);
1406 self = this;
1407 this.marker = gMarker;
1408 this.marker.set("labelContent", opt_options.labelContent);
1409 this.marker.set("labelAnchor", this.getLabelPositionPoint(opt_options.labelAnchor));
1410 this.marker.set("labelClass", opt_options.labelClass || 'labels');
1411 this.marker.set("labelStyle", opt_options.labelStyle || {
1412 opacity: 100
1413 });
1414 this.marker.set("labelInBackground", opt_options.labelInBackground || false);
1415 if (!opt_options.labelVisible) {
1416 this.marker.set("labelVisible", true);
1417 }
1418 if (!opt_options.raiseOnDrag) {
1419 this.marker.set("raiseOnDrag", true);
1420 }
1421 if (!opt_options.clickable) {
1422 this.marker.set("clickable", true);
1423 }
1424 if (!opt_options.draggable) {
1425 this.marker.set("draggable", false);
1426 }
1427 if (!opt_options.optimized) {
1428 this.marker.set("optimized", false);
1429 }
1430 opt_options.crossImage = (_ref = opt_options.crossImage) != null ? _ref : document.location.protocol + "//maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png";
1431 opt_options.handCursor = (_ref1 = opt_options.handCursor) != null ? _ref1 : document.location.protocol + "//maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur";
1432 this.markerLabel = new MarkerLabel_(this.marker, opt_options.crossImage, opt_options.handCursor);
1433 this.marker.set("setMap", function(theMap) {
1434 google.maps.Marker.prototype.setMap.apply(this, arguments);
1435 return self.markerLabel.setMap(theMap);
1436 });
1437 this.marker.setMap(this.marker.getMap());
1438 }
1439
1440 MarkerLabelChildModel.prototype.getSharedCross = function(crossUrl) {
1441 return this.markerLabel.getSharedCross(crossUrl);
1442 };
1443
1444 MarkerLabelChildModel.prototype.setTitle = function() {
1445 return this.markerLabel.setTitle();
1446 };
1447
1448 MarkerLabelChildModel.prototype.setContent = function() {
1449 return this.markerLabel.setContent();
1450 };
1451
1452 MarkerLabelChildModel.prototype.setStyles = function() {
1453 return this.markerLabel.setStyles();
1454 };
1455
1456 MarkerLabelChildModel.prototype.setMandatoryStyles = function() {
1457 return this.markerLabel.setMandatoryStyles();
1458 };
1459
1460 MarkerLabelChildModel.prototype.setAnchor = function() {
1461 return this.markerLabel.setAnchor();
1462 };
1463
1464 MarkerLabelChildModel.prototype.setVisible = function() {
1465 return this.markerLabel.setVisible();
1466 };
1467
1468 MarkerLabelChildModel.prototype.setZIndex = function() {
1469 return this.markerLabel.setZIndex();
1470 };
1471
1472 MarkerLabelChildModel.prototype.setPosition = function() {
1473 return this.markerLabel.setPosition();
1474 };
1475
1476 MarkerLabelChildModel.prototype.draw = function() {
1477 return this.markerLabel.draw();
1478 };
1479
1480 MarkerLabelChildModel.prototype.destroy = function() {
1481 if ((this.markerLabel.labelDiv_.parentNode != null) && (this.markerLabel.eventDiv_.parentNode != null)) {
1482 return this.markerLabel.onRemove();
1483 }
1484 };
1485
1486 return MarkerLabelChildModel;
1487
1488 })(BaseObject);
1489 return MarkerLabelChildModel;
1490 }
1491 ]);
1492
1493 }).call(this);
1494
1495 (function() {
1496 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
1497 __hasProp = {}.hasOwnProperty,
1498 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
1499
1500 angular.module("google-maps.directives.api.models.child").factory("MarkerChildModel", [
1501 "ModelKey", "GmapUtil", "Logger", "$injector", "EventsHelper", function(ModelKey, GmapUtil, Logger, $injector, EventsHelper) {
1502 var MarkerChildModel;
1503 MarkerChildModel = (function(_super) {
1504 __extends(MarkerChildModel, _super);
1505
1506 MarkerChildModel.include(GmapUtil);
1507
1508 MarkerChildModel.include(EventsHelper);
1509
1510 function MarkerChildModel(model, parentScope, gMap, $timeout, defaults, doClick, gMarkerManager, idKey) {
1511 var self,
1512 _this = this;
1513 this.model = model;
1514 this.parentScope = parentScope;
1515 this.gMap = gMap;
1516 this.$timeout = $timeout;
1517 this.defaults = defaults;
1518 this.doClick = doClick;
1519 this.gMarkerManager = gMarkerManager;
1520 this.idKey = idKey;
1521 this.watchDestroy = __bind(this.watchDestroy, this);
1522 this.setLabelOptions = __bind(this.setLabelOptions, this);
1523 this.isLabelDefined = __bind(this.isLabelDefined, this);
1524 this.setOptions = __bind(this.setOptions, this);
1525 this.setIcon = __bind(this.setIcon, this);
1526 this.setCoords = __bind(this.setCoords, this);
1527 this.destroy = __bind(this.destroy, this);
1528 this.maybeSetScopeValue = __bind(this.maybeSetScopeValue, this);
1529 this.createMarker = __bind(this.createMarker, this);
1530 this.setMyScope = __bind(this.setMyScope, this);
1531 self = this;
1532 if (this.model[this.idKey]) {
1533 this.id = this.model[this.idKey];
1534 }
1535 this.iconKey = this.parentScope.icon;
1536 this.coordsKey = this.parentScope.coords;
1537 this.clickKey = this.parentScope.click();
1538 this.labelContentKey = this.parentScope.labelContent;
1539 this.optionsKey = this.parentScope.options;
1540 this.labelOptionsKey = this.parentScope.labelOptions;
1541 MarkerChildModel.__super__.constructor.call(this, this.parentScope.$new(false));
1542 this.scope.model = this.model;
1543 this.setMyScope(this.model, void 0, true);
1544 this.createMarker(this.model);
1545 this.scope.$watch('model', function(newValue, oldValue) {
1546 if (newValue !== oldValue) {
1547 return _this.setMyScope(newValue, oldValue);
1548 }
1549 }, true);
1550 this.$log = Logger;
1551 this.$log.info(self);
1552 this.watchDestroy(this.scope);
1553 }
1554
1555 MarkerChildModel.prototype.setMyScope = function(model, oldModel, isInit) {
1556 var _this = this;
1557 if (oldModel == null) {
1558 oldModel = void 0;
1559 }
1560 if (isInit == null) {
1561 isInit = false;
1562 }
1563 this.maybeSetScopeValue('icon', model, oldModel, this.iconKey, this.evalModelHandle, isInit, this.setIcon);
1564 this.maybeSetScopeValue('coords', model, oldModel, this.coordsKey, this.evalModelHandle, isInit, this.setCoords);
1565 this.maybeSetScopeValue('labelContent', model, oldModel, this.labelContentKey, this.evalModelHandle, isInit);
1566 if (_.isFunction(this.clickKey) && $injector) {
1567 return this.scope.click = function() {
1568 return $injector.invoke(_this.clickKey, void 0, {
1569 "$markerModel": model
1570 });
1571 };
1572 } else {
1573 this.maybeSetScopeValue('click', model, oldModel, this.clickKey, this.evalModelHandle, isInit);
1574 return this.createMarker(model, oldModel, isInit);
1575 }
1576 };
1577
1578 MarkerChildModel.prototype.createMarker = function(model, oldModel, isInit) {
1579 var _this = this;
1580 if (oldModel == null) {
1581 oldModel = void 0;
1582 }
1583 if (isInit == null) {
1584 isInit = false;
1585 }
1586 return this.maybeSetScopeValue('options', model, oldModel, this.optionsKey, function(lModel, lModelKey) {
1587 var value;
1588 if (lModel === void 0) {
1589 return void 0;
1590 }
1591 value = lModelKey === 'self' ? lModel : lModel[lModelKey];
1592 if (value === void 0) {
1593 return value = lModelKey === void 0 ? _this.defaults : _this.scope.options;
1594 } else {
1595 return value;
1596 }
1597 }, isInit, this.setOptions);
1598 };
1599
1600 MarkerChildModel.prototype.maybeSetScopeValue = function(scopePropName, model, oldModel, modelKey, evaluate, isInit, gSetter) {
1601 var newValue, oldVal;
1602 if (gSetter == null) {
1603 gSetter = void 0;
1604 }
1605 if (oldModel === void 0) {
1606 this.scope[scopePropName] = evaluate(model, modelKey);
1607 if (!isInit) {
1608 if (gSetter != null) {
1609 gSetter(this.scope);
1610 }
1611 }
1612 return;
1613 }
1614 oldVal = evaluate(oldModel, modelKey);
1615 newValue = evaluate(model, modelKey);
1616 if (newValue !== oldVal && this.scope[scopePropName] !== newValue) {
1617 this.scope[scopePropName] = newValue;
1618 if (!isInit) {
1619 if (gSetter != null) {
1620 gSetter(this.scope);
1621 }
1622 return this.gMarkerManager.draw();
1623 }
1624 }
1625 };
1626
1627 MarkerChildModel.prototype.destroy = function() {
1628 return this.scope.$destroy();
1629 };
1630
1631 MarkerChildModel.prototype.setCoords = function(scope) {
1632 if (scope.$id !== this.scope.$id || this.gMarker === void 0) {
1633 return;
1634 }
1635 if ((scope.coords != null)) {
1636 if (!this.validateCoords(this.scope.coords)) {
1637 this.$log.error("MarkerChildMarker cannot render marker as scope.coords as no position on marker: " + (JSON.stringify(this.model)));
1638 return;
1639 }
1640 this.gMarker.setPosition(this.getCoords(scope.coords));
1641 this.gMarker.setVisible(this.validateCoords(scope.coords));
1642 this.gMarkerManager.remove(this.gMarker);
1643 return this.gMarkerManager.add(this.gMarker);
1644 } else {
1645 return this.gMarkerManager.remove(this.gMarker);
1646 }
1647 };
1648
1649 MarkerChildModel.prototype.setIcon = function(scope) {
1650 if (scope.$id !== this.scope.$id || this.gMarker === void 0) {
1651 return;
1652 }
1653 this.gMarkerManager.remove(this.gMarker);
1654 this.gMarker.setIcon(scope.icon);
1655 this.gMarkerManager.add(this.gMarker);
1656 this.gMarker.setPosition(this.getCoords(scope.coords));
1657 return this.gMarker.setVisible(this.validateCoords(scope.coords));
1658 };
1659
1660 MarkerChildModel.prototype.setOptions = function(scope) {
1661 var _ref,
1662 _this = this;
1663 if (scope.$id !== this.scope.$id) {
1664 return;
1665 }
1666 if (this.gMarker != null) {
1667 this.gMarkerManager.remove(this.gMarker);
1668 delete this.gMarker;
1669 }
1670 if (!((_ref = scope.coords) != null ? _ref : typeof scope.icon === "function" ? scope.icon(scope.options != null) : void 0)) {
1671 return;
1672 }
1673 this.opts = this.createMarkerOptions(scope.coords, scope.icon, scope.options);
1674 delete this.gMarker;
1675 if (this.isLabelDefined(scope)) {
1676 this.gMarker = new MarkerWithLabel(this.setLabelOptions(this.opts, scope));
1677 } else {
1678 this.gMarker = new google.maps.Marker(this.opts);
1679 }
1680 this.setEvents(this.gMarker, this.parentScope, this.model);
1681 if (this.id) {
1682 this.gMarker.key = this.id;
1683 }
1684 this.gMarkerManager.add(this.gMarker);
1685 return google.maps.event.addListener(this.gMarker, 'click', function() {
1686 if (_this.doClick && (_this.scope.click != null)) {
1687 return _this.scope.click();
1688 }
1689 });
1690 };
1691
1692 MarkerChildModel.prototype.isLabelDefined = function(scope) {
1693 return scope.labelContent != null;
1694 };
1695
1696 MarkerChildModel.prototype.setLabelOptions = function(opts, scope) {
1697 opts.labelAnchor = this.getLabelPositionPoint(scope.labelAnchor);
1698 opts.labelClass = scope.labelClass;
1699 opts.labelContent = scope.labelContent;
1700 return opts;
1701 };
1702
1703 MarkerChildModel.prototype.watchDestroy = function(scope) {
1704 var _this = this;
1705 return scope.$on("$destroy", function() {
1706 var self, _ref;
1707 if (_this.gMarker != null) {
1708 google.maps.event.clearListeners(_this.gMarker, 'click');
1709 if (((_ref = _this.parentScope) != null ? _ref.events : void 0) && _.isArray(_this.parentScope.events)) {
1710 _this.parentScope.events.forEach(function(event, eventName) {
1711 return google.maps.event.clearListeners(this.gMarker, eventName);
1712 });
1713 }
1714 _this.gMarkerManager.remove(_this.gMarker, true);
1715 delete _this.gMarker;
1716 }
1717 return self = void 0;
1718 });
1719 };
1720
1721 return MarkerChildModel;
1722
1723 })(ModelKey);
1724 return MarkerChildModel;
1725 }
1726 ]);
1727
1728 }).call(this);
1729
1730 (function() {
1731 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
1732 __hasProp = {}.hasOwnProperty,
1733 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
1734
1735 angular.module("google-maps.directives.api").factory("PolylineChildModel", [
1736 "BaseObject", "Logger", "$timeout", "array-sync", "GmapUtil", function(BaseObject, Logger, $timeout, arraySync, GmapUtil) {
1737 var $log, PolylineChildModel;
1738 $log = Logger;
1739 return PolylineChildModel = (function(_super) {
1740 __extends(PolylineChildModel, _super);
1741
1742 PolylineChildModel.include(GmapUtil);
1743
1744 function PolylineChildModel(scope, attrs, map, defaults, model) {
1745 var arraySyncer, pathPoints,
1746 _this = this;
1747 this.scope = scope;
1748 this.attrs = attrs;
1749 this.map = map;
1750 this.defaults = defaults;
1751 this.model = model;
1752 this.buildOpts = __bind(this.buildOpts, this);
1753 pathPoints = this.convertPathPoints(scope.path);
1754 this.polyline = new google.maps.Polyline(this.buildOpts(pathPoints));
1755 if (scope.fit) {
1756 GmapUtil.extendMapBounds(map, pathPoints);
1757 }
1758 if (!scope["static"] && angular.isDefined(scope.editable)) {
1759 scope.$watch("editable", function(newValue, oldValue) {
1760 if (newValue !== oldValue) {
1761 return _this.polyline.setEditable(newValue);
1762 }
1763 });
1764 }
1765 if (angular.isDefined(scope.draggable)) {
1766 scope.$watch("draggable", function(newValue, oldValue) {
1767 if (newValue !== oldValue) {
1768 return _this.polyline.setDraggable(newValue);
1769 }
1770 });
1771 }
1772 if (angular.isDefined(scope.visible)) {
1773 scope.$watch("visible", function(newValue, oldValue) {
1774 if (newValue !== oldValue) {
1775 return _this.polyline.setVisible(newValue);
1776 }
1777 });
1778 }
1779 if (angular.isDefined(scope.geodesic)) {
1780 scope.$watch("geodesic", function(newValue, oldValue) {
1781 if (newValue !== oldValue) {
1782 return _this.polyline.setOptions(_this.buildOpts(_this.polyline.getPath()));
1783 }
1784 });
1785 }
1786 if (angular.isDefined(scope.stroke) && angular.isDefined(scope.stroke.weight)) {
1787 scope.$watch("stroke.weight", function(newValue, oldValue) {
1788 if (newValue !== oldValue) {
1789 return _this.polyline.setOptions(_this.buildOpts(_this.polyline.getPath()));
1790 }
1791 });
1792 }
1793 if (angular.isDefined(scope.stroke) && angular.isDefined(scope.stroke.color)) {
1794 scope.$watch("stroke.color", function(newValue, oldValue) {
1795 if (newValue !== oldValue) {
1796 return _this.polyline.setOptions(_this.buildOpts(_this.polyline.getPath()));
1797 }
1798 });
1799 }
1800 if (angular.isDefined(scope.stroke) && angular.isDefined(scope.stroke.opacity)) {
1801 scope.$watch("stroke.opacity", function(newValue, oldValue) {
1802 if (newValue !== oldValue) {
1803 return _this.polyline.setOptions(_this.buildOpts(_this.polyline.getPath()));
1804 }
1805 });
1806 }
1807 if (angular.isDefined(scope.icons)) {
1808 scope.$watch("icons", function(newValue, oldValue) {
1809 if (newValue !== oldValue) {
1810 return _this.polyline.setOptions(_this.buildOpts(_this.polyline.getPath()));
1811 }
1812 });
1813 }
1814 arraySyncer = arraySync(this.polyline.getPath(), scope, "path", function(pathPoints) {
1815 if (scope.fit) {
1816 return GmapUtil.extendMapBounds(map, pathPoints);
1817 }
1818 });
1819 scope.$on("$destroy", function() {
1820 _this.polyline.setMap(null);
1821 _this.polyline = null;
1822 _this.scope = null;
1823 if (arraySyncer) {
1824 arraySyncer();
1825 return arraySyncer = null;
1826 }
1827 });
1828 $log.info(this);
1829 }
1830
1831 PolylineChildModel.prototype.buildOpts = function(pathPoints) {
1832 var opts,
1833 _this = this;
1834 opts = angular.extend({}, this.defaults, {
1835 map: this.map,
1836 path: pathPoints,
1837 icons: this.scope.icons,
1838 strokeColor: this.scope.stroke && this.scope.stroke.color,
1839 strokeOpacity: this.scope.stroke && this.scope.stroke.opacity,
1840 strokeWeight: this.scope.stroke && this.scope.stroke.weight
1841 });
1842 angular.forEach({
1843 clickable: true,
1844 draggable: false,
1845 editable: false,
1846 geodesic: false,
1847 visible: true,
1848 "static": false,
1849 fit: false
1850 }, function(defaultValue, key) {
1851 if (angular.isUndefined(_this.scope[key]) || _this.scope[key] === null) {
1852 return opts[key] = defaultValue;
1853 } else {
1854 return opts[key] = _this.scope[key];
1855 }
1856 });
1857 if (opts["static"]) {
1858 opts.editable = false;
1859 }
1860 return opts;
1861 };
1862
1863 PolylineChildModel.prototype.destroy = function() {
1864 return this.scope.$destroy();
1865 };
1866
1867 return PolylineChildModel;
1868
1869 })(BaseObject);
1870 }
1871 ]);
1872
1873 }).call(this);
1874
1875 (function() {
1876 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
1877 __hasProp = {}.hasOwnProperty,
1878 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
1879
1880 angular.module("google-maps.directives.api.models.child").factory("WindowChildModel", [
1881 "BaseObject", "GmapUtil", "Logger", "$compile", "$http", "$templateCache", function(BaseObject, GmapUtil, Logger, $compile, $http, $templateCache) {
1882 var WindowChildModel;
1883 WindowChildModel = (function(_super) {
1884 __extends(WindowChildModel, _super);
1885
1886 WindowChildModel.include(GmapUtil);
1887
1888 function WindowChildModel(model, scope, opts, isIconVisibleOnClick, mapCtrl, markerCtrl, element, needToManualDestroy, markerIsVisibleAfterWindowClose) {
1889 this.model = model;
1890 this.scope = scope;
1891 this.opts = opts;
1892 this.isIconVisibleOnClick = isIconVisibleOnClick;
1893 this.mapCtrl = mapCtrl;
1894 this.markerCtrl = markerCtrl;
1895 this.element = element;
1896 this.needToManualDestroy = needToManualDestroy != null ? needToManualDestroy : false;
1897 this.markerIsVisibleAfterWindowClose = markerIsVisibleAfterWindowClose != null ? markerIsVisibleAfterWindowClose : true;
1898 this.destroy = __bind(this.destroy, this);
1899 this.remove = __bind(this.remove, this);
1900 this.hideWindow = __bind(this.hideWindow, this);
1901 this.getLatestPosition = __bind(this.getLatestPosition, this);
1902 this.showWindow = __bind(this.showWindow, this);
1903 this.handleClick = __bind(this.handleClick, this);
1904 this.watchCoords = __bind(this.watchCoords, this);
1905 this.watchShow = __bind(this.watchShow, this);
1906 this.createGWin = __bind(this.createGWin, this);
1907 this.watchElement = __bind(this.watchElement, this);
1908 this.googleMapsHandles = [];
1909 this.$log = Logger;
1910 this.createGWin();
1911 if (this.markerCtrl != null) {
1912 this.markerCtrl.setClickable(true);
1913 }
1914 this.handleClick();
1915 this.watchElement();
1916 this.watchShow();
1917 this.watchCoords();
1918 this.$log.info(this);
1919 }
1920
1921 WindowChildModel.prototype.watchElement = function() {
1922 var _this = this;
1923 return this.scope.$watch(function() {
1924 var _ref;
1925 if (!_this.element || !_this.html) {
1926 return;
1927 }
1928 if (_this.html !== _this.element.html()) {
1929 if (_this.gWin) {
1930 if ((_ref = _this.opts) != null) {
1931 _ref.content = void 0;
1932 }
1933 _this.remove();
1934 _this.createGWin();
1935 return _this.showHide();
1936 }
1937 }
1938 });
1939 };
1940
1941 WindowChildModel.prototype.createGWin = function() {
1942 var defaults,
1943 _this = this;
1944 if (this.gWin == null) {
1945 defaults = {};
1946 if (this.opts != null) {
1947 if (this.scope.coords) {
1948 this.opts.position = this.getCoords(this.scope.coords);
1949 }
1950 defaults = this.opts;
1951 }
1952 if (this.element) {
1953 this.html = _.isObject(this.element) ? this.element.html() : this.element;
1954 }
1955 this.opts = this.createWindowOptions(this.markerCtrl, this.scope, this.html, defaults);
1956 }
1957 if ((this.opts != null) && !this.gWin) {
1958 if (this.opts.boxClass && (window.InfoBox && typeof window.InfoBox === 'function')) {
1959 this.gWin = new window.InfoBox(this.opts);
1960 } else {
1961 this.gWin = new google.maps.InfoWindow(this.opts);
1962 }
1963 return this.googleMapsHandles.push(google.maps.event.addListener(this.gWin, 'closeclick', function() {
1964 var _ref;
1965 if ((_ref = _this.markerCtrl) != null) {
1966 _ref.setVisible(_this.markerIsVisibleAfterWindowClose);
1967 }
1968 _this.gWin.isOpen(false);
1969 if (_this.scope.closeClick != null) {
1970 return _this.scope.closeClick();
1971 }
1972 }));
1973 }
1974 };
1975
1976 WindowChildModel.prototype.watchShow = function() {
1977 var _this = this;
1978 return this.scope.$watch('show', function(newValue, oldValue) {
1979 if (newValue !== oldValue) {
1980 if (newValue) {
1981 return _this.showWindow();
1982 } else {
1983 return _this.hideWindow();
1984 }
1985 } else {
1986 if (_this.gWin != null) {
1987 if (newValue && !_this.gWin.getMap()) {
1988 return _this.showWindow();
1989 }
1990 }
1991 }
1992 }, true);
1993 };
1994
1995 WindowChildModel.prototype.watchCoords = function() {
1996 var scope,
1997 _this = this;
1998 scope = this.markerCtrl != null ? this.scope.$parent : this.scope;
1999 return scope.$watch('coords', function(newValue, oldValue) {
2000 var pos;
2001 if (newValue !== oldValue) {
2002 if (newValue == null) {
2003 return _this.hideWindow();
2004 } else {
2005 if (!_this.validateCoords(newValue)) {
2006 _this.$log.error("WindowChildMarker cannot render marker as scope.coords as no position on marker: " + (JSON.stringify(_this.model)));
2007 return;
2008 }
2009 pos = _this.getCoords(newValue);
2010 _this.gWin.setPosition(pos);
2011 if (_this.opts) {
2012 return _this.opts.position = pos;
2013 }
2014 }
2015 }
2016 }, true);
2017 };
2018
2019 WindowChildModel.prototype.handleClick = function() {
2020 var _this = this;
2021 if (this.markerCtrl != null) {
2022 return this.googleMapsHandles.push(google.maps.event.addListener(this.markerCtrl, 'click', function() {
2023 var pos;
2024 if (_this.gWin == null) {
2025 _this.createGWin();
2026 }
2027 pos = _this.markerCtrl.getPosition();
2028 if (_this.gWin != null) {
2029 _this.gWin.setPosition(pos);
2030 if (_this.opts) {
2031 _this.opts.position = pos;
2032 }
2033 _this.showWindow();
2034 }
2035 _this.initialMarkerVisibility = _this.markerCtrl.getVisible();
2036 return _this.markerCtrl.setVisible(_this.isIconVisibleOnClick);
2037 }));
2038 }
2039 };
2040
2041 WindowChildModel.prototype.showWindow = function() {
2042 var show,
2043 _this = this;
2044 show = function() {
2045 if (_this.gWin) {
2046 if ((_this.scope.show || (_this.scope.show == null)) && !_this.gWin.isOpen()) {
2047 return _this.gWin.open(_this.mapCtrl);
2048 }
2049 }
2050 };
2051 if (this.scope.templateUrl) {
2052 if (this.gWin) {
2053 $http.get(this.scope.templateUrl, {
2054 cache: $templateCache
2055 }).then(function(content) {
2056 var compiled, templateScope;
2057 templateScope = _this.scope.$new();
2058 if (angular.isDefined(_this.scope.templateParameter)) {
2059 templateScope.parameter = _this.scope.templateParameter;
2060 }
2061 compiled = $compile(content.data)(templateScope);
2062 return _this.gWin.setContent(compiled[0]);
2063 });
2064 }
2065 return show();
2066 } else {
2067 return show();
2068 }
2069 };
2070
2071 WindowChildModel.prototype.showHide = function() {
2072 if (this.scope.show) {
2073 return this.showWindow();
2074 } else {
2075 return this.hideWindow();
2076 }
2077 };
2078
2079 WindowChildModel.prototype.getLatestPosition = function() {
2080 if ((this.gWin != null) && (this.markerCtrl != null)) {
2081 return this.gWin.setPosition(this.markerCtrl.getPosition());
2082 }
2083 };
2084
2085 WindowChildModel.prototype.hideWindow = function() {
2086 if ((this.gWin != null) && this.gWin.isOpen()) {
2087 return this.gWin.close();
2088 }
2089 };
2090
2091 WindowChildModel.prototype.remove = function() {
2092 this.hideWindow();
2093 _.each(this.googleMapsHandles, function(h) {
2094 return google.maps.event.removeListener(h);
2095 });
2096 this.googleMapsHandles.length = 0;
2097 return delete this.gWin;
2098 };
2099
2100 WindowChildModel.prototype.destroy = function(manualOverride) {
2101 var self;
2102 if (manualOverride == null) {
2103 manualOverride = false;
2104 }
2105 this.remove();
2106 if ((this.scope != null) && (this.needToManualDestroy || manualOverride)) {
2107 this.scope.$destroy();
2108 }
2109 return self = void 0;
2110 };
2111
2112 return WindowChildModel;
2113
2114 })(BaseObject);
2115 return WindowChildModel;
2116 }
2117 ]);
2118
2119 }).call(this);
2120
2121 /*
2122 - interface for all markers to derrive from
2123 - to enforce a minimum set of requirements
2124 - attributes
2125 - coords
2126 - icon
2127 - implementation needed on watches
2128 */
2129
2130
2131 (function() {
2132 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
2133 __hasProp = {}.hasOwnProperty,
2134 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
2135
2136 angular.module("google-maps.directives.api.models.parent").factory("IMarkerParentModel", [
2137 "ModelKey", "Logger", function(ModelKey, Logger) {
2138 var IMarkerParentModel;
2139 IMarkerParentModel = (function(_super) {
2140 __extends(IMarkerParentModel, _super);
2141
2142 IMarkerParentModel.prototype.DEFAULTS = {};
2143
2144 function IMarkerParentModel(scope, element, attrs, mapCtrl, $timeout) {
2145 var self,
2146 _this = this;
2147 this.scope = scope;
2148 this.element = element;
2149 this.attrs = attrs;
2150 this.mapCtrl = mapCtrl;
2151 this.$timeout = $timeout;
2152 this.linkInit = __bind(this.linkInit, this);
2153 this.onDestroy = __bind(this.onDestroy, this);
2154 this.onWatch = __bind(this.onWatch, this);
2155 this.watch = __bind(this.watch, this);
2156 this.validateScope = __bind(this.validateScope, this);
2157 this.onTimeOut = __bind(this.onTimeOut, this);
2158 IMarkerParentModel.__super__.constructor.call(this, this.scope);
2159 self = this;
2160 this.$log = Logger;
2161 if (!this.validateScope(scope)) {
2162 throw new String("Unable to construct IMarkerParentModel due to invalid scope");
2163 }
2164 this.doClick = angular.isDefined(attrs.click);
2165 if (scope.options != null) {
2166 this.DEFAULTS = scope.options;
2167 }
2168 this.$timeout(function() {
2169 _this.onTimeOut(scope);
2170 _this.watch('coords', _this.scope);
2171 _this.watch('icon', _this.scope);
2172 _this.watch('options', _this.scope);
2173 return scope.$on("$destroy", function() {
2174 return _this.onDestroy(scope);
2175 });
2176 });
2177 }
2178
2179 IMarkerParentModel.prototype.onTimeOut = function(scope) {};
2180
2181 IMarkerParentModel.prototype.validateScope = function(scope) {
2182 var ret;
2183 if (scope == null) {
2184 this.$log.error(this.constructor.name + ": invalid scope used");
2185 return false;
2186 }
2187 ret = scope.coords != null;
2188 if (!ret) {
2189 this.$log.error(this.constructor.name + ": no valid coords attribute found");
2190 return false;
2191 }
2192 return ret;
2193 };
2194
2195 IMarkerParentModel.prototype.watch = function(propNameToWatch, scope) {
2196 var watchFunc,
2197 _this = this;
2198 watchFunc = function(newValue, oldValue) {
2199 if (newValue !== oldValue) {
2200 return _this.onWatch(propNameToWatch, scope, newValue, oldValue);
2201 }
2202 };
2203 return scope.$watch(propNameToWatch, watchFunc, true);
2204 };
2205
2206 IMarkerParentModel.prototype.onWatch = function(propNameToWatch, scope, newValue, oldValue) {
2207 throw new String("OnWatch Not Implemented!!");
2208 };
2209
2210 IMarkerParentModel.prototype.onDestroy = function(scope) {
2211 throw new String("OnDestroy Not Implemented!!");
2212 };
2213
2214 IMarkerParentModel.prototype.linkInit = function(element, mapCtrl, scope, animate) {
2215 throw new String("LinkInit Not Implemented!!");
2216 };
2217
2218 return IMarkerParentModel;
2219
2220 })(ModelKey);
2221 return IMarkerParentModel;
2222 }
2223 ]);
2224
2225 }).call(this);
2226
2227 /*
2228 - interface directive for all window(s) to derrive from
2229 */
2230
2231
2232 (function() {
2233 var __hasProp = {}.hasOwnProperty,
2234 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
2235
2236 angular.module("google-maps.directives.api.models.parent").factory("IWindowParentModel", [
2237 "ModelKey", "GmapUtil", "Logger", function(ModelKey, GmapUtil, Logger) {
2238 var IWindowParentModel;
2239 IWindowParentModel = (function(_super) {
2240 __extends(IWindowParentModel, _super);
2241
2242 IWindowParentModel.include(GmapUtil);
2243
2244 IWindowParentModel.prototype.DEFAULTS = {};
2245
2246 function IWindowParentModel(scope, element, attrs, ctrls, $timeout, $compile, $http, $templateCache) {
2247 var self;
2248 IWindowParentModel.__super__.constructor.call(this, scope);
2249 self = this;
2250 this.$log = Logger;
2251 this.$timeout = $timeout;
2252 this.$compile = $compile;
2253 this.$http = $http;
2254 this.$templateCache = $templateCache;
2255 if (scope.options != null) {
2256 this.DEFAULTS = scope.options;
2257 }
2258 }
2259
2260 return IWindowParentModel;
2261
2262 })(ModelKey);
2263 return IWindowParentModel;
2264 }
2265 ]);
2266
2267 }).call(this);
2268
2269 (function() {
2270 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
2271 __hasProp = {}.hasOwnProperty,
2272 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
2273
2274 angular.module("google-maps.directives.api.models.parent").factory("LayerParentModel", [
2275 "BaseObject", "Logger", function(BaseObject, Logger) {
2276 var LayerParentModel;
2277 LayerParentModel = (function(_super) {
2278 __extends(LayerParentModel, _super);
2279
2280 function LayerParentModel(scope, element, attrs, mapCtrl, $timeout, onLayerCreated, $log) {
2281 var _this = this;
2282 this.scope = scope;
2283 this.element = element;
2284 this.attrs = attrs;
2285 this.mapCtrl = mapCtrl;
2286 this.$timeout = $timeout;
2287 this.onLayerCreated = onLayerCreated != null ? onLayerCreated : void 0;
2288 this.$log = $log != null ? $log : Logger;
2289 this.createGoogleLayer = __bind(this.createGoogleLayer, this);
2290 if (this.attrs.type == null) {
2291 this.$log.info("type attribute for the layer directive is mandatory. Layer creation aborted!!");
2292 return;
2293 }
2294 this.createGoogleLayer();
2295 this.gMap = void 0;
2296 this.doShow = true;
2297 this.$timeout(function() {
2298 _this.gMap = mapCtrl.getMap();
2299 if (angular.isDefined(_this.attrs.show)) {
2300 _this.doShow = _this.scope.show;
2301 }
2302 if (_this.doShow && (_this.gMap != null)) {
2303 _this.layer.setMap(_this.gMap);
2304 }
2305 _this.scope.$watch("show", function(newValue, oldValue) {
2306 if (newValue !== oldValue) {
2307 _this.doShow = newValue;
2308 if (newValue) {
2309 return _this.layer.setMap(_this.gMap);
2310 } else {
2311 return _this.layer.setMap(null);
2312 }
2313 }
2314 }, true);
2315 _this.scope.$watch("options", function(newValue, oldValue) {
2316 if (newValue !== oldValue) {
2317 _this.layer.setMap(null);
2318 _this.layer = null;
2319 return _this.createGoogleLayer();
2320 }
2321 }, true);
2322 return _this.scope.$on("$destroy", function() {
2323 return _this.layer.setMap(null);
2324 });
2325 });
2326 }
2327
2328 LayerParentModel.prototype.createGoogleLayer = function() {
2329 var _this = this;
2330 if (this.attrs.options == null) {
2331 this.layer = this.attrs.namespace === void 0 ? new google.maps[this.attrs.type]() : new google.maps[this.attrs.namespace][this.attrs.type]();
2332 } else {
2333 this.layer = this.attrs.namespace === void 0 ? new google.maps[this.attrs.type](this.scope.options) : new google.maps[this.attrs.namespace][this.attrs.type](this.scope.options);
2334 }
2335 return this.$timeout(function() {
2336 var fn;
2337 if ((_this.layer != null) && (_this.onLayerCreated != null)) {
2338 fn = _this.onLayerCreated(_this.scope, _this.layer);
2339 if (fn) {
2340 return fn(_this.layer);
2341 }
2342 }
2343 });
2344 };
2345
2346 return LayerParentModel;
2347
2348 })(BaseObject);
2349 return LayerParentModel;
2350 }
2351 ]);
2352
2353 }).call(this);
2354
2355 /*
2356 Basic Directive api for a marker. Basic in the sense that this directive contains 1:1 on scope and model.
2357 Thus there will be one html element per marker within the directive.
2358 */
2359
2360
2361 (function() {
2362 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
2363 __hasProp = {}.hasOwnProperty,
2364 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
2365
2366 angular.module("google-maps.directives.api.models.parent").factory("MarkerParentModel", [
2367 "IMarkerParentModel", "GmapUtil", "EventsHelper", function(IMarkerParentModel, GmapUtil, EventsHelper) {
2368 var MarkerParentModel;
2369 MarkerParentModel = (function(_super) {
2370 __extends(MarkerParentModel, _super);
2371
2372 MarkerParentModel.include(GmapUtil);
2373
2374 MarkerParentModel.include(EventsHelper);
2375
2376 function MarkerParentModel(scope, element, attrs, mapCtrl, $timeout, gMarkerManager, doFit) {
2377 var self;
2378 this.gMarkerManager = gMarkerManager;
2379 this.doFit = doFit;
2380 this.onDestroy = __bind(this.onDestroy, this);
2381 this.setGMarker = __bind(this.setGMarker, this);
2382 this.onWatch = __bind(this.onWatch, this);
2383 this.onTimeOut = __bind(this.onTimeOut, this);
2384 MarkerParentModel.__super__.constructor.call(this, scope, element, attrs, mapCtrl, $timeout);
2385 self = this;
2386 }
2387
2388 MarkerParentModel.prototype.onTimeOut = function(scope) {
2389 var opts,
2390 _this = this;
2391 opts = this.createMarkerOptions(scope.coords, scope.icon, scope.options, this.mapCtrl.getMap());
2392 this.setGMarker(new google.maps.Marker(opts));
2393 google.maps.event.addListener(this.scope.gMarker, 'click', function() {
2394 if (_this.doClick && (scope.click != null)) {
2395 return _this.$timeout(function() {
2396 return _this.scope.click();
2397 });
2398 }
2399 });
2400 this.setEvents(this.scope.gMarker, scope, scope);
2401 return this.$log.info(this);
2402 };
2403
2404 MarkerParentModel.prototype.onWatch = function(propNameToWatch, scope) {
2405 switch (propNameToWatch) {
2406 case 'coords':
2407 if (this.validateCoords(scope.coords) && (this.scope.gMarker != null)) {
2408 this.scope.gMarker.setMap(this.mapCtrl.getMap());
2409 this.scope.gMarker.setPosition(this.getCoords(scope.coords));
2410 this.scope.gMarker.setVisible(this.validateCoords(scope.coords));
2411 return this.scope.gMarker.setOptions(scope.options);
2412 } else {
2413 return this.scope.gMarker.setMap(null);
2414 }
2415 break;
2416 case 'icon':
2417 if ((scope.icon != null) && this.validateCoords(scope.coords) && (this.scope.gMarker != null)) {
2418 this.scope.gMarker.setOptions(scope.options);
2419 this.scope.gMarker.setIcon(scope.icon);
2420 this.scope.gMarker.setMap(null);
2421 this.scope.gMarker.setMap(this.mapCtrl.getMap());
2422 this.scope.gMarker.setPosition(this.getCoords(scope.coords));
2423 return this.scope.gMarker.setVisible(this.validateCoords(scope.coords));
2424 }
2425 break;
2426 case 'options':
2427 if (this.validateCoords(scope.coords) && (scope.icon != null) && scope.options) {
2428 if (this.scope.gMarker != null) {
2429 this.scope.gMarker.setMap(null);
2430 }
2431 return this.setGMarker(new google.maps.Marker(this.createMarkerOptions(scope.coords, scope.icon, scope.options, this.mapCtrl.getMap())));
2432 }
2433 }
2434 };
2435
2436 MarkerParentModel.prototype.setGMarker = function(gMarker) {
2437 if (this.scope.gMarker) {
2438 delete this.scope.gMarker;
2439 this.gMarkerManager.remove(this.scope.gMarker, false);
2440 }
2441 this.scope.gMarker = gMarker;
2442 if (this.scope.gMarker) {
2443 this.gMarkerManager.add(this.scope.gMarker, false);
2444 if (this.doFit) {
2445 return this.gMarkerManager.fit();
2446 }
2447 }
2448 };
2449
2450 MarkerParentModel.prototype.onDestroy = function(scope) {
2451 var self;
2452 if (!this.scope.gMarker) {
2453 self = void 0;
2454 return;
2455 }
2456 this.scope.gMarker.setMap(null);
2457 this.gMarkerManager.remove(this.scope.gMarker, false);
2458 delete this.scope.gMarker;
2459 return self = void 0;
2460 };
2461
2462 return MarkerParentModel;
2463
2464 })(IMarkerParentModel);
2465 return MarkerParentModel;
2466 }
2467 ]);
2468
2469 }).call(this);
2470
2471 (function() {
2472 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
2473 __hasProp = {}.hasOwnProperty,
2474 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
2475
2476 angular.module("google-maps.directives.api.models.parent").factory("MarkersParentModel", [
2477 "IMarkerParentModel", "ModelsWatcher", "PropMap", "MarkerChildModel", "ClustererMarkerManager", "MarkerManager", function(IMarkerParentModel, ModelsWatcher, PropMap, MarkerChildModel, ClustererMarkerManager, MarkerManager) {
2478 var MarkersParentModel;
2479 MarkersParentModel = (function(_super) {
2480 __extends(MarkersParentModel, _super);
2481
2482 MarkersParentModel.include(ModelsWatcher);
2483
2484 function MarkersParentModel(scope, element, attrs, mapCtrl, $timeout) {
2485 this.onDestroy = __bind(this.onDestroy, this);
2486 this.newChildMarker = __bind(this.newChildMarker, this);
2487 this.pieceMealMarkers = __bind(this.pieceMealMarkers, this);
2488 this.reBuildMarkers = __bind(this.reBuildMarkers, this);
2489 this.createMarkersFromScratch = __bind(this.createMarkersFromScratch, this);
2490 this.validateScope = __bind(this.validateScope, this);
2491 this.onWatch = __bind(this.onWatch, this);
2492 this.onTimeOut = __bind(this.onTimeOut, this);
2493 var self,
2494 _this = this;
2495 MarkersParentModel.__super__.constructor.call(this, scope, element, attrs, mapCtrl, $timeout);
2496 self = this;
2497 this.scope.markerModels = new PropMap();
2498 this.$timeout = $timeout;
2499 this.$log.info(this);
2500 this.doRebuildAll = this.scope.doRebuildAll != null ? this.scope.doRebuildAll : false;
2501 this.setIdKey(scope);
2502 this.scope.$watch('doRebuildAll', function(newValue, oldValue) {
2503 if (newValue !== oldValue) {
2504 return _this.doRebuildAll = newValue;
2505 }
2506 });
2507 }
2508
2509 MarkersParentModel.prototype.onTimeOut = function(scope) {
2510 this.watch('models', scope);
2511 this.watch('doCluster', scope);
2512 this.watch('clusterOptions', scope);
2513 this.watch('clusterEvents', scope);
2514 this.watch('fit', scope);
2515 this.watch('idKey', scope);
2516 this.gMarkerManager = void 0;
2517 return this.createMarkersFromScratch(scope);
2518 };
2519
2520 MarkersParentModel.prototype.onWatch = function(propNameToWatch, scope, newValue, oldValue) {
2521 if (propNameToWatch === "idKey" && newValue !== oldValue) {
2522 this.idKey = newValue;
2523 }
2524 if (this.doRebuildAll) {
2525 return this.reBuildMarkers(scope);
2526 } else {
2527 return this.pieceMealMarkers(scope);
2528 }
2529 };
2530
2531 MarkersParentModel.prototype.validateScope = function(scope) {
2532 var modelsNotDefined;
2533 modelsNotDefined = angular.isUndefined(scope.models) || scope.models === void 0;
2534 if (modelsNotDefined) {
2535 this.$log.error(this.constructor.name + ": no valid models attribute found");
2536 }
2537 return MarkersParentModel.__super__.validateScope.call(this, scope) || modelsNotDefined;
2538 };
2539
2540 MarkersParentModel.prototype.createMarkersFromScratch = function(scope) {
2541 var _this = this;
2542 if (scope.doCluster) {
2543 if (scope.clusterEvents) {
2544 this.clusterInternalOptions = _.once(function() {
2545 var self, _ref, _ref1, _ref2;
2546 self = _this;
2547 if (!_this.origClusterEvents) {
2548 _this.origClusterEvents = {
2549 click: (_ref = scope.clusterEvents) != null ? _ref.click : void 0,
2550 mouseout: (_ref1 = scope.clusterEvents) != null ? _ref1.mouseout : void 0,
2551 mouseover: (_ref2 = scope.clusterEvents) != null ? _ref2.mouseover : void 0
2552 };
2553 return _.extend(scope.clusterEvents, {
2554 click: function(cluster) {
2555 return self.maybeExecMappedEvent(cluster, "click");
2556 },
2557 mouseout: function(cluster) {
2558 return self.maybeExecMappedEvent(cluster, "mouseout");
2559 },
2560 mouseover: function(cluster) {
2561 return self.maybeExecMappedEvent(cluster, "mouseover");
2562 }
2563 });
2564 }
2565 })();
2566 }
2567 if (scope.clusterOptions || scope.clusterEvents) {
2568 if (this.gMarkerManager === void 0) {
2569 this.gMarkerManager = new ClustererMarkerManager(this.mapCtrl.getMap(), void 0, scope.clusterOptions, this.clusterInternalOptions);
2570 } else {
2571 if (this.gMarkerManager.opt_options !== scope.clusterOptions) {
2572 this.gMarkerManager = new ClustererMarkerManager(this.mapCtrl.getMap(), void 0, scope.clusterOptions, this.clusterInternalOptions);
2573 }
2574 }
2575 } else {
2576 this.gMarkerManager = new ClustererMarkerManager(this.mapCtrl.getMap());
2577 }
2578 } else {
2579 this.gMarkerManager = new MarkerManager(this.mapCtrl.getMap());
2580 }
2581 return _async.each(scope.models, function(model) {
2582 return _this.newChildMarker(model, scope);
2583 }, function() {
2584 _this.gMarkerManager.draw();
2585 if (scope.fit) {
2586 return _this.gMarkerManager.fit();
2587 }
2588 });
2589 };
2590
2591 MarkersParentModel.prototype.reBuildMarkers = function(scope) {
2592 if (!scope.doRebuild && scope.doRebuild !== void 0) {
2593 return;
2594 }
2595 this.onDestroy(scope);
2596 return this.createMarkersFromScratch(scope);
2597 };
2598
2599 MarkersParentModel.prototype.pieceMealMarkers = function(scope) {
2600 var _this = this;
2601 if ((this.scope.models != null) && this.scope.models.length > 0 && this.scope.markerModels.length > 0) {
2602 return this.figureOutState(this.idKey, scope, this.scope.markerModels, this.modelKeyComparison, function(state) {
2603 var payload;
2604 payload = state;
2605 return _async.each(payload.removals, function(child) {
2606 if (child != null) {
2607 child.destroy();
2608 return _this.scope.markerModels.remove(child.id);
2609 }
2610 }, function() {
2611 return _async.each(payload.adds, function(modelToAdd) {
2612 return _this.newChildMarker(modelToAdd, scope);
2613 }, function() {
2614 if (payload.adds.length > 0 || payload.removals.length > 0) {
2615 _this.gMarkerManager.draw();
2616 return scope.markerModels = _this.scope.markerModels;
2617 }
2618 });
2619 });
2620 });
2621 } else {
2622 return this.reBuildMarkers(scope);
2623 }
2624 };
2625
2626 MarkersParentModel.prototype.newChildMarker = function(model, scope) {
2627 var child;
2628 if (model[this.idKey] == null) {
2629 this.$log.error("Marker model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key.");
2630 return;
2631 }
2632 this.$log.info('child', child, 'markers', this.scope.markerModels);
2633 child = new MarkerChildModel(model, scope, this.mapCtrl, this.$timeout, this.DEFAULTS, this.doClick, this.gMarkerManager, this.idKey);
2634 this.scope.markerModels.put(model[this.idKey], child);
2635 return child;
2636 };
2637
2638 MarkersParentModel.prototype.onDestroy = function(scope) {
2639 _.each(this.scope.markerModels.values(), function(model) {
2640 if (model != null) {
2641 return model.destroy();
2642 }
2643 });
2644 delete this.scope.markerModels;
2645 this.scope.markerModels = new PropMap();
2646 if (this.gMarkerManager != null) {
2647 return this.gMarkerManager.clear();
2648 }
2649 };
2650
2651 MarkersParentModel.prototype.maybeExecMappedEvent = function(cluster, fnName) {
2652 var pair, _ref;
2653 if (_.isFunction((_ref = this.scope.clusterEvents) != null ? _ref[fnName] : void 0)) {
2654 pair = this.mapClusterToMarkerModels(cluster);
2655 if (this.origClusterEvents[fnName]) {
2656 return this.origClusterEvents[fnName](pair.cluster, pair.mapped);
2657 }
2658 }
2659 };
2660
2661 MarkersParentModel.prototype.mapClusterToMarkerModels = function(cluster) {
2662 var gMarkers, mapped,
2663 _this = this;
2664 gMarkers = cluster.getMarkers();
2665 mapped = gMarkers.map(function(g) {
2666 return _this.scope.markerModels[g.key].model;
2667 });
2668 return {
2669 cluster: cluster,
2670 mapped: mapped
2671 };
2672 };
2673
2674 return MarkersParentModel;
2675
2676 })(IMarkerParentModel);
2677 return MarkersParentModel;
2678 }
2679 ]);
2680
2681 }).call(this);
2682
2683 /*
2684 Windows directive where many windows map to the models property
2685 */
2686
2687
2688 (function() {
2689 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
2690 __hasProp = {}.hasOwnProperty,
2691 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
2692
2693 angular.module("google-maps.directives.api.models.parent").factory("PolylinesParentModel", [
2694 "$timeout", "Logger", "ModelKey", "ModelsWatcher", "PropMap", "PolylineChildModel", function($timeout, Logger, ModelKey, ModelsWatcher, PropMap, PolylineChildModel) {
2695 var PolylinesParentModel;
2696 return PolylinesParentModel = (function(_super) {
2697 __extends(PolylinesParentModel, _super);
2698
2699 PolylinesParentModel.include(ModelsWatcher);
2700
2701 function PolylinesParentModel(scope, element, attrs, gMap, defaults) {
2702 var self,
2703 _this = this;
2704 this.scope = scope;
2705 this.element = element;
2706 this.attrs = attrs;
2707 this.gMap = gMap;
2708 this.defaults = defaults;
2709 this.modelKeyComparison = __bind(this.modelKeyComparison, this);
2710 this.setChildScope = __bind(this.setChildScope, this);
2711 this.createChild = __bind(this.createChild, this);
2712 this.pieceMeal = __bind(this.pieceMeal, this);
2713 this.createAllNew = __bind(this.createAllNew, this);
2714 this.watchIdKey = __bind(this.watchIdKey, this);
2715 this.createChildScopes = __bind(this.createChildScopes, this);
2716 this.watchOurScope = __bind(this.watchOurScope, this);
2717 this.watchDestroy = __bind(this.watchDestroy, this);
2718 this.rebuildAll = __bind(this.rebuildAll, this);
2719 this.doINeedToWipe = __bind(this.doINeedToWipe, this);
2720 this.watchModels = __bind(this.watchModels, this);
2721 this.watch = __bind(this.watch, this);
2722 PolylinesParentModel.__super__.constructor.call(this, scope);
2723 self = this;
2724 this.$log = Logger;
2725 this.plurals = new PropMap();
2726 this.scopePropNames = ['path', 'stroke', 'clickable', 'draggable', 'editable', 'geodesic', 'icons', 'visible'];
2727 _.each(this.scopePropNames, function(name) {
2728 return _this[name + 'Key'] = void 0;
2729 });
2730 this.models = void 0;
2731 this.firstTime = true;
2732 this.$log.info(this);
2733 $timeout(function() {
2734 _this.watchOurScope(scope);
2735 return _this.createChildScopes();
2736 });
2737 }
2738
2739 PolylinesParentModel.prototype.watch = function(scope, name, nameKey) {
2740 var _this = this;
2741 return scope.$watch(name, function(newValue, oldValue) {
2742 if (newValue !== oldValue) {
2743 _this[nameKey] = typeof newValue === 'function' ? newValue() : newValue;
2744 return _async.each(_.values(_this.plurals), function(model) {
2745 return model.scope[name] = _this[nameKey] === 'self' ? model : model[_this[nameKey]];
2746 }, function() {});
2747 }
2748 });
2749 };
2750
2751 PolylinesParentModel.prototype.watchModels = function(scope) {
2752 var _this = this;
2753 return scope.$watch('models', function(newValue, oldValue) {
2754 if (!_.isEqual(newValue, oldValue)) {
2755 if (_this.doINeedToWipe(newValue)) {
2756 return _this.rebuildAll(scope, true, true);
2757 } else {
2758 return _this.createChildScopes(false);
2759 }
2760 }
2761 }, true);
2762 };
2763
2764 PolylinesParentModel.prototype.doINeedToWipe = function(newValue) {
2765 var newValueIsEmpty;
2766 newValueIsEmpty = newValue != null ? newValue.length === 0 : true;
2767 return this.plurals.length > 0 && newValueIsEmpty;
2768 };
2769
2770 PolylinesParentModel.prototype.rebuildAll = function(scope, doCreate, doDelete) {
2771 var _this = this;
2772 return _async.each(this.plurals.values(), function(model) {
2773 return model.destroy();
2774 }, function() {
2775 if (doDelete) {
2776 delete _this.plurals;
2777 }
2778 _this.plurals = new PropMap();
2779 if (doCreate) {
2780 return _this.createChildScopes();
2781 }
2782 });
2783 };
2784
2785 PolylinesParentModel.prototype.watchDestroy = function(scope) {
2786 var _this = this;
2787 return scope.$on("$destroy", function() {
2788 return _this.rebuildAll(scope, false, true);
2789 });
2790 };
2791
2792 PolylinesParentModel.prototype.watchOurScope = function(scope) {
2793 var _this = this;
2794 return _.each(this.scopePropNames, function(name) {
2795 var nameKey;
2796 nameKey = name + 'Key';
2797 _this[nameKey] = typeof scope[name] === 'function' ? scope[name]() : scope[name];
2798 return _this.watch(scope, name, nameKey);
2799 });
2800 };
2801
2802 PolylinesParentModel.prototype.createChildScopes = function(isCreatingFromScratch) {
2803 if (isCreatingFromScratch == null) {
2804 isCreatingFromScratch = true;
2805 }
2806 if (angular.isUndefined(this.scope.models)) {
2807 this.$log.error("No models to create polylines from! I Need direct models!");
2808 return;
2809 }
2810 if (this.gMap != null) {
2811 if (this.scope.models != null) {
2812 this.watchIdKey(this.scope);
2813 if (isCreatingFromScratch) {
2814 return this.createAllNew(this.scope, false);
2815 } else {
2816 return this.pieceMeal(this.scope, false);
2817 }
2818 }
2819 }
2820 };
2821
2822 PolylinesParentModel.prototype.watchIdKey = function(scope) {
2823 var _this = this;
2824 this.setIdKey(scope);
2825 return scope.$watch('idKey', function(newValue, oldValue) {
2826 if (newValue !== oldValue && (newValue == null)) {
2827 _this.idKey = newValue;
2828 return _this.rebuildAll(scope, true, true);
2829 }
2830 });
2831 };
2832
2833 PolylinesParentModel.prototype.createAllNew = function(scope, isArray) {
2834 var _this = this;
2835 if (isArray == null) {
2836 isArray = false;
2837 }
2838 this.models = scope.models;
2839 if (this.firstTime) {
2840 this.watchModels(scope);
2841 this.watchDestroy(scope);
2842 }
2843 return _async.each(scope.models, function(model) {
2844 return _this.createChild(model, _this.gMap);
2845 }, function() {
2846 return _this.firstTime = false;
2847 });
2848 };
2849
2850 PolylinesParentModel.prototype.pieceMeal = function(scope, isArray) {
2851 var _this = this;
2852 if (isArray == null) {
2853 isArray = true;
2854 }
2855 this.models = scope.models;
2856 if ((scope != null) && (scope.models != null) && scope.models.length > 0 && this.plurals.length > 0) {
2857 return this.figureOutState(this.idKey, scope, this.plurals, this.modelKeyComparison, function(state) {
2858 var payload;
2859 payload = state;
2860 return _async.each(payload.removals, function(id) {
2861 var child;
2862 child = _this.plurals[id];
2863 if (child != null) {
2864 child.destroy();
2865 return _this.plurals.remove(id);
2866 }
2867 }, function() {
2868 return _async.each(payload.adds, function(modelToAdd) {
2869 return _this.createChild(modelToAdd, _this.gMap);
2870 }, function() {});
2871 });
2872 });
2873 } else {
2874 return this.rebuildAll(this.scope, true, true);
2875 }
2876 };
2877
2878 PolylinesParentModel.prototype.createChild = function(model, gMap) {
2879 var child, childScope,
2880 _this = this;
2881 childScope = this.scope.$new(false);
2882 this.setChildScope(childScope, model);
2883 childScope.$watch('model', function(newValue, oldValue) {
2884 if (newValue !== oldValue) {
2885 return _this.setChildScope(childScope, newValue);
2886 }
2887 }, true);
2888 childScope["static"] = this.scope["static"];
2889 child = new PolylineChildModel(childScope, this.attrs, gMap, this.defaults, model);
2890 if (model[this.idKey] == null) {
2891 this.$log.error("Polyline model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key.");
2892 return;
2893 }
2894 this.plurals.put(model[this.idKey], child);
2895 return child;
2896 };
2897
2898 PolylinesParentModel.prototype.setChildScope = function(childScope, model) {
2899 var _this = this;
2900 _.each(this.scopePropNames, function(name) {
2901 var nameKey, newValue;
2902 nameKey = name + 'Key';
2903 newValue = _this[nameKey] === 'self' ? model : model[_this[nameKey]];
2904 if (newValue !== childScope[name]) {
2905 return childScope[name] = newValue;
2906 }
2907 });
2908 return childScope.model = model;
2909 };
2910
2911 PolylinesParentModel.prototype.modelKeyComparison = function(model1, model2) {
2912 return _.isEqual(this.evalModelHandle(model1, this.scope.path), this.evalModelHandle(model2, this.scope.path));
2913 };
2914
2915 return PolylinesParentModel;
2916
2917 })(ModelKey);
2918 }
2919 ]);
2920
2921 }).call(this);
2922
2923 /*
2924 Windows directive where many windows map to the models property
2925 */
2926
2927
2928 (function() {
2929 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
2930 __hasProp = {}.hasOwnProperty,
2931 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
2932
2933 angular.module("google-maps.directives.api.models.parent").factory("WindowsParentModel", [
2934 "IWindowParentModel", "ModelsWatcher", "PropMap", "WindowChildModel", "Linked", function(IWindowParentModel, ModelsWatcher, PropMap, WindowChildModel, Linked) {
2935 var WindowsParentModel;
2936 WindowsParentModel = (function(_super) {
2937 __extends(WindowsParentModel, _super);
2938
2939 WindowsParentModel.include(ModelsWatcher);
2940
2941 function WindowsParentModel(scope, element, attrs, ctrls, $timeout, $compile, $http, $templateCache, $interpolate) {
2942 var self,
2943 _this = this;
2944 this.$interpolate = $interpolate;
2945 this.interpolateContent = __bind(this.interpolateContent, this);
2946 this.setChildScope = __bind(this.setChildScope, this);
2947 this.createWindow = __bind(this.createWindow, this);
2948 this.setContentKeys = __bind(this.setContentKeys, this);
2949 this.pieceMealWindows = __bind(this.pieceMealWindows, this);
2950 this.createAllNewWindows = __bind(this.createAllNewWindows, this);
2951 this.watchIdKey = __bind(this.watchIdKey, this);
2952 this.createChildScopesWindows = __bind(this.createChildScopesWindows, this);
2953 this.watchOurScope = __bind(this.watchOurScope, this);
2954 this.watchDestroy = __bind(this.watchDestroy, this);
2955 this.rebuildAll = __bind(this.rebuildAll, this);
2956 this.doINeedToWipe = __bind(this.doINeedToWipe, this);
2957 this.watchModels = __bind(this.watchModels, this);
2958 this.watch = __bind(this.watch, this);
2959 WindowsParentModel.__super__.constructor.call(this, scope, element, attrs, ctrls, $timeout, $compile, $http, $templateCache);
2960 self = this;
2961 this.windows = new PropMap();
2962 this.scopePropNames = ['show', 'coords', 'templateUrl', 'templateParameter', 'isIconVisibleOnClick', 'closeClick'];
2963 _.each(this.scopePropNames, function(name) {
2964 return _this[name + 'Key'] = void 0;
2965 });
2966 this.linked = new Linked(scope, element, attrs, ctrls);
2967 this.models = void 0;
2968 this.contentKeys = void 0;
2969 this.isIconVisibleOnClick = void 0;
2970 this.firstTime = true;
2971 this.$log.info(self);
2972 this.parentScope = void 0;
2973 this.$timeout(function() {
2974 _this.watchOurScope(scope);
2975 _this.doRebuildAll = _this.scope.doRebuildAll != null ? _this.scope.doRebuildAll : false;
2976 scope.$watch('doRebuildAll', function(newValue, oldValue) {
2977 if (newValue !== oldValue) {
2978 return _this.doRebuildAll = newValue;
2979 }
2980 });
2981 return _this.createChildScopesWindows();
2982 }, 50);
2983 }
2984
2985 WindowsParentModel.prototype.watch = function(scope, name, nameKey) {
2986 var _this = this;
2987 return scope.$watch(name, function(newValue, oldValue) {
2988 if (newValue !== oldValue) {
2989 _this[nameKey] = typeof newValue === 'function' ? newValue() : newValue;
2990 return _async.each(_.values(_this.windows), function(model) {
2991 return model.scope[name] = _this[nameKey] === 'self' ? model : model[_this[nameKey]];
2992 }, function() {});
2993 }
2994 });
2995 };
2996
2997 WindowsParentModel.prototype.watchModels = function(scope) {
2998 var _this = this;
2999 return scope.$watch('models', function(newValue, oldValue) {
3000 if (!_.isEqual(newValue, oldValue)) {
3001 if (_this.doRebuildAll || _this.doINeedToWipe(newValue)) {
3002 return _this.rebuildAll(scope, true, true);
3003 } else {
3004 return _this.createChildScopesWindows(false);
3005 }
3006 }
3007 });
3008 };
3009
3010 WindowsParentModel.prototype.doINeedToWipe = function(newValue) {
3011 var newValueIsEmpty;
3012 newValueIsEmpty = newValue != null ? newValue.length === 0 : true;
3013 return this.windows.length > 0 && newValueIsEmpty;
3014 };
3015
3016 WindowsParentModel.prototype.rebuildAll = function(scope, doCreate, doDelete) {
3017 var _this = this;
3018 return _async.each(this.windows.values(), function(model) {
3019 return model.destroy();
3020 }, function() {
3021 if (doDelete) {
3022 delete _this.windows;
3023 }
3024 _this.windows = new PropMap();
3025 if (doCreate) {
3026 return _this.createChildScopesWindows();
3027 }
3028 });
3029 };
3030
3031 WindowsParentModel.prototype.watchDestroy = function(scope) {
3032 var _this = this;
3033 return scope.$on("$destroy", function() {
3034 return _this.rebuildAll(scope, false, true);
3035 });
3036 };
3037
3038 WindowsParentModel.prototype.watchOurScope = function(scope) {
3039 var _this = this;
3040 return _.each(this.scopePropNames, function(name) {
3041 var nameKey;
3042 nameKey = name + 'Key';
3043 _this[nameKey] = typeof scope[name] === 'function' ? scope[name]() : scope[name];
3044 return _this.watch(scope, name, nameKey);
3045 });
3046 };
3047
3048 WindowsParentModel.prototype.createChildScopesWindows = function(isCreatingFromScratch) {
3049 var markersScope, modelsNotDefined;
3050 if (isCreatingFromScratch == null) {
3051 isCreatingFromScratch = true;
3052 }
3053 /*
3054 being that we cannot tell the difference in Key String vs. a normal value string (TemplateUrl)
3055 we will assume that all scope values are string expressions either pointing to a key (propName) or using
3056 'self' to point the model as container/object of interest.
3057
3058 This may force redundant information into the model, but this appears to be the most flexible approach.
3059 */
3060
3061 this.isIconVisibleOnClick = true;
3062 if (angular.isDefined(this.linked.attrs.isiconvisibleonclick)) {
3063 this.isIconVisibleOnClick = this.linked.scope.isIconVisibleOnClick;
3064 }
3065 this.gMap = this.linked.ctrls[0].getMap();
3066 if (this.linked.ctrls[1] != null) {
3067 markersScope = this.linked.ctrls.length > 1 ? this.linked.ctrls[1].getMarkersScope() : void 0;
3068 }
3069 modelsNotDefined = angular.isUndefined(this.linked.scope.models);
3070 if (modelsNotDefined && (markersScope === void 0 || (markersScope.markerModels === void 0 || markersScope.models === void 0))) {
3071 this.$log.error("No models to create windows from! Need direct models or models derrived from markers!");
3072 return;
3073 }
3074 if (this.gMap != null) {
3075 if (this.linked.scope.models != null) {
3076 this.watchIdKey(this.linked.scope);
3077 if (isCreatingFromScratch) {
3078 return this.createAllNewWindows(this.linked.scope, false);
3079 } else {
3080 return this.pieceMealWindows(this.linked.scope, false);
3081 }
3082 } else {
3083 this.parentScope = markersScope;
3084 this.watchIdKey(this.parentScope);
3085 if (isCreatingFromScratch) {
3086 return this.createAllNewWindows(markersScope, true, 'markerModels', false);
3087 } else {
3088 return this.pieceMealWindows(markersScope, true, 'markerModels', false);
3089 }
3090 }
3091 }
3092 };
3093
3094 WindowsParentModel.prototype.watchIdKey = function(scope) {
3095 var _this = this;
3096 this.setIdKey(scope);
3097 return scope.$watch('idKey', function(newValue, oldValue) {
3098 if (newValue !== oldValue && (newValue == null)) {
3099 _this.idKey = newValue;
3100 return _this.rebuildAll(scope, true, true);
3101 }
3102 });
3103 };
3104
3105 WindowsParentModel.prototype.createAllNewWindows = function(scope, hasGMarker, modelsPropToIterate, isArray) {
3106 var _this = this;
3107 if (modelsPropToIterate == null) {
3108 modelsPropToIterate = 'models';
3109 }
3110 if (isArray == null) {
3111 isArray = false;
3112 }
3113 this.models = scope.models;
3114 if (this.firstTime) {
3115 this.watchModels(scope);
3116 this.watchDestroy(scope);
3117 }
3118 this.setContentKeys(scope.models);
3119 return _async.each(scope.models, function(model) {
3120 var gMarker;
3121 gMarker = hasGMarker ? scope[modelsPropToIterate][[model[_this.idKey]]].gMarker : void 0;
3122 return _this.createWindow(model, gMarker, _this.gMap);
3123 }, function() {
3124 return _this.firstTime = false;
3125 });
3126 };
3127
3128 WindowsParentModel.prototype.pieceMealWindows = function(scope, hasGMarker, modelsPropToIterate, isArray) {
3129 var _this = this;
3130 if (modelsPropToIterate == null) {
3131 modelsPropToIterate = 'models';
3132 }
3133 if (isArray == null) {
3134 isArray = true;
3135 }
3136 this.models = scope.models;
3137 if ((scope != null) && (scope.models != null) && scope.models.length > 0 && this.windows.length > 0) {
3138 return this.figureOutState(this.idKey, scope, this.windows, this.modelKeyComparison, function(state) {
3139 var payload;
3140 payload = state;
3141 return _async.each(payload.removals, function(child) {
3142 if (child != null) {
3143 child.destroy();
3144 return _this.windows.remove(child.id);
3145 }
3146 }, function() {
3147 return _async.each(payload.adds, function(modelToAdd) {
3148 var gMarker;
3149 gMarker = scope[modelsPropToIterate][modelToAdd[_this.idKey]].gMarker;
3150 return _this.createWindow(modelToAdd, gMarker, _this.gMap);
3151 }, function() {});
3152 });
3153 });
3154 } else {
3155 return this.rebuildAll(this.scope, true, true);
3156 }
3157 };
3158
3159 WindowsParentModel.prototype.setContentKeys = function(models) {
3160 if (models.length > 0) {
3161 return this.contentKeys = Object.keys(models[0]);
3162 }
3163 };
3164
3165 WindowsParentModel.prototype.createWindow = function(model, gMarker, gMap) {
3166 var child, childScope, fakeElement, opts,
3167 _this = this;
3168 childScope = this.linked.scope.$new(false);
3169 this.setChildScope(childScope, model);
3170 childScope.$watch('model', function(newValue, oldValue) {
3171 if (newValue !== oldValue) {
3172 return _this.setChildScope(childScope, newValue);
3173 }
3174 }, true);
3175 fakeElement = {
3176 html: function() {
3177 return _this.interpolateContent(_this.linked.element.html(), model);
3178 }
3179 };
3180 opts = this.createWindowOptions(gMarker, childScope, fakeElement.html(), this.DEFAULTS);
3181 child = new WindowChildModel(model, childScope, opts, this.isIconVisibleOnClick, gMap, gMarker, fakeElement, true, true);
3182 if (model[this.idKey] == null) {
3183 this.$log.error("Window model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key.");
3184 return;
3185 }
3186 this.windows.put(model[this.idKey], child);
3187 return child;
3188 };
3189
3190 WindowsParentModel.prototype.setChildScope = function(childScope, model) {
3191 var _this = this;
3192 _.each(this.scopePropNames, function(name) {
3193 var nameKey, newValue;
3194 nameKey = name + 'Key';
3195 newValue = _this[nameKey] === 'self' ? model : model[_this[nameKey]];
3196 if (newValue !== childScope[name]) {
3197 return childScope[name] = newValue;
3198 }
3199 });
3200 return childScope.model = model;
3201 };
3202
3203 WindowsParentModel.prototype.interpolateContent = function(content, model) {
3204 var exp, interpModel, key, _i, _len, _ref;
3205 if (this.contentKeys === void 0 || this.contentKeys.length === 0) {
3206 return;
3207 }
3208 exp = this.$interpolate(content);
3209 interpModel = {};
3210 _ref = this.contentKeys;
3211 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
3212 key = _ref[_i];
3213 interpModel[key] = model[key];
3214 }
3215 return exp(interpModel);
3216 };
3217
3218 return WindowsParentModel;
3219
3220 })(IWindowParentModel);
3221 return WindowsParentModel;
3222 }
3223 ]);
3224
3225 }).call(this);
3226
3227 /*
3228 - interface for all labels to derrive from
3229 - to enforce a minimum set of requirements
3230 - attributes
3231 - content
3232 - anchor
3233 - implementation needed on watches
3234 */
3235
3236
3237 (function() {
3238 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3239 __hasProp = {}.hasOwnProperty,
3240 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3241
3242 angular.module("google-maps.directives.api").factory("ILabel", [
3243 "BaseObject", "Logger", function(BaseObject, Logger) {
3244 var ILabel;
3245 return ILabel = (function(_super) {
3246 __extends(ILabel, _super);
3247
3248 function ILabel($timeout) {
3249 this.link = __bind(this.link, this);
3250 var self;
3251 self = this;
3252 this.restrict = 'ECMA';
3253 this.replace = true;
3254 this.template = void 0;
3255 this.require = void 0;
3256 this.transclude = true;
3257 this.priority = -100;
3258 this.scope = {
3259 labelContent: '=content',
3260 labelAnchor: '@anchor',
3261 labelClass: '@class',
3262 labelStyle: '=style'
3263 };
3264 this.$log = Logger;
3265 this.$timeout = $timeout;
3266 }
3267
3268 ILabel.prototype.link = function(scope, element, attrs, ctrl) {
3269 throw new Exception("Not Implemented!!");
3270 };
3271
3272 return ILabel;
3273
3274 })(BaseObject);
3275 }
3276 ]);
3277
3278 }).call(this);
3279
3280 /*
3281 - interface for all markers to derrive from
3282 - to enforce a minimum set of requirements
3283 - attributes
3284 - coords
3285 - icon
3286 - implementation needed on watches
3287 */
3288
3289
3290 (function() {
3291 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3292 __hasProp = {}.hasOwnProperty,
3293 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3294
3295 angular.module("google-maps.directives.api").factory("IMarker", [
3296 "Logger", "BaseObject", function(Logger, BaseObject) {
3297 var IMarker;
3298 return IMarker = (function(_super) {
3299 __extends(IMarker, _super);
3300
3301 function IMarker($timeout) {
3302 this.link = __bind(this.link, this);
3303 var self;
3304 self = this;
3305 this.$log = Logger;
3306 this.$timeout = $timeout;
3307 this.restrict = 'ECMA';
3308 this.require = '^googleMap';
3309 this.priority = -1;
3310 this.transclude = true;
3311 this.replace = true;
3312 this.scope = {
3313 coords: '=coords',
3314 icon: '=icon',
3315 click: '&click',
3316 options: '=options',
3317 events: '=events',
3318 fit: '=fit'
3319 };
3320 }
3321
3322 IMarker.prototype.controller = [
3323 '$scope', '$element', function($scope, $element) {
3324 throw new Exception("Not Implemented!!");
3325 }
3326 ];
3327
3328 IMarker.prototype.link = function(scope, element, attrs, ctrl) {
3329 throw new Exception("Not implemented!!");
3330 };
3331
3332 return IMarker;
3333
3334 })(BaseObject);
3335 }
3336 ]);
3337
3338 }).call(this);
3339
3340 (function() {
3341 var __hasProp = {}.hasOwnProperty,
3342 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3343
3344 angular.module("google-maps.directives.api").factory("IPolyline", [
3345 "GmapUtil", "BaseObject", "Logger", function(GmapUtil, BaseObject, Logger) {
3346 var IPolyline;
3347 return IPolyline = (function(_super) {
3348 __extends(IPolyline, _super);
3349
3350 IPolyline.include(GmapUtil);
3351
3352 function IPolyline() {
3353 var self;
3354 self = this;
3355 }
3356
3357 IPolyline.prototype.restrict = "ECA";
3358
3359 IPolyline.prototype.replace = true;
3360
3361 IPolyline.prototype.require = "^googleMap";
3362
3363 IPolyline.prototype.scope = {
3364 path: "=",
3365 stroke: "=",
3366 clickable: "=",
3367 draggable: "=",
3368 editable: "=",
3369 geodesic: "=",
3370 icons: "=",
3371 visible: "=",
3372 "static": "=",
3373 fit: "="
3374 };
3375
3376 IPolyline.prototype.DEFAULTS = {};
3377
3378 IPolyline.prototype.$log = Logger;
3379
3380 return IPolyline;
3381
3382 })(BaseObject);
3383 }
3384 ]);
3385
3386 }).call(this);
3387
3388 /*
3389 - interface directive for all window(s) to derrive from
3390 */
3391
3392
3393 (function() {
3394 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3395 __hasProp = {}.hasOwnProperty,
3396 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3397
3398 angular.module("google-maps.directives.api").factory("IWindow", [
3399 "BaseObject", "ChildEvents", "Logger", function(BaseObject, ChildEvents, Logger) {
3400 var IWindow;
3401 return IWindow = (function(_super) {
3402 __extends(IWindow, _super);
3403
3404 IWindow.include(ChildEvents);
3405
3406 function IWindow($timeout, $compile, $http, $templateCache) {
3407 var self;
3408 this.$timeout = $timeout;
3409 this.$compile = $compile;
3410 this.$http = $http;
3411 this.$templateCache = $templateCache;
3412 this.link = __bind(this.link, this);
3413 self = this;
3414 this.restrict = 'ECMA';
3415 this.template = void 0;
3416 this.transclude = true;
3417 this.priority = -100;
3418 this.require = void 0;
3419 this.replace = true;
3420 this.scope = {
3421 coords: '=coords',
3422 show: '=show',
3423 templateUrl: '=templateurl',
3424 templateParameter: '=templateparameter',
3425 isIconVisibleOnClick: '=isiconvisibleonclick',
3426 closeClick: '&closeclick',
3427 options: '=options'
3428 };
3429 this.$log = Logger;
3430 }
3431
3432 IWindow.prototype.link = function(scope, element, attrs, ctrls) {
3433 throw new Exception("Not Implemented!!");
3434 };
3435
3436 return IWindow;
3437
3438 })(BaseObject);
3439 }
3440 ]);
3441
3442 }).call(this);
3443
3444 (function() {
3445 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3446 __hasProp = {}.hasOwnProperty,
3447 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3448
3449 angular.module("google-maps.directives.api").factory("Map", [
3450 "$timeout", "Logger", "GmapUtil", "BaseObject", function($timeout, Logger, GmapUtil, BaseObject) {
3451 "use strict";
3452 var $log, DEFAULTS, Map;
3453 $log = Logger;
3454 DEFAULTS = {
3455 mapTypeId: google.maps.MapTypeId.ROADMAP
3456 };
3457 return Map = (function(_super) {
3458 __extends(Map, _super);
3459
3460 Map.include(GmapUtil);
3461
3462 function Map() {
3463 this.link = __bind(this.link, this);
3464 var self;
3465 self = this;
3466 }
3467
3468 Map.prototype.restrict = "ECMA";
3469
3470 Map.prototype.transclude = true;
3471
3472 Map.prototype.replace = false;
3473
3474 Map.prototype.template = "<div class=\"angular-google-map\"><div class=\"angular-google-map-container\"></div><div ng-transclude style=\"display: none\"></div></div>";
3475
3476 Map.prototype.scope = {
3477 center: "=center",
3478 zoom: "=zoom",
3479 dragging: "=dragging",
3480 control: "=",
3481 windows: "=windows",
3482 options: "=options",
3483 events: "=events",
3484 styles: "=styles",
3485 bounds: "=bounds"
3486 };
3487
3488 Map.prototype.controller = [
3489 "$scope", function($scope) {
3490 return {
3491 getMap: function() {
3492 return $scope.map;
3493 }
3494 };
3495 }
3496 ];
3497
3498 /*
3499 @param scope
3500 @param element
3501 @param attrs
3502 */
3503
3504
3505 Map.prototype.link = function(scope, element, attrs) {
3506 var dragging, el, eventName, getEventHandler, opts, settingCenterFromScope, type, _m,
3507 _this = this;
3508 if (!this.validateCoords(scope.center)) {
3509 $log.error("angular-google-maps: could not find a valid center property");
3510 return;
3511 }
3512 if (!angular.isDefined(scope.zoom)) {
3513 $log.error("angular-google-maps: map zoom property not set");
3514 return;
3515 }
3516 el = angular.element(element);
3517 el.addClass("angular-google-map");
3518 opts = {
3519 options: {}
3520 };
3521 if (attrs.options) {
3522 opts.options = scope.options;
3523 }
3524 if (attrs.styles) {
3525 opts.styles = scope.styles;
3526 }
3527 if (attrs.type) {
3528 type = attrs.type.toUpperCase();
3529 if (google.maps.MapTypeId.hasOwnProperty(type)) {
3530 opts.mapTypeId = google.maps.MapTypeId[attrs.type.toUpperCase()];
3531 } else {
3532 $log.error("angular-google-maps: invalid map type \"" + attrs.type + "\"");
3533 }
3534 }
3535 _m = new google.maps.Map(el.find("div")[1], angular.extend({}, DEFAULTS, opts, {
3536 center: this.getCoords(scope.center),
3537 draggable: this.isTrue(attrs.draggable),
3538 zoom: scope.zoom,
3539 bounds: scope.bounds
3540 }));
3541 dragging = false;
3542 google.maps.event.addListener(_m, "dragstart", function() {
3543 dragging = true;
3544 return _.defer(function() {
3545 return scope.$apply(function(s) {
3546 if (s.dragging != null) {
3547 return s.dragging = dragging;
3548 }
3549 });
3550 });
3551 });
3552 google.maps.event.addListener(_m, "dragend", function() {
3553 dragging = false;
3554 return _.defer(function() {
3555 return scope.$apply(function(s) {
3556 if (s.dragging != null) {
3557 return s.dragging = dragging;
3558 }
3559 });
3560 });
3561 });
3562 google.maps.event.addListener(_m, "drag", function() {
3563 var c;
3564 c = _m.center;
3565 return _.defer(function() {
3566 return scope.$apply(function(s) {
3567 if (angular.isDefined(s.center.type)) {
3568 s.center.coordinates[1] = c.lat();
3569 return s.center.coordinates[0] = c.lng();
3570 } else {
3571 s.center.latitude = c.lat();
3572 return s.center.longitude = c.lng();
3573 }
3574 });
3575 });
3576 });
3577 google.maps.event.addListener(_m, "zoom_changed", function() {
3578 if (scope.zoom !== _m.zoom) {
3579 return _.defer(function() {
3580 return scope.$apply(function(s) {
3581 return s.zoom = _m.zoom;
3582 });
3583 });
3584 }
3585 });
3586 settingCenterFromScope = false;
3587 google.maps.event.addListener(_m, "center_changed", function() {
3588 var c;
3589 c = _m.center;
3590 if (settingCenterFromScope) {
3591 return;
3592 }
3593 return _.defer(function() {
3594 return scope.$apply(function(s) {
3595 if (!_m.dragging) {
3596 if (angular.isDefined(s.center.type)) {
3597 if (s.center.coordinates[1] !== c.lat()) {
3598 s.center.coordinates[1] = c.lat();
3599 }
3600 if (s.center.coordinates[0] !== c.lng()) {
3601 return s.center.coordinates[0] = c.lng();
3602 }
3603 } else {
3604 if (s.center.latitude !== c.lat()) {
3605 s.center.latitude = c.lat();
3606 }
3607 if (s.center.longitude !== c.lng()) {
3608 return s.center.longitude = c.lng();
3609 }
3610 }
3611 }
3612 });
3613 });
3614 });
3615 google.maps.event.addListener(_m, "idle", function() {
3616 var b, ne, sw;
3617 b = _m.getBounds();
3618 ne = b.getNorthEast();
3619 sw = b.getSouthWest();
3620 return _.defer(function() {
3621 return scope.$apply(function(s) {
3622 if (s.bounds !== null && s.bounds !== undefined && s.bounds !== void 0) {
3623 s.bounds.northeast = {
3624 latitude: ne.lat(),
3625 longitude: ne.lng()
3626 };
3627 return s.bounds.southwest = {
3628 latitude: sw.lat(),
3629 longitude: sw.lng()
3630 };
3631 }
3632 });
3633 });
3634 });
3635 if (angular.isDefined(scope.events) && scope.events !== null && angular.isObject(scope.events)) {
3636 getEventHandler = function(eventName) {
3637 return function() {
3638 return scope.events[eventName].apply(scope, [_m, eventName, arguments]);
3639 };
3640 };
3641 for (eventName in scope.events) {
3642 if (scope.events.hasOwnProperty(eventName) && angular.isFunction(scope.events[eventName])) {
3643 google.maps.event.addListener(_m, eventName, getEventHandler(eventName));
3644 }
3645 }
3646 }
3647 scope.map = _m;
3648 if ((attrs.control != null) && (scope.control != null)) {
3649 scope.control.refresh = function(maybeCoords) {
3650 var coords;
3651 if (_m == null) {
3652 return;
3653 }
3654 google.maps.event.trigger(_m, "resize");
3655 if (((maybeCoords != null ? maybeCoords.latitude : void 0) != null) && ((maybeCoords != null ? maybeCoords.latitude : void 0) != null)) {
3656 coords = _this.getCoords(maybeCoords);
3657 if (_this.isTrue(attrs.pan)) {
3658 return _m.panTo(coords);
3659 } else {
3660 return _m.setCenter(coords);
3661 }
3662 }
3663 };
3664 /*
3665 I am sure you all will love this. You want the instance here you go.. BOOM!
3666 */
3667
3668 scope.control.getGMap = function() {
3669 return _m;
3670 };
3671 }
3672 scope.$watch("center", (function(newValue, oldValue) {
3673 var coords;
3674 coords = _this.getCoords(newValue);
3675 if (coords.lat() === _m.center.lat() && coords.lng() === _m.center.lng()) {
3676 return;
3677 }
3678 settingCenterFromScope = true;
3679 if (!dragging) {
3680 if (!_this.validateCoords(newValue)) {
3681 $log.error("Invalid center for newValue: " + (JSON.stringify(newValue)));
3682 }
3683 if (_this.isTrue(attrs.pan) && scope.zoom === _m.zoom) {
3684 _m.panTo(coords);
3685 } else {
3686 _m.setCenter(coords);
3687 }
3688 }
3689 return settingCenterFromScope = false;
3690 }), true);
3691 scope.$watch("zoom", function(newValue, oldValue) {
3692 if (newValue === _m.zoom) {
3693 return;
3694 }
3695 return _.defer(function() {
3696 return _m.setZoom(newValue);
3697 });
3698 });
3699 scope.$watch("bounds", function(newValue, oldValue) {
3700 var bounds, ne, sw;
3701 if (newValue === oldValue) {
3702 return;
3703 }
3704 if ((newValue.northeast.latitude == null) || (newValue.northeast.longitude == null) || (newValue.southwest.latitude == null) || (newValue.southwest.longitude == null)) {
3705 $log.error("Invalid map bounds for new value: " + (JSON.stringify(newValue)));
3706 return;
3707 }
3708 ne = new google.maps.LatLng(newValue.northeast.latitude, newValue.northeast.longitude);
3709 sw = new google.maps.LatLng(newValue.southwest.latitude, newValue.southwest.longitude);
3710 bounds = new google.maps.LatLngBounds(sw, ne);
3711 return _m.fitBounds(bounds);
3712 });
3713 scope.$watch("options", function(newValue, oldValue) {
3714 if (!_.isEqual(newValue, oldValue)) {
3715 opts.options = newValue;
3716 if (_m != null) {
3717 return _m.setOptions(opts);
3718 }
3719 }
3720 }, true);
3721 return scope.$watch("styles", function(newValue, oldValue) {
3722 if (!_.isEqual(newValue, oldValue)) {
3723 opts.styles = newValue;
3724 if (_m != null) {
3725 return _m.setOptions(opts);
3726 }
3727 }
3728 }, true);
3729 };
3730
3731 return Map;
3732
3733 })(BaseObject);
3734 }
3735 ]);
3736
3737 }).call(this);
3738
3739 (function() {
3740 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3741 __hasProp = {}.hasOwnProperty,
3742 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3743
3744 angular.module("google-maps.directives.api").factory("Marker", [
3745 "IMarker", "MarkerParentModel", "MarkerManager", function(IMarker, MarkerParentModel, MarkerManager) {
3746 var Marker;
3747 return Marker = (function(_super) {
3748 __extends(Marker, _super);
3749
3750 function Marker($timeout) {
3751 this.link = __bind(this.link, this);
3752 var self;
3753 Marker.__super__.constructor.call(this, $timeout);
3754 self = this;
3755 this.template = '<span class="angular-google-map-marker" ng-transclude></span>';
3756 this.$log.info(this);
3757 }
3758
3759 Marker.prototype.controller = [
3760 '$scope', '$element', function($scope, $element) {
3761 return {
3762 getMarkerScope: function() {
3763 return $scope;
3764 }
3765 };
3766 }
3767 ];
3768
3769 Marker.prototype.link = function(scope, element, attrs, ctrl) {
3770 var doFit;
3771 if (scope.fit) {
3772 doFit = true;
3773 }
3774 if (!this.gMarkerManager) {
3775 this.gMarkerManager = new MarkerManager(ctrl.getMap());
3776 }
3777 return new MarkerParentModel(scope, element, attrs, ctrl, this.$timeout, this.gMarkerManager, doFit);
3778 };
3779
3780 return Marker;
3781
3782 })(IMarker);
3783 }
3784 ]);
3785
3786 }).call(this);
3787
3788 (function() {
3789 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3790 __hasProp = {}.hasOwnProperty,
3791 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3792
3793 angular.module("google-maps.directives.api").factory("Markers", [
3794 "IMarker", "MarkersParentModel", function(IMarker, MarkersParentModel) {
3795 var Markers;
3796 return Markers = (function(_super) {
3797 __extends(Markers, _super);
3798
3799 function Markers($timeout) {
3800 this.link = __bind(this.link, this);
3801 var self;
3802 Markers.__super__.constructor.call(this, $timeout);
3803 this.template = '<span class="angular-google-map-markers" ng-transclude></span>';
3804 this.scope.idKey = '=idkey';
3805 this.scope.doRebuildAll = '=dorebuildall';
3806 this.scope.models = '=models';
3807 this.scope.doCluster = '=docluster';
3808 this.scope.clusterOptions = '=clusteroptions';
3809 this.scope.clusterEvents = '=clusterevents';
3810 this.scope.labelContent = '=labelcontent';
3811 this.scope.labelAnchor = '@labelanchor';
3812 this.scope.labelClass = '@labelclass';
3813 this.$timeout = $timeout;
3814 self = this;
3815 this.$log.info(this);
3816 }
3817
3818 Markers.prototype.controller = [
3819 '$scope', '$element', function($scope, $element) {
3820 return {
3821 getMarkersScope: function() {
3822 return $scope;
3823 }
3824 };
3825 }
3826 ];
3827
3828 Markers.prototype.link = function(scope, element, attrs, ctrl) {
3829 return new MarkersParentModel(scope, element, attrs, ctrl, this.$timeout);
3830 };
3831
3832 return Markers;
3833
3834 })(IMarker);
3835 }
3836 ]);
3837
3838 }).call(this);
3839
3840 (function() {
3841 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3842 __hasProp = {}.hasOwnProperty,
3843 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3844
3845 angular.module("google-maps.directives.api").factory("Polyline", [
3846 "IPolyline", "$timeout", "array-sync", "PolylineChildModel", function(IPolyline, $timeout, arraySync, PolylineChildModel) {
3847 var Polyline, _ref;
3848 return Polyline = (function(_super) {
3849 __extends(Polyline, _super);
3850
3851 function Polyline() {
3852 this.link = __bind(this.link, this);
3853 _ref = Polyline.__super__.constructor.apply(this, arguments);
3854 return _ref;
3855 }
3856
3857 Polyline.prototype.link = function(scope, element, attrs, mapCtrl) {
3858 var _this = this;
3859 if (angular.isUndefined(scope.path) || scope.path === null || !this.validatePath(scope.path)) {
3860 this.$log.error("polyline: no valid path attribute found");
3861 return;
3862 }
3863 return $timeout(function() {
3864 return new PolylineChildModel(scope, attrs, mapCtrl.getMap(), _this.DEFAULTS);
3865 });
3866 };
3867
3868 return Polyline;
3869
3870 })(IPolyline);
3871 }
3872 ]);
3873
3874 }).call(this);
3875
3876 (function() {
3877 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3878 __hasProp = {}.hasOwnProperty,
3879 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3880
3881 angular.module("google-maps.directives.api").factory("Polylines", [
3882 "IPolyline", "$timeout", "array-sync", "PolylinesParentModel", function(IPolyline, $timeout, arraySync, PolylinesParentModel) {
3883 var Polylines;
3884 return Polylines = (function(_super) {
3885 __extends(Polylines, _super);
3886
3887 function Polylines() {
3888 this.link = __bind(this.link, this);
3889 Polylines.__super__.constructor.call(this);
3890 this.scope.idKey = '=idkey';
3891 this.scope.models = '=models';
3892 this.$log.info(this);
3893 }
3894
3895 Polylines.prototype.link = function(scope, element, attrs, mapCtrl) {
3896 var _this = this;
3897 if (angular.isUndefined(scope.path) || scope.path === null) {
3898 this.$log.error("polylines: no valid path attribute found");
3899 return;
3900 }
3901 if (!scope.models) {
3902 this.$log.error("polylines: no models found to create from");
3903 return;
3904 }
3905 return $timeout(function() {
3906 return new PolylinesParentModel(scope, element, attrs, mapCtrl.getMap(), _this.DEFAULTS);
3907 });
3908 };
3909
3910 return Polylines;
3911
3912 })(IPolyline);
3913 }
3914 ]);
3915
3916 }).call(this);
3917
3918 (function() {
3919 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3920 __hasProp = {}.hasOwnProperty,
3921 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3922
3923 angular.module("google-maps.directives.api").factory("Window", [
3924 "IWindow", "GmapUtil", "WindowChildModel", function(IWindow, GmapUtil, WindowChildModel) {
3925 var Window;
3926 return Window = (function(_super) {
3927 __extends(Window, _super);
3928
3929 Window.include(GmapUtil);
3930
3931 function Window($timeout, $compile, $http, $templateCache) {
3932 this.link = __bind(this.link, this);
3933 var self;
3934 Window.__super__.constructor.call(this, $timeout, $compile, $http, $templateCache);
3935 self = this;
3936 this.require = ['^googleMap', '^?marker'];
3937 this.template = '<span class="angular-google-maps-window" ng-transclude></span>';
3938 this.$log.info(self);
3939 }
3940
3941 Window.prototype.link = function(scope, element, attrs, ctrls) {
3942 var _this = this;
3943 return this.$timeout(function() {
3944 var defaults, hasScopeCoords, isIconVisibleOnClick, mapCtrl, markerCtrl, markerScope, opts, window;
3945 isIconVisibleOnClick = true;
3946 if (angular.isDefined(attrs.isiconvisibleonclick)) {
3947 isIconVisibleOnClick = scope.isIconVisibleOnClick;
3948 }
3949 mapCtrl = ctrls[0].getMap();
3950 markerCtrl = ctrls.length > 1 && (ctrls[1] != null) ? ctrls[1].getMarkerScope().gMarker : void 0;
3951 defaults = scope.options != null ? scope.options : {};
3952 hasScopeCoords = (scope != null) && _this.validateCoords(scope.coords);
3953 opts = hasScopeCoords ? _this.createWindowOptions(markerCtrl, scope, element.html(), defaults) : defaults;
3954 if (mapCtrl != null) {
3955 window = new WindowChildModel({}, scope, opts, isIconVisibleOnClick, mapCtrl, markerCtrl, element);
3956 }
3957 scope.$on("$destroy", function() {
3958 return window.destroy();
3959 });
3960 if (ctrls[1] != null) {
3961 markerScope = ctrls[1].getMarkerScope();
3962 markerScope.$watch('coords', function(newValue, oldValue) {
3963 if (!_this.validateCoords(newValue)) {
3964 return window.hideWindow();
3965 }
3966 if (!angular.equals(newValue, oldValue)) {
3967 return window.getLatestPosition();
3968 }
3969 }, true);
3970 }
3971 if ((_this.onChildCreation != null) && (window != null)) {
3972 return _this.onChildCreation(window);
3973 }
3974 }, GmapUtil.defaultDelay + 25);
3975 };
3976
3977 return Window;
3978
3979 })(IWindow);
3980 }
3981 ]);
3982
3983 }).call(this);
3984
3985 (function() {
3986 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3987 __hasProp = {}.hasOwnProperty,
3988 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3989
3990 angular.module("google-maps.directives.api").factory("Windows", [
3991 "IWindow", "WindowsParentModel", function(IWindow, WindowsParentModel) {
3992 /*
3993 Windows directive where many windows map to the models property
3994 */
3995
3996 var Windows;
3997 return Windows = (function(_super) {
3998 __extends(Windows, _super);
3999
4000 function Windows($timeout, $compile, $http, $templateCache, $interpolate) {
4001 this.link = __bind(this.link, this);
4002 var self;
4003 Windows.__super__.constructor.call(this, $timeout, $compile, $http, $templateCache);
4004 self = this;
4005 this.$interpolate = $interpolate;
4006 this.require = ['^googleMap', '^?markers'];
4007 this.template = '<span class="angular-google-maps-windows" ng-transclude></span>';
4008 this.scope.idKey = '=idkey';
4009 this.scope.doRebuildAll = '=dorebuildall';
4010 this.scope.models = '=models';
4011 this.$log.info(this);
4012 }
4013
4014 Windows.prototype.link = function(scope, element, attrs, ctrls) {
4015 return new WindowsParentModel(scope, element, attrs, ctrls, this.$timeout, this.$compile, this.$http, this.$templateCache, this.$interpolate);
4016 };
4017
4018 return Windows;
4019
4020 })(IWindow);
4021 }
4022 ]);
4023
4024 }).call(this);
4025
4026 /*
4027 !
4028 The MIT License
4029
4030 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4031
4032 Permission is hereby granted, free of charge, to any person obtaining a copy
4033 of this software and associated documentation files (the "Software"), to deal
4034 in the Software without restriction, including without limitation the rights
4035 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4036 copies of the Software, and to permit persons to whom the Software is
4037 furnished to do so, subject to the following conditions:
4038
4039 The above copyright notice and this permission notice shall be included in
4040 all copies or substantial portions of the Software.
4041
4042 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4043 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4044 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4045 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4046 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4047 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4048 THE SOFTWARE.
4049
4050 angular-google-maps
4051 https://github.com/nlaplante/angular-google-maps
4052
4053 @authors
4054 Nicolas Laplante - https://plus.google.com/108189012221374960701
4055 Nicholas McCready - https://twitter.com/nmccready
4056 Nick Baugh - https://github.com/niftylettuce
4057 */
4058
4059
4060 (function() {
4061 angular.module("google-maps").directive("googleMap", [
4062 "Map", function(Map) {
4063 return new Map();
4064 }
4065 ]);
4066
4067 }).call(this);
4068
4069 /*
4070 !
4071 The MIT License
4072
4073 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4074
4075 Permission is hereby granted, free of charge, to any person obtaining a copy
4076 of this software and associated documentation files (the "Software"), to deal
4077 in the Software without restriction, including without limitation the rights
4078 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4079 copies of the Software, and to permit persons to whom the Software is
4080 furnished to do so, subject to the following conditions:
4081
4082 The above copyright notice and this permission notice shall be included in
4083 all copies or substantial portions of the Software.
4084
4085 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4086 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4087 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4088 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4089 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4090 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4091 THE SOFTWARE.
4092
4093 angular-google-maps
4094 https://github.com/nlaplante/angular-google-maps
4095
4096 @authors
4097 Nicolas Laplante - https://plus.google.com/108189012221374960701
4098 Nicholas McCready - https://twitter.com/nmccready
4099 */
4100
4101
4102 /*
4103 Map marker directive
4104
4105 This directive is used to create a marker on an existing map.
4106 This directive creates a new scope.
4107
4108 {attribute coords required} object containing latitude and longitude properties
4109 {attribute icon optional} string url to image used for marker icon
4110 {attribute animate optional} if set to false, the marker won't be animated (on by default)
4111 */
4112
4113
4114 (function() {
4115 angular.module("google-maps").directive("marker", [
4116 "$timeout", "Marker", function($timeout, Marker) {
4117 return new Marker($timeout);
4118 }
4119 ]);
4120
4121 }).call(this);
4122
4123 /*
4124 !
4125 The MIT License
4126
4127 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4128
4129 Permission is hereby granted, free of charge, to any person obtaining a copy
4130 of this software and associated documentation files (the "Software"), to deal
4131 in the Software without restriction, including without limitation the rights
4132 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4133 copies of the Software, and to permit persons to whom the Software is
4134 furnished to do so, subject to the following conditions:
4135
4136 The above copyright notice and this permission notice shall be included in
4137 all copies or substantial portions of the Software.
4138
4139 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4140 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4141 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4142 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4143 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4144 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4145 THE SOFTWARE.
4146
4147 angular-google-maps
4148 https://github.com/nlaplante/angular-google-maps
4149
4150 @authors
4151 Nicolas Laplante - https://plus.google.com/108189012221374960701
4152 Nicholas McCready - https://twitter.com/nmccready
4153 */
4154
4155
4156 /*
4157 Map marker directive
4158
4159 This directive is used to create a marker on an existing map.
4160 This directive creates a new scope.
4161
4162 {attribute coords required} object containing latitude and longitude properties
4163 {attribute icon optional} string url to image used for marker icon
4164 {attribute animate optional} if set to false, the marker won't be animated (on by default)
4165 */
4166
4167
4168 (function() {
4169 angular.module("google-maps").directive("markers", [
4170 "$timeout", "Markers", function($timeout, Markers) {
4171 return new Markers($timeout);
4172 }
4173 ]);
4174
4175 }).call(this);
4176
4177 /*
4178 !
4179 The MIT License
4180
4181 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4182
4183 Permission is hereby granted, free of charge, to any person obtaining a copy
4184 of this software and associated documentation files (the "Software"), to deal
4185 in the Software without restriction, including without limitation the rights
4186 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4187 copies of the Software, and to permit persons to whom the Software is
4188 furnished to do so, subject to the following conditions:
4189
4190 The above copyright notice and this permission notice shall be included in
4191 all copies or substantial portions of the Software.
4192
4193 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4194 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4195 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4196 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4197 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4198 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4199 THE SOFTWARE.
4200
4201 angular-google-maps
4202 https://github.com/nlaplante/angular-google-maps
4203
4204 @authors Bruno Queiroz, creativelikeadog@gmail.com
4205 */
4206
4207
4208 /*
4209 Marker label directive
4210
4211 This directive is used to create a marker label on an existing map.
4212
4213 {attribute content required} content of the label
4214 {attribute anchor required} string that contains the x and y point position of the label
4215 {attribute class optional} class to DOM object
4216 {attribute style optional} style for the label
4217 */
4218
4219
4220 /*
4221 Basic Directive api for a label. Basic in the sense that this directive contains 1:1 on scope and model.
4222 Thus there will be one html element per marker within the directive.
4223 */
4224
4225
4226 (function() {
4227 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
4228 __hasProp = {}.hasOwnProperty,
4229 __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
4230
4231 angular.module("google-maps").directive("markerLabel", [
4232 "$timeout", "ILabel", "MarkerLabelChildModel", "GmapUtil", function($timeout, ILabel, MarkerLabelChildModel, GmapUtil) {
4233 var Label;
4234 Label = (function(_super) {
4235 __extends(Label, _super);
4236
4237 function Label($timeout) {
4238 this.link = __bind(this.link, this);
4239 var self;
4240 Label.__super__.constructor.call(this, $timeout);
4241 self = this;
4242 this.require = '^marker';
4243 this.template = '<span class="angular-google-maps-marker-label" ng-transclude></span>';
4244 this.$log.info(this);
4245 }
4246
4247 Label.prototype.link = function(scope, element, attrs, ctrl) {
4248 var _this = this;
4249 return this.$timeout(function() {
4250 var label, markerCtrl;
4251 markerCtrl = ctrl.getMarkerScope().gMarker;
4252 if (markerCtrl != null) {
4253 label = new MarkerLabelChildModel(markerCtrl, scope);
4254 }
4255 return scope.$on("$destroy", function() {
4256 return label.destroy();
4257 });
4258 }, GmapUtil.defaultDelay + 25);
4259 };
4260
4261 return Label;
4262
4263 })(ILabel);
4264 return new Label($timeout);
4265 }
4266 ]);
4267
4268 }).call(this);
4269
4270 /*
4271 !
4272 The MIT License
4273
4274 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4275
4276 Permission is hereby granted, free of charge, to any person obtaining a copy
4277 of this software and associated documentation files (the "Software"), to deal
4278 in the Software without restriction, including without limitation the rights
4279 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4280 copies of the Software, and to permit persons to whom the Software is
4281 furnished to do so, subject to the following conditions:
4282
4283 The above copyright notice and this permission notice shall be included in
4284 all copies or substantial portions of the Software.
4285
4286 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4287 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4288 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4289 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4290 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4291 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4292 THE SOFTWARE.
4293
4294 angular-google-maps
4295 https://github.com/nlaplante/angular-google-maps
4296
4297 @authors
4298 Nicolas Laplante - https://plus.google.com/108189012221374960701
4299 Nicholas McCready - https://twitter.com/nmccready
4300 Rick Huizinga - https://plus.google.com/+RickHuizinga
4301 */
4302
4303
4304 (function() {
4305 angular.module("google-maps").directive("polygon", [
4306 "$log", "$timeout", "array-sync", "GmapUtil", function($log, $timeout, arraySync, GmapUtil) {
4307 /*
4308 Check if a value is true
4309 */
4310
4311 var DEFAULTS, isTrue;
4312 isTrue = function(val) {
4313 return angular.isDefined(val) && val !== null && val === true || val === "1" || val === "y" || val === "true";
4314 };
4315 "use strict";
4316 DEFAULTS = {};
4317 return {
4318 restrict: "ECA",
4319 replace: true,
4320 require: "^googleMap",
4321 scope: {
4322 path: "=path",
4323 stroke: "=stroke",
4324 clickable: "=",
4325 draggable: "=",
4326 editable: "=",
4327 geodesic: "=",
4328 fill: "=",
4329 icons: "=icons",
4330 visible: "=",
4331 "static": "=",
4332 events: "=",
4333 zIndex: "=zindex",
4334 fit: "="
4335 },
4336 link: function(scope, element, attrs, mapCtrl) {
4337 if (angular.isUndefined(scope.path) || scope.path === null || !GmapUtil.validatePath(scope.path)) {
4338 $log.error("polygon: no valid path attribute found");
4339 return;
4340 }
4341 return $timeout(function() {
4342 var arraySyncer, buildOpts, eventName, getEventHandler, map, pathPoints, polygon;
4343 buildOpts = function(pathPoints) {
4344 var opts;
4345 opts = angular.extend({}, DEFAULTS, {
4346 map: map,
4347 path: pathPoints,
4348 strokeColor: scope.stroke && scope.stroke.color,
4349 strokeOpacity: scope.stroke && scope.stroke.opacity,
4350 strokeWeight: scope.stroke && scope.stroke.weight,
4351 fillColor: scope.fill && scope.fill.color,
4352 fillOpacity: scope.fill && scope.fill.opacity
4353 });
4354 angular.forEach({
4355 clickable: true,
4356 draggable: false,
4357 editable: false,
4358 geodesic: false,
4359 visible: true,
4360 "static": false,
4361 fit: false,
4362 zIndex: 0
4363 }, function(defaultValue, key) {
4364 if (angular.isUndefined(scope[key]) || scope[key] === null) {
4365 return opts[key] = defaultValue;
4366 } else {
4367 return opts[key] = scope[key];
4368 }
4369 });
4370 if (opts["static"]) {
4371 opts.editable = false;
4372 }
4373 return opts;
4374 };
4375 map = mapCtrl.getMap();
4376 pathPoints = GmapUtil.convertPathPoints(scope.path);
4377 polygon = new google.maps.Polygon(buildOpts(pathPoints));
4378 if (scope.fit) {
4379 GmapUtil.extendMapBounds(map, pathPoints);
4380 }
4381 if (!scope["static"] && angular.isDefined(scope.editable)) {
4382 scope.$watch("editable", function(newValue, oldValue) {
4383 if (newValue !== oldValue) {
4384 return polygon.setEditable(newValue);
4385 }
4386 });
4387 }
4388 if (angular.isDefined(scope.draggable)) {
4389 scope.$watch("draggable", function(newValue, oldValue) {
4390 if (newValue !== oldValue) {
4391 return polygon.setDraggable(newValue);
4392 }
4393 });
4394 }
4395 if (angular.isDefined(scope.visible)) {
4396 scope.$watch("visible", function(newValue, oldValue) {
4397 if (newValue !== oldValue) {
4398 return polygon.setVisible(newValue);
4399 }
4400 });
4401 }
4402 if (angular.isDefined(scope.geodesic)) {
4403 scope.$watch("geodesic", function(newValue, oldValue) {
4404 if (newValue !== oldValue) {
4405 return polygon.setOptions(buildOpts(polygon.getPath()));
4406 }
4407 });
4408 }
4409 if (angular.isDefined(scope.stroke) && angular.isDefined(scope.stroke.opacity)) {
4410 scope.$watch("stroke.opacity", function(newValue, oldValue) {
4411 return polygon.setOptions(buildOpts(polygon.getPath()));
4412 });
4413 }
4414 if (angular.isDefined(scope.stroke) && angular.isDefined(scope.stroke.weight)) {
4415 scope.$watch("stroke.weight", function(newValue, oldValue) {
4416 if (newValue !== oldValue) {
4417 return polygon.setOptions(buildOpts(polygon.getPath()));
4418 }
4419 });
4420 }
4421 if (angular.isDefined(scope.stroke) && angular.isDefined(scope.stroke.color)) {
4422 scope.$watch("stroke.color", function(newValue, oldValue) {
4423 if (newValue !== oldValue) {
4424 return polygon.setOptions(buildOpts(polygon.getPath()));
4425 }
4426 });
4427 }
4428 if (angular.isDefined(scope.fill) && angular.isDefined(scope.fill.color)) {
4429 scope.$watch("fill.color", function(newValue, oldValue) {
4430 if (newValue !== oldValue) {
4431 return polygon.setOptions(buildOpts(polygon.getPath()));
4432 }
4433 });
4434 }
4435 if (angular.isDefined(scope.fill) && angular.isDefined(scope.fill.opacity)) {
4436 scope.$watch("fill.opacity", function(newValue, oldValue) {
4437 if (newValue !== oldValue) {
4438 return polygon.setOptions(buildOpts(polygon.getPath()));
4439 }
4440 });
4441 }
4442 if (angular.isDefined(scope.zIndex)) {
4443 scope.$watch("zIndex", function(newValue, oldValue) {
4444 if (newValue !== oldValue) {
4445 return polygon.setOptions(buildOpts(polygon.getPath()));
4446 }
4447 });
4448 }
4449 if (angular.isDefined(scope.events) && scope.events !== null && angular.isObject(scope.events)) {
4450 getEventHandler = function(eventName) {
4451 return function() {
4452 return scope.events[eventName].apply(scope, [polygon, eventName, arguments]);
4453 };
4454 };
4455 for (eventName in scope.events) {
4456 if (scope.events.hasOwnProperty(eventName) && angular.isFunction(scope.events[eventName])) {
4457 polygon.addListener(eventName, getEventHandler(eventName));
4458 }
4459 }
4460 }
4461 arraySyncer = arraySync(polygon.getPath(), scope, "path", function(pathPoints) {
4462 if (scope.fit) {
4463 return GmapUtil.extendMapBounds(map, pathPoints);
4464 }
4465 });
4466 return scope.$on("$destroy", function() {
4467 polygon.setMap(null);
4468 if (arraySyncer) {
4469 arraySyncer();
4470 return arraySyncer = null;
4471 }
4472 });
4473 });
4474 }
4475 };
4476 }
4477 ]);
4478
4479 }).call(this);
4480
4481 /*
4482 !
4483 The MIT License
4484
4485 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4486
4487 Permission is hereby granted, free of charge, to any person obtaining a copy
4488 of this software and associated documentation files (the "Software"), to deal
4489 in the Software without restriction, including without limitation the rights
4490 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4491 copies of the Software, and to permit persons to whom the Software is
4492 furnished to do so, subject to the following conditions:
4493
4494 The above copyright notice and this permission notice shall be included in
4495 all copies or substantial portions of the Software.
4496
4497 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4498 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4499 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4500 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4501 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4502 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4503 THE SOFTWARE.
4504
4505 @authors
4506 Julian Popescu - https://github.com/jpopesculian
4507 Rick Huizinga - https://plus.google.com/+RickHuizinga
4508 */
4509
4510
4511 (function() {
4512 angular.module("google-maps").directive("circle", [
4513 "$log", "$timeout", "GmapUtil", "EventsHelper", function($log, $timeout, GmapUtil, EventsHelper) {
4514 "use strict";
4515 var DEFAULTS;
4516 DEFAULTS = {};
4517 return {
4518 restrict: "ECA",
4519 replace: true,
4520 require: "^googleMap",
4521 scope: {
4522 center: "=center",
4523 radius: "=radius",
4524 stroke: "=stroke",
4525 fill: "=fill",
4526 clickable: "=",
4527 draggable: "=",
4528 editable: "=",
4529 geodesic: "=",
4530 icons: "=icons",
4531 visible: "=",
4532 events: "="
4533 },
4534 link: function(scope, element, attrs, mapCtrl) {
4535 return $timeout(function() {
4536 var buildOpts, circle, map;
4537 buildOpts = function() {
4538 var opts;
4539 if (!GmapUtil.validateCoords(scope.center)) {
4540 $log.error("circle: no valid center attribute found");
4541 return;
4542 }
4543 opts = angular.extend({}, DEFAULTS, {
4544 map: map,
4545 center: GmapUtil.getCoords(scope.center),
4546 radius: scope.radius,
4547 strokeColor: scope.stroke && scope.stroke.color,
4548 strokeOpacity: scope.stroke && scope.stroke.opacity,
4549 strokeWeight: scope.stroke && scope.stroke.weight,
4550 fillColor: scope.fill && scope.fill.color,
4551 fillOpacity: scope.fill && scope.fill.opacity
4552 });
4553 angular.forEach({
4554 clickable: true,
4555 draggable: false,
4556 editable: false,
4557 geodesic: false,
4558 visible: true
4559 }, function(defaultValue, key) {
4560 if (angular.isUndefined(scope[key]) || scope[key] === null) {
4561 return opts[key] = defaultValue;
4562 } else {
4563 return opts[key] = scope[key];
4564 }
4565 });
4566 return opts;
4567 };
4568 map = mapCtrl.getMap();
4569 circle = new google.maps.Circle(buildOpts());
4570 scope.$watchCollection('center', function(newVals, oldVals) {
4571 if (newVals !== oldVals) {
4572 return circle.setOptions(buildOpts());
4573 }
4574 });
4575 scope.$watchCollection('stroke', function(newVals, oldVals) {
4576 if (newVals !== oldVals) {
4577 return circle.setOptions(buildOpts());
4578 }
4579 });
4580 scope.$watchCollection('fill', function(newVals, oldVals) {
4581 if (newVals !== oldVals) {
4582 return circle.setOptions(buildOpts());
4583 }
4584 });
4585 scope.$watch('radius', function(newVal, oldVal) {
4586 if (newVal !== oldVal) {
4587 return circle.setOptions(buildOpts());
4588 }
4589 });
4590 scope.$watch('clickable', function(newVal, oldVal) {
4591 if (newVal !== oldVal) {
4592 return circle.setOptions(buildOpts());
4593 }
4594 });
4595 scope.$watch('editable', function(newVal, oldVal) {
4596 if (newVal !== oldVal) {
4597 return circle.setOptions(buildOpts());
4598 }
4599 });
4600 scope.$watch('draggable', function(newVal, oldVal) {
4601 if (newVal !== oldVal) {
4602 return circle.setOptions(buildOpts());
4603 }
4604 });
4605 scope.$watch('visible', function(newVal, oldVal) {
4606 if (newVal !== oldVal) {
4607 return circle.setOptions(buildOpts());
4608 }
4609 });
4610 scope.$watch('geodesic', function(newVal, oldVal) {
4611 if (newVal !== oldVal) {
4612 return circle.setOptions(buildOpts());
4613 }
4614 });
4615 EventsHelper.setEvents(circle, scope, scope);
4616 google.maps.event.addListener(circle, 'radius_changed', function() {
4617 scope.radius = circle.getRadius();
4618 return $timeout(function() {
4619 return scope.$apply();
4620 });
4621 });
4622 google.maps.event.addListener(circle, 'center_changed', function() {
4623 if (angular.isDefined(scope.center.type)) {
4624 scope.center.coordinates[1] = circle.getCenter().lat();
4625 scope.center.coordinates[0] = circle.getCenter().lng();
4626 } else {
4627 scope.center.latitude = circle.getCenter().lat();
4628 scope.center.longitude = circle.getCenter().lng();
4629 }
4630 return $timeout(function() {
4631 return scope.$apply();
4632 });
4633 });
4634 return scope.$on("$destroy", function() {
4635 return circle.setMap(null);
4636 });
4637 });
4638 }
4639 };
4640 }
4641 ]);
4642
4643 }).call(this);
4644
4645 /*
4646 !
4647 The MIT License
4648
4649 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4650
4651 Permission is hereby granted, free of charge, to any person obtaining a copy
4652 of this software and associated documentation files (the "Software"), to deal
4653 in the Software without restriction, including without limitation the rights
4654 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4655 copies of the Software, and to permit persons to whom the Software is
4656 furnished to do so, subject to the following conditions:
4657
4658 The above copyright notice and this permission notice shall be included in
4659 all copies or substantial portions of the Software.
4660
4661 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4662 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4663 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4664 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4665 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4666 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4667 THE SOFTWARE.
4668
4669 angular-google-maps
4670 https://github.com/nlaplante/angular-google-maps
4671
4672 @authors
4673 Nicolas Laplante - https://plus.google.com/108189012221374960701
4674 Nicholas McCready - https://twitter.com/nmccready
4675 */
4676
4677
4678 (function() {
4679 angular.module("google-maps").directive("polyline", [
4680 "Polyline", function(Polyline) {
4681 return new Polyline();
4682 }
4683 ]);
4684
4685 }).call(this);
4686
4687 /*
4688 !
4689 The MIT License
4690
4691 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4692
4693 Permission is hereby granted, free of charge, to any person obtaining a copy
4694 of this software and associated documentation files (the "Software"), to deal
4695 in the Software without restriction, including without limitation the rights
4696 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4697 copies of the Software, and to permit persons to whom the Software is
4698 furnished to do so, subject to the following conditions:
4699
4700 The above copyright notice and this permission notice shall be included in
4701 all copies or substantial portions of the Software.
4702
4703 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4704 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4705 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4706 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4707 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4708 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4709 THE SOFTWARE.
4710
4711 angular-google-maps
4712 https://github.com/nlaplante/angular-google-maps
4713
4714 @authors
4715 Nicolas Laplante - https://plus.google.com/108189012221374960701
4716 Nicholas McCready - https://twitter.com/nmccready
4717 */
4718
4719
4720 (function() {
4721 angular.module("google-maps").directive("polylines", [
4722 "Polylines", function(Polylines) {
4723 return new Polylines();
4724 }
4725 ]);
4726
4727 }).call(this);
4728
4729 /*
4730 !
4731 The MIT License
4732
4733 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4734
4735 Permission is hereby granted, free of charge, to any person obtaining a copy
4736 of this software and associated documentation files (the "Software"), to deal
4737 in the Software without restriction, including without limitation the rights
4738 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4739 copies of the Software, and to permit persons to whom the Software is
4740 furnished to do so, subject to the following conditions:
4741
4742 The above copyright notice and this permission notice shall be included in
4743 all copies or substantial portions of the Software.
4744
4745 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4746 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4747 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4748 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4749 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4750 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4751 THE SOFTWARE.
4752
4753 angular-google-maps
4754 https://github.com/nlaplante/angular-google-maps
4755
4756 @authors
4757 Nicolas Laplante - https://plus.google.com/108189012221374960701
4758 Nicholas McCready - https://twitter.com/nmccready
4759 Chentsu Lin - https://github.com/ChenTsuLin
4760 */
4761
4762
4763 (function() {
4764 angular.module("google-maps").directive("rectangle", [
4765 "$log", "$timeout", function($log, $timeout) {
4766 var DEFAULTS, convertBoundPoints, fitMapBounds, isTrue, validateBoundPoints;
4767 validateBoundPoints = function(bounds) {
4768 if (angular.isUndefined(bounds.sw.latitude) || angular.isUndefined(bounds.sw.longitude) || angular.isUndefined(bounds.ne.latitude) || angular.isUndefined(bounds.ne.longitude)) {
4769 return false;
4770 }
4771 return true;
4772 };
4773 convertBoundPoints = function(bounds) {
4774 var result;
4775 result = new google.maps.LatLngBounds(new google.maps.LatLng(bounds.sw.latitude, bounds.sw.longitude), new google.maps.LatLng(bounds.ne.latitude, bounds.ne.longitude));
4776 return result;
4777 };
4778 fitMapBounds = function(map, bounds) {
4779 return map.fitBounds(bounds);
4780 };
4781 /*
4782 Check if a value is true
4783 */
4784
4785 isTrue = function(val) {
4786 return angular.isDefined(val) && val !== null && val === true || val === "1" || val === "y" || val === "true";
4787 };
4788 "use strict";
4789 DEFAULTS = {};
4790 return {
4791 restrict: "ECA",
4792 require: "^googleMap",
4793 replace: true,
4794 scope: {
4795 bounds: "=",
4796 stroke: "=",
4797 clickable: "=",
4798 draggable: "=",
4799 editable: "=",
4800 fill: "=",
4801 visible: "="
4802 },
4803 link: function(scope, element, attrs, mapCtrl) {
4804 if (angular.isUndefined(scope.bounds) || scope.bounds === null || angular.isUndefined(scope.bounds.sw) || scope.bounds.sw === null || angular.isUndefined(scope.bounds.ne) || scope.bounds.ne === null || !validateBoundPoints(scope.bounds)) {
4805 $log.error("rectangle: no valid bound attribute found");
4806 return;
4807 }
4808 return $timeout(function() {
4809 var buildOpts, dragging, map, rectangle, settingBoundsFromScope;
4810 buildOpts = function(bounds) {
4811 var opts;
4812 opts = angular.extend({}, DEFAULTS, {
4813 map: map,
4814 bounds: bounds,
4815 strokeColor: scope.stroke && scope.stroke.color,
4816 strokeOpacity: scope.stroke && scope.stroke.opacity,
4817 strokeWeight: scope.stroke && scope.stroke.weight,
4818 fillColor: scope.fill && scope.fill.color,
4819 fillOpacity: scope.fill && scope.fill.opacity
4820 });
4821 angular.forEach({
4822 clickable: true,
4823 draggable: false,
4824 editable: false,
4825 visible: true
4826 }, function(defaultValue, key) {
4827 if (angular.isUndefined(scope[key]) || scope[key] === null) {
4828 return opts[key] = defaultValue;
4829 } else {
4830 return opts[key] = scope[key];
4831 }
4832 });
4833 return opts;
4834 };
4835 map = mapCtrl.getMap();
4836 rectangle = new google.maps.Rectangle(buildOpts(convertBoundPoints(scope.bounds)));
4837 if (isTrue(attrs.fit)) {
4838 fitMapBounds(map, bounds);
4839 }
4840 dragging = false;
4841 google.maps.event.addListener(rectangle, "mousedown", function() {
4842 google.maps.event.addListener(rectangle, "mousemove", function() {
4843 dragging = true;
4844 return _.defer(function() {
4845 return scope.$apply(function(s) {
4846 if (s.dragging != null) {
4847 return s.dragging = dragging;
4848 }
4849 });
4850 });
4851 });
4852 google.maps.event.addListener(rectangle, "mouseup", function() {
4853 google.maps.event.clearListeners(rectangle, 'mousemove');
4854 google.maps.event.clearListeners(rectangle, 'mouseup');
4855 dragging = false;
4856 return _.defer(function() {
4857 return scope.$apply(function(s) {
4858 if (s.dragging != null) {
4859 return s.dragging = dragging;
4860 }
4861 });
4862 });
4863 });
4864 });
4865 settingBoundsFromScope = false;
4866 google.maps.event.addListener(rectangle, "bounds_changed", function() {
4867 var b, ne, sw;
4868 b = rectangle.getBounds();
4869 ne = b.getNorthEast();
4870 sw = b.getSouthWest();
4871 if (settingBoundsFromScope) {
4872 return;
4873 }
4874 return _.defer(function() {
4875 return scope.$apply(function(s) {
4876 if (!rectangle.dragging) {
4877 if (s.bounds !== null && s.bounds !== undefined && s.bounds !== void 0) {
4878 s.bounds.ne = {
4879 latitude: ne.lat(),
4880 longitude: ne.lng()
4881 };
4882 s.bounds.sw = {
4883 latitude: sw.lat(),
4884 longitude: sw.lng()
4885 };
4886 }
4887 }
4888 });
4889 });
4890 });
4891 scope.$watch("bounds", (function(newValue, oldValue) {
4892 var bounds;
4893 if (_.isEqual(newValue, oldValue)) {
4894 return;
4895 }
4896 settingBoundsFromScope = true;
4897 if (!dragging) {
4898 if ((newValue.sw.latitude == null) || (newValue.sw.longitude == null) || (newValue.ne.latitude == null) || (newValue.ne.longitude == null)) {
4899 $log.error("Invalid bounds for newValue: " + (JSON.stringify(newValue)));
4900 }
4901 bounds = new google.maps.LatLngBounds(new google.maps.LatLng(newValue.sw.latitude, newValue.sw.longitude), new google.maps.LatLng(newValue.ne.latitude, newValue.ne.longitude));
4902 rectangle.setBounds(bounds);
4903 }
4904 return settingBoundsFromScope = false;
4905 }), true);
4906 if (angular.isDefined(scope.editable)) {
4907 scope.$watch("editable", function(newValue, oldValue) {
4908 return rectangle.setEditable(newValue);
4909 });
4910 }
4911 if (angular.isDefined(scope.draggable)) {
4912 scope.$watch("draggable", function(newValue, oldValue) {
4913 return rectangle.setDraggable(newValue);
4914 });
4915 }
4916 if (angular.isDefined(scope.visible)) {
4917 scope.$watch("visible", function(newValue, oldValue) {
4918 return rectangle.setVisible(newValue);
4919 });
4920 }
4921 if (angular.isDefined(scope.stroke)) {
4922 if (angular.isDefined(scope.stroke.color)) {
4923 scope.$watch("stroke.color", function(newValue, oldValue) {
4924 return rectangle.setOptions(buildOpts(rectangle.getBounds()));
4925 });
4926 }
4927 if (angular.isDefined(scope.stroke.weight)) {
4928 scope.$watch("stroke.weight", function(newValue, oldValue) {
4929 return rectangle.setOptions(buildOpts(rectangle.getBounds()));
4930 });
4931 }
4932 if (angular.isDefined(scope.stroke.opacity)) {
4933 scope.$watch("stroke.opacity", function(newValue, oldValue) {
4934 return rectangle.setOptions(buildOpts(rectangle.getBounds()));
4935 });
4936 }
4937 }
4938 if (angular.isDefined(scope.fill)) {
4939 if (angular.isDefined(scope.fill.color)) {
4940 scope.$watch("fill.color", function(newValue, oldValue) {
4941 return rectangle.setOptions(buildOpts(rectangle.getBounds()));
4942 });
4943 }
4944 if (angular.isDefined(scope.fill.opacity)) {
4945 scope.$watch("fill.opacity", function(newValue, oldValue) {
4946 return rectangle.setOptions(buildOpts(rectangle.getBounds()));
4947 });
4948 }
4949 }
4950 return scope.$on("$destroy", function() {
4951 return rectangle.setMap(null);
4952 });
4953 });
4954 }
4955 };
4956 }
4957 ]);
4958
4959 }).call(this);
4960
4961 /*
4962 !
4963 The MIT License
4964
4965 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
4966
4967 Permission is hereby granted, free of charge, to any person obtaining a copy
4968 of this software and associated documentation files (the "Software"), to deal
4969 in the Software without restriction, including without limitation the rights
4970 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4971 copies of the Software, and to permit persons to whom the Software is
4972 furnished to do so, subject to the following conditions:
4973
4974 The above copyright notice and this permission notice shall be included in
4975 all copies or substantial portions of the Software.
4976
4977 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4978 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4979 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4980 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4981 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4982 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4983 THE SOFTWARE.
4984
4985 angular-google-maps
4986 https://github.com/nlaplante/angular-google-maps
4987
4988 @authors
4989 Nicolas Laplante - https://plus.google.com/108189012221374960701
4990 Nicholas McCready - https://twitter.com/nmccready
4991 */
4992
4993
4994 /*
4995 Map info window directive
4996
4997 This directive is used to create an info window on an existing map.
4998 This directive creates a new scope.
4999
5000 {attribute coords required} object containing latitude and longitude properties
5001 {attribute show optional} map will show when this expression returns true
5002 */
5003
5004
5005 (function() {
5006 angular.module("google-maps").directive("window", [
5007 "$timeout", "$compile", "$http", "$templateCache", "Window", function($timeout, $compile, $http, $templateCache, Window) {
5008 return new Window($timeout, $compile, $http, $templateCache);
5009 }
5010 ]);
5011
5012 }).call(this);
5013
5014 /*
5015 !
5016 The MIT License
5017
5018 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
5019
5020 Permission is hereby granted, free of charge, to any person obtaining a copy
5021 of this software and associated documentation files (the "Software"), to deal
5022 in the Software without restriction, including without limitation the rights
5023 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5024 copies of the Software, and to permit persons to whom the Software is
5025 furnished to do so, subject to the following conditions:
5026
5027 The above copyright notice and this permission notice shall be included in
5028 all copies or substantial portions of the Software.
5029
5030 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5031 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5032 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
5033 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
5034 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
5035 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
5036 THE SOFTWARE.
5037
5038 angular-google-maps
5039 https://github.com/nlaplante/angular-google-maps
5040
5041 @authors
5042 Nicolas Laplante - https://plus.google.com/108189012221374960701
5043 Nicholas McCready - https://twitter.com/nmccready
5044 */
5045
5046
5047 /*
5048 Map info window directive
5049
5050 This directive is used to create an info window on an existing map.
5051 This directive creates a new scope.
5052
5053 {attribute coords required} object containing latitude and longitude properties
5054 {attribute show optional} map will show when this expression returns true
5055 */
5056
5057
5058 (function() {
5059 angular.module("google-maps").directive("windows", [
5060 "$timeout", "$compile", "$http", "$templateCache", "$interpolate", "Windows", function($timeout, $compile, $http, $templateCache, $interpolate, Windows) {
5061 return new Windows($timeout, $compile, $http, $templateCache, $interpolate);
5062 }
5063 ]);
5064
5065 }).call(this);
5066
5067 /*
5068 !
5069 The MIT License
5070
5071 Copyright (c) 2010-2013 Google, Inc. http://angularjs.org
5072
5073 Permission is hereby granted, free of charge, to any person obtaining a copy
5074 of this software and associated documentation files (the "Software"), to deal
5075 in the Software without restriction, including without limitation the rights
5076 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5077 copies of the Software, and to permit persons to whom the Software is
5078 furnished to do so, subject to the following conditions:
5079
5080 The above copyright notice and this permission notice shall be included in
5081 all copies or substantial portions of the Software.
5082
5083 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5084 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5085 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
5086 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
5087 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
5088 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
5089 THE SOFTWARE.
5090
5091 angular-google-maps
5092 https://github.com/nlaplante/angular-google-maps
5093
5094 @authors:
5095 - Nicolas Laplante https://plus.google.com/108189012221374960701
5096 - Nicholas McCready - https://twitter.com/nmccready
5097 */
5098
5099
5100 /*
5101 Map Layer directive
5102
5103 This directive is used to create any type of Layer from the google maps sdk.
5104 This directive creates a new scope.
5105
5106 {attribute show optional} true (default) shows the trafficlayer otherwise it is hidden
5107 */
5108
5109
5110 (function() {
5111 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
5112
5113 angular.module("google-maps").directive("layer", [
5114 "$timeout", "Logger", "LayerParentModel", function($timeout, Logger, LayerParentModel) {
5115 var Layer;
5116 Layer = (function() {
5117 function Layer($timeout) {
5118 this.$timeout = $timeout;
5119 this.link = __bind(this.link, this);
5120 this.$log = Logger;
5121 this.restrict = "ECMA";
5122 this.require = "^googleMap";
5123 this.priority = -1;
5124 this.transclude = true;
5125 this.template = '<span class=\"angular-google-map-layer\" ng-transclude></span>';
5126 this.replace = true;
5127 this.scope = {
5128 show: "=show",
5129 type: "=type",
5130 namespace: "=namespace",
5131 options: '=options',
5132 onCreated: '&oncreated'
5133 };
5134 }
5135
5136 Layer.prototype.link = function(scope, element, attrs, mapCtrl) {
5137 if (attrs.oncreated != null) {
5138 return new LayerParentModel(scope, element, attrs, mapCtrl, this.$timeout, scope.onCreated);
5139 } else {
5140 return new LayerParentModel(scope, element, attrs, mapCtrl, this.$timeout);
5141 }
5142 };
5143
5144 return Layer;
5145
5146 })();
5147 return new Layer($timeout);
5148 }
5149 ]);
5150
5151 }).call(this);
5152 ;/**
5153 * @name InfoBox
5154 * @version 1.1.12 [December 11, 2012]
5155 * @author Gary Little (inspired by proof-of-concept code from Pamela Fox of Google)
5156 * @copyright Copyright 2010 Gary Little [gary at luxcentral.com]
5157 * @fileoverview InfoBox extends the Google Maps JavaScript API V3 <tt>OverlayView</tt> class.
5158 * <p>
5159 * An InfoBox behaves like a <tt>google.maps.InfoWindow</tt>, but it supports several
5160 * additional properties for advanced styling. An InfoBox can also be used as a map label.
5161 * <p>
5162 * An InfoBox also fires the same events as a <tt>google.maps.InfoWindow</tt>.
5163 */
5164
5165 /*!
5166 *
5167 * Licensed under the Apache License, Version 2.0 (the "License");
5168 * you may not use this file except in compliance with the License.
5169 * You may obtain a copy of the License at
5170 *
5171 * http://www.apache.org/licenses/LICENSE-2.0
5172 *
5173 * Unless required by applicable law or agreed to in writing, software
5174 * distributed under the License is distributed on an "AS IS" BASIS,
5175 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5176 * See the License for the specific language governing permissions and
5177 * limitations under the License.
5178 */
5179
5180 /*jslint browser:true */
5181 /*global google */
5182
5183 /**
5184 * @name InfoBoxOptions
5185 * @class This class represents the optional parameter passed to the {@link InfoBox} constructor.
5186 * @property {string|Node} content The content of the InfoBox (plain text or an HTML DOM node).
5187 * @property {boolean} [disableAutoPan=false] Disable auto-pan on <tt>open</tt>.
5188 * @property {number} maxWidth The maximum width (in pixels) of the InfoBox. Set to 0 if no maximum.
5189 * @property {Size} pixelOffset The offset (in pixels) from the top left corner of the InfoBox
5190 * (or the bottom left corner if the <code>alignBottom</code> property is <code>true</code>)
5191 * to the map pixel corresponding to <tt>position</tt>.
5192 * @property {LatLng} position The geographic location at which to display the InfoBox.
5193 * @property {number} zIndex The CSS z-index style value for the InfoBox.
5194 * Note: This value overrides a zIndex setting specified in the <tt>boxStyle</tt> property.
5195 * @property {string} [boxClass="infoBox"] The name of the CSS class defining the styles for the InfoBox container.
5196 * @property {Object} [boxStyle] An object literal whose properties define specific CSS
5197 * style values to be applied to the InfoBox. Style values defined here override those that may
5198 * be defined in the <code>boxClass</code> style sheet. If this property is changed after the
5199 * InfoBox has been created, all previously set styles (except those defined in the style sheet)
5200 * are removed from the InfoBox before the new style values are applied.
5201 * @property {string} closeBoxMargin The CSS margin style value for the close box.
5202 * The default is "2px" (a 2-pixel margin on all sides).
5203 * @property {string} closeBoxURL The URL of the image representing the close box.
5204 * Note: The default is the URL for Google's standard close box.
5205 * Set this property to "" if no close box is required.
5206 * @property {Size} infoBoxClearance Minimum offset (in pixels) from the InfoBox to the
5207 * map edge after an auto-pan.
5208 * @property {boolean} [isHidden=false] Hide the InfoBox on <tt>open</tt>.
5209 * [Deprecated in favor of the <tt>visible</tt> property.]
5210 * @property {boolean} [visible=true] Show the InfoBox on <tt>open</tt>.
5211 * @property {boolean} alignBottom Align the bottom left corner of the InfoBox to the <code>position</code>
5212 * location (default is <tt>false</tt> which means that the top left corner of the InfoBox is aligned).
5213 * @property {string} pane The pane where the InfoBox is to appear (default is "floatPane").
5214 * Set the pane to "mapPane" if the InfoBox is being used as a map label.
5215 * Valid pane names are the property names for the <tt>google.maps.MapPanes</tt> object.
5216 * @property {boolean} enableEventPropagation Propagate mousedown, mousemove, mouseover, mouseout,
5217 * mouseup, click, dblclick, touchstart, touchend, touchmove, and contextmenu events in the InfoBox
5218 * (default is <tt>false</tt> to mimic the behavior of a <tt>google.maps.InfoWindow</tt>). Set
5219 * this property to <tt>true</tt> if the InfoBox is being used as a map label.
5220 */
5221
5222 /**
5223 * Creates an InfoBox with the options specified in {@link InfoBoxOptions}.
5224 * Call <tt>InfoBox.open</tt> to add the box to the map.
5225 * @constructor
5226 * @param {InfoBoxOptions} [opt_opts]
5227 */
5228 function InfoBox(opt_opts) {
5229
5230 opt_opts = opt_opts || {};
5231
5232 google.maps.OverlayView.apply(this, arguments);
5233
5234 // Standard options (in common with google.maps.InfoWindow):
5235 //
5236 this.content_ = opt_opts.content || "";
5237 this.disableAutoPan_ = opt_opts.disableAutoPan || false;
5238 this.maxWidth_ = opt_opts.maxWidth || 0;
5239 this.pixelOffset_ = opt_opts.pixelOffset || new google.maps.Size(0, 0);
5240 this.position_ = opt_opts.position || new google.maps.LatLng(0, 0);
5241 this.zIndex_ = opt_opts.zIndex || null;
5242
5243 // Additional options (unique to InfoBox):
5244 //
5245 this.boxClass_ = opt_opts.boxClass || "infoBox";
5246 this.boxStyle_ = opt_opts.boxStyle || {};
5247 this.closeBoxMargin_ = opt_opts.closeBoxMargin || "2px";
5248 this.closeBoxURL_ = opt_opts.closeBoxURL || "http://www.google.com/intl/en_us/mapfiles/close.gif";
5249 if (opt_opts.closeBoxURL === "") {
5250 this.closeBoxURL_ = "";
5251 }
5252 this.infoBoxClearance_ = opt_opts.infoBoxClearance || new google.maps.Size(1, 1);
5253
5254 if (typeof opt_opts.visible === "undefined") {
5255 if (typeof opt_opts.isHidden === "undefined") {
5256 opt_opts.visible = true;
5257 } else {
5258 opt_opts.visible = !opt_opts.isHidden;
5259 }
5260 }
5261 this.isHidden_ = !opt_opts.visible;
5262
5263 this.alignBottom_ = opt_opts.alignBottom || false;
5264 this.pane_ = opt_opts.pane || "floatPane";
5265 this.enableEventPropagation_ = opt_opts.enableEventPropagation || false;
5266
5267 this.div_ = null;
5268 this.closeListener_ = null;
5269 this.moveListener_ = null;
5270 this.contextListener_ = null;
5271 this.eventListeners_ = null;
5272 this.fixedWidthSet_ = null;
5273 }
5274
5275 /* InfoBox extends OverlayView in the Google Maps API v3.
5276 */
5277 InfoBox.prototype = new google.maps.OverlayView();
5278
5279 /**
5280 * Creates the DIV representing the InfoBox.
5281 * @private
5282 */
5283 InfoBox.prototype.createInfoBoxDiv_ = function () {
5284
5285 var i;
5286 var events;
5287 var bw;
5288 var me = this;
5289
5290 // This handler prevents an event in the InfoBox from being passed on to the map.
5291 //
5292 var cancelHandler = function (e) {
5293 e.cancelBubble = true;
5294 if (e.stopPropagation) {
5295 e.stopPropagation();
5296 }
5297 };
5298
5299 // This handler ignores the current event in the InfoBox and conditionally prevents
5300 // the event from being passed on to the map. It is used for the contextmenu event.
5301 //
5302 var ignoreHandler = function (e) {
5303
5304 e.returnValue = false;
5305
5306 if (e.preventDefault) {
5307
5308 e.preventDefault();
5309 }
5310
5311 if (!me.enableEventPropagation_) {
5312
5313 cancelHandler(e);
5314 }
5315 };
5316
5317 if (!this.div_) {
5318
5319 this.div_ = document.createElement("div");
5320
5321 this.setBoxStyle_();
5322
5323 if (typeof this.content_.nodeType === "undefined") {
5324 this.div_.innerHTML = this.getCloseBoxImg_() + this.content_;
5325 } else {
5326 this.div_.innerHTML = this.getCloseBoxImg_();
5327 this.div_.appendChild(this.content_);
5328 }
5329
5330 // Add the InfoBox DIV to the DOM
5331 this.getPanes()[this.pane_].appendChild(this.div_);
5332
5333 this.addClickHandler_();
5334
5335 if (this.div_.style.width) {
5336
5337 this.fixedWidthSet_ = true;
5338
5339 } else {
5340
5341 if (this.maxWidth_ !== 0 && this.div_.offsetWidth > this.maxWidth_) {
5342
5343 this.div_.style.width = this.maxWidth_;
5344 this.div_.style.overflow = "auto";
5345 this.fixedWidthSet_ = true;
5346
5347 } else { // The following code is needed to overcome problems with MSIE
5348
5349 bw = this.getBoxWidths_();
5350
5351 this.div_.style.width = (this.div_.offsetWidth - bw.left - bw.right) + "px";
5352 this.fixedWidthSet_ = false;
5353 }
5354 }
5355
5356 this.panBox_(this.disableAutoPan_);
5357
5358 if (!this.enableEventPropagation_) {
5359
5360 this.eventListeners_ = [];
5361
5362 // Cancel event propagation.
5363 //
5364 // Note: mousemove not included (to resolve Issue 152)
5365 events = ["mousedown", "mouseover", "mouseout", "mouseup",
5366 "click", "dblclick", "touchstart", "touchend", "touchmove"];
5367
5368 for (i = 0; i < events.length; i++) {
5369
5370 this.eventListeners_.push(google.maps.event.addDomListener(this.div_, events[i], cancelHandler));
5371 }
5372
5373 // Workaround for Google bug that causes the cursor to change to a pointer
5374 // when the mouse moves over a marker underneath InfoBox.
5375 this.eventListeners_.push(google.maps.event.addDomListener(this.div_, "mouseover", function (e) {
5376 this.style.cursor = "default";
5377 }));
5378 }
5379
5380 this.contextListener_ = google.maps.event.addDomListener(this.div_, "contextmenu", ignoreHandler);
5381
5382 /**
5383 * This event is fired when the DIV containing the InfoBox's content is attached to the DOM.
5384 * @name InfoBox#domready
5385 * @event
5386 */
5387 google.maps.event.trigger(this, "domready");
5388 }
5389 };
5390
5391 /**
5392 * Returns the HTML <IMG> tag for the close box.
5393 * @private
5394 */
5395 InfoBox.prototype.getCloseBoxImg_ = function () {
5396
5397 var img = "";
5398
5399 if (this.closeBoxURL_ !== "") {
5400
5401 img = "<img";
5402 img += " src='" + this.closeBoxURL_ + "'";
5403 img += " align=right"; // Do this because Opera chokes on style='float: right;'
5404 img += " style='";
5405 img += " position: relative;"; // Required by MSIE
5406 img += " cursor: pointer;";
5407 img += " margin: " + this.closeBoxMargin_ + ";";
5408 img += "'>";
5409 }
5410
5411 return img;
5412 };
5413
5414 /**
5415 * Adds the click handler to the InfoBox close box.
5416 * @private
5417 */
5418 InfoBox.prototype.addClickHandler_ = function () {
5419
5420 var closeBox;
5421
5422 if (this.closeBoxURL_ !== "") {
5423
5424 closeBox = this.div_.firstChild;
5425 this.closeListener_ = google.maps.event.addDomListener(closeBox, "click", this.getCloseClickHandler_());
5426
5427 } else {
5428
5429 this.closeListener_ = null;
5430 }
5431 };
5432
5433 /**
5434 * Returns the function to call when the user clicks the close box of an InfoBox.
5435 * @private
5436 */
5437 InfoBox.prototype.getCloseClickHandler_ = function () {
5438
5439 var me = this;
5440
5441 return function (e) {
5442
5443 // 1.0.3 fix: Always prevent propagation of a close box click to the map:
5444 e.cancelBubble = true;
5445
5446 if (e.stopPropagation) {
5447
5448 e.stopPropagation();
5449 }
5450
5451 /**
5452 * This event is fired when the InfoBox's close box is clicked.
5453 * @name InfoBox#closeclick
5454 * @event
5455 */
5456 google.maps.event.trigger(me, "closeclick");
5457
5458 me.close();
5459 };
5460 };
5461
5462 /**
5463 * Pans the map so that the InfoBox appears entirely within the map's visible area.
5464 * @private
5465 */
5466 InfoBox.prototype.panBox_ = function (disablePan) {
5467
5468 var map;
5469 var bounds;
5470 var xOffset = 0, yOffset = 0;
5471
5472 if (!disablePan) {
5473
5474 map = this.getMap();
5475
5476 if (map instanceof google.maps.Map) { // Only pan if attached to map, not panorama
5477
5478 if (!map.getBounds().contains(this.position_)) {
5479 // Marker not in visible area of map, so set center
5480 // of map to the marker position first.
5481 map.setCenter(this.position_);
5482 }
5483
5484 bounds = map.getBounds();
5485
5486 var mapDiv = map.getDiv();
5487 var mapWidth = mapDiv.offsetWidth;
5488 var mapHeight = mapDiv.offsetHeight;
5489 var iwOffsetX = this.pixelOffset_.width;
5490 var iwOffsetY = this.pixelOffset_.height;
5491 var iwWidth = this.div_.offsetWidth;
5492 var iwHeight = this.div_.offsetHeight;
5493 var padX = this.infoBoxClearance_.width;
5494 var padY = this.infoBoxClearance_.height;
5495 var pixPosition = this.getProjection().fromLatLngToContainerPixel(this.position_);
5496
5497 if (pixPosition.x < (-iwOffsetX + padX)) {
5498 xOffset = pixPosition.x + iwOffsetX - padX;
5499 } else if ((pixPosition.x + iwWidth + iwOffsetX + padX) > mapWidth) {
5500 xOffset = pixPosition.x + iwWidth + iwOffsetX + padX - mapWidth;
5501 }
5502 if (this.alignBottom_) {
5503 if (pixPosition.y < (-iwOffsetY + padY + iwHeight)) {
5504 yOffset = pixPosition.y + iwOffsetY - padY - iwHeight;
5505 } else if ((pixPosition.y + iwOffsetY + padY) > mapHeight) {
5506 yOffset = pixPosition.y + iwOffsetY + padY - mapHeight;
5507 }
5508 } else {
5509 if (pixPosition.y < (-iwOffsetY + padY)) {
5510 yOffset = pixPosition.y + iwOffsetY - padY;
5511 } else if ((pixPosition.y + iwHeight + iwOffsetY + padY) > mapHeight) {
5512 yOffset = pixPosition.y + iwHeight + iwOffsetY + padY - mapHeight;
5513 }
5514 }
5515
5516 if (!(xOffset === 0 && yOffset === 0)) {
5517
5518 // Move the map to the shifted center.
5519 //
5520 var c = map.getCenter();
5521 map.panBy(xOffset, yOffset);
5522 }
5523 }
5524 }
5525 };
5526
5527 /**
5528 * Sets the style of the InfoBox by setting the style sheet and applying
5529 * other specific styles requested.
5530 * @private
5531 */
5532 InfoBox.prototype.setBoxStyle_ = function () {
5533
5534 var i, boxStyle;
5535
5536 if (this.div_) {
5537
5538 // Apply style values from the style sheet defined in the boxClass parameter:
5539 this.div_.className = this.boxClass_;
5540
5541 // Clear existing inline style values:
5542 this.div_.style.cssText = "";
5543
5544 // Apply style values defined in the boxStyle parameter:
5545 boxStyle = this.boxStyle_;
5546 for (i in boxStyle) {
5547
5548 if (boxStyle.hasOwnProperty(i)) {
5549
5550 this.div_.style[i] = boxStyle[i];
5551 }
5552 }
5553
5554 // Fix up opacity style for benefit of MSIE:
5555 //
5556 if (typeof this.div_.style.opacity !== "undefined" && this.div_.style.opacity !== "") {
5557
5558 this.div_.style.filter = "alpha(opacity=" + (this.div_.style.opacity * 100) + ")";
5559 }
5560
5561 // Apply required styles:
5562 //
5563 this.div_.style.position = "absolute";
5564 this.div_.style.visibility = 'hidden';
5565 if (this.zIndex_ !== null) {
5566
5567 this.div_.style.zIndex = this.zIndex_;
5568 }
5569 }
5570 };
5571
5572 /**
5573 * Get the widths of the borders of the InfoBox.
5574 * @private
5575 * @return {Object} widths object (top, bottom left, right)
5576 */
5577 InfoBox.prototype.getBoxWidths_ = function () {
5578
5579 var computedStyle;
5580 var bw = {top: 0, bottom: 0, left: 0, right: 0};
5581 var box = this.div_;
5582
5583 if (document.defaultView && document.defaultView.getComputedStyle) {
5584
5585 computedStyle = box.ownerDocument.defaultView.getComputedStyle(box, "");
5586
5587 if (computedStyle) {
5588
5589 // The computed styles are always in pixel units (good!)
5590 bw.top = parseInt(computedStyle.borderTopWidth, 10) || 0;
5591 bw.bottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
5592 bw.left = parseInt(computedStyle.borderLeftWidth, 10) || 0;
5593 bw.right = parseInt(computedStyle.borderRightWidth, 10) || 0;
5594 }
5595
5596 } else if (document.documentElement.currentStyle) { // MSIE
5597
5598 if (box.currentStyle) {
5599
5600 // The current styles may not be in pixel units, but assume they are (bad!)
5601 bw.top = parseInt(box.currentStyle.borderTopWidth, 10) || 0;
5602 bw.bottom = parseInt(box.currentStyle.borderBottomWidth, 10) || 0;
5603 bw.left = parseInt(box.currentStyle.borderLeftWidth, 10) || 0;
5604 bw.right = parseInt(box.currentStyle.borderRightWidth, 10) || 0;
5605 }
5606 }
5607
5608 return bw;
5609 };
5610
5611 /**
5612 * Invoked when <tt>close</tt> is called. Do not call it directly.
5613 */
5614 InfoBox.prototype.onRemove = function () {
5615
5616 if (this.div_) {
5617
5618 this.div_.parentNode.removeChild(this.div_);
5619 this.div_ = null;
5620 }
5621 };
5622
5623 /**
5624 * Draws the InfoBox based on the current map projection and zoom level.
5625 */
5626 InfoBox.prototype.draw = function () {
5627
5628 this.createInfoBoxDiv_();
5629
5630 var pixPosition = this.getProjection().fromLatLngToDivPixel(this.position_);
5631
5632 this.div_.style.left = (pixPosition.x + this.pixelOffset_.width) + "px";
5633
5634 if (this.alignBottom_) {
5635 this.div_.style.bottom = -(pixPosition.y + this.pixelOffset_.height) + "px";
5636 } else {
5637 this.div_.style.top = (pixPosition.y + this.pixelOffset_.height) + "px";
5638 }
5639
5640 if (this.isHidden_) {
5641
5642 this.div_.style.visibility = 'hidden';
5643
5644 } else {
5645
5646 this.div_.style.visibility = "visible";
5647 }
5648 };
5649
5650 /**
5651 * Sets the options for the InfoBox. Note that changes to the <tt>maxWidth</tt>,
5652 * <tt>closeBoxMargin</tt>, <tt>closeBoxURL</tt>, and <tt>enableEventPropagation</tt>
5653 * properties have no affect until the current InfoBox is <tt>close</tt>d and a new one
5654 * is <tt>open</tt>ed.
5655 * @param {InfoBoxOptions} opt_opts
5656 */
5657 InfoBox.prototype.setOptions = function (opt_opts) {
5658 if (typeof opt_opts.boxClass !== "undefined") { // Must be first
5659
5660 this.boxClass_ = opt_opts.boxClass;
5661 this.setBoxStyle_();
5662 }
5663 if (typeof opt_opts.boxStyle !== "undefined") { // Must be second
5664
5665 this.boxStyle_ = opt_opts.boxStyle;
5666 this.setBoxStyle_();
5667 }
5668 if (typeof opt_opts.content !== "undefined") {
5669
5670 this.setContent(opt_opts.content);
5671 }
5672 if (typeof opt_opts.disableAutoPan !== "undefined") {
5673
5674 this.disableAutoPan_ = opt_opts.disableAutoPan;
5675 }
5676 if (typeof opt_opts.maxWidth !== "undefined") {
5677
5678 this.maxWidth_ = opt_opts.maxWidth;
5679 }
5680 if (typeof opt_opts.pixelOffset !== "undefined") {
5681
5682 this.pixelOffset_ = opt_opts.pixelOffset;
5683 }
5684 if (typeof opt_opts.alignBottom !== "undefined") {
5685
5686 this.alignBottom_ = opt_opts.alignBottom;
5687 }
5688 if (typeof opt_opts.position !== "undefined") {
5689
5690 this.setPosition(opt_opts.position);
5691 }
5692 if (typeof opt_opts.zIndex !== "undefined") {
5693
5694 this.setZIndex(opt_opts.zIndex);
5695 }
5696 if (typeof opt_opts.closeBoxMargin !== "undefined") {
5697
5698 this.closeBoxMargin_ = opt_opts.closeBoxMargin;
5699 }
5700 if (typeof opt_opts.closeBoxURL !== "undefined") {
5701
5702 this.closeBoxURL_ = opt_opts.closeBoxURL;
5703 }
5704 if (typeof opt_opts.infoBoxClearance !== "undefined") {
5705
5706 this.infoBoxClearance_ = opt_opts.infoBoxClearance;
5707 }
5708 if (typeof opt_opts.isHidden !== "undefined") {
5709
5710 this.isHidden_ = opt_opts.isHidden;
5711 }
5712 if (typeof opt_opts.visible !== "undefined") {
5713
5714 this.isHidden_ = !opt_opts.visible;
5715 }
5716 if (typeof opt_opts.enableEventPropagation !== "undefined") {
5717
5718 this.enableEventPropagation_ = opt_opts.enableEventPropagation;
5719 }
5720
5721 if (this.div_) {
5722
5723 this.draw();
5724 }
5725 };
5726
5727 /**
5728 * Sets the content of the InfoBox.
5729 * The content can be plain text or an HTML DOM node.
5730 * @param {string|Node} content
5731 */
5732 InfoBox.prototype.setContent = function (content) {
5733 this.content_ = content;
5734
5735 if (this.div_) {
5736
5737 if (this.closeListener_) {
5738
5739 google.maps.event.removeListener(this.closeListener_);
5740 this.closeListener_ = null;
5741 }
5742
5743 // Odd code required to make things work with MSIE.
5744 //
5745 if (!this.fixedWidthSet_) {
5746
5747 this.div_.style.width = "";
5748 }
5749
5750 if (typeof content.nodeType === "undefined") {
5751 this.div_.innerHTML = this.getCloseBoxImg_() + content;
5752 } else {
5753 this.div_.innerHTML = this.getCloseBoxImg_();
5754 this.div_.appendChild(content);
5755 }
5756
5757 // Perverse code required to make things work with MSIE.
5758 // (Ensures the close box does, in fact, float to the right.)
5759 //
5760 if (!this.fixedWidthSet_) {
5761 this.div_.style.width = this.div_.offsetWidth + "px";
5762 if (typeof content.nodeType === "undefined") {
5763 this.div_.innerHTML = this.getCloseBoxImg_() + content;
5764 } else {
5765 this.div_.innerHTML = this.getCloseBoxImg_();
5766 this.div_.appendChild(content);
5767 }
5768 }
5769
5770 this.addClickHandler_();
5771 }
5772
5773 /**
5774 * This event is fired when the content of the InfoBox changes.
5775 * @name InfoBox#content_changed
5776 * @event
5777 */
5778 google.maps.event.trigger(this, "content_changed");
5779 };
5780
5781 /**
5782 * Sets the geographic location of the InfoBox.
5783 * @param {LatLng} latlng
5784 */
5785 InfoBox.prototype.setPosition = function (latlng) {
5786
5787 this.position_ = latlng;
5788
5789 if (this.div_) {
5790
5791 this.draw();
5792 }
5793
5794 /**
5795 * This event is fired when the position of the InfoBox changes.
5796 * @name InfoBox#position_changed
5797 * @event
5798 */
5799 google.maps.event.trigger(this, "position_changed");
5800 };
5801
5802 /**
5803 * Sets the zIndex style for the InfoBox.
5804 * @param {number} index
5805 */
5806 InfoBox.prototype.setZIndex = function (index) {
5807
5808 this.zIndex_ = index;
5809
5810 if (this.div_) {
5811
5812 this.div_.style.zIndex = index;
5813 }
5814
5815 /**
5816 * This event is fired when the zIndex of the InfoBox changes.
5817 * @name InfoBox#zindex_changed
5818 * @event
5819 */
5820 google.maps.event.trigger(this, "zindex_changed");
5821 };
5822
5823 /**
5824 * Sets the visibility of the InfoBox.
5825 * @param {boolean} isVisible
5826 */
5827 InfoBox.prototype.setVisible = function (isVisible) {
5828
5829 this.isHidden_ = !isVisible;
5830 if (this.div_) {
5831 this.div_.style.visibility = (this.isHidden_ ? "hidden" : "visible");
5832 }
5833 };
5834
5835 /**
5836 * Returns the content of the InfoBox.
5837 * @returns {string}
5838 */
5839 InfoBox.prototype.getContent = function () {
5840
5841 return this.content_;
5842 };
5843
5844 /**
5845 * Returns the geographic location of the InfoBox.
5846 * @returns {LatLng}
5847 */
5848 InfoBox.prototype.getPosition = function () {
5849
5850 return this.position_;
5851 };
5852
5853 /**
5854 * Returns the zIndex for the InfoBox.
5855 * @returns {number}
5856 */
5857 InfoBox.prototype.getZIndex = function () {
5858
5859 return this.zIndex_;
5860 };
5861
5862 /**
5863 * Returns a flag indicating whether the InfoBox is visible.
5864 * @returns {boolean}
5865 */
5866 InfoBox.prototype.getVisible = function () {
5867
5868 var isVisible;
5869
5870 if ((typeof this.getMap() === "undefined") || (this.getMap() === null)) {
5871 isVisible = false;
5872 } else {
5873 isVisible = !this.isHidden_;
5874 }
5875 return isVisible;
5876 };
5877
5878 /**
5879 * Shows the InfoBox. [Deprecated; use <tt>setVisible</tt> instead.]
5880 */
5881 InfoBox.prototype.show = function () {
5882
5883 this.isHidden_ = false;
5884 if (this.div_) {
5885 this.div_.style.visibility = "visible";
5886 }
5887 };
5888
5889 /**
5890 * Hides the InfoBox. [Deprecated; use <tt>setVisible</tt> instead.]
5891 */
5892 InfoBox.prototype.hide = function () {
5893
5894 this.isHidden_ = true;
5895 if (this.div_) {
5896 this.div_.style.visibility = "hidden";
5897 }
5898 };
5899
5900 /**
5901 * Adds the InfoBox to the specified map or Street View panorama. If <tt>anchor</tt>
5902 * (usually a <tt>google.maps.Marker</tt>) is specified, the position
5903 * of the InfoBox is set to the position of the <tt>anchor</tt>. If the
5904 * anchor is dragged to a new location, the InfoBox moves as well.
5905 * @param {Map|StreetViewPanorama} map
5906 * @param {MVCObject} [anchor]
5907 */
5908 InfoBox.prototype.open = function (map, anchor) {
5909
5910 var me = this;
5911
5912 if (anchor) {
5913
5914 this.position_ = anchor.getPosition();
5915 this.moveListener_ = google.maps.event.addListener(anchor, "position_changed", function () {
5916 me.setPosition(this.getPosition());
5917 });
5918 }
5919
5920 this.setMap(map);
5921
5922 if (this.div_) {
5923
5924 this.panBox_();
5925 }
5926 };
5927
5928 /**
5929 * Removes the InfoBox from the map.
5930 */
5931 InfoBox.prototype.close = function () {
5932
5933 var i;
5934
5935 if (this.closeListener_) {
5936
5937 google.maps.event.removeListener(this.closeListener_);
5938 this.closeListener_ = null;
5939 }
5940
5941 if (this.eventListeners_) {
5942
5943 for (i = 0; i < this.eventListeners_.length; i++) {
5944
5945 google.maps.event.removeListener(this.eventListeners_[i]);
5946 }
5947 this.eventListeners_ = null;
5948 }
5949
5950 if (this.moveListener_) {
5951
5952 google.maps.event.removeListener(this.moveListener_);
5953 this.moveListener_ = null;
5954 }
5955
5956 if (this.contextListener_) {
5957
5958 google.maps.event.removeListener(this.contextListener_);
5959 this.contextListener_ = null;
5960 }
5961
5962 this.setMap(null);
5963 };;/**
5964 * @name MarkerClustererPlus for Google Maps V3
5965 * @version 2.1.1 [November 4, 2013]
5966 * @author Gary Little
5967 * @fileoverview
5968 * The library creates and manages per-zoom-level clusters for large amounts of markers.
5969 * <p>
5970 * This is an enhanced V3 implementation of the
5971 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/"
5972 * >V2 MarkerClusterer</a> by Xiaoxi Wu. It is based on the
5973 * <a href="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclusterer/"
5974 * >V3 MarkerClusterer</a> port by Luke Mahe. MarkerClustererPlus was created by Gary Little.
5975 * <p>
5976 * v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It
5977 * adds support for the <code>ignoreHidden</code>, <code>title</code>, <code>batchSizeIE</code>,
5978 * and <code>calculator</code> properties as well as support for four more events. It also allows
5979 * greater control over the styling of the text that appears on the cluster marker. The
5980 * documentation has been significantly improved and the overall code has been simplified and
5981 * polished. Very large numbers of markers can now be managed without causing Javascript timeout
5982 * errors on Internet Explorer. Note that the name of the <code>clusterclick</code> event has been
5983 * deprecated. The new name is <code>click</code>, so please change your application code now.
5984 */
5985
5986 /**
5987 * Licensed under the Apache License, Version 2.0 (the "License");
5988 * you may not use this file except in compliance with the License.
5989 * You may obtain a copy of the License at
5990 *
5991 * http://www.apache.org/licenses/LICENSE-2.0
5992 *
5993 * Unless required by applicable law or agreed to in writing, software
5994 * distributed under the License is distributed on an "AS IS" BASIS,
5995 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5996 * See the License for the specific language governing permissions and
5997 * limitations under the License.
5998 */
5999
6000
6001 /**
6002 * @name ClusterIconStyle
6003 * @class This class represents the object for values in the <code>styles</code> array passed
6004 * to the {@link MarkerClusterer} constructor. The element in this array that is used to
6005 * style the cluster icon is determined by calling the <code>calculator</code> function.
6006 *
6007 * @property {string} url The URL of the cluster icon image file. Required.
6008 * @property {number} height The display height (in pixels) of the cluster icon. Required.
6009 * @property {number} width The display width (in pixels) of the cluster icon. Required.
6010 * @property {Array} [anchorText] The position (in pixels) from the center of the cluster icon to
6011 * where the text label is to be centered and drawn. The format is <code>[yoffset, xoffset]</code>
6012 * where <code>yoffset</code> increases as you go down from center and <code>xoffset</code>
6013 * increases to the right of center. The default is <code>[0, 0]</code>.
6014 * @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the
6015 * spot on the cluster icon that is to be aligned with the cluster position. The format is
6016 * <code>[yoffset, xoffset]</code> where <code>yoffset</code> increases as you go down and
6017 * <code>xoffset</code> increases to the right of the top-left corner of the icon. The default
6018 * anchor position is the center of the cluster icon.
6019 * @property {string} [textColor="black"] The color of the label text shown on the
6020 * cluster icon.
6021 * @property {number} [textSize=11] The size (in pixels) of the label text shown on the
6022 * cluster icon.
6023 * @property {string} [textDecoration="none"] The value of the CSS <code>text-decoration</code>
6024 * property for the label text shown on the cluster icon.
6025 * @property {string} [fontWeight="bold"] The value of the CSS <code>font-weight</code>
6026 * property for the label text shown on the cluster icon.
6027 * @property {string} [fontStyle="normal"] The value of the CSS <code>font-style</code>
6028 * property for the label text shown on the cluster icon.
6029 * @property {string} [fontFamily="Arial,sans-serif"] The value of the CSS <code>font-family</code>
6030 * property for the label text shown on the cluster icon.
6031 * @property {string} [backgroundPosition="0 0"] The position of the cluster icon image
6032 * within the image defined by <code>url</code>. The format is <code>"xpos ypos"</code>
6033 * (the same format as for the CSS <code>background-position</code> property). You must set
6034 * this property appropriately when the image defined by <code>url</code> represents a sprite
6035 * containing multiple images. Note that the position <i>must</i> be specified in px units.
6036 */
6037 /**
6038 * @name ClusterIconInfo
6039 * @class This class is an object containing general information about a cluster icon. This is
6040 * the object that a <code>calculator</code> function returns.
6041 *
6042 * @property {string} text The text of the label to be shown on the cluster icon.
6043 * @property {number} index The index plus 1 of the element in the <code>styles</code>
6044 * array to be used to style the cluster icon.
6045 * @property {string} title The tooltip to display when the mouse moves over the cluster icon.
6046 * If this value is <code>undefined</code> or <code>""</code>, <code>title</code> is set to the
6047 * value of the <code>title</code> property passed to the MarkerClusterer.
6048 */
6049 /**
6050 * A cluster icon.
6051 *
6052 * @constructor
6053 * @extends google.maps.OverlayView
6054 * @param {Cluster} cluster The cluster with which the icon is to be associated.
6055 * @param {Array} [styles] An array of {@link ClusterIconStyle} defining the cluster icons
6056 * to use for various cluster sizes.
6057 * @private
6058 */
6059 function ClusterIcon(cluster, styles) {
6060 cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);
6061
6062 this.cluster_ = cluster;
6063 this.className_ = cluster.getMarkerClusterer().getClusterClass();
6064 this.styles_ = styles;
6065 this.center_ = null;
6066 this.div_ = null;
6067 this.sums_ = null;
6068 this.visible_ = false;
6069
6070 this.setMap(cluster.getMap()); // Note: this causes onAdd to be called
6071 }
6072
6073
6074 /**
6075 * Adds the icon to the DOM.
6076 */
6077 ClusterIcon.prototype.onAdd = function () {
6078 var cClusterIcon = this;
6079 var cMouseDownInCluster;
6080 var cDraggingMapByCluster;
6081
6082 this.div_ = document.createElement("div");
6083 this.div_.className = this.className_;
6084 if (this.visible_) {
6085 this.show();
6086 }
6087
6088 this.getPanes().overlayMouseTarget.appendChild(this.div_);
6089
6090 // Fix for Issue 157
6091 this.boundsChangedListener_ = google.maps.event.addListener(this.getMap(), "bounds_changed", function () {
6092 cDraggingMapByCluster = cMouseDownInCluster;
6093 });
6094
6095 google.maps.event.addDomListener(this.div_, "mousedown", function () {
6096 cMouseDownInCluster = true;
6097 cDraggingMapByCluster = false;
6098 });
6099
6100 google.maps.event.addDomListener(this.div_, "click", function (e) {
6101 cMouseDownInCluster = false;
6102 if (!cDraggingMapByCluster) {
6103 var theBounds;
6104 var mz;
6105 var mc = cClusterIcon.cluster_.getMarkerClusterer();
6106 /**
6107 * This event is fired when a cluster marker is clicked.
6108 * @name MarkerClusterer#click
6109 * @param {Cluster} c The cluster that was clicked.
6110 * @event
6111 */
6112 google.maps.event.trigger(mc, "click", cClusterIcon.cluster_);
6113 google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name
6114
6115 // The default click handler follows. Disable it by setting
6116 // the zoomOnClick property to false.
6117 if (mc.getZoomOnClick()) {
6118 // Zoom into the cluster.
6119 mz = mc.getMaxZoom();
6120 theBounds = cClusterIcon.cluster_.getBounds();
6121 mc.getMap().fitBounds(theBounds);
6122 // There is a fix for Issue 170 here:
6123 setTimeout(function () {
6124 mc.getMap().fitBounds(theBounds);
6125 // Don't zoom beyond the max zoom level
6126 if (mz !== null && (mc.getMap().getZoom() > mz)) {
6127 mc.getMap().setZoom(mz + 1);
6128 }
6129 }, 100);
6130 }
6131
6132 // Prevent event propagation to the map:
6133 e.cancelBubble = true;
6134 if (e.stopPropagation) {
6135 e.stopPropagation();
6136 }
6137 }
6138 });
6139
6140 google.maps.event.addDomListener(this.div_, "mouseover", function () {
6141 var mc = cClusterIcon.cluster_.getMarkerClusterer();
6142 /**
6143 * This event is fired when the mouse moves over a cluster marker.
6144 * @name MarkerClusterer#mouseover
6145 * @param {Cluster} c The cluster that the mouse moved over.
6146 * @event
6147 */
6148 google.maps.event.trigger(mc, "mouseover", cClusterIcon.cluster_);
6149 });
6150
6151 google.maps.event.addDomListener(this.div_, "mouseout", function () {
6152 var mc = cClusterIcon.cluster_.getMarkerClusterer();
6153 /**
6154 * This event is fired when the mouse moves out of a cluster marker.
6155 * @name MarkerClusterer#mouseout
6156 * @param {Cluster} c The cluster that the mouse moved out of.
6157 * @event
6158 */
6159 google.maps.event.trigger(mc, "mouseout", cClusterIcon.cluster_);
6160 });
6161 };
6162
6163
6164 /**
6165 * Removes the icon from the DOM.
6166 */
6167 ClusterIcon.prototype.onRemove = function () {
6168 if (this.div_ && this.div_.parentNode) {
6169 this.hide();
6170 google.maps.event.removeListener(this.boundsChangedListener_);
6171 google.maps.event.clearInstanceListeners(this.div_);
6172 this.div_.parentNode.removeChild(this.div_);
6173 this.div_ = null;
6174 }
6175 };
6176
6177
6178 /**
6179 * Draws the icon.
6180 */
6181 ClusterIcon.prototype.draw = function () {
6182 if (this.visible_) {
6183 var pos = this.getPosFromLatLng_(this.center_);
6184 this.div_.style.top = pos.y + "px";
6185 this.div_.style.left = pos.x + "px";
6186 }
6187 };
6188
6189
6190 /**
6191 * Hides the icon.
6192 */
6193 ClusterIcon.prototype.hide = function () {
6194 if (this.div_) {
6195 this.div_.style.display = "none";
6196 }
6197 this.visible_ = false;
6198 };
6199
6200
6201 /**
6202 * Positions and shows the icon.
6203 */
6204 ClusterIcon.prototype.show = function () {
6205 if (this.div_) {
6206 var img = "";
6207 // NOTE: values must be specified in px units
6208 var bp = this.backgroundPosition_.split(" ");
6209 var spriteH = parseInt(bp[0].trim(), 10);
6210 var spriteV = parseInt(bp[1].trim(), 10);
6211 var pos = this.getPosFromLatLng_(this.center_);
6212 this.div_.style.cssText = this.createCss(pos);
6213 img = "<img src='" + this.url_ + "' style='position: absolute; top: " + spriteV + "px; left: " + spriteH + "px; ";
6214 if (!this.cluster_.getMarkerClusterer().enableRetinaIcons_) {
6215 img += "clip: rect(" + (-1 * spriteV) + "px, " + ((-1 * spriteH) + this.width_) + "px, " +
6216 ((-1 * spriteV) + this.height_) + "px, " + (-1 * spriteH) + "px);";
6217 }
6218 img += "'>";
6219 this.div_.innerHTML = img + "<div style='" +
6220 "position: absolute;" +
6221 "top: " + this.anchorText_[0] + "px;" +
6222 "left: " + this.anchorText_[1] + "px;" +
6223 "color: " + this.textColor_ + ";" +
6224 "font-size: " + this.textSize_ + "px;" +
6225 "font-family: " + this.fontFamily_ + ";" +
6226 "font-weight: " + this.fontWeight_ + ";" +
6227 "font-style: " + this.fontStyle_ + ";" +
6228 "text-decoration: " + this.textDecoration_ + ";" +
6229 "text-align: center;" +
6230 "width: " + this.width_ + "px;" +
6231 "line-height:" + this.height_ + "px;" +
6232 "'>" + this.sums_.text + "</div>";
6233 if (typeof this.sums_.title === "undefined" || this.sums_.title === "") {
6234 this.div_.title = this.cluster_.getMarkerClusterer().getTitle();
6235 } else {
6236 this.div_.title = this.sums_.title;
6237 }
6238 this.div_.style.display = "";
6239 }
6240 this.visible_ = true;
6241 };
6242
6243
6244 /**
6245 * Sets the icon styles to the appropriate element in the styles array.
6246 *
6247 * @param {ClusterIconInfo} sums The icon label text and styles index.
6248 */
6249 ClusterIcon.prototype.useStyle = function (sums) {
6250 this.sums_ = sums;
6251 var index = Math.max(0, sums.index - 1);
6252 index = Math.min(this.styles_.length - 1, index);
6253 var style = this.styles_[index];
6254 this.url_ = style.url;
6255 this.height_ = style.height;
6256 this.width_ = style.width;
6257 this.anchorText_ = style.anchorText || [0, 0];
6258 this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)];
6259 this.textColor_ = style.textColor || "black";
6260 this.textSize_ = style.textSize || 11;
6261 this.textDecoration_ = style.textDecoration || "none";
6262 this.fontWeight_ = style.fontWeight || "bold";
6263 this.fontStyle_ = style.fontStyle || "normal";
6264 this.fontFamily_ = style.fontFamily || "Arial,sans-serif";
6265 this.backgroundPosition_ = style.backgroundPosition || "0 0";
6266 };
6267
6268
6269 /**
6270 * Sets the position at which to center the icon.
6271 *
6272 * @param {google.maps.LatLng} center The latlng to set as the center.
6273 */
6274 ClusterIcon.prototype.setCenter = function (center) {
6275 this.center_ = center;
6276 };
6277
6278
6279 /**
6280 * Creates the cssText style parameter based on the position of the icon.
6281 *
6282 * @param {google.maps.Point} pos The position of the icon.
6283 * @return {string} The CSS style text.
6284 */
6285 ClusterIcon.prototype.createCss = function (pos) {
6286 var style = [];
6287 style.push("cursor: pointer;");
6288 style.push("position: absolute; top: " + pos.y + "px; left: " + pos.x + "px;");
6289 style.push("width: " + this.width_ + "px; height: " + this.height_ + "px;");
6290 return style.join("");
6291 };
6292
6293
6294 /**
6295 * Returns the position at which to place the DIV depending on the latlng.
6296 *
6297 * @param {google.maps.LatLng} latlng The position in latlng.
6298 * @return {google.maps.Point} The position in pixels.
6299 */
6300 ClusterIcon.prototype.getPosFromLatLng_ = function (latlng) {
6301 var pos = this.getProjection().fromLatLngToDivPixel(latlng);
6302 pos.x -= this.anchorIcon_[1];
6303 pos.y -= this.anchorIcon_[0];
6304 pos.x = parseInt(pos.x, 10);
6305 pos.y = parseInt(pos.y, 10);
6306 return pos;
6307 };
6308
6309
6310 /**
6311 * Creates a single cluster that manages a group of proximate markers.
6312 * Used internally, do not call this constructor directly.
6313 * @constructor
6314 * @param {MarkerClusterer} mc The <code>MarkerClusterer</code> object with which this
6315 * cluster is associated.
6316 */
6317 function Cluster(mc) {
6318 this.markerClusterer_ = mc;
6319 this.map_ = mc.getMap();
6320 this.gridSize_ = mc.getGridSize();
6321 this.minClusterSize_ = mc.getMinimumClusterSize();
6322 this.averageCenter_ = mc.getAverageCenter();
6323 this.markers_ = [];
6324 this.center_ = null;
6325 this.bounds_ = null;
6326 this.clusterIcon_ = new ClusterIcon(this, mc.getStyles());
6327 }
6328
6329
6330 /**
6331 * Returns the number of markers managed by the cluster. You can call this from
6332 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
6333 * for the <code>MarkerClusterer</code> object.
6334 *
6335 * @return {number} The number of markers in the cluster.
6336 */
6337 Cluster.prototype.getSize = function () {
6338 return this.markers_.length;
6339 };
6340
6341
6342 /**
6343 * Returns the array of markers managed by the cluster. You can call this from
6344 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
6345 * for the <code>MarkerClusterer</code> object.
6346 *
6347 * @return {Array} The array of markers in the cluster.
6348 */
6349 Cluster.prototype.getMarkers = function () {
6350 return this.markers_;
6351 };
6352
6353
6354 /**
6355 * Returns the center of the cluster. You can call this from
6356 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
6357 * for the <code>MarkerClusterer</code> object.
6358 *
6359 * @return {google.maps.LatLng} The center of the cluster.
6360 */
6361 Cluster.prototype.getCenter = function () {
6362 return this.center_;
6363 };
6364
6365
6366 /**
6367 * Returns the map with which the cluster is associated.
6368 *
6369 * @return {google.maps.Map} The map.
6370 * @ignore
6371 */
6372 Cluster.prototype.getMap = function () {
6373 return this.map_;
6374 };
6375
6376
6377 /**
6378 * Returns the <code>MarkerClusterer</code> object with which the cluster is associated.
6379 *
6380 * @return {MarkerClusterer} The associated marker clusterer.
6381 * @ignore
6382 */
6383 Cluster.prototype.getMarkerClusterer = function () {
6384 return this.markerClusterer_;
6385 };
6386
6387
6388 /**
6389 * Returns the bounds of the cluster.
6390 *
6391 * @return {google.maps.LatLngBounds} the cluster bounds.
6392 * @ignore
6393 */
6394 Cluster.prototype.getBounds = function () {
6395 var i;
6396 var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
6397 var markers = this.getMarkers();
6398 for (i = 0; i < markers.length; i++) {
6399 bounds.extend(markers[i].getPosition());
6400 }
6401 return bounds;
6402 };
6403
6404
6405 /**
6406 * Removes the cluster from the map.
6407 *
6408 * @ignore
6409 */
6410 Cluster.prototype.remove = function () {
6411 this.clusterIcon_.setMap(null);
6412 this.markers_ = [];
6413 delete this.markers_;
6414 };
6415
6416
6417 /**
6418 * Adds a marker to the cluster.
6419 *
6420 * @param {google.maps.Marker} marker The marker to be added.
6421 * @return {boolean} True if the marker was added.
6422 * @ignore
6423 */
6424 Cluster.prototype.addMarker = function (marker) {
6425 var i;
6426 var mCount;
6427 var mz;
6428
6429 if (this.isMarkerAlreadyAdded_(marker)) {
6430 return false;
6431 }
6432
6433 if (!this.center_) {
6434 this.center_ = marker.getPosition();
6435 this.calculateBounds_();
6436 } else {
6437 if (this.averageCenter_) {
6438 var l = this.markers_.length + 1;
6439 var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;
6440 var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;
6441 this.center_ = new google.maps.LatLng(lat, lng);
6442 this.calculateBounds_();
6443 }
6444 }
6445
6446 marker.isAdded = true;
6447 this.markers_.push(marker);
6448
6449 mCount = this.markers_.length;
6450 mz = this.markerClusterer_.getMaxZoom();
6451 if (mz !== null && this.map_.getZoom() > mz) {
6452 // Zoomed in past max zoom, so show the marker.
6453 if (marker.getMap() !== this.map_) {
6454 marker.setMap(this.map_);
6455 }
6456 } else if (mCount < this.minClusterSize_) {
6457 // Min cluster size not reached so show the marker.
6458 if (marker.getMap() !== this.map_) {
6459 marker.setMap(this.map_);
6460 }
6461 } else if (mCount === this.minClusterSize_) {
6462 // Hide the markers that were showing.
6463 for (i = 0; i < mCount; i++) {
6464 this.markers_[i].setMap(null);
6465 }
6466 } else {
6467 marker.setMap(null);
6468 }
6469
6470 this.updateIcon_();
6471 return true;
6472 };
6473
6474
6475 /**
6476 * Determines if a marker lies within the cluster's bounds.
6477 *
6478 * @param {google.maps.Marker} marker The marker to check.
6479 * @return {boolean} True if the marker lies in the bounds.
6480 * @ignore
6481 */
6482 Cluster.prototype.isMarkerInClusterBounds = function (marker) {
6483 return this.bounds_.contains(marker.getPosition());
6484 };
6485
6486
6487 /**
6488 * Calculates the extended bounds of the cluster with the grid.
6489 */
6490 Cluster.prototype.calculateBounds_ = function () {
6491 var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
6492 this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
6493 };
6494
6495
6496 /**
6497 * Updates the cluster icon.
6498 */
6499 Cluster.prototype.updateIcon_ = function () {
6500 var mCount = this.markers_.length;
6501 var mz = this.markerClusterer_.getMaxZoom();
6502
6503 if (mz !== null && this.map_.getZoom() > mz) {
6504 this.clusterIcon_.hide();
6505 return;
6506 }
6507
6508 if (mCount < this.minClusterSize_) {
6509 // Min cluster size not yet reached.
6510 this.clusterIcon_.hide();
6511 return;
6512 }
6513
6514 var numStyles = this.markerClusterer_.getStyles().length;
6515 var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
6516 this.clusterIcon_.setCenter(this.center_);
6517 this.clusterIcon_.useStyle(sums);
6518 this.clusterIcon_.show();
6519 };
6520
6521
6522 /**
6523 * Determines if a marker has already been added to the cluster.
6524 *
6525 * @param {google.maps.Marker} marker The marker to check.
6526 * @return {boolean} True if the marker has already been added.
6527 */
6528 Cluster.prototype.isMarkerAlreadyAdded_ = function (marker) {
6529 var i;
6530 if (this.markers_.indexOf) {
6531 return this.markers_.indexOf(marker) !== -1;
6532 } else {
6533 for (i = 0; i < this.markers_.length; i++) {
6534 if (marker === this.markers_[i]) {
6535 return true;
6536 }
6537 }
6538 }
6539 return false;
6540 };
6541
6542
6543 /**
6544 * @name MarkerClustererOptions
6545 * @class This class represents the optional parameter passed to
6546 * the {@link MarkerClusterer} constructor.
6547 * @property {number} [gridSize=60] The grid size of a cluster in pixels. The grid is a square.
6548 * @property {number} [maxZoom=null] The maximum zoom level at which clustering is enabled or
6549 * <code>null</code> if clustering is to be enabled at all zoom levels.
6550 * @property {boolean} [zoomOnClick=true] Whether to zoom the map when a cluster marker is
6551 * clicked. You may want to set this to <code>false</code> if you have installed a handler
6552 * for the <code>click</code> event and it deals with zooming on its own.
6553 * @property {boolean} [averageCenter=false] Whether the position of a cluster marker should be
6554 * the average position of all markers in the cluster. If set to <code>false</code>, the
6555 * cluster marker is positioned at the location of the first marker added to the cluster.
6556 * @property {number} [minimumClusterSize=2] The minimum number of markers needed in a cluster
6557 * before the markers are hidden and a cluster marker appears.
6558 * @property {boolean} [ignoreHidden=false] Whether to ignore hidden markers in clusters. You
6559 * may want to set this to <code>true</code> to ensure that hidden markers are not included
6560 * in the marker count that appears on a cluster marker (this count is the value of the
6561 * <code>text</code> property of the result returned by the default <code>calculator</code>).
6562 * If set to <code>true</code> and you change the visibility of a marker being clustered, be
6563 * sure to also call <code>MarkerClusterer.repaint()</code>.
6564 * @property {string} [title=""] The tooltip to display when the mouse moves over a cluster
6565 * marker. (Alternatively, you can use a custom <code>calculator</code> function to specify a
6566 * different tooltip for each cluster marker.)
6567 * @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine
6568 * the text to be displayed on a cluster marker and the index indicating which style to use
6569 * for the cluster marker. The input parameters for the function are (1) the array of markers
6570 * represented by a cluster marker and (2) the number of cluster icon styles. It returns a
6571 * {@link ClusterIconInfo} object. The default <code>calculator</code> returns a
6572 * <code>text</code> property which is the number of markers in the cluster and an
6573 * <code>index</code> property which is one higher than the lowest integer such that
6574 * <code>10^i</code> exceeds the number of markers in the cluster, or the size of the styles
6575 * array, whichever is less. The <code>styles</code> array element used has an index of
6576 * <code>index</code> minus 1. For example, the default <code>calculator</code> returns a
6577 * <code>text</code> value of <code>"125"</code> and an <code>index</code> of <code>3</code>
6578 * for a cluster icon representing 125 markers so the element used in the <code>styles</code>
6579 * array is <code>2</code>. A <code>calculator</code> may also return a <code>title</code>
6580 * property that contains the text of the tooltip to be used for the cluster marker. If
6581 * <code>title</code> is not defined, the tooltip is set to the value of the <code>title</code>
6582 * property for the MarkerClusterer.
6583 * @property {string} [clusterClass="cluster"] The name of the CSS class defining general styles
6584 * for the cluster markers. Use this class to define CSS styles that are not set up by the code
6585 * that processes the <code>styles</code> array.
6586 * @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles
6587 * of the cluster markers to be used. The element to be used to style a given cluster marker
6588 * is determined by the function defined by the <code>calculator</code> property.
6589 * The default is an array of {@link ClusterIconStyle} elements whose properties are derived
6590 * from the values for <code>imagePath</code>, <code>imageExtension</code>, and
6591 * <code>imageSizes</code>.
6592 * @property {boolean} [enableRetinaIcons=false] Whether to allow the use of cluster icons that
6593 * have sizes that are some multiple (typically double) of their actual display size. Icons such
6594 * as these look better when viewed on high-resolution monitors such as Apple's Retina displays.
6595 * Note: if this property is <code>true</code>, sprites cannot be used as cluster icons.
6596 * @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the
6597 * number of markers to be processed in a single batch when using a browser other than
6598 * Internet Explorer (for Internet Explorer, use the batchSizeIE property instead).
6599 * @property {number} [batchSizeIE=MarkerClusterer.BATCH_SIZE_IE] When Internet Explorer is
6600 * being used, markers are processed in several batches with a small delay inserted between
6601 * each batch in an attempt to avoid Javascript timeout errors. Set this property to the
6602 * number of markers to be processed in a single batch; select as high a number as you can
6603 * without causing a timeout error in the browser. This number might need to be as low as 100
6604 * if 15,000 markers are being managed, for example.
6605 * @property {string} [imagePath=MarkerClusterer.IMAGE_PATH]
6606 * The full URL of the root name of the group of image files to use for cluster icons.
6607 * The complete file name is of the form <code>imagePath</code>n.<code>imageExtension</code>
6608 * where n is the image file number (1, 2, etc.).
6609 * @property {string} [imageExtension=MarkerClusterer.IMAGE_EXTENSION]
6610 * The extension name for the cluster icon image files (e.g., <code>"png"</code> or
6611 * <code>"jpg"</code>).
6612 * @property {Array} [imageSizes=MarkerClusterer.IMAGE_SIZES]
6613 * An array of numbers containing the widths of the group of
6614 * <code>imagePath</code>n.<code>imageExtension</code> image files.
6615 * (The images are assumed to be square.)
6616 */
6617 /**
6618 * Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}.
6619 * @constructor
6620 * @extends google.maps.OverlayView
6621 * @param {google.maps.Map} map The Google map to attach to.
6622 * @param {Array.<google.maps.Marker>} [opt_markers] The markers to be added to the cluster.
6623 * @param {MarkerClustererOptions} [opt_options] The optional parameters.
6624 */
6625 function MarkerClusterer(map, opt_markers, opt_options) {
6626 // MarkerClusterer implements google.maps.OverlayView interface. We use the
6627 // extend function to extend MarkerClusterer with google.maps.OverlayView
6628 // because it might not always be available when the code is defined so we
6629 // look for it at the last possible moment. If it doesn't exist now then
6630 // there is no point going ahead :)
6631 this.extend(MarkerClusterer, google.maps.OverlayView);
6632
6633 opt_markers = opt_markers || [];
6634 opt_options = opt_options || {};
6635
6636 this.markers_ = [];
6637 this.clusters_ = [];
6638 this.listeners_ = [];
6639 this.activeMap_ = null;
6640 this.ready_ = false;
6641
6642 this.gridSize_ = opt_options.gridSize || 60;
6643 this.minClusterSize_ = opt_options.minimumClusterSize || 2;
6644 this.maxZoom_ = opt_options.maxZoom || null;
6645 this.styles_ = opt_options.styles || [];
6646 this.title_ = opt_options.title || "";
6647 this.zoomOnClick_ = true;
6648 if (opt_options.zoomOnClick !== undefined) {
6649 this.zoomOnClick_ = opt_options.zoomOnClick;
6650 }
6651 this.averageCenter_ = false;
6652 if (opt_options.averageCenter !== undefined) {
6653 this.averageCenter_ = opt_options.averageCenter;
6654 }
6655 this.ignoreHidden_ = false;
6656 if (opt_options.ignoreHidden !== undefined) {
6657 this.ignoreHidden_ = opt_options.ignoreHidden;
6658 }
6659 this.enableRetinaIcons_ = false;
6660 if (opt_options.enableRetinaIcons !== undefined) {
6661 this.enableRetinaIcons_ = opt_options.enableRetinaIcons;
6662 }
6663 this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH;
6664 this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION;
6665 this.imageSizes_ = opt_options.imageSizes || MarkerClusterer.IMAGE_SIZES;
6666 this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR;
6667 this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE;
6668 this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE;
6669 this.clusterClass_ = opt_options.clusterClass || "cluster";
6670
6671 if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) {
6672 // Try to avoid IE timeout when processing a huge number of markers:
6673 this.batchSize_ = this.batchSizeIE_;
6674 }
6675
6676 this.setupStyles_();
6677
6678 this.addMarkers(opt_markers, true);
6679 this.setMap(map); // Note: this causes onAdd to be called
6680 }
6681
6682
6683 /**
6684 * Implementation of the onAdd interface method.
6685 * @ignore
6686 */
6687 MarkerClusterer.prototype.onAdd = function () {
6688 var cMarkerClusterer = this;
6689
6690 this.activeMap_ = this.getMap();
6691 this.ready_ = true;
6692
6693 this.repaint();
6694
6695 // Add the map event listeners
6696 this.listeners_ = [
6697 google.maps.event.addListener(this.getMap(), "zoom_changed", function () {
6698 cMarkerClusterer.resetViewport_(false);
6699 // Workaround for this Google bug: when map is at level 0 and "-" of
6700 // zoom slider is clicked, a "zoom_changed" event is fired even though
6701 // the map doesn't zoom out any further. In this situation, no "idle"
6702 // event is triggered so the cluster markers that have been removed
6703 // do not get redrawn. Same goes for a zoom in at maxZoom.
6704 if (this.getZoom() === (this.get("minZoom") || 0) || this.getZoom() === this.get("maxZoom")) {
6705 google.maps.event.trigger(this, "idle");
6706 }
6707 }),
6708 google.maps.event.addListener(this.getMap(), "idle", function () {
6709 cMarkerClusterer.redraw_();
6710 })
6711 ];
6712 };
6713
6714
6715 /**
6716 * Implementation of the onRemove interface method.
6717 * Removes map event listeners and all cluster icons from the DOM.
6718 * All managed markers are also put back on the map.
6719 * @ignore
6720 */
6721 MarkerClusterer.prototype.onRemove = function () {
6722 var i;
6723
6724 // Put all the managed markers back on the map:
6725 for (i = 0; i < this.markers_.length; i++) {
6726 if (this.markers_[i].getMap() !== this.activeMap_) {
6727 this.markers_[i].setMap(this.activeMap_);
6728 }
6729 }
6730
6731 // Remove all clusters:
6732 for (i = 0; i < this.clusters_.length; i++) {
6733 this.clusters_[i].remove();
6734 }
6735 this.clusters_ = [];
6736
6737 // Remove map event listeners:
6738 for (i = 0; i < this.listeners_.length; i++) {
6739 google.maps.event.removeListener(this.listeners_[i]);
6740 }
6741 this.listeners_ = [];
6742
6743 this.activeMap_ = null;
6744 this.ready_ = false;
6745 };
6746
6747
6748 /**
6749 * Implementation of the draw interface method.
6750 * @ignore
6751 */
6752 MarkerClusterer.prototype.draw = function () {};
6753
6754
6755 /**
6756 * Sets up the styles object.
6757 */
6758 MarkerClusterer.prototype.setupStyles_ = function () {
6759 var i, size;
6760 if (this.styles_.length > 0) {
6761 return;
6762 }
6763
6764 for (i = 0; i < this.imageSizes_.length; i++) {
6765 size = this.imageSizes_[i];
6766 this.styles_.push({
6767 url: this.imagePath_ + (i + 1) + "." + this.imageExtension_,
6768 height: size,
6769 width: size
6770 });
6771 }
6772 };
6773
6774
6775 /**
6776 * Fits the map to the bounds of the markers managed by the clusterer.
6777 */
6778 MarkerClusterer.prototype.fitMapToMarkers = function () {
6779 var i;
6780 var markers = this.getMarkers();
6781 var bounds = new google.maps.LatLngBounds();
6782 for (i = 0; i < markers.length; i++) {
6783 bounds.extend(markers[i].getPosition());
6784 }
6785
6786 this.getMap().fitBounds(bounds);
6787 };
6788
6789
6790 /**
6791 * Returns the value of the <code>gridSize</code> property.
6792 *
6793 * @return {number} The grid size.
6794 */
6795 MarkerClusterer.prototype.getGridSize = function () {
6796 return this.gridSize_;
6797 };
6798
6799
6800 /**
6801 * Sets the value of the <code>gridSize</code> property.
6802 *
6803 * @param {number} gridSize The grid size.
6804 */
6805 MarkerClusterer.prototype.setGridSize = function (gridSize) {
6806 this.gridSize_ = gridSize;
6807 };
6808
6809
6810 /**
6811 * Returns the value of the <code>minimumClusterSize</code> property.
6812 *
6813 * @return {number} The minimum cluster size.
6814 */
6815 MarkerClusterer.prototype.getMinimumClusterSize = function () {
6816 return this.minClusterSize_;
6817 };
6818
6819 /**
6820 * Sets the value of the <code>minimumClusterSize</code> property.
6821 *
6822 * @param {number} minimumClusterSize The minimum cluster size.
6823 */
6824 MarkerClusterer.prototype.setMinimumClusterSize = function (minimumClusterSize) {
6825 this.minClusterSize_ = minimumClusterSize;
6826 };
6827
6828
6829 /**
6830 * Returns the value of the <code>maxZoom</code> property.
6831 *
6832 * @return {number} The maximum zoom level.
6833 */
6834 MarkerClusterer.prototype.getMaxZoom = function () {
6835 return this.maxZoom_;
6836 };
6837
6838
6839 /**
6840 * Sets the value of the <code>maxZoom</code> property.
6841 *
6842 * @param {number} maxZoom The maximum zoom level.
6843 */
6844 MarkerClusterer.prototype.setMaxZoom = function (maxZoom) {
6845 this.maxZoom_ = maxZoom;
6846 };
6847
6848
6849 /**
6850 * Returns the value of the <code>styles</code> property.
6851 *
6852 * @return {Array} The array of styles defining the cluster markers to be used.
6853 */
6854 MarkerClusterer.prototype.getStyles = function () {
6855 return this.styles_;
6856 };
6857
6858
6859 /**
6860 * Sets the value of the <code>styles</code> property.
6861 *
6862 * @param {Array.<ClusterIconStyle>} styles The array of styles to use.
6863 */
6864 MarkerClusterer.prototype.setStyles = function (styles) {
6865 this.styles_ = styles;
6866 };
6867
6868
6869 /**
6870 * Returns the value of the <code>title</code> property.
6871 *
6872 * @return {string} The content of the title text.
6873 */
6874 MarkerClusterer.prototype.getTitle = function () {
6875 return this.title_;
6876 };
6877
6878
6879 /**
6880 * Sets the value of the <code>title</code> property.
6881 *
6882 * @param {string} title The value of the title property.
6883 */
6884 MarkerClusterer.prototype.setTitle = function (title) {
6885 this.title_ = title;
6886 };
6887
6888
6889 /**
6890 * Returns the value of the <code>zoomOnClick</code> property.
6891 *
6892 * @return {boolean} True if zoomOnClick property is set.
6893 */
6894 MarkerClusterer.prototype.getZoomOnClick = function () {
6895 return this.zoomOnClick_;
6896 };
6897
6898
6899 /**
6900 * Sets the value of the <code>zoomOnClick</code> property.
6901 *
6902 * @param {boolean} zoomOnClick The value of the zoomOnClick property.
6903 */
6904 MarkerClusterer.prototype.setZoomOnClick = function (zoomOnClick) {
6905 this.zoomOnClick_ = zoomOnClick;
6906 };
6907
6908
6909 /**
6910 * Returns the value of the <code>averageCenter</code> property.
6911 *
6912 * @return {boolean} True if averageCenter property is set.
6913 */
6914 MarkerClusterer.prototype.getAverageCenter = function () {
6915 return this.averageCenter_;
6916 };
6917
6918
6919 /**
6920 * Sets the value of the <code>averageCenter</code> property.
6921 *
6922 * @param {boolean} averageCenter The value of the averageCenter property.
6923 */
6924 MarkerClusterer.prototype.setAverageCenter = function (averageCenter) {
6925 this.averageCenter_ = averageCenter;
6926 };
6927
6928
6929 /**
6930 * Returns the value of the <code>ignoreHidden</code> property.
6931 *
6932 * @return {boolean} True if ignoreHidden property is set.
6933 */
6934 MarkerClusterer.prototype.getIgnoreHidden = function () {
6935 return this.ignoreHidden_;
6936 };
6937
6938
6939 /**
6940 * Sets the value of the <code>ignoreHidden</code> property.
6941 *
6942 * @param {boolean} ignoreHidden The value of the ignoreHidden property.
6943 */
6944 MarkerClusterer.prototype.setIgnoreHidden = function (ignoreHidden) {
6945 this.ignoreHidden_ = ignoreHidden;
6946 };
6947
6948
6949 /**
6950 * Returns the value of the <code>enableRetinaIcons</code> property.
6951 *
6952 * @return {boolean} True if enableRetinaIcons property is set.
6953 */
6954 MarkerClusterer.prototype.getEnableRetinaIcons = function () {
6955 return this.enableRetinaIcons_;
6956 };
6957
6958
6959 /**
6960 * Sets the value of the <code>enableRetinaIcons</code> property.
6961 *
6962 * @param {boolean} enableRetinaIcons The value of the enableRetinaIcons property.
6963 */
6964 MarkerClusterer.prototype.setEnableRetinaIcons = function (enableRetinaIcons) {
6965 this.enableRetinaIcons_ = enableRetinaIcons;
6966 };
6967
6968
6969 /**
6970 * Returns the value of the <code>imageExtension</code> property.
6971 *
6972 * @return {string} The value of the imageExtension property.
6973 */
6974 MarkerClusterer.prototype.getImageExtension = function () {
6975 return this.imageExtension_;
6976 };
6977
6978
6979 /**
6980 * Sets the value of the <code>imageExtension</code> property.
6981 *
6982 * @param {string} imageExtension The value of the imageExtension property.
6983 */
6984 MarkerClusterer.prototype.setImageExtension = function (imageExtension) {
6985 this.imageExtension_ = imageExtension;
6986 };
6987
6988
6989 /**
6990 * Returns the value of the <code>imagePath</code> property.
6991 *
6992 * @return {string} The value of the imagePath property.
6993 */
6994 MarkerClusterer.prototype.getImagePath = function () {
6995 return this.imagePath_;
6996 };
6997
6998
6999 /**
7000 * Sets the value of the <code>imagePath</code> property.
7001 *
7002 * @param {string} imagePath The value of the imagePath property.
7003 */
7004 MarkerClusterer.prototype.setImagePath = function (imagePath) {
7005 this.imagePath_ = imagePath;
7006 };
7007
7008
7009 /**
7010 * Returns the value of the <code>imageSizes</code> property.
7011 *
7012 * @return {Array} The value of the imageSizes property.
7013 */
7014 MarkerClusterer.prototype.getImageSizes = function () {
7015 return this.imageSizes_;
7016 };
7017
7018
7019 /**
7020 * Sets the value of the <code>imageSizes</code> property.
7021 *
7022 * @param {Array} imageSizes The value of the imageSizes property.
7023 */
7024 MarkerClusterer.prototype.setImageSizes = function (imageSizes) {
7025 this.imageSizes_ = imageSizes;
7026 };
7027
7028
7029 /**
7030 * Returns the value of the <code>calculator</code> property.
7031 *
7032 * @return {function} the value of the calculator property.
7033 */
7034 MarkerClusterer.prototype.getCalculator = function () {
7035 return this.calculator_;
7036 };
7037
7038
7039 /**
7040 * Sets the value of the <code>calculator</code> property.
7041 *
7042 * @param {function(Array.<google.maps.Marker>, number)} calculator The value
7043 * of the calculator property.
7044 */
7045 MarkerClusterer.prototype.setCalculator = function (calculator) {
7046 this.calculator_ = calculator;
7047 };
7048
7049
7050 /**
7051 * Returns the value of the <code>batchSizeIE</code> property.
7052 *
7053 * @return {number} the value of the batchSizeIE property.
7054 */
7055 MarkerClusterer.prototype.getBatchSizeIE = function () {
7056 return this.batchSizeIE_;
7057 };
7058
7059
7060 /**
7061 * Sets the value of the <code>batchSizeIE</code> property.
7062 *
7063 * @param {number} batchSizeIE The value of the batchSizeIE property.
7064 */
7065 MarkerClusterer.prototype.setBatchSizeIE = function (batchSizeIE) {
7066 this.batchSizeIE_ = batchSizeIE;
7067 };
7068
7069
7070 /**
7071 * Returns the value of the <code>clusterClass</code> property.
7072 *
7073 * @return {string} the value of the clusterClass property.
7074 */
7075 MarkerClusterer.prototype.getClusterClass = function () {
7076 return this.clusterClass_;
7077 };
7078
7079
7080 /**
7081 * Sets the value of the <code>clusterClass</code> property.
7082 *
7083 * @param {string} clusterClass The value of the clusterClass property.
7084 */
7085 MarkerClusterer.prototype.setClusterClass = function (clusterClass) {
7086 this.clusterClass_ = clusterClass;
7087 };
7088
7089
7090 /**
7091 * Returns the array of markers managed by the clusterer.
7092 *
7093 * @return {Array} The array of markers managed by the clusterer.
7094 */
7095 MarkerClusterer.prototype.getMarkers = function () {
7096 return this.markers_;
7097 };
7098
7099
7100 /**
7101 * Returns the number of markers managed by the clusterer.
7102 *
7103 * @return {number} The number of markers.
7104 */
7105 MarkerClusterer.prototype.getTotalMarkers = function () {
7106 return this.markers_.length;
7107 };
7108
7109
7110 /**
7111 * Returns the current array of clusters formed by the clusterer.
7112 *
7113 * @return {Array} The array of clusters formed by the clusterer.
7114 */
7115 MarkerClusterer.prototype.getClusters = function () {
7116 return this.clusters_;
7117 };
7118
7119
7120 /**
7121 * Returns the number of clusters formed by the clusterer.
7122 *
7123 * @return {number} The number of clusters formed by the clusterer.
7124 */
7125 MarkerClusterer.prototype.getTotalClusters = function () {
7126 return this.clusters_.length;
7127 };
7128
7129
7130 /**
7131 * Adds a marker to the clusterer. The clusters are redrawn unless
7132 * <code>opt_nodraw</code> is set to <code>true</code>.
7133 *
7134 * @param {google.maps.Marker} marker The marker to add.
7135 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
7136 */
7137 MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) {
7138 this.pushMarkerTo_(marker);
7139 if (!opt_nodraw) {
7140 this.redraw_();
7141 }
7142 };
7143
7144
7145 /**
7146 * Adds an array of markers to the clusterer. The clusters are redrawn unless
7147 * <code>opt_nodraw</code> is set to <code>true</code>.
7148 *
7149 * @param {Array.<google.maps.Marker>} markers The markers to add.
7150 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
7151 */
7152 MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) {
7153 var key;
7154 for (key in markers) {
7155 if (markers.hasOwnProperty(key)) {
7156 this.pushMarkerTo_(markers[key]);
7157 }
7158 }
7159 if (!opt_nodraw) {
7160 this.redraw_();
7161 }
7162 };
7163
7164
7165 /**
7166 * Pushes a marker to the clusterer.
7167 *
7168 * @param {google.maps.Marker} marker The marker to add.
7169 */
7170 MarkerClusterer.prototype.pushMarkerTo_ = function (marker) {
7171 // If the marker is draggable add a listener so we can update the clusters on the dragend:
7172 if (marker.getDraggable()) {
7173 var cMarkerClusterer = this;
7174 google.maps.event.addListener(marker, "dragend", function () {
7175 if (cMarkerClusterer.ready_) {
7176 this.isAdded = false;
7177 cMarkerClusterer.repaint();
7178 }
7179 });
7180 }
7181 marker.isAdded = false;
7182 this.markers_.push(marker);
7183 };
7184
7185
7186 /**
7187 * Removes a marker from the cluster. The clusters are redrawn unless
7188 * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if the
7189 * marker was removed from the clusterer.
7190 *
7191 * @param {google.maps.Marker} marker The marker to remove.
7192 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
7193 * @return {boolean} True if the marker was removed from the clusterer.
7194 */
7195 MarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw) {
7196 var removed = this.removeMarker_(marker);
7197
7198 if (!opt_nodraw && removed) {
7199 this.repaint();
7200 }
7201
7202 return removed;
7203 };
7204
7205
7206 /**
7207 * Removes an array of markers from the cluster. The clusters are redrawn unless
7208 * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if markers
7209 * were removed from the clusterer.
7210 *
7211 * @param {Array.<google.maps.Marker>} markers The markers to remove.
7212 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
7213 * @return {boolean} True if markers were removed from the clusterer.
7214 */
7215 MarkerClusterer.prototype.removeMarkers = function (markers, opt_nodraw) {
7216 var i, r;
7217 var removed = false;
7218
7219 for (i = 0; i < markers.length; i++) {
7220 r = this.removeMarker_(markers[i]);
7221 removed = removed || r;
7222 }
7223
7224 if (!opt_nodraw && removed) {
7225 this.repaint();
7226 }
7227
7228 return removed;
7229 };
7230
7231
7232 /**
7233 * Removes a marker and returns true if removed, false if not.
7234 *
7235 * @param {google.maps.Marker} marker The marker to remove
7236 * @return {boolean} Whether the marker was removed or not
7237 */
7238 MarkerClusterer.prototype.removeMarker_ = function (marker) {
7239 var i;
7240 var index = -1;
7241 if (this.markers_.indexOf) {
7242 index = this.markers_.indexOf(marker);
7243 } else {
7244 for (i = 0; i < this.markers_.length; i++) {
7245 if (marker === this.markers_[i]) {
7246 index = i;
7247 break;
7248 }
7249 }
7250 }
7251
7252 if (index === -1) {
7253 // Marker is not in our list of markers, so do nothing:
7254 return false;
7255 }
7256
7257 marker.setMap(null);
7258 this.markers_.splice(index, 1); // Remove the marker from the list of managed markers
7259 return true;
7260 };
7261
7262
7263 /**
7264 * Removes all clusters and markers from the map and also removes all markers
7265 * managed by the clusterer.
7266 */
7267 MarkerClusterer.prototype.clearMarkers = function () {
7268 this.resetViewport_(true);
7269 this.markers_ = [];
7270 };
7271
7272
7273 /**
7274 * Recalculates and redraws all the marker clusters from scratch.
7275 * Call this after changing any properties.
7276 */
7277 MarkerClusterer.prototype.repaint = function () {
7278 var oldClusters = this.clusters_.slice();
7279 this.clusters_ = [];
7280 this.resetViewport_(false);
7281 this.redraw_();
7282
7283 // Remove the old clusters.
7284 // Do it in a timeout to prevent blinking effect.
7285 setTimeout(function () {
7286 var i;
7287 for (i = 0; i < oldClusters.length; i++) {
7288 oldClusters[i].remove();
7289 }
7290 }, 0);
7291 };
7292
7293
7294 /**
7295 * Returns the current bounds extended by the grid size.
7296 *
7297 * @param {google.maps.LatLngBounds} bounds The bounds to extend.
7298 * @return {google.maps.LatLngBounds} The extended bounds.
7299 * @ignore
7300 */
7301 MarkerClusterer.prototype.getExtendedBounds = function (bounds) {
7302 var projection = this.getProjection();
7303
7304 // Turn the bounds into latlng.
7305 var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
7306 bounds.getNorthEast().lng());
7307 var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
7308 bounds.getSouthWest().lng());
7309
7310 // Convert the points to pixels and the extend out by the grid size.
7311 var trPix = projection.fromLatLngToDivPixel(tr);
7312 trPix.x += this.gridSize_;
7313 trPix.y -= this.gridSize_;
7314
7315 var blPix = projection.fromLatLngToDivPixel(bl);
7316 blPix.x -= this.gridSize_;
7317 blPix.y += this.gridSize_;
7318
7319 // Convert the pixel points back to LatLng
7320 var ne = projection.fromDivPixelToLatLng(trPix);
7321 var sw = projection.fromDivPixelToLatLng(blPix);
7322
7323 // Extend the bounds to contain the new bounds.
7324 bounds.extend(ne);
7325 bounds.extend(sw);
7326
7327 return bounds;
7328 };
7329
7330
7331 /**
7332 * Redraws all the clusters.
7333 */
7334 MarkerClusterer.prototype.redraw_ = function () {
7335 this.createClusters_(0);
7336 };
7337
7338
7339 /**
7340 * Removes all clusters from the map. The markers are also removed from the map
7341 * if <code>opt_hide</code> is set to <code>true</code>.
7342 *
7343 * @param {boolean} [opt_hide] Set to <code>true</code> to also remove the markers
7344 * from the map.
7345 */
7346 MarkerClusterer.prototype.resetViewport_ = function (opt_hide) {
7347 var i, marker;
7348 // Remove all the clusters
7349 for (i = 0; i < this.clusters_.length; i++) {
7350 this.clusters_[i].remove();
7351 }
7352 this.clusters_ = [];
7353
7354 // Reset the markers to not be added and to be removed from the map.
7355 for (i = 0; i < this.markers_.length; i++) {
7356 marker = this.markers_[i];
7357 marker.isAdded = false;
7358 if (opt_hide) {
7359 marker.setMap(null);
7360 }
7361 }
7362 };
7363
7364
7365 /**
7366 * Calculates the distance between two latlng locations in km.
7367 *
7368 * @param {google.maps.LatLng} p1 The first lat lng point.
7369 * @param {google.maps.LatLng} p2 The second lat lng point.
7370 * @return {number} The distance between the two points in km.
7371 * @see http://www.movable-type.co.uk/scripts/latlong.html
7372 */
7373 MarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) {
7374 var R = 6371; // Radius of the Earth in km
7375 var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
7376 var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
7377 var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
7378 Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
7379 Math.sin(dLon / 2) * Math.sin(dLon / 2);
7380 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
7381 var d = R * c;
7382 return d;
7383 };
7384
7385
7386 /**
7387 * Determines if a marker is contained in a bounds.
7388 *
7389 * @param {google.maps.Marker} marker The marker to check.
7390 * @param {google.maps.LatLngBounds} bounds The bounds to check against.
7391 * @return {boolean} True if the marker is in the bounds.
7392 */
7393 MarkerClusterer.prototype.isMarkerInBounds_ = function (marker, bounds) {
7394 return bounds.contains(marker.getPosition());
7395 };
7396
7397
7398 /**
7399 * Adds a marker to a cluster, or creates a new cluster.
7400 *
7401 * @param {google.maps.Marker} marker The marker to add.
7402 */
7403 MarkerClusterer.prototype.addToClosestCluster_ = function (marker) {
7404 var i, d, cluster, center;
7405 var distance = 40000; // Some large number
7406 var clusterToAddTo = null;
7407 for (i = 0; i < this.clusters_.length; i++) {
7408 cluster = this.clusters_[i];
7409 center = cluster.getCenter();
7410 if (center) {
7411 d = this.distanceBetweenPoints_(center, marker.getPosition());
7412 if (d < distance) {
7413 distance = d;
7414 clusterToAddTo = cluster;
7415 }
7416 }
7417 }
7418
7419 if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {
7420 clusterToAddTo.addMarker(marker);
7421 } else {
7422 cluster = new Cluster(this);
7423 cluster.addMarker(marker);
7424 this.clusters_.push(cluster);
7425 }
7426 };
7427
7428
7429 /**
7430 * Creates the clusters. This is done in batches to avoid timeout errors
7431 * in some browsers when there is a huge number of markers.
7432 *
7433 * @param {number} iFirst The index of the first marker in the batch of
7434 * markers to be added to clusters.
7435 */
7436 MarkerClusterer.prototype.createClusters_ = function (iFirst) {
7437 var i, marker;
7438 var mapBounds;
7439 var cMarkerClusterer = this;
7440 if (!this.ready_) {
7441 return;
7442 }
7443
7444 // Cancel previous batch processing if we're working on the first batch:
7445 if (iFirst === 0) {
7446 /**
7447 * This event is fired when the <code>MarkerClusterer</code> begins
7448 * clustering markers.
7449 * @name MarkerClusterer#clusteringbegin
7450 * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
7451 * @event
7452 */
7453 google.maps.event.trigger(this, "clusteringbegin", this);
7454
7455 if (typeof this.timerRefStatic !== "undefined") {
7456 clearTimeout(this.timerRefStatic);
7457 delete this.timerRefStatic;
7458 }
7459 }
7460
7461 // Get our current map view bounds.
7462 // Create a new bounds object so we don't affect the map.
7463 //
7464 // See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug:
7465 if (this.getMap().getZoom() > 3) {
7466 mapBounds = new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(),
7467 this.getMap().getBounds().getNorthEast());
7468 } else {
7469 mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472, -178.48388434375), new google.maps.LatLng(-85.08136444384544, 178.00048865625));
7470 }
7471 var bounds = this.getExtendedBounds(mapBounds);
7472
7473 var iLast = Math.min(iFirst + this.batchSize_, this.markers_.length);
7474
7475 for (i = iFirst; i < iLast; i++) {
7476 marker = this.markers_[i];
7477 if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
7478 if (!this.ignoreHidden_ || (this.ignoreHidden_ && marker.getVisible())) {
7479 this.addToClosestCluster_(marker);
7480 }
7481 }
7482 }
7483
7484 if (iLast < this.markers_.length) {
7485 this.timerRefStatic = setTimeout(function () {
7486 cMarkerClusterer.createClusters_(iLast);
7487 }, 0);
7488 } else {
7489 delete this.timerRefStatic;
7490
7491 /**
7492 * This event is fired when the <code>MarkerClusterer</code> stops
7493 * clustering markers.
7494 * @name MarkerClusterer#clusteringend
7495 * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
7496 * @event
7497 */
7498 google.maps.event.trigger(this, "clusteringend", this);
7499 }
7500 };
7501
7502
7503 /**
7504 * Extends an object's prototype by another's.
7505 *
7506 * @param {Object} obj1 The object to be extended.
7507 * @param {Object} obj2 The object to extend with.
7508 * @return {Object} The new extended object.
7509 * @ignore
7510 */
7511 MarkerClusterer.prototype.extend = function (obj1, obj2) {
7512 return (function (object) {
7513 var property;
7514 for (property in object.prototype) {
7515 this.prototype[property] = object.prototype[property];
7516 }
7517 return this;
7518 }).apply(obj1, [obj2]);
7519 };
7520
7521
7522 /**
7523 * The default function for determining the label text and style
7524 * for a cluster icon.
7525 *
7526 * @param {Array.<google.maps.Marker>} markers The array of markers represented by the cluster.
7527 * @param {number} numStyles The number of marker styles available.
7528 * @return {ClusterIconInfo} The information resource for the cluster.
7529 * @constant
7530 * @ignore
7531 */
7532 MarkerClusterer.CALCULATOR = function (markers, numStyles) {
7533 var index = 0;
7534 var title = "";
7535 var count = markers.length.toString();
7536
7537 var dv = count;
7538 while (dv !== 0) {
7539 dv = parseInt(dv / 10, 10);
7540 index++;
7541 }
7542
7543 index = Math.min(index, numStyles);
7544 return {
7545 text: count,
7546 index: index,
7547 title: title
7548 };
7549 };
7550
7551
7552 /**
7553 * The number of markers to process in one batch.
7554 *
7555 * @type {number}
7556 * @constant
7557 */
7558 MarkerClusterer.BATCH_SIZE = 2000;
7559
7560
7561 /**
7562 * The number of markers to process in one batch (IE only).
7563 *
7564 * @type {number}
7565 * @constant
7566 */
7567 MarkerClusterer.BATCH_SIZE_IE = 500;
7568
7569
7570 /**
7571 * The default root name for the marker cluster images.
7572 *
7573 * @type {string}
7574 * @constant
7575 */
7576 MarkerClusterer.IMAGE_PATH = "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m";
7577
7578
7579 /**
7580 * The default extension name for the marker cluster images.
7581 *
7582 * @type {string}
7583 * @constant
7584 */
7585 MarkerClusterer.IMAGE_EXTENSION = "png";
7586
7587
7588 /**
7589 * The default array of sizes for the marker cluster images.
7590 *
7591 * @type {Array.<number>}
7592 * @constant
7593 */
7594 MarkerClusterer.IMAGE_SIZES = [53, 56, 66, 78, 90];
7595
7596 if (typeof String.prototype.trim !== 'function') {
7597 /**
7598 * IE hack since trim() doesn't exist in all browsers
7599 * @return {string} The string with removed whitespace
7600 */
7601 String.prototype.trim = function() {
7602 return this.replace(/^\s+|\s+$/g, '');
7603 }
7604 }
7605
7606 ;/**
7607 * 1.1.9-patched
7608 * @name MarkerWithLabel for V3
7609 * @version 1.1.8 [February 26, 2013]
7610 * @author Gary Little (inspired by code from Marc Ridey of Google).
7611 * @copyright Copyright 2012 Gary Little [gary at luxcentral.com]
7612 * @fileoverview MarkerWithLabel extends the Google Maps JavaScript API V3
7613 * <code>google.maps.Marker</code> class.
7614 * <p>
7615 * MarkerWithLabel allows you to define markers with associated labels. As you would expect,
7616 * if the marker is draggable, so too will be the label. In addition, a marker with a label
7617 * responds to all mouse events in the same manner as a regular marker. It also fires mouse
7618 * events and "property changed" events just as a regular marker would. Version 1.1 adds
7619 * support for the raiseOnDrag feature introduced in API V3.3.
7620 * <p>
7621 * If you drag a marker by its label, you can cancel the drag and return the marker to its
7622 * original position by pressing the <code>Esc</code> key. This doesn't work if you drag the marker
7623 * itself because this feature is not (yet) supported in the <code>google.maps.Marker</code> class.
7624 */
7625
7626 /*!
7627 *
7628 * Licensed under the Apache License, Version 2.0 (the "License");
7629 * you may not use this file except in compliance with the License.
7630 * You may obtain a copy of the License at
7631 *
7632 * http://www.apache.org/licenses/LICENSE-2.0
7633 *
7634 * Unless required by applicable law or agreed to in writing, software
7635 * distributed under the License is distributed on an "AS IS" BASIS,
7636 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7637 * See the License for the specific language governing permissions and
7638 * limitations under the License.
7639 */
7640
7641 /*jslint browser:true */
7642 /*global document,google */
7643
7644 /**
7645 * @param {Function} childCtor Child class.
7646 * @param {Function} parentCtor Parent class.
7647 */
7648 function inherits(childCtor, parentCtor) {
7649 /** @constructor */
7650 function tempCtor() {}
7651 tempCtor.prototype = parentCtor.prototype;
7652 childCtor.superClass_ = parentCtor.prototype;
7653 childCtor.prototype = new tempCtor();
7654 /** @override */
7655 childCtor.prototype.constructor = childCtor;
7656 }
7657
7658 /**
7659 * This constructor creates a label and associates it with a marker.
7660 * It is for the private use of the MarkerWithLabel class.
7661 * @constructor
7662 * @param {Marker} marker The marker with which the label is to be associated.
7663 * @param {string} crossURL The URL of the cross image =.
7664 * @param {string} handCursor The URL of the hand cursor.
7665 * @private
7666 */
7667 function MarkerLabel_(marker, crossURL, handCursorURL) {
7668 this.marker_ = marker;
7669 this.handCursorURL_ = marker.handCursorURL;
7670
7671 this.labelDiv_ = document.createElement("div");
7672 this.labelDiv_.style.cssText = "position: absolute; overflow: hidden;";
7673
7674 // Set up the DIV for handling mouse events in the label. This DIV forms a transparent veil
7675 // in the "overlayMouseTarget" pane, a veil that covers just the label. This is done so that
7676 // events can be captured even if the label is in the shadow of a google.maps.InfoWindow.
7677 // Code is included here to ensure the veil is always exactly the same size as the label.
7678 this.eventDiv_ = document.createElement("div");
7679 this.eventDiv_.style.cssText = this.labelDiv_.style.cssText;
7680
7681 // This is needed for proper behavior on MSIE:
7682 this.eventDiv_.setAttribute("onselectstart", "return false;");
7683 this.eventDiv_.setAttribute("ondragstart", "return false;");
7684
7685 // Get the DIV for the "X" to be displayed when the marker is raised.
7686 this.crossDiv_ = MarkerLabel_.getSharedCross(crossURL);
7687 }
7688 inherits(MarkerLabel_, google.maps.OverlayView);
7689
7690 /**
7691 * Returns the DIV for the cross used when dragging a marker when the
7692 * raiseOnDrag parameter set to true. One cross is shared with all markers.
7693 * @param {string} crossURL The URL of the cross image =.
7694 * @private
7695 */
7696 MarkerLabel_.getSharedCross = function (crossURL) {
7697 var div;
7698 if (typeof MarkerLabel_.getSharedCross.crossDiv === "undefined") {
7699 div = document.createElement("img");
7700 div.style.cssText = "position: absolute; z-index: 1000002; display: none;";
7701 // Hopefully Google never changes the standard "X" attributes:
7702 div.style.marginLeft = "-8px";
7703 div.style.marginTop = "-9px";
7704 div.src = crossURL;
7705 MarkerLabel_.getSharedCross.crossDiv = div;
7706 }
7707 return MarkerLabel_.getSharedCross.crossDiv;
7708 };
7709
7710 /**
7711 * Adds the DIV representing the label to the DOM. This method is called
7712 * automatically when the marker's <code>setMap</code> method is called.
7713 * @private
7714 */
7715 MarkerLabel_.prototype.onAdd = function () {
7716 var me = this;
7717 var cMouseIsDown = false;
7718 var cDraggingLabel = false;
7719 var cSavedZIndex;
7720 var cLatOffset, cLngOffset;
7721 var cIgnoreClick;
7722 var cRaiseEnabled;
7723 var cStartPosition;
7724 var cStartCenter;
7725 // Constants:
7726 var cRaiseOffset = 20;
7727 var cDraggingCursor = "url(" + this.handCursorURL_ + ")";
7728
7729 // Stops all processing of an event.
7730 //
7731 var cAbortEvent = function (e) {
7732 if (e.preventDefault) {
7733 e.preventDefault();
7734 }
7735 e.cancelBubble = true;
7736 if (e.stopPropagation) {
7737 e.stopPropagation();
7738 }
7739 };
7740
7741 var cStopBounce = function () {
7742 me.marker_.setAnimation(null);
7743 };
7744
7745 this.getPanes().overlayImage.appendChild(this.labelDiv_);
7746 this.getPanes().overlayMouseTarget.appendChild(this.eventDiv_);
7747 // One cross is shared with all markers, so only add it once:
7748 if (typeof MarkerLabel_.getSharedCross.processed === "undefined") {
7749 this.getPanes().overlayImage.appendChild(this.crossDiv_);
7750 MarkerLabel_.getSharedCross.processed = true;
7751 }
7752
7753 this.listeners_ = [
7754 google.maps.event.addDomListener(this.eventDiv_, "mouseover", function (e) {
7755 if (me.marker_.getDraggable() || me.marker_.getClickable()) {
7756 this.style.cursor = "pointer";
7757 google.maps.event.trigger(me.marker_, "mouseover", e);
7758 }
7759 }),
7760 google.maps.event.addDomListener(this.eventDiv_, "mouseout", function (e) {
7761 if ((me.marker_.getDraggable() || me.marker_.getClickable()) && !cDraggingLabel) {
7762 this.style.cursor = me.marker_.getCursor();
7763 google.maps.event.trigger(me.marker_, "mouseout", e);
7764 }
7765 }),
7766 google.maps.event.addDomListener(this.eventDiv_, "mousedown", function (e) {
7767 cDraggingLabel = false;
7768 if (me.marker_.getDraggable()) {
7769 cMouseIsDown = true;
7770 this.style.cursor = cDraggingCursor;
7771 }
7772 if (me.marker_.getDraggable() || me.marker_.getClickable()) {
7773 google.maps.event.trigger(me.marker_, "mousedown", e);
7774 cAbortEvent(e); // Prevent map pan when starting a drag on a label
7775 }
7776 }),
7777 google.maps.event.addDomListener(document, "mouseup", function (mEvent) {
7778 var position;
7779 if (cMouseIsDown) {
7780 cMouseIsDown = false;
7781 me.eventDiv_.style.cursor = "pointer";
7782 google.maps.event.trigger(me.marker_, "mouseup", mEvent);
7783 }
7784 if (cDraggingLabel) {
7785 if (cRaiseEnabled) { // Lower the marker & label
7786 position = me.getProjection().fromLatLngToDivPixel(me.marker_.getPosition());
7787 position.y += cRaiseOffset;
7788 me.marker_.setPosition(me.getProjection().fromDivPixelToLatLng(position));
7789 // This is not the same bouncing style as when the marker portion is dragged,
7790 // but it will have to do:
7791 try { // Will fail if running Google Maps API earlier than V3.3
7792 me.marker_.setAnimation(google.maps.Animation.BOUNCE);
7793 setTimeout(cStopBounce, 1406);
7794 } catch (e) {}
7795 }
7796 me.crossDiv_.style.display = "none";
7797 me.marker_.setZIndex(cSavedZIndex);
7798 cIgnoreClick = true; // Set flag to ignore the click event reported after a label drag
7799 cDraggingLabel = false;
7800 mEvent.latLng = me.marker_.getPosition();
7801 google.maps.event.trigger(me.marker_, "dragend", mEvent);
7802 }
7803 }),
7804 google.maps.event.addListener(me.marker_.getMap(), "mousemove", function (mEvent) {
7805 var position;
7806 if (cMouseIsDown) {
7807 if (cDraggingLabel) {
7808 // Change the reported location from the mouse position to the marker position:
7809 mEvent.latLng = new google.maps.LatLng(mEvent.latLng.lat() - cLatOffset, mEvent.latLng.lng() - cLngOffset);
7810 position = me.getProjection().fromLatLngToDivPixel(mEvent.latLng);
7811 if (cRaiseEnabled) {
7812 me.crossDiv_.style.left = position.x + "px";
7813 me.crossDiv_.style.top = position.y + "px";
7814 me.crossDiv_.style.display = "";
7815 position.y -= cRaiseOffset;
7816 }
7817 me.marker_.setPosition(me.getProjection().fromDivPixelToLatLng(position));
7818 if (cRaiseEnabled) { // Don't raise the veil; this hack needed to make MSIE act properly
7819 me.eventDiv_.style.top = (position.y + cRaiseOffset) + "px";
7820 }
7821 google.maps.event.trigger(me.marker_, "drag", mEvent);
7822 } else {
7823 // Calculate offsets from the click point to the marker position:
7824 cLatOffset = mEvent.latLng.lat() - me.marker_.getPosition().lat();
7825 cLngOffset = mEvent.latLng.lng() - me.marker_.getPosition().lng();
7826 cSavedZIndex = me.marker_.getZIndex();
7827 cStartPosition = me.marker_.getPosition();
7828 cStartCenter = me.marker_.getMap().getCenter();
7829 cRaiseEnabled = me.marker_.get("raiseOnDrag");
7830 cDraggingLabel = true;
7831 me.marker_.setZIndex(1000000); // Moves the marker & label to the foreground during a drag
7832 mEvent.latLng = me.marker_.getPosition();
7833 google.maps.event.trigger(me.marker_, "dragstart", mEvent);
7834 }
7835 }
7836 }),
7837 google.maps.event.addDomListener(document, "keydown", function (e) {
7838 if (cDraggingLabel) {
7839 if (e.keyCode === 27) { // Esc key
7840 cRaiseEnabled = false;
7841 me.marker_.setPosition(cStartPosition);
7842 me.marker_.getMap().setCenter(cStartCenter);
7843 google.maps.event.trigger(document, "mouseup", e);
7844 }
7845 }
7846 }),
7847 google.maps.event.addDomListener(this.eventDiv_, "click", function (e) {
7848 if (me.marker_.getDraggable() || me.marker_.getClickable()) {
7849 if (cIgnoreClick) { // Ignore the click reported when a label drag ends
7850 cIgnoreClick = false;
7851 } else {
7852 google.maps.event.trigger(me.marker_, "click", e);
7853 cAbortEvent(e); // Prevent click from being passed on to map
7854 }
7855 }
7856 }),
7857 google.maps.event.addDomListener(this.eventDiv_, "dblclick", function (e) {
7858 if (me.marker_.getDraggable() || me.marker_.getClickable()) {
7859 google.maps.event.trigger(me.marker_, "dblclick", e);
7860 cAbortEvent(e); // Prevent map zoom when double-clicking on a label
7861 }
7862 }),
7863 google.maps.event.addListener(this.marker_, "dragstart", function (mEvent) {
7864 if (!cDraggingLabel) {
7865 cRaiseEnabled = this.get("raiseOnDrag");
7866 }
7867 }),
7868 google.maps.event.addListener(this.marker_, "drag", function (mEvent) {
7869 if (!cDraggingLabel) {
7870 if (cRaiseEnabled) {
7871 me.setPosition(cRaiseOffset);
7872 // During a drag, the marker's z-index is temporarily set to 1000000 to
7873 // ensure it appears above all other markers. Also set the label's z-index
7874 // to 1000000 (plus or minus 1 depending on whether the label is supposed
7875 // to be above or below the marker).
7876 me.labelDiv_.style.zIndex = 1000000 + (this.get("labelInBackground") ? -1 : +1);
7877 }
7878 }
7879 }),
7880 google.maps.event.addListener(this.marker_, "dragend", function (mEvent) {
7881 if (!cDraggingLabel) {
7882 if (cRaiseEnabled) {
7883 me.setPosition(0); // Also restores z-index of label
7884 }
7885 }
7886 }),
7887 google.maps.event.addListener(this.marker_, "position_changed", function () {
7888 me.setPosition();
7889 }),
7890 google.maps.event.addListener(this.marker_, "zindex_changed", function () {
7891 me.setZIndex();
7892 }),
7893 google.maps.event.addListener(this.marker_, "visible_changed", function () {
7894 me.setVisible();
7895 }),
7896 google.maps.event.addListener(this.marker_, "labelvisible_changed", function () {
7897 me.setVisible();
7898 }),
7899 google.maps.event.addListener(this.marker_, "title_changed", function () {
7900 me.setTitle();
7901 }),
7902 google.maps.event.addListener(this.marker_, "labelcontent_changed", function () {
7903 me.setContent();
7904 }),
7905 google.maps.event.addListener(this.marker_, "labelanchor_changed", function () {
7906 me.setAnchor();
7907 }),
7908 google.maps.event.addListener(this.marker_, "labelclass_changed", function () {
7909 me.setStyles();
7910 }),
7911 google.maps.event.addListener(this.marker_, "labelstyle_changed", function () {
7912 me.setStyles();
7913 })
7914 ];
7915 };
7916
7917 /**
7918 * Removes the DIV for the label from the DOM. It also removes all event handlers.
7919 * This method is called automatically when the marker's <code>setMap(null)</code>
7920 * method is called.
7921 * @private
7922 */
7923 MarkerLabel_.prototype.onRemove = function () {
7924 var i;
7925 if (this.labelDiv_.parentNode !== null)
7926 this.labelDiv_.parentNode.removeChild(this.labelDiv_);
7927 if (this.eventDiv_.parentNode !== null)
7928 this.eventDiv_.parentNode.removeChild(this.eventDiv_);
7929
7930 // Remove event listeners:
7931 for (i = 0; i < this.listeners_.length; i++) {
7932 google.maps.event.removeListener(this.listeners_[i]);
7933 }
7934 };
7935
7936 /**
7937 * Draws the label on the map.
7938 * @private
7939 */
7940 MarkerLabel_.prototype.draw = function () {
7941 this.setContent();
7942 this.setTitle();
7943 this.setStyles();
7944 };
7945
7946 /**
7947 * Sets the content of the label.
7948 * The content can be plain text or an HTML DOM node.
7949 * @private
7950 */
7951 MarkerLabel_.prototype.setContent = function () {
7952 var content = this.marker_.get("labelContent");
7953 if (typeof content.nodeType === "undefined") {
7954 this.labelDiv_.innerHTML = content;
7955 this.eventDiv_.innerHTML = this.labelDiv_.innerHTML;
7956 } else {
7957 this.labelDiv_.innerHTML = ""; // Remove current content
7958 this.labelDiv_.appendChild(content);
7959 content = content.cloneNode(true);
7960 this.eventDiv_.appendChild(content);
7961 }
7962 };
7963
7964 /**
7965 * Sets the content of the tool tip for the label. It is
7966 * always set to be the same as for the marker itself.
7967 * @private
7968 */
7969 MarkerLabel_.prototype.setTitle = function () {
7970 this.eventDiv_.title = this.marker_.getTitle() || "";
7971 };
7972
7973 /**
7974 * Sets the style of the label by setting the style sheet and applying
7975 * other specific styles requested.
7976 * @private
7977 */
7978 MarkerLabel_.prototype.setStyles = function () {
7979 var i, labelStyle;
7980
7981 // Apply style values from the style sheet defined in the labelClass parameter:
7982 this.labelDiv_.className = this.marker_.get("labelClass");
7983 this.eventDiv_.className = this.labelDiv_.className;
7984
7985 // Clear existing inline style values:
7986 this.labelDiv_.style.cssText = "";
7987 this.eventDiv_.style.cssText = "";
7988 // Apply style values defined in the labelStyle parameter:
7989 labelStyle = this.marker_.get("labelStyle");
7990 for (i in labelStyle) {
7991 if (labelStyle.hasOwnProperty(i)) {
7992 this.labelDiv_.style[i] = labelStyle[i];
7993 this.eventDiv_.style[i] = labelStyle[i];
7994 }
7995 }
7996 this.setMandatoryStyles();
7997 };
7998
7999 /**
8000 * Sets the mandatory styles to the DIV representing the label as well as to the
8001 * associated event DIV. This includes setting the DIV position, z-index, and visibility.
8002 * @private
8003 */
8004 MarkerLabel_.prototype.setMandatoryStyles = function () {
8005 this.labelDiv_.style.position = "absolute";
8006 this.labelDiv_.style.overflow = "hidden";
8007 // Make sure the opacity setting causes the desired effect on MSIE:
8008 if (typeof this.labelDiv_.style.opacity !== "undefined" && this.labelDiv_.style.opacity !== "") {
8009 this.labelDiv_.style.MsFilter = "\"progid:DXImageTransform.Microsoft.Alpha(opacity=" + (this.labelDiv_.style.opacity * 100) + ")\"";
8010 this.labelDiv_.style.filter = "alpha(opacity=" + (this.labelDiv_.style.opacity * 100) + ")";
8011 }
8012
8013 this.eventDiv_.style.position = this.labelDiv_.style.position;
8014 this.eventDiv_.style.overflow = this.labelDiv_.style.overflow;
8015 this.eventDiv_.style.opacity = 0.01; // Don't use 0; DIV won't be clickable on MSIE
8016 this.eventDiv_.style.MsFilter = "\"progid:DXImageTransform.Microsoft.Alpha(opacity=1)\"";
8017 this.eventDiv_.style.filter = "alpha(opacity=1)"; // For MSIE
8018
8019 this.setAnchor();
8020 this.setPosition(); // This also updates z-index, if necessary.
8021 this.setVisible();
8022 };
8023
8024 /**
8025 * Sets the anchor point of the label.
8026 * @private
8027 */
8028 MarkerLabel_.prototype.setAnchor = function () {
8029 var anchor = this.marker_.get("labelAnchor");
8030 this.labelDiv_.style.marginLeft = -anchor.x + "px";
8031 this.labelDiv_.style.marginTop = -anchor.y + "px";
8032 this.eventDiv_.style.marginLeft = -anchor.x + "px";
8033 this.eventDiv_.style.marginTop = -anchor.y + "px";
8034 };
8035
8036 /**
8037 * Sets the position of the label. The z-index is also updated, if necessary.
8038 * @private
8039 */
8040 MarkerLabel_.prototype.setPosition = function (yOffset) {
8041 var position = this.getProjection().fromLatLngToDivPixel(this.marker_.getPosition());
8042 if (typeof yOffset === "undefined") {
8043 yOffset = 0;
8044 }
8045 this.labelDiv_.style.left = Math.round(position.x) + "px";
8046 this.labelDiv_.style.top = Math.round(position.y - yOffset) + "px";
8047 this.eventDiv_.style.left = this.labelDiv_.style.left;
8048 this.eventDiv_.style.top = this.labelDiv_.style.top;
8049
8050 this.setZIndex();
8051 };
8052
8053 /**
8054 * Sets the z-index of the label. If the marker's z-index property has not been defined, the z-index
8055 * of the label is set to the vertical coordinate of the label. This is in keeping with the default
8056 * stacking order for Google Maps: markers to the south are in front of markers to the north.
8057 * @private
8058 */
8059 MarkerLabel_.prototype.setZIndex = function () {
8060 var zAdjust = (this.marker_.get("labelInBackground") ? -1 : +1);
8061 if (typeof this.marker_.getZIndex() === "undefined") {
8062 this.labelDiv_.style.zIndex = parseInt(this.labelDiv_.style.top, 10) + zAdjust;
8063 this.eventDiv_.style.zIndex = this.labelDiv_.style.zIndex;
8064 } else {
8065 this.labelDiv_.style.zIndex = this.marker_.getZIndex() + zAdjust;
8066 this.eventDiv_.style.zIndex = this.labelDiv_.style.zIndex;
8067 }
8068 };
8069
8070 /**
8071 * Sets the visibility of the label. The label is visible only if the marker itself is
8072 * visible (i.e., its visible property is true) and the labelVisible property is true.
8073 * @private
8074 */
8075 MarkerLabel_.prototype.setVisible = function () {
8076 if (this.marker_.get("labelVisible")) {
8077 this.labelDiv_.style.display = this.marker_.getVisible() ? "block" : "none";
8078 } else {
8079 this.labelDiv_.style.display = "none";
8080 }
8081 this.eventDiv_.style.display = this.labelDiv_.style.display;
8082 };
8083
8084 /**
8085 * @name MarkerWithLabelOptions
8086 * @class This class represents the optional parameter passed to the {@link MarkerWithLabel} constructor.
8087 * The properties available are the same as for <code>google.maps.Marker</code> with the addition
8088 * of the properties listed below. To change any of these additional properties after the labeled
8089 * marker has been created, call <code>google.maps.Marker.set(propertyName, propertyValue)</code>.
8090 * <p>
8091 * When any of these properties changes, a property changed event is fired. The names of these
8092 * events are derived from the name of the property and are of the form <code>propertyname_changed</code>.
8093 * For example, if the content of the label changes, a <code>labelcontent_changed</code> event
8094 * is fired.
8095 * <p>
8096 * @property {string|Node} [labelContent] The content of the label (plain text or an HTML DOM node).
8097 * @property {Point} [labelAnchor] By default, a label is drawn with its anchor point at (0,0) so
8098 * that its top left corner is positioned at the anchor point of the associated marker. Use this
8099 * property to change the anchor point of the label. For example, to center a 50px-wide label
8100 * beneath a marker, specify a <code>labelAnchor</code> of <code>google.maps.Point(25, 0)</code>.
8101 * (Note: x-values increase to the right and y-values increase to the top.)
8102 * @property {string} [labelClass] The name of the CSS class defining the styles for the label.
8103 * Note that style values for <code>position</code>, <code>overflow</code>, <code>top</code>,
8104 * <code>left</code>, <code>zIndex</code>, <code>display</code>, <code>marginLeft</code>, and
8105 * <code>marginTop</code> are ignored; these styles are for internal use only.
8106 * @property {Object} [labelStyle] An object literal whose properties define specific CSS
8107 * style values to be applied to the label. Style values defined here override those that may
8108 * be defined in the <code>labelClass</code> style sheet. If this property is changed after the
8109 * label has been created, all previously set styles (except those defined in the style sheet)
8110 * are removed from the label before the new style values are applied.
8111 * Note that style values for <code>position</code>, <code>overflow</code>, <code>top</code>,
8112 * <code>left</code>, <code>zIndex</code>, <code>display</code>, <code>marginLeft</code>, and
8113 * <code>marginTop</code> are ignored; these styles are for internal use only.
8114 * @property {boolean} [labelInBackground] A flag indicating whether a label that overlaps its
8115 * associated marker should appear in the background (i.e., in a plane below the marker).
8116 * The default is <code>false</code>, which causes the label to appear in the foreground.
8117 * @property {boolean} [labelVisible] A flag indicating whether the label is to be visible.
8118 * The default is <code>true</code>. Note that even if <code>labelVisible</code> is
8119 * <code>true</code>, the label will <i>not</i> be visible unless the associated marker is also
8120 * visible (i.e., unless the marker's <code>visible</code> property is <code>true</code>).
8121 * @property {boolean} [raiseOnDrag] A flag indicating whether the label and marker are to be
8122 * raised when the marker is dragged. The default is <code>true</code>. If a draggable marker is
8123 * being created and a version of Google Maps API earlier than V3.3 is being used, this property
8124 * must be set to <code>false</code>.
8125 * @property {boolean} [optimized] A flag indicating whether rendering is to be optimized for the
8126 * marker. <b>Important: The optimized rendering technique is not supported by MarkerWithLabel,
8127 * so the value of this parameter is always forced to <code>false</code>.
8128 * @property {string} [crossImage="http://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png"]
8129 * The URL of the cross image to be displayed while dragging a marker.
8130 * @property {string} [handCursor="http://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur"]
8131 * The URL of the cursor to be displayed while dragging a marker.
8132 */
8133 /**
8134 * Creates a MarkerWithLabel with the options specified in {@link MarkerWithLabelOptions}.
8135 * @constructor
8136 * @param {MarkerWithLabelOptions} [opt_options] The optional parameters.
8137 */
8138 function MarkerWithLabel(opt_options) {
8139 opt_options = opt_options || {};
8140 opt_options.labelContent = opt_options.labelContent || "";
8141 opt_options.labelAnchor = opt_options.labelAnchor || new google.maps.Point(0, 0);
8142 opt_options.labelClass = opt_options.labelClass || "markerLabels";
8143 opt_options.labelStyle = opt_options.labelStyle || {};
8144 opt_options.labelInBackground = opt_options.labelInBackground || false;
8145 if (typeof opt_options.labelVisible === "undefined") {
8146 opt_options.labelVisible = true;
8147 }
8148 if (typeof opt_options.raiseOnDrag === "undefined") {
8149 opt_options.raiseOnDrag = true;
8150 }
8151 if (typeof opt_options.clickable === "undefined") {
8152 opt_options.clickable = true;
8153 }
8154 if (typeof opt_options.draggable === "undefined") {
8155 opt_options.draggable = false;
8156 }
8157 if (typeof opt_options.optimized === "undefined") {
8158 opt_options.optimized = false;
8159 }
8160 opt_options.crossImage = opt_options.crossImage || "http" + (document.location.protocol === "https:" ? "s" : "") + "://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png";
8161 opt_options.handCursor = opt_options.handCursor || "http" + (document.location.protocol === "https:" ? "s" : "") + "://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur";
8162 opt_options.optimized = false; // Optimized rendering is not supported
8163
8164 this.label = new MarkerLabel_(this, opt_options.crossImage, opt_options.handCursor); // Bind the label to the marker
8165
8166 // Call the parent constructor. It calls Marker.setValues to initialize, so all
8167 // the new parameters are conveniently saved and can be accessed with get/set.
8168 // Marker.set triggers a property changed event (called "propertyname_changed")
8169 // that the marker label listens for in order to react to state changes.
8170 google.maps.Marker.apply(this, arguments);
8171 }
8172 inherits(MarkerWithLabel, google.maps.Marker);
8173
8174 /**
8175 * Overrides the standard Marker setMap function.
8176 * @param {Map} theMap The map to which the marker is to be added.
8177 * @private
8178 */
8179 MarkerWithLabel.prototype.setMap = function (theMap) {
8180
8181 // Call the inherited function...
8182 google.maps.Marker.prototype.setMap.apply(this, arguments);
8183
8184 // ... then deal with the label:
8185 this.label.setMap(theMap);
8186 };