Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:58:27

0001 "use strict";
0002 (function(root, factory) {
0003   if(typeof exports === 'object') {
0004     module.exports = factory();
0005   }
0006   else if(typeof define === 'function' && define.amd) {
0007     define('GMaps', [], factory);
0008   }
0009 
0010   root.GMaps = factory();
0011 
0012 }(this, function() {
0013 
0014 /*!

0015  * GMaps.js v0.4.18

0016  * http://hpneo.github.com/gmaps/

0017  *

0018  * Copyright 2015, Gustavo Leon

0019  * Released under the MIT License.

0020  */
0021 
0022 if (!(typeof window.google === 'object' && window.google.maps)) {
0023   throw 'Google Maps API is required. Please register the following JavaScript library http://maps.google.com/maps/api/js?sensor=true.'
0024 }
0025 
0026 var extend_object = function(obj, new_obj) {
0027   var name;
0028 
0029   if (obj === new_obj) {
0030     return obj;
0031   }
0032 
0033   for (name in new_obj) {
0034     obj[name] = new_obj[name];
0035   }
0036 
0037   return obj;
0038 };
0039 
0040 var replace_object = function(obj, replace) {
0041   var name;
0042 
0043   if (obj === replace) {
0044     return obj;
0045   }
0046 
0047   for (name in replace) {
0048     if (obj[name] != undefined) {
0049       obj[name] = replace[name];
0050     }
0051   }
0052 
0053   return obj;
0054 };
0055 
0056 var array_map = function(array, callback) {
0057   var original_callback_params = Array.prototype.slice.call(arguments, 2),
0058       array_return = [],
0059       array_length = array.length,
0060       i;
0061 
0062   if (Array.prototype.map && array.map === Array.prototype.map) {
0063     array_return = Array.prototype.map.call(array, function(item) {
0064       var callback_params = original_callback_params.slice(0);
0065       callback_params.splice(0, 0, item);
0066 
0067       return callback.apply(this, callback_params);
0068     });
0069   }
0070   else {
0071     for (i = 0; i < array_length; i++) {
0072       callback_params = original_callback_params;
0073       callback_params.splice(0, 0, array[i]);
0074       array_return.push(callback.apply(this, callback_params));
0075     }
0076   }
0077 
0078   return array_return;
0079 };
0080 
0081 var array_flat = function(array) {
0082   var new_array = [],
0083       i;
0084 
0085   for (i = 0; i < array.length; i++) {
0086     new_array = new_array.concat(array[i]);
0087   }
0088 
0089   return new_array;
0090 };
0091 
0092 var coordsToLatLngs = function(coords, useGeoJSON) {
0093   var first_coord = coords[0],
0094       second_coord = coords[1];
0095 
0096   if (useGeoJSON) {
0097     first_coord = coords[1];
0098     second_coord = coords[0];
0099   }
0100 
0101   return new google.maps.LatLng(first_coord, second_coord);
0102 };
0103 
0104 var arrayToLatLng = function(coords, useGeoJSON) {
0105   var i;
0106 
0107   for (i = 0; i < coords.length; i++) {
0108     if (!(coords[i] instanceof google.maps.LatLng)) {
0109       if (coords[i].length > 0 && typeof(coords[i][0]) === "object") {
0110         coords[i] = arrayToLatLng(coords[i], useGeoJSON);
0111       }
0112       else {
0113         coords[i] = coordsToLatLngs(coords[i], useGeoJSON);
0114       }
0115     }
0116   }
0117 
0118   return coords;
0119 };
0120 
0121 
0122 var getElementsByClassName = function (class_name, context) {
0123 
0124     var element,
0125         _class = class_name.replace('.', '');
0126 
0127     if ('jQuery' in this && context) {
0128         element = $("." + _class, context)[0];
0129     } else {
0130         element = document.getElementsByClassName(_class)[0];
0131     }
0132     return element;
0133 
0134 };
0135 
0136 var getElementById = function(id, context) {
0137   var element,
0138   id = id.replace('#', '');
0139 
0140   if ('jQuery' in window && context) {
0141     element = $('#' + id, context)[0];
0142   } else {
0143     element = document.getElementById(id);
0144   };
0145 
0146   return element;
0147 };
0148 
0149 var findAbsolutePosition = function(obj)  {
0150   var curleft = 0,
0151       curtop = 0;
0152 
0153   if (obj.offsetParent) {
0154     do {
0155       curleft += obj.offsetLeft;
0156       curtop += obj.offsetTop;
0157     } while (obj = obj.offsetParent);
0158   }
0159 
0160   return [curleft, curtop];
0161 };
0162 
0163 var GMaps = (function(global) {
0164   "use strict";
0165 
0166   var doc = document;
0167 
0168   var GMaps = function(options) {
0169     if (!this) return new GMaps(options);
0170 
0171     options.zoom = options.zoom || 15;
0172     options.mapType = options.mapType || 'roadmap';
0173 
0174     var self = this,
0175         i,
0176         events_that_hide_context_menu = [
0177           'bounds_changed', 'center_changed', 'click', 'dblclick', 'drag',
0178           'dragend', 'dragstart', 'idle', 'maptypeid_changed', 'projection_changed',
0179           'resize', 'tilesloaded', 'zoom_changed'
0180         ],
0181         events_that_doesnt_hide_context_menu = ['mousemove', 'mouseout', 'mouseover'],
0182         options_to_be_deleted = ['el', 'lat', 'lng', 'mapType', 'width', 'height', 'markerClusterer', 'enableNewStyle'],
0183         identifier = options.el || options.div,
0184         markerClustererFunction = options.markerClusterer,
0185         mapType = google.maps.MapTypeId[options.mapType.toUpperCase()],
0186         map_center = new google.maps.LatLng(options.lat, options.lng),
0187         zoomControl = options.zoomControl || true,
0188         zoomControlOpt = options.zoomControlOpt || {
0189           style: 'DEFAULT',
0190           position: 'TOP_LEFT'
0191         },
0192         zoomControlStyle = zoomControlOpt.style || 'DEFAULT',
0193         zoomControlPosition = zoomControlOpt.position || 'TOP_LEFT',
0194         panControl = options.panControl || true,
0195         mapTypeControl = options.mapTypeControl || true,
0196         scaleControl = options.scaleControl || true,
0197         streetViewControl = options.streetViewControl || true,
0198         overviewMapControl = overviewMapControl || true,
0199         map_options = {},
0200         map_base_options = {
0201           zoom: this.zoom,
0202           center: map_center,
0203           mapTypeId: mapType
0204         },
0205         map_controls_options = {
0206           panControl: panControl,
0207           zoomControl: zoomControl,
0208           zoomControlOptions: {
0209             style: google.maps.ZoomControlStyle[zoomControlStyle],
0210             position: google.maps.ControlPosition[zoomControlPosition]
0211           },
0212           mapTypeControl: mapTypeControl,
0213           scaleControl: scaleControl,
0214           streetViewControl: streetViewControl,
0215           overviewMapControl: overviewMapControl
0216         };
0217 
0218       if (typeof(options.el) === 'string' || typeof(options.div) === 'string') {
0219 
0220           if (identifier.indexOf("#") > -1) {
0221               this.el = getElementById(identifier, options.context);
0222           } else {
0223               this.el = getElementsByClassName.apply(this, [identifier, options.context]);
0224           }
0225 
0226       } else {
0227           this.el = identifier;
0228       }
0229 
0230     if (typeof(this.el) === 'undefined' || this.el === null) {
0231       throw 'No element defined.';
0232     }
0233 
0234     window.context_menu = window.context_menu || {};
0235     window.context_menu[self.el.id] = {};
0236 
0237     this.controls = [];
0238     this.overlays = [];
0239     this.layers = []; // array with kml/georss and fusiontables layers, can be as many

0240     this.singleLayers = {}; // object with the other layers, only one per layer

0241     this.markers = [];
0242     this.polylines = [];
0243     this.routes = [];
0244     this.polygons = [];
0245     this.infoWindow = null;
0246     this.overlay_el = null;
0247     this.zoom = options.zoom;
0248     this.registered_events = {};
0249 
0250     this.el.style.width = options.width || this.el.scrollWidth || this.el.offsetWidth;
0251     this.el.style.height = options.height || this.el.scrollHeight || this.el.offsetHeight;
0252 
0253     google.maps.visualRefresh = options.enableNewStyle;
0254 
0255     for (i = 0; i < options_to_be_deleted.length; i++) {
0256       delete options[options_to_be_deleted[i]];
0257     }
0258 
0259     if(options.disableDefaultUI != true) {
0260       map_base_options = extend_object(map_base_options, map_controls_options);
0261     }
0262 
0263     map_options = extend_object(map_base_options, options);
0264 
0265     for (i = 0; i < events_that_hide_context_menu.length; i++) {
0266       delete map_options[events_that_hide_context_menu[i]];
0267     }
0268 
0269     for (i = 0; i < events_that_doesnt_hide_context_menu.length; i++) {
0270       delete map_options[events_that_doesnt_hide_context_menu[i]];
0271     }
0272 
0273     this.map = new google.maps.Map(this.el, map_options);
0274 
0275     if (markerClustererFunction) {
0276       this.markerClusterer = markerClustererFunction.apply(this, [this.map]);
0277     }
0278 
0279     var buildContextMenuHTML = function(control, e) {
0280       var html = '',
0281           options = window.context_menu[self.el.id][control];
0282 
0283       for (var i in options){
0284         if (options.hasOwnProperty(i)) {
0285           var option = options[i];
0286 
0287           html += '<li><a id="' + control + '_' + i + '" href="#">' + option.title + '</a></li>';
0288         }
0289       }
0290 
0291       if (!getElementById('gmaps_context_menu')) return;
0292 
0293       var context_menu_element = getElementById('gmaps_context_menu');
0294       
0295       context_menu_element.innerHTML = html;
0296 
0297       var context_menu_items = context_menu_element.getElementsByTagName('a'),
0298           context_menu_items_count = context_menu_items.length,
0299           i;
0300 
0301       for (i = 0; i < context_menu_items_count; i++) {
0302         var context_menu_item = context_menu_items[i];
0303 
0304         var assign_menu_item_action = function(ev){
0305           ev.preventDefault();
0306 
0307           options[this.id.replace(control + '_', '')].action.apply(self, [e]);
0308           self.hideContextMenu();
0309         };
0310 
0311         google.maps.event.clearListeners(context_menu_item, 'click');
0312         google.maps.event.addDomListenerOnce(context_menu_item, 'click', assign_menu_item_action, false);
0313       }
0314 
0315       var position = findAbsolutePosition.apply(this, [self.el]),
0316           left = position[0] + e.pixel.x - 15,
0317           top = position[1] + e.pixel.y- 15;
0318 
0319       context_menu_element.style.left = left + "px";
0320       context_menu_element.style.top = top + "px";
0321 
0322       context_menu_element.style.display = 'block';
0323     };
0324 
0325     this.buildContextMenu = function(control, e) {
0326       if (control === 'marker') {
0327         e.pixel = {};
0328 
0329         var overlay = new google.maps.OverlayView();
0330         overlay.setMap(self.map);
0331         
0332         overlay.draw = function() {
0333           var projection = overlay.getProjection(),
0334               position = e.marker.getPosition();
0335           
0336           e.pixel = projection.fromLatLngToContainerPixel(position);
0337 
0338           buildContextMenuHTML(control, e);
0339         };
0340       }
0341       else {
0342         buildContextMenuHTML(control, e);
0343       }
0344     };
0345 
0346     this.setContextMenu = function(options) {
0347       window.context_menu[self.el.id][options.control] = {};
0348 
0349       var i,
0350           ul = doc.createElement('ul');
0351 
0352       for (i in options.options) {
0353         if (options.options.hasOwnProperty(i)) {
0354           var option = options.options[i];
0355 
0356           window.context_menu[self.el.id][options.control][option.name] = {
0357             title: option.title,
0358             action: option.action
0359           };
0360         }
0361       }
0362 
0363       ul.id = 'gmaps_context_menu';
0364       ul.style.display = 'none';
0365       ul.style.position = 'absolute';
0366       ul.style.minWidth = '100px';
0367       ul.style.background = 'white';
0368       ul.style.listStyle = 'none';
0369       ul.style.padding = '8px';
0370       ul.style.boxShadow = '2px 2px 6px #ccc';
0371 
0372       doc.body.appendChild(ul);
0373 
0374       var context_menu_element = getElementById('gmaps_context_menu')
0375 
0376       google.maps.event.addDomListener(context_menu_element, 'mouseout', function(ev) {
0377         if (!ev.relatedTarget || !this.contains(ev.relatedTarget)) {
0378           window.setTimeout(function(){
0379             context_menu_element.style.display = 'none';
0380           }, 400);
0381         }
0382       }, false);
0383     };
0384 
0385     this.hideContextMenu = function() {
0386       var context_menu_element = getElementById('gmaps_context_menu');
0387 
0388       if (context_menu_element) {
0389         context_menu_element.style.display = 'none';
0390       }
0391     };
0392 
0393     var setupListener = function(object, name) {
0394       google.maps.event.addListener(object, name, function(e){
0395         if (e == undefined) {
0396           e = this;
0397         }
0398 
0399         options[name].apply(this, [e]);
0400 
0401         self.hideContextMenu();
0402       });
0403     };
0404 
0405     //google.maps.event.addListener(this.map, 'idle', this.hideContextMenu);

0406     google.maps.event.addListener(this.map, 'zoom_changed', this.hideContextMenu);
0407 
0408     for (var ev = 0; ev < events_that_hide_context_menu.length; ev++) {
0409       var name = events_that_hide_context_menu[ev];
0410 
0411       if (name in options) {
0412         setupListener(this.map, name);
0413       }
0414     }
0415 
0416     for (var ev = 0; ev < events_that_doesnt_hide_context_menu.length; ev++) {
0417       var name = events_that_doesnt_hide_context_menu[ev];
0418 
0419       if (name in options) {
0420         setupListener(this.map, name);
0421       }
0422     }
0423 
0424     google.maps.event.addListener(this.map, 'rightclick', function(e) {
0425       if (options.rightclick) {
0426         options.rightclick.apply(this, [e]);
0427       }
0428 
0429       if(window.context_menu[self.el.id]['map'] != undefined) {
0430         self.buildContextMenu('map', e);
0431       }
0432     });
0433 
0434     this.refresh = function() {
0435       google.maps.event.trigger(this.map, 'resize');
0436     };
0437 
0438     this.fitZoom = function() {
0439       var latLngs = [],
0440           markers_length = this.markers.length,
0441           i;
0442 
0443       for (i = 0; i < markers_length; i++) {
0444         if(typeof(this.markers[i].visible) === 'boolean' && this.markers[i].visible) {
0445           latLngs.push(this.markers[i].getPosition());
0446         }
0447       }
0448 
0449       this.fitLatLngBounds(latLngs);
0450     };
0451 
0452     this.fitLatLngBounds = function(latLngs) {
0453       var total = latLngs.length,
0454           bounds = new google.maps.LatLngBounds(),
0455           i;
0456 
0457       for(i = 0; i < total; i++) {
0458         bounds.extend(latLngs[i]);
0459       }
0460 
0461       this.map.fitBounds(bounds);
0462     };
0463 
0464     this.setCenter = function(lat, lng, callback) {
0465       this.map.panTo(new google.maps.LatLng(lat, lng));
0466 
0467       if (callback) {
0468         callback();
0469       }
0470     };
0471 
0472     this.getElement = function() {
0473       return this.el;
0474     };
0475 
0476     this.zoomIn = function(value) {
0477       value = value || 1;
0478 
0479       this.zoom = this.map.getZoom() + value;
0480       this.map.setZoom(this.zoom);
0481     };
0482 
0483     this.zoomOut = function(value) {
0484       value = value || 1;
0485 
0486       this.zoom = this.map.getZoom() - value;
0487       this.map.setZoom(this.zoom);
0488     };
0489 
0490     var native_methods = [],
0491         method;
0492 
0493     for (method in this.map) {
0494       if (typeof(this.map[method]) == 'function' && !this[method]) {
0495         native_methods.push(method);
0496       }
0497     }
0498 
0499     for (i = 0; i < native_methods.length; i++) {
0500       (function(gmaps, scope, method_name) {
0501         gmaps[method_name] = function(){
0502           return scope[method_name].apply(scope, arguments);
0503         };
0504       })(this, this.map, native_methods[i]);
0505     }
0506   };
0507 
0508   return GMaps;
0509 })(this);
0510 
0511 GMaps.prototype.createControl = function(options) {
0512   var control = document.createElement('div');
0513 
0514   control.style.cursor = 'pointer';
0515   
0516   if (options.disableDefaultStyles !== true) {
0517     control.style.fontFamily = 'Roboto, Arial, sans-serif';
0518     control.style.fontSize = '11px';
0519     control.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px';
0520   }
0521 
0522   for (var option in options.style) {
0523     control.style[option] = options.style[option];
0524   }
0525 
0526   if (options.id) {
0527     control.id = options.id;
0528   }
0529 
0530   if (options.classes) {
0531     control.className = options.classes;
0532   }
0533 
0534   if (options.content) {
0535     if (typeof options.content === 'string') {
0536       control.innerHTML = options.content;
0537     }
0538     else if (options.content instanceof HTMLElement) {
0539       control.appendChild(options.content);
0540     }
0541   }
0542 
0543   if (options.position) {
0544     control.position = google.maps.ControlPosition[options.position.toUpperCase()];
0545   }
0546 
0547   for (var ev in options.events) {
0548     (function(object, name) {
0549       google.maps.event.addDomListener(object, name, function(){
0550         options.events[name].apply(this, [this]);
0551       });
0552     })(control, ev);
0553   }
0554 
0555   control.index = 1;
0556 
0557   return control;
0558 };
0559 
0560 GMaps.prototype.addControl = function(options) {
0561   var control = this.createControl(options);
0562   
0563   this.controls.push(control);
0564   this.map.controls[control.position].push(control);
0565 
0566   return control;
0567 };
0568 
0569 GMaps.prototype.removeControl = function(control) {
0570   var position = null,
0571       i;
0572 
0573   for (i = 0; i < this.controls.length; i++) {
0574     if (this.controls[i] == control) {
0575       position = this.controls[i].position;
0576       this.controls.splice(i, 1);
0577     }
0578   }
0579 
0580   if (position) {
0581     for (i = 0; i < this.map.controls.length; i++) {
0582       var controlsForPosition = this.map.controls[control.position];
0583 
0584       if (controlsForPosition.getAt(i) == control) {
0585         controlsForPosition.removeAt(i);
0586 
0587         break;
0588       }
0589     }
0590   }
0591 
0592   return control;
0593 };
0594 
0595 GMaps.prototype.createMarker = function(options) {
0596   if (options.lat == undefined && options.lng == undefined && options.position == undefined) {
0597     throw 'No latitude or longitude defined.';
0598   }
0599 
0600   var self = this,
0601       details = options.details,
0602       fences = options.fences,
0603       outside = options.outside,
0604       base_options = {
0605         position: new google.maps.LatLng(options.lat, options.lng),
0606         map: null
0607       },
0608       marker_options = extend_object(base_options, options);
0609 
0610   delete marker_options.lat;
0611   delete marker_options.lng;
0612   delete marker_options.fences;
0613   delete marker_options.outside;
0614 
0615   var marker = new google.maps.Marker(marker_options);
0616 
0617   marker.fences = fences;
0618 
0619   if (options.infoWindow) {
0620     marker.infoWindow = new google.maps.InfoWindow(options.infoWindow);
0621 
0622     var info_window_events = ['closeclick', 'content_changed', 'domready', 'position_changed', 'zindex_changed'];
0623 
0624     for (var ev = 0; ev < info_window_events.length; ev++) {
0625       (function(object, name) {
0626         if (options.infoWindow[name]) {
0627           google.maps.event.addListener(object, name, function(e){
0628             options.infoWindow[name].apply(this, [e]);
0629           });
0630         }
0631       })(marker.infoWindow, info_window_events[ev]);
0632     }
0633   }
0634 
0635   var marker_events = ['animation_changed', 'clickable_changed', 'cursor_changed', 'draggable_changed', 'flat_changed', 'icon_changed', 'position_changed', 'shadow_changed', 'shape_changed', 'title_changed', 'visible_changed', 'zindex_changed'];
0636 
0637   var marker_events_with_mouse = ['dblclick', 'drag', 'dragend', 'dragstart', 'mousedown', 'mouseout', 'mouseover', 'mouseup'];
0638 
0639   for (var ev = 0; ev < marker_events.length; ev++) {
0640     (function(object, name) {
0641       if (options[name]) {
0642         google.maps.event.addListener(object, name, function(){
0643           options[name].apply(this, [this]);
0644         });
0645       }
0646     })(marker, marker_events[ev]);
0647   }
0648 
0649   for (var ev = 0; ev < marker_events_with_mouse.length; ev++) {
0650     (function(map, object, name) {
0651       if (options[name]) {
0652         google.maps.event.addListener(object, name, function(me){
0653           if(!me.pixel){
0654             me.pixel = map.getProjection().fromLatLngToPoint(me.latLng)
0655           }
0656           
0657           options[name].apply(this, [me]);
0658         });
0659       }
0660     })(this.map, marker, marker_events_with_mouse[ev]);
0661   }
0662 
0663   google.maps.event.addListener(marker, 'click', function() {
0664     this.details = details;
0665 
0666     if (options.click) {
0667       options.click.apply(this, [this]);
0668     }
0669 
0670     if (marker.infoWindow) {
0671       self.hideInfoWindows();
0672       marker.infoWindow.open(self.map, marker);
0673     }
0674   });
0675 
0676   google.maps.event.addListener(marker, 'rightclick', function(e) {
0677     e.marker = this;
0678 
0679     if (options.rightclick) {
0680       options.rightclick.apply(this, [e]);
0681     }
0682 
0683     if (window.context_menu[self.el.id]['marker'] != undefined) {
0684       self.buildContextMenu('marker', e);
0685     }
0686   });
0687 
0688   if (marker.fences) {
0689     google.maps.event.addListener(marker, 'dragend', function() {
0690       self.checkMarkerGeofence(marker, function(m, f) {
0691         outside(m, f);
0692       });
0693     });
0694   }
0695 
0696   return marker;
0697 };
0698 
0699 GMaps.prototype.addMarker = function(options) {
0700   var marker;
0701   if(options.hasOwnProperty('gm_accessors_')) {
0702     // Native google.maps.Marker object

0703     marker = options;
0704   }
0705   else {
0706     if ((options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) || options.position) {
0707       marker = this.createMarker(options);
0708     }
0709     else {
0710       throw 'No latitude or longitude defined.';
0711     }
0712   }
0713 
0714   marker.setMap(this.map);
0715 
0716   if(this.markerClusterer) {
0717     this.markerClusterer.addMarker(marker);
0718   }
0719 
0720   this.markers.push(marker);
0721 
0722   GMaps.fire('marker_added', marker, this);
0723 
0724   return marker;
0725 };
0726 
0727 GMaps.prototype.addMarkers = function(array) {
0728   for (var i = 0, marker; marker=array[i]; i++) {
0729     this.addMarker(marker);
0730   }
0731 
0732   return this.markers;
0733 };
0734 
0735 GMaps.prototype.hideInfoWindows = function() {
0736   for (var i = 0, marker; marker = this.markers[i]; i++){
0737     if (marker.infoWindow) {
0738       marker.infoWindow.close();
0739     }
0740   }
0741 };
0742 
0743 GMaps.prototype.removeMarker = function(marker) {
0744   for (var i = 0; i < this.markers.length; i++) {
0745     if (this.markers[i] === marker) {
0746       this.markers[i].setMap(null);
0747       this.markers.splice(i, 1);
0748 
0749       if(this.markerClusterer) {
0750         this.markerClusterer.removeMarker(marker);
0751       }
0752 
0753       GMaps.fire('marker_removed', marker, this);
0754 
0755       break;
0756     }
0757   }
0758 
0759   return marker;
0760 };
0761 
0762 GMaps.prototype.removeMarkers = function (collection) {
0763   var new_markers = [];
0764 
0765   if (typeof collection == 'undefined') {
0766     for (var i = 0; i < this.markers.length; i++) {
0767       var marker = this.markers[i];
0768       marker.setMap(null);
0769 
0770       if(this.markerClusterer) {
0771         this.markerClusterer.removeMarker(marker);
0772       }
0773 
0774       GMaps.fire('marker_removed', marker, this);
0775     }
0776     
0777     this.markers = new_markers;
0778   }
0779   else {
0780     for (var i = 0; i < collection.length; i++) {
0781       var index = this.markers.indexOf(collection[i]);
0782 
0783       if (index > -1) {
0784         var marker = this.markers[index];
0785         marker.setMap(null);
0786 
0787         if(this.markerClusterer) {
0788           this.markerClusterer.removeMarker(marker);
0789         }
0790 
0791         GMaps.fire('marker_removed', marker, this);
0792       }
0793     }
0794 
0795     for (var i = 0; i < this.markers.length; i++) {
0796       var marker = this.markers[i];
0797       if (marker.getMap() != null) {
0798         new_markers.push(marker);
0799       }
0800     }
0801 
0802     this.markers = new_markers;
0803   }
0804 };
0805 
0806 GMaps.prototype.drawOverlay = function(options) {
0807   var overlay = new google.maps.OverlayView(),
0808       auto_show = true;
0809 
0810   overlay.setMap(this.map);
0811 
0812   if (options.auto_show != null) {
0813     auto_show = options.auto_show;
0814   }
0815 
0816   overlay.onAdd = function() {
0817     var el = document.createElement('div');
0818 
0819     el.style.borderStyle = "none";
0820     el.style.borderWidth = "0px";
0821     el.style.position = "absolute";
0822     el.style.zIndex = 100;
0823     el.innerHTML = options.content;
0824 
0825     overlay.el = el;
0826 
0827     if (!options.layer) {
0828       options.layer = 'overlayLayer';
0829     }
0830     
0831     var panes = this.getPanes(),
0832         overlayLayer = panes[options.layer],
0833         stop_overlay_events = ['contextmenu', 'DOMMouseScroll', 'dblclick', 'mousedown'];
0834 
0835     overlayLayer.appendChild(el);
0836 
0837     for (var ev = 0; ev < stop_overlay_events.length; ev++) {
0838       (function(object, name) {
0839         google.maps.event.addDomListener(object, name, function(e){
0840           if (navigator.userAgent.toLowerCase().indexOf('msie') != -1 && document.all) {
0841             e.cancelBubble = true;
0842             e.returnValue = false;
0843           }
0844           else {
0845             e.stopPropagation();
0846           }
0847         });
0848       })(el, stop_overlay_events[ev]);
0849     }
0850 
0851     if (options.click) {
0852       panes.overlayMouseTarget.appendChild(overlay.el);
0853       google.maps.event.addDomListener(overlay.el, 'click', function() {
0854         options.click.apply(overlay, [overlay]);
0855       });
0856     }
0857 
0858     google.maps.event.trigger(this, 'ready');
0859   };
0860 
0861   overlay.draw = function() {
0862     var projection = this.getProjection(),
0863         pixel = projection.fromLatLngToDivPixel(new google.maps.LatLng(options.lat, options.lng));
0864 
0865     options.horizontalOffset = options.horizontalOffset || 0;
0866     options.verticalOffset = options.verticalOffset || 0;
0867 
0868     var el = overlay.el,
0869         content = el.children[0],
0870         content_height = content.clientHeight,
0871         content_width = content.clientWidth;
0872 
0873     switch (options.verticalAlign) {
0874       case 'top':
0875         el.style.top = (pixel.y - content_height + options.verticalOffset) + 'px';
0876         break;
0877       default:
0878       case 'middle':
0879         el.style.top = (pixel.y - (content_height / 2) + options.verticalOffset) + 'px';
0880         break;
0881       case 'bottom':
0882         el.style.top = (pixel.y + options.verticalOffset) + 'px';
0883         break;
0884     }
0885 
0886     switch (options.horizontalAlign) {
0887       case 'left':
0888         el.style.left = (pixel.x - content_width + options.horizontalOffset) + 'px';
0889         break;
0890       default:
0891       case 'center':
0892         el.style.left = (pixel.x - (content_width / 2) + options.horizontalOffset) + 'px';
0893         break;
0894       case 'right':
0895         el.style.left = (pixel.x + options.horizontalOffset) + 'px';
0896         break;
0897     }
0898 
0899     el.style.display = auto_show ? 'block' : 'none';
0900 
0901     if (!auto_show) {
0902       options.show.apply(this, [el]);
0903     }
0904   };
0905 
0906   overlay.onRemove = function() {
0907     var el = overlay.el;
0908 
0909     if (options.remove) {
0910       options.remove.apply(this, [el]);
0911     }
0912     else {
0913       overlay.el.parentNode.removeChild(overlay.el);
0914       overlay.el = null;
0915     }
0916   };
0917 
0918   this.overlays.push(overlay);
0919   return overlay;
0920 };
0921 
0922 GMaps.prototype.removeOverlay = function(overlay) {
0923   for (var i = 0; i < this.overlays.length; i++) {
0924     if (this.overlays[i] === overlay) {
0925       this.overlays[i].setMap(null);
0926       this.overlays.splice(i, 1);
0927 
0928       break;
0929     }
0930   }
0931 };
0932 
0933 GMaps.prototype.removeOverlays = function() {
0934   for (var i = 0, item; item = this.overlays[i]; i++) {
0935     item.setMap(null);
0936   }
0937 
0938   this.overlays = [];
0939 };
0940 
0941 GMaps.prototype.drawPolyline = function(options) {
0942   var path = [],
0943       points = options.path;
0944 
0945   if (points.length) {
0946     if (points[0][0] === undefined) {
0947       path = points;
0948     }
0949     else {
0950       for (var i = 0, latlng; latlng = points[i]; i++) {
0951         path.push(new google.maps.LatLng(latlng[0], latlng[1]));
0952       }
0953     }
0954   }
0955 
0956   var polyline_options = {
0957     map: this.map,
0958     path: path,
0959     strokeColor: options.strokeColor,
0960     strokeOpacity: options.strokeOpacity,
0961     strokeWeight: options.strokeWeight,
0962     geodesic: options.geodesic,
0963     clickable: true,
0964     editable: false,
0965     visible: true
0966   };
0967 
0968   if (options.hasOwnProperty("clickable")) {
0969     polyline_options.clickable = options.clickable;
0970   }
0971 
0972   if (options.hasOwnProperty("editable")) {
0973     polyline_options.editable = options.editable;
0974   }
0975 
0976   if (options.hasOwnProperty("icons")) {
0977     polyline_options.icons = options.icons;
0978   }
0979 
0980   if (options.hasOwnProperty("zIndex")) {
0981     polyline_options.zIndex = options.zIndex;
0982   }
0983 
0984   var polyline = new google.maps.Polyline(polyline_options);
0985 
0986   var polyline_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
0987 
0988   for (var ev = 0; ev < polyline_events.length; ev++) {
0989     (function(object, name) {
0990       if (options[name]) {
0991         google.maps.event.addListener(object, name, function(e){
0992           options[name].apply(this, [e]);
0993         });
0994       }
0995     })(polyline, polyline_events[ev]);
0996   }
0997 
0998   this.polylines.push(polyline);
0999 
1000   GMaps.fire('polyline_added', polyline, this);
1001 
1002   return polyline;
1003 };
1004 
1005 GMaps.prototype.removePolyline = function(polyline) {
1006   for (var i = 0; i < this.polylines.length; i++) {
1007     if (this.polylines[i] === polyline) {
1008       this.polylines[i].setMap(null);
1009       this.polylines.splice(i, 1);
1010 
1011       GMaps.fire('polyline_removed', polyline, this);
1012 
1013       break;
1014     }
1015   }
1016 };
1017 
1018 GMaps.prototype.removePolylines = function() {
1019   for (var i = 0, item; item = this.polylines[i]; i++) {
1020     item.setMap(null);
1021   }
1022 
1023   this.polylines = [];
1024 };
1025 
1026 GMaps.prototype.drawCircle = function(options) {
1027   options =  extend_object({
1028     map: this.map,
1029     center: new google.maps.LatLng(options.lat, options.lng)
1030   }, options);
1031 
1032   delete options.lat;
1033   delete options.lng;
1034 
1035   var polygon = new google.maps.Circle(options),
1036       polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
1037 
1038   for (var ev = 0; ev < polygon_events.length; ev++) {
1039     (function(object, name) {
1040       if (options[name]) {
1041         google.maps.event.addListener(object, name, function(e){
1042           options[name].apply(this, [e]);
1043         });
1044       }
1045     })(polygon, polygon_events[ev]);
1046   }
1047 
1048   this.polygons.push(polygon);
1049 
1050   return polygon;
1051 };
1052 
1053 GMaps.prototype.drawRectangle = function(options) {
1054   options = extend_object({
1055     map: this.map
1056   }, options);
1057 
1058   var latLngBounds = new google.maps.LatLngBounds(
1059     new google.maps.LatLng(options.bounds[0][0], options.bounds[0][1]),
1060     new google.maps.LatLng(options.bounds[1][0], options.bounds[1][1])
1061   );
1062 
1063   options.bounds = latLngBounds;
1064 
1065   var polygon = new google.maps.Rectangle(options),
1066       polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
1067 
1068   for (var ev = 0; ev < polygon_events.length; ev++) {
1069     (function(object, name) {
1070       if (options[name]) {
1071         google.maps.event.addListener(object, name, function(e){
1072           options[name].apply(this, [e]);
1073         });
1074       }
1075     })(polygon, polygon_events[ev]);
1076   }
1077 
1078   this.polygons.push(polygon);
1079 
1080   return polygon;
1081 };
1082 
1083 GMaps.prototype.drawPolygon = function(options) {
1084   var useGeoJSON = false;
1085 
1086   if(options.hasOwnProperty("useGeoJSON")) {
1087     useGeoJSON = options.useGeoJSON;
1088   }
1089 
1090   delete options.useGeoJSON;
1091 
1092   options = extend_object({
1093     map: this.map
1094   }, options);
1095 
1096   if (useGeoJSON == false) {
1097     options.paths = [options.paths.slice(0)];
1098   }
1099 
1100   if (options.paths.length > 0) {
1101     if (options.paths[0].length > 0) {
1102       options.paths = array_flat(array_map(options.paths, arrayToLatLng, useGeoJSON));
1103     }
1104   }
1105 
1106   var polygon = new google.maps.Polygon(options),
1107       polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
1108 
1109   for (var ev = 0; ev < polygon_events.length; ev++) {
1110     (function(object, name) {
1111       if (options[name]) {
1112         google.maps.event.addListener(object, name, function(e){
1113           options[name].apply(this, [e]);
1114         });
1115       }
1116     })(polygon, polygon_events[ev]);
1117   }
1118 
1119   this.polygons.push(polygon);
1120 
1121   GMaps.fire('polygon_added', polygon, this);
1122 
1123   return polygon;
1124 };
1125 
1126 GMaps.prototype.removePolygon = function(polygon) {
1127   for (var i = 0; i < this.polygons.length; i++) {
1128     if (this.polygons[i] === polygon) {
1129       this.polygons[i].setMap(null);
1130       this.polygons.splice(i, 1);
1131 
1132       GMaps.fire('polygon_removed', polygon, this);
1133 
1134       break;
1135     }
1136   }
1137 };
1138 
1139 GMaps.prototype.removePolygons = function() {
1140   for (var i = 0, item; item = this.polygons[i]; i++) {
1141     item.setMap(null);
1142   }
1143 
1144   this.polygons = [];
1145 };
1146 
1147 GMaps.prototype.getFromFusionTables = function(options) {
1148   var events = options.events;
1149 
1150   delete options.events;
1151 
1152   var fusion_tables_options = options,
1153       layer = new google.maps.FusionTablesLayer(fusion_tables_options);
1154 
1155   for (var ev in events) {
1156     (function(object, name) {
1157       google.maps.event.addListener(object, name, function(e) {
1158         events[name].apply(this, [e]);
1159       });
1160     })(layer, ev);
1161   }
1162 
1163   this.layers.push(layer);
1164 
1165   return layer;
1166 };
1167 
1168 GMaps.prototype.loadFromFusionTables = function(options) {
1169   var layer = this.getFromFusionTables(options);
1170   layer.setMap(this.map);
1171 
1172   return layer;
1173 };
1174 
1175 GMaps.prototype.getFromKML = function(options) {
1176   var url = options.url,
1177       events = options.events;
1178 
1179   delete options.url;
1180   delete options.events;
1181 
1182   var kml_options = options,
1183       layer = new google.maps.KmlLayer(url, kml_options);
1184 
1185   for (var ev in events) {
1186     (function(object, name) {
1187       google.maps.event.addListener(object, name, function(e) {
1188         events[name].apply(this, [e]);
1189       });
1190     })(layer, ev);
1191   }
1192 
1193   this.layers.push(layer);
1194 
1195   return layer;
1196 };
1197 
1198 GMaps.prototype.loadFromKML = function(options) {
1199   var layer = this.getFromKML(options);
1200   layer.setMap(this.map);
1201 
1202   return layer;
1203 };
1204 
1205 GMaps.prototype.addLayer = function(layerName, options) {
1206   //var default_layers = ['weather', 'clouds', 'traffic', 'transit', 'bicycling', 'panoramio', 'places'];

1207   options = options || {};
1208   var layer;
1209 
1210   switch(layerName) {
1211     case 'weather': this.singleLayers.weather = layer = new google.maps.weather.WeatherLayer();
1212       break;
1213     case 'clouds': this.singleLayers.clouds = layer = new google.maps.weather.CloudLayer();
1214       break;
1215     case 'traffic': this.singleLayers.traffic = layer = new google.maps.TrafficLayer();
1216       break;
1217     case 'transit': this.singleLayers.transit = layer = new google.maps.TransitLayer();
1218       break;
1219     case 'bicycling': this.singleLayers.bicycling = layer = new google.maps.BicyclingLayer();
1220       break;
1221     case 'panoramio':
1222         this.singleLayers.panoramio = layer = new google.maps.panoramio.PanoramioLayer();
1223         layer.setTag(options.filter);
1224         delete options.filter;
1225 
1226         //click event

1227         if (options.click) {
1228           google.maps.event.addListener(layer, 'click', function(event) {
1229             options.click(event);
1230             delete options.click;
1231           });
1232         }
1233       break;
1234       case 'places':
1235         this.singleLayers.places = layer = new google.maps.places.PlacesService(this.map);
1236 
1237         //search, nearbySearch, radarSearch callback, Both are the same

1238         if (options.search || options.nearbySearch || options.radarSearch) {
1239           var placeSearchRequest  = {
1240             bounds : options.bounds || null,
1241             keyword : options.keyword || null,
1242             location : options.location || null,
1243             name : options.name || null,
1244             radius : options.radius || null,
1245             rankBy : options.rankBy || null,
1246             types : options.types || null
1247           };
1248 
1249           if (options.radarSearch) {
1250             layer.radarSearch(placeSearchRequest, options.radarSearch);
1251           }
1252 
1253           if (options.search) {
1254             layer.search(placeSearchRequest, options.search);
1255           }
1256 
1257           if (options.nearbySearch) {
1258             layer.nearbySearch(placeSearchRequest, options.nearbySearch);
1259           }
1260         }
1261 
1262         //textSearch callback

1263         if (options.textSearch) {
1264           var textSearchRequest  = {
1265             bounds : options.bounds || null,
1266             location : options.location || null,
1267             query : options.query || null,
1268             radius : options.radius || null
1269           };
1270 
1271           layer.textSearch(textSearchRequest, options.textSearch);
1272         }
1273       break;
1274   }
1275 
1276   if (layer !== undefined) {
1277     if (typeof layer.setOptions == 'function') {
1278       layer.setOptions(options);
1279     }
1280     if (typeof layer.setMap == 'function') {
1281       layer.setMap(this.map);
1282     }
1283 
1284     return layer;
1285   }
1286 };
1287 
1288 GMaps.prototype.removeLayer = function(layer) {
1289   if (typeof(layer) == "string" && this.singleLayers[layer] !== undefined) {
1290      this.singleLayers[layer].setMap(null);
1291 
1292      delete this.singleLayers[layer];
1293   }
1294   else {
1295     for (var i = 0; i < this.layers.length; i++) {
1296       if (this.layers[i] === layer) {
1297         this.layers[i].setMap(null);
1298         this.layers.splice(i, 1);
1299 
1300         break;
1301       }
1302     }
1303   }
1304 };
1305 
1306 var travelMode, unitSystem;
1307 
1308 GMaps.prototype.getRoutes = function(options) {
1309   switch (options.travelMode) {
1310     case 'bicycling':
1311       travelMode = google.maps.TravelMode.BICYCLING;
1312       break;
1313     case 'transit':
1314       travelMode = google.maps.TravelMode.TRANSIT;
1315       break;
1316     case 'driving':
1317       travelMode = google.maps.TravelMode.DRIVING;
1318       break;
1319     default:
1320       travelMode = google.maps.TravelMode.WALKING;
1321       break;
1322   }
1323 
1324   if (options.unitSystem === 'imperial') {
1325     unitSystem = google.maps.UnitSystem.IMPERIAL;
1326   }
1327   else {
1328     unitSystem = google.maps.UnitSystem.METRIC;
1329   }
1330 
1331   var base_options = {
1332         avoidHighways: false,
1333         avoidTolls: false,
1334         optimizeWaypoints: false,
1335         waypoints: []
1336       },
1337       request_options =  extend_object(base_options, options);
1338 
1339   request_options.origin = /string/.test(typeof options.origin) ? options.origin : new google.maps.LatLng(options.origin[0], options.origin[1]);
1340   request_options.destination = /string/.test(typeof options.destination) ? options.destination : new google.maps.LatLng(options.destination[0], options.destination[1]);
1341   request_options.travelMode = travelMode;
1342   request_options.unitSystem = unitSystem;
1343 
1344   delete request_options.callback;
1345   delete request_options.error;
1346 
1347   var self = this,
1348       service = new google.maps.DirectionsService();
1349 
1350   service.route(request_options, function(result, status) {
1351     if (status === google.maps.DirectionsStatus.OK) {
1352       for (var r in result.routes) {
1353         if (result.routes.hasOwnProperty(r)) {
1354           self.routes.push(result.routes[r]);
1355         }
1356       }
1357 
1358       if (options.callback) {
1359         options.callback(self.routes);
1360       }
1361     }
1362     else {
1363       if (options.error) {
1364         options.error(result, status);
1365       }
1366     }
1367   });
1368 };
1369 
1370 GMaps.prototype.removeRoutes = function() {
1371   this.routes = [];
1372 };
1373 
1374 GMaps.prototype.getElevations = function(options) {
1375   options = extend_object({
1376     locations: [],
1377     path : false,
1378     samples : 256
1379   }, options);
1380 
1381   if (options.locations.length > 0) {
1382     if (options.locations[0].length > 0) {
1383       options.locations = array_flat(array_map([options.locations], arrayToLatLng,  false));
1384     }
1385   }
1386 
1387   var callback = options.callback;
1388   delete options.callback;
1389 
1390   var service = new google.maps.ElevationService();
1391 
1392   //location request

1393   if (!options.path) {
1394     delete options.path;
1395     delete options.samples;
1396 
1397     service.getElevationForLocations(options, function(result, status) {
1398       if (callback && typeof(callback) === "function") {
1399         callback(result, status);
1400       }
1401     });
1402   //path request

1403   } else {
1404     var pathRequest = {
1405       path : options.locations,
1406       samples : options.samples
1407     };
1408 
1409     service.getElevationAlongPath(pathRequest, function(result, status) {
1410      if (callback && typeof(callback) === "function") {
1411         callback(result, status);
1412       }
1413     });
1414   }
1415 };
1416 
1417 GMaps.prototype.cleanRoute = GMaps.prototype.removePolylines;
1418 
1419 GMaps.prototype.drawRoute = function(options) {
1420   var self = this;
1421 
1422   this.getRoutes({
1423     origin: options.origin,
1424     destination: options.destination,
1425     travelMode: options.travelMode,
1426     waypoints: options.waypoints,
1427     unitSystem: options.unitSystem,
1428     error: options.error,
1429     callback: function(e) {
1430       if (e.length > 0) {
1431         var polyline_options = {
1432           path: e[e.length - 1].overview_path,
1433           strokeColor: options.strokeColor,
1434           strokeOpacity: options.strokeOpacity,
1435           strokeWeight: options.strokeWeight
1436         };
1437 
1438         if (options.hasOwnProperty("icons")) {
1439           polyline_options.icons = options.icons;
1440         }
1441 
1442         self.drawPolyline(polyline_options);
1443         
1444         if (options.callback) {
1445           options.callback(e[e.length - 1]);
1446         }
1447       }
1448     }
1449   });
1450 };
1451 
1452 GMaps.prototype.travelRoute = function(options) {
1453   if (options.origin && options.destination) {
1454     this.getRoutes({
1455       origin: options.origin,
1456       destination: options.destination,
1457       travelMode: options.travelMode,
1458       waypoints : options.waypoints,
1459       unitSystem: options.unitSystem,
1460       error: options.error,
1461       callback: function(e) {
1462         //start callback

1463         if (e.length > 0 && options.start) {
1464           options.start(e[e.length - 1]);
1465         }
1466 
1467         //step callback

1468         if (e.length > 0 && options.step) {
1469           var route = e[e.length - 1];
1470           if (route.legs.length > 0) {
1471             var steps = route.legs[0].steps;
1472             for (var i = 0, step; step = steps[i]; i++) {
1473               step.step_number = i;
1474               options.step(step, (route.legs[0].steps.length - 1));
1475             }
1476           }
1477         }
1478 
1479         //end callback

1480         if (e.length > 0 && options.end) {
1481            options.end(e[e.length - 1]);
1482         }
1483       }
1484     });
1485   }
1486   else if (options.route) {
1487     if (options.route.legs.length > 0) {
1488       var steps = options.route.legs[0].steps;
1489       for (var i = 0, step; step = steps[i]; i++) {
1490         step.step_number = i;
1491         options.step(step);
1492       }
1493     }
1494   }
1495 };
1496 
1497 GMaps.prototype.drawSteppedRoute = function(options) {
1498   var self = this;
1499   
1500   if (options.origin && options.destination) {
1501     this.getRoutes({
1502       origin: options.origin,
1503       destination: options.destination,
1504       travelMode: options.travelMode,
1505       waypoints : options.waypoints,
1506       error: options.error,
1507       callback: function(e) {
1508         //start callback

1509         if (e.length > 0 && options.start) {
1510           options.start(e[e.length - 1]);
1511         }
1512 
1513         //step callback

1514         if (e.length > 0 && options.step) {
1515           var route = e[e.length - 1];
1516           if (route.legs.length > 0) {
1517             var steps = route.legs[0].steps;
1518             for (var i = 0, step; step = steps[i]; i++) {
1519               step.step_number = i;
1520               var polyline_options = {
1521                 path: step.path,
1522                 strokeColor: options.strokeColor,
1523                 strokeOpacity: options.strokeOpacity,
1524                 strokeWeight: options.strokeWeight
1525               };
1526 
1527               if (options.hasOwnProperty("icons")) {
1528                 polyline_options.icons = options.icons;
1529               }
1530 
1531               self.drawPolyline(polyline_options);
1532               options.step(step, (route.legs[0].steps.length - 1));
1533             }
1534           }
1535         }
1536 
1537         //end callback

1538         if (e.length > 0 && options.end) {
1539            options.end(e[e.length - 1]);
1540         }
1541       }
1542     });
1543   }
1544   else if (options.route) {
1545     if (options.route.legs.length > 0) {
1546       var steps = options.route.legs[0].steps;
1547       for (var i = 0, step; step = steps[i]; i++) {
1548         step.step_number = i;
1549         var polyline_options = {
1550           path: step.path,
1551           strokeColor: options.strokeColor,
1552           strokeOpacity: options.strokeOpacity,
1553           strokeWeight: options.strokeWeight
1554         };
1555 
1556         if (options.hasOwnProperty("icons")) {
1557           polyline_options.icons = options.icons;
1558         }
1559 
1560         self.drawPolyline(polyline_options);
1561         options.step(step);
1562       }
1563     }
1564   }
1565 };
1566 
1567 GMaps.Route = function(options) {
1568   this.origin = options.origin;
1569   this.destination = options.destination;
1570   this.waypoints = options.waypoints;
1571 
1572   this.map = options.map;
1573   this.route = options.route;
1574   this.step_count = 0;
1575   this.steps = this.route.legs[0].steps;
1576   this.steps_length = this.steps.length;
1577 
1578   var polyline_options = {
1579     path: new google.maps.MVCArray(),
1580     strokeColor: options.strokeColor,
1581     strokeOpacity: options.strokeOpacity,
1582     strokeWeight: options.strokeWeight
1583   };
1584 
1585   if (options.hasOwnProperty("icons")) {
1586     polyline_options.icons = options.icons;
1587   }
1588 
1589   this.polyline = this.map.drawPolyline(polyline_options).getPath();
1590 };
1591 
1592 GMaps.Route.prototype.getRoute = function(options) {
1593   var self = this;
1594 
1595   this.map.getRoutes({
1596     origin : this.origin,
1597     destination : this.destination,
1598     travelMode : options.travelMode,
1599     waypoints : this.waypoints || [],
1600     error: options.error,
1601     callback : function() {
1602       self.route = e[0];
1603 
1604       if (options.callback) {
1605         options.callback.call(self);
1606       }
1607     }
1608   });
1609 };
1610 
1611 GMaps.Route.prototype.back = function() {
1612   if (this.step_count > 0) {
1613     this.step_count--;
1614     var path = this.route.legs[0].steps[this.step_count].path;
1615 
1616     for (var p in path){
1617       if (path.hasOwnProperty(p)){
1618         this.polyline.pop();
1619       }
1620     }
1621   }
1622 };
1623 
1624 GMaps.Route.prototype.forward = function() {
1625   if (this.step_count < this.steps_length) {
1626     var path = this.route.legs[0].steps[this.step_count].path;
1627 
1628     for (var p in path){
1629       if (path.hasOwnProperty(p)){
1630         this.polyline.push(path[p]);
1631       }
1632     }
1633     this.step_count++;
1634   }
1635 };
1636 
1637 GMaps.prototype.checkGeofence = function(lat, lng, fence) {
1638   return fence.containsLatLng(new google.maps.LatLng(lat, lng));
1639 };
1640 
1641 GMaps.prototype.checkMarkerGeofence = function(marker, outside_callback) {
1642   if (marker.fences) {
1643     for (var i = 0, fence; fence = marker.fences[i]; i++) {
1644       var pos = marker.getPosition();
1645       if (!this.checkGeofence(pos.lat(), pos.lng(), fence)) {
1646         outside_callback(marker, fence);
1647       }
1648     }
1649   }
1650 };
1651 
1652 GMaps.prototype.toImage = function(options) {
1653   var options = options || {},
1654       static_map_options = {};
1655 
1656   static_map_options['size'] = options['size'] || [this.el.clientWidth, this.el.clientHeight];
1657   static_map_options['lat'] = this.getCenter().lat();
1658   static_map_options['lng'] = this.getCenter().lng();
1659 
1660   if (this.markers.length > 0) {
1661     static_map_options['markers'] = [];
1662     
1663     for (var i = 0; i < this.markers.length; i++) {
1664       static_map_options['markers'].push({
1665         lat: this.markers[i].getPosition().lat(),
1666         lng: this.markers[i].getPosition().lng()
1667       });
1668     }
1669   }
1670 
1671   if (this.polylines.length > 0) {
1672     var polyline = this.polylines[0];
1673     
1674     static_map_options['polyline'] = {};
1675     static_map_options['polyline']['path'] = google.maps.geometry.encoding.encodePath(polyline.getPath());
1676     static_map_options['polyline']['strokeColor'] = polyline.strokeColor
1677     static_map_options['polyline']['strokeOpacity'] = polyline.strokeOpacity
1678     static_map_options['polyline']['strokeWeight'] = polyline.strokeWeight
1679   }
1680 
1681   return GMaps.staticMapURL(static_map_options);
1682 };
1683 
1684 GMaps.staticMapURL = function(options){
1685   var parameters = [],
1686       data,
1687       static_root = (location.protocol === 'file:' ? 'http:' : location.protocol ) + '//maps.googleapis.com/maps/api/staticmap';
1688 
1689   if (options.url) {
1690     static_root = options.url;
1691     delete options.url;
1692   }
1693 
1694   static_root += '?';
1695 
1696   var markers = options.markers;
1697   
1698   delete options.markers;
1699 
1700   if (!markers && options.marker) {
1701     markers = [options.marker];
1702     delete options.marker;
1703   }
1704 
1705   var styles = options.styles;
1706 
1707   delete options.styles;
1708 
1709   var polyline = options.polyline;
1710   delete options.polyline;
1711 
1712   /** Map options **/
1713   if (options.center) {
1714     parameters.push('center=' + options.center);
1715     delete options.center;
1716   }
1717   else if (options.address) {
1718     parameters.push('center=' + options.address);
1719     delete options.address;
1720   }
1721   else if (options.lat) {
1722     parameters.push(['center=', options.lat, ',', options.lng].join(''));
1723     delete options.lat;
1724     delete options.lng;
1725   }
1726   else if (options.visible) {
1727     var visible = encodeURI(options.visible.join('|'));
1728     parameters.push('visible=' + visible);
1729   }
1730 
1731   var size = options.size;
1732   if (size) {
1733     if (size.join) {
1734       size = size.join('x');
1735     }
1736     delete options.size;
1737   }
1738   else {
1739     size = '630x300';
1740   }
1741   parameters.push('size=' + size);
1742 
1743   if (!options.zoom && options.zoom !== false) {
1744     options.zoom = 15;
1745   }
1746 
1747   var sensor = options.hasOwnProperty('sensor') ? !!options.sensor : true;
1748   delete options.sensor;
1749   parameters.push('sensor=' + sensor);
1750 
1751   for (var param in options) {
1752     if (options.hasOwnProperty(param)) {
1753       parameters.push(param + '=' + options[param]);
1754     }
1755   }
1756 
1757   /** Markers **/
1758   if (markers) {
1759     var marker, loc;
1760 
1761     for (var i = 0; data = markers[i]; i++) {
1762       marker = [];
1763 
1764       if (data.size && data.size !== 'normal') {
1765         marker.push('size:' + data.size);
1766         delete data.size;
1767       }
1768       else if (data.icon) {
1769         marker.push('icon:' + encodeURI(data.icon));
1770         delete data.icon;
1771       }
1772 
1773       if (data.color) {
1774         marker.push('color:' + data.color.replace('#', '0x'));
1775         delete data.color;
1776       }
1777 
1778       if (data.label) {
1779         marker.push('label:' + data.label[0].toUpperCase());
1780         delete data.label;
1781       }
1782 
1783       loc = (data.address ? data.address : data.lat + ',' + data.lng);
1784       delete data.address;
1785       delete data.lat;
1786       delete data.lng;
1787 
1788       for(var param in data){
1789         if (data.hasOwnProperty(param)) {
1790           marker.push(param + ':' + data[param]);
1791         }
1792       }
1793 
1794       if (marker.length || i === 0) {
1795         marker.push(loc);
1796         marker = marker.join('|');
1797         parameters.push('markers=' + encodeURI(marker));
1798       }
1799       // New marker without styles

1800       else {
1801         marker = parameters.pop() + encodeURI('|' + loc);
1802         parameters.push(marker);
1803       }
1804     }
1805   }
1806 
1807   /** Map Styles **/
1808   if (styles) {
1809     for (var i = 0; i < styles.length; i++) {
1810       var styleRule = [];
1811       if (styles[i].featureType){
1812         styleRule.push('feature:' + styles[i].featureType.toLowerCase());
1813       }
1814 
1815       if (styles[i].elementType) {
1816         styleRule.push('element:' + styles[i].elementType.toLowerCase());
1817       }
1818 
1819       for (var j = 0; j < styles[i].stylers.length; j++) {
1820         for (var p in styles[i].stylers[j]) {
1821           var ruleArg = styles[i].stylers[j][p];
1822           if (p == 'hue' || p == 'color') {
1823             ruleArg = '0x' + ruleArg.substring(1);
1824           }
1825           styleRule.push(p + ':' + ruleArg);
1826         }
1827       }
1828 
1829       var rule = styleRule.join('|');
1830       if (rule != '') {
1831         parameters.push('style=' + rule);
1832       }
1833     }
1834   }
1835 
1836   /** Polylines **/
1837   function parseColor(color, opacity) {
1838     if (color[0] === '#'){
1839       color = color.replace('#', '0x');
1840 
1841       if (opacity) {
1842         opacity = parseFloat(opacity);
1843         opacity = Math.min(1, Math.max(opacity, 0));
1844         if (opacity === 0) {
1845           return '0x00000000';
1846         }
1847         opacity = (opacity * 255).toString(16);
1848         if (opacity.length === 1) {
1849           opacity += opacity;
1850         }
1851 
1852         color = color.slice(0,8) + opacity;
1853       }
1854     }
1855     return color;
1856   }
1857 
1858   if (polyline) {
1859     data = polyline;
1860     polyline = [];
1861 
1862     if (data.strokeWeight) {
1863       polyline.push('weight:' + parseInt(data.strokeWeight, 10));
1864     }
1865 
1866     if (data.strokeColor) {
1867       var color = parseColor(data.strokeColor, data.strokeOpacity);
1868       polyline.push('color:' + color);
1869     }
1870 
1871     if (data.fillColor) {
1872       var fillcolor = parseColor(data.fillColor, data.fillOpacity);
1873       polyline.push('fillcolor:' + fillcolor);
1874     }
1875 
1876     var path = data.path;
1877     if (path.join) {
1878       for (var j=0, pos; pos=path[j]; j++) {
1879         polyline.push(pos.join(','));
1880       }
1881     }
1882     else {
1883       polyline.push('enc:' + path);
1884     }
1885 
1886     polyline = polyline.join('|');
1887     parameters.push('path=' + encodeURI(polyline));
1888   }
1889 
1890   /** Retina support **/
1891   var dpi = window.devicePixelRatio || 1;
1892   parameters.push('scale=' + dpi);
1893 
1894   parameters = parameters.join('&');
1895   return static_root + parameters;
1896 };
1897 
1898 GMaps.prototype.addMapType = function(mapTypeId, options) {
1899   if (options.hasOwnProperty("getTileUrl") && typeof(options["getTileUrl"]) == "function") {
1900     options.tileSize = options.tileSize || new google.maps.Size(256, 256);
1901 
1902     var mapType = new google.maps.ImageMapType(options);
1903 
1904     this.map.mapTypes.set(mapTypeId, mapType);
1905   }
1906   else {
1907     throw "'getTileUrl' function required.";
1908   }
1909 };
1910 
1911 GMaps.prototype.addOverlayMapType = function(options) {
1912   if (options.hasOwnProperty("getTile") && typeof(options["getTile"]) == "function") {
1913     var overlayMapTypeIndex = options.index;
1914 
1915     delete options.index;
1916 
1917     this.map.overlayMapTypes.insertAt(overlayMapTypeIndex, options);
1918   }
1919   else {
1920     throw "'getTile' function required.";
1921   }
1922 };
1923 
1924 GMaps.prototype.removeOverlayMapType = function(overlayMapTypeIndex) {
1925   this.map.overlayMapTypes.removeAt(overlayMapTypeIndex);
1926 };
1927 
1928 GMaps.prototype.addStyle = function(options) {
1929   var styledMapType = new google.maps.StyledMapType(options.styles, { name: options.styledMapName });
1930 
1931   this.map.mapTypes.set(options.mapTypeId, styledMapType);
1932 };
1933 
1934 GMaps.prototype.setStyle = function(mapTypeId) {
1935   this.map.setMapTypeId(mapTypeId);
1936 };
1937 
1938 GMaps.prototype.createPanorama = function(streetview_options) {
1939   if (!streetview_options.hasOwnProperty('lat') || !streetview_options.hasOwnProperty('lng')) {
1940     streetview_options.lat = this.getCenter().lat();
1941     streetview_options.lng = this.getCenter().lng();
1942   }
1943 
1944   this.panorama = GMaps.createPanorama(streetview_options);
1945 
1946   this.map.setStreetView(this.panorama);
1947 
1948   return this.panorama;
1949 };
1950 
1951 GMaps.createPanorama = function(options) {
1952   var el = getElementById(options.el, options.context);
1953 
1954   options.position = new google.maps.LatLng(options.lat, options.lng);
1955 
1956   delete options.el;
1957   delete options.context;
1958   delete options.lat;
1959   delete options.lng;
1960 
1961   var streetview_events = ['closeclick', 'links_changed', 'pano_changed', 'position_changed', 'pov_changed', 'resize', 'visible_changed'],
1962       streetview_options = extend_object({visible : true}, options);
1963 
1964   for (var i = 0; i < streetview_events.length; i++) {
1965     delete streetview_options[streetview_events[i]];
1966   }
1967 
1968   var panorama = new google.maps.StreetViewPanorama(el, streetview_options);
1969 
1970   for (var i = 0; i < streetview_events.length; i++) {
1971     (function(object, name) {
1972       if (options[name]) {
1973         google.maps.event.addListener(object, name, function(){
1974           options[name].apply(this);
1975         });
1976       }
1977     })(panorama, streetview_events[i]);
1978   }
1979 
1980   return panorama;
1981 };
1982 
1983 GMaps.prototype.on = function(event_name, handler) {
1984   return GMaps.on(event_name, this, handler);
1985 };
1986 
1987 GMaps.prototype.off = function(event_name) {
1988   GMaps.off(event_name, this);
1989 };
1990 
1991 GMaps.custom_events = ['marker_added', 'marker_removed', 'polyline_added', 'polyline_removed', 'polygon_added', 'polygon_removed', 'geolocated', 'geolocation_failed'];
1992 
1993 GMaps.on = function(event_name, object, handler) {
1994   if (GMaps.custom_events.indexOf(event_name) == -1) {
1995     if(object instanceof GMaps) object = object.map; 
1996     return google.maps.event.addListener(object, event_name, handler);
1997   }
1998   else {
1999     var registered_event = {
2000       handler : handler,
2001       eventName : event_name
2002     };
2003 
2004     object.registered_events[event_name] = object.registered_events[event_name] || [];
2005     object.registered_events[event_name].push(registered_event);
2006 
2007     return registered_event;
2008   }
2009 };
2010 
2011 GMaps.off = function(event_name, object) {
2012   if (GMaps.custom_events.indexOf(event_name) == -1) {
2013     if(object instanceof GMaps) object = object.map; 
2014     google.maps.event.clearListeners(object, event_name);
2015   }
2016   else {
2017     object.registered_events[event_name] = [];
2018   }
2019 };
2020 
2021 GMaps.fire = function(event_name, object, scope) {
2022   if (GMaps.custom_events.indexOf(event_name) == -1) {
2023     google.maps.event.trigger(object, event_name, Array.prototype.slice.apply(arguments).slice(2));
2024   }
2025   else {
2026     if(event_name in scope.registered_events) {
2027       var firing_events = scope.registered_events[event_name];
2028 
2029       for(var i = 0; i < firing_events.length; i++) {
2030         (function(handler, scope, object) {
2031           handler.apply(scope, [object]);
2032         })(firing_events[i]['handler'], scope, object);
2033       }
2034     }
2035   }
2036 };
2037 
2038 GMaps.geolocate = function(options) {
2039   var complete_callback = options.always || options.complete;
2040 
2041   if (navigator.geolocation) {
2042     navigator.geolocation.getCurrentPosition(function(position) {
2043       options.success(position);
2044 
2045       if (complete_callback) {
2046         complete_callback();
2047       }
2048     }, function(error) {
2049       options.error(error);
2050 
2051       if (complete_callback) {
2052         complete_callback();
2053       }
2054     }, options.options);
2055   }
2056   else {
2057     options.not_supported();
2058 
2059     if (complete_callback) {
2060       complete_callback();
2061     }
2062   }
2063 };
2064 
2065 GMaps.geocode = function(options) {
2066   this.geocoder = new google.maps.Geocoder();
2067   var callback = options.callback;
2068   if (options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) {
2069     options.latLng = new google.maps.LatLng(options.lat, options.lng);
2070   }
2071 
2072   delete options.lat;
2073   delete options.lng;
2074   delete options.callback;
2075   
2076   this.geocoder.geocode(options, function(results, status) {
2077     callback(results, status);
2078   });
2079 };
2080 
2081 //==========================

2082 // Polygon containsLatLng

2083 // https://github.com/tparkin/Google-Maps-Point-in-Polygon

2084 // Poygon getBounds extension - google-maps-extensions

2085 // http://code.google.com/p/google-maps-extensions/source/browse/google.maps.Polygon.getBounds.js

2086 if (!google.maps.Polygon.prototype.getBounds) {
2087   google.maps.Polygon.prototype.getBounds = function(latLng) {
2088     var bounds = new google.maps.LatLngBounds();
2089     var paths = this.getPaths();
2090     var path;
2091 
2092     for (var p = 0; p < paths.getLength(); p++) {
2093       path = paths.getAt(p);
2094       for (var i = 0; i < path.getLength(); i++) {
2095         bounds.extend(path.getAt(i));
2096       }
2097     }
2098 
2099     return bounds;
2100   };
2101 }
2102 
2103 if (!google.maps.Polygon.prototype.containsLatLng) {
2104   // Polygon containsLatLng - method to determine if a latLng is within a polygon

2105   google.maps.Polygon.prototype.containsLatLng = function(latLng) {
2106     // Exclude points outside of bounds as there is no way they are in the poly

2107     var bounds = this.getBounds();
2108 
2109     if (bounds !== null && !bounds.contains(latLng)) {
2110       return false;
2111     }
2112 
2113     // Raycast point in polygon method

2114     var inPoly = false;
2115 
2116     var numPaths = this.getPaths().getLength();
2117     for (var p = 0; p < numPaths; p++) {
2118       var path = this.getPaths().getAt(p);
2119       var numPoints = path.getLength();
2120       var j = numPoints - 1;
2121 
2122       for (var i = 0; i < numPoints; i++) {
2123         var vertex1 = path.getAt(i);
2124         var vertex2 = path.getAt(j);
2125 
2126         if (vertex1.lng() < latLng.lng() && vertex2.lng() >= latLng.lng() || vertex2.lng() < latLng.lng() && vertex1.lng() >= latLng.lng()) {
2127           if (vertex1.lat() + (latLng.lng() - vertex1.lng()) / (vertex2.lng() - vertex1.lng()) * (vertex2.lat() - vertex1.lat()) < latLng.lat()) {
2128             inPoly = !inPoly;
2129           }
2130         }
2131 
2132         j = i;
2133       }
2134     }
2135 
2136     return inPoly;
2137   };
2138 }
2139 
2140 if (!google.maps.Circle.prototype.containsLatLng) {
2141   google.maps.Circle.prototype.containsLatLng = function(latLng) {
2142     if (google.maps.geometry) {
2143       return google.maps.geometry.spherical.computeDistanceBetween(this.getCenter(), latLng) <= this.getRadius();
2144     }
2145     else {
2146       return true;
2147     }
2148   };
2149 }
2150 
2151 google.maps.LatLngBounds.prototype.containsLatLng = function(latLng) {
2152   return this.contains(latLng);
2153 };
2154 
2155 google.maps.Marker.prototype.setFences = function(fences) {
2156   this.fences = fences;
2157 };
2158 
2159 google.maps.Marker.prototype.addFence = function(fence) {
2160   this.fences.push(fence);
2161 };
2162 
2163 google.maps.Marker.prototype.getId = function() {
2164   return this['__gm_id'];
2165 };
2166 
2167 //==========================

2168 // Array indexOf

2169 // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf

2170 if (!Array.prototype.indexOf) {
2171   Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
2172       "use strict";
2173       if (this == null) {
2174           throw new TypeError();
2175       }
2176       var t = Object(this);
2177       var len = t.length >>> 0;
2178       if (len === 0) {
2179           return -1;
2180       }
2181       var n = 0;
2182       if (arguments.length > 1) {
2183           n = Number(arguments[1]);
2184           if (n != n) { // shortcut for verifying if it's NaN

2185               n = 0;
2186           } else if (n != 0 && n != Infinity && n != -Infinity) {
2187               n = (n > 0 || -1) * Math.floor(Math.abs(n));
2188           }
2189       }
2190       if (n >= len) {
2191           return -1;
2192       }
2193       var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
2194       for (; k < len; k++) {
2195           if (k in t && t[k] === searchElement) {
2196               return k;
2197           }
2198       }
2199       return -1;
2200   }
2201 }
2202   
2203 return GMaps;
2204 }));