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 };