PageSourceSearch

https://nobts.edu/_skin/assets/plugins/gmaps/gmaps.js

js nobts.edu collected 2026-09-24 09:44:20 UTC 61,017 bytes, 2,204 lines download raw bytes

1"use strict";
2(function(root, factory) {
3  if(typeof exports === 'object') {
4    module.exports = factory();
5  }
6  else if(typeof define === 'function' && define.amd) {
7    define('GMaps', [], factory);
8  }
9
10  root.GMaps = factory();
11
12}(this, function() {
13
14/*!
15 * GMaps.js v0.4.17
16 * http://hpneo.github.com/gmaps/
17 *
18 * Copyright 2015, Gustavo Leon
19 * Released under the MIT License.
20 */
21
22if (!(typeof window.google === 'object' && window.google.maps)) {
23  throw 'Google Maps API is required. Please register the following JavaScript library http://maps.google.com/maps/api/js?sensor=true.'
24}
25
26var extend_object = function(obj, new_obj) {
27  var name;
28
29  if (obj === new_obj) {
30    return obj;
31  }
32
33  for (name in new_obj) {
34    obj[name] = new_obj[name];
35  }
36
37  return obj;
38};
39
40var replace_object = function(obj, replace) {
41  var name;
42
43  if (obj === replace) {
44    return obj;
45  }
46
47  for (name in replace) {
48    if (obj[name] != undefined) {
49      obj[name] = replace[name];
50    }
51  }
52
53  return obj;
54};
55
56var array_map = function(array, callback) {
57  var original_callback_params = Array.prototype.slice.call(arguments, 2),
58      array_return = [],
59      array_length = array.length,
60      i;
61
62  if (Array.prototype.map && array.map === Array.prototype.map) {
63    array_return = Array.prototype.map.call(array, function(item) {
64      callback_params = original_callback_params;
65      callback_params.splice(0, 0, item);
66
67      return callback.apply(this, callback_params);
68    });
69  }
70  else {
71    for (i = 0; i < array_length; i++) {
72      callback_params = original_callback_params;
73      callback_params.splice(0, 0, array[i]);
74      array_return.push(callback.apply(this, callback_params));
75    }
76  }
77
78  return array_return;
79};
80
81var array_flat = function(array) {
82  var new_array = [],
83      i;
84
85  for (i = 0; i < array.length; i++) {
86    new_array = new_array.concat(array[i]);
87  }
88
89  return new_array;
90};
91
92var coordsToLatLngs = function(coords, useGeoJSON) {
93  var first_coord = coords[0],
94      second_coord = coords[1];
95
96  if (useGeoJSON) {
97    first_coord = coords[1];
98    second_coord = coords[0];
99  }
100
101  return new google.maps.LatLng(first_coord, second_coord);
102};
103
104var arrayToLatLng = function(coords, useGeoJSON) {
105  var i;
106
107  for (i = 0; i < coords.length; i++) {
108    if (!(coords[i] instanceof google.maps.LatLng)) {
109      if (coords[i].length > 0 && typeof(coords[i][0]) === "object") {
110        coords[i] = arrayToLatLng(coords[i], useGeoJSON);
111      }
112      else {
113        coords[i] = coordsToLatLngs(coords[i], useGeoJSON);
114      }
115    }
116  }
117
118  return coords;
119};
120
121
122var getElementsByClassName = function (class_name, context) {
123
124    var element,
125        _class = class_name.replace('.', '');
126
127    if ('jQuery' in this && context) {
128        element = $("." + _class, context)[0];
129    } else {
130        element = document.getElementsByClassName(_class)[0];
131    }
132    return element;
133
134};
135
136var getElementById = function(id, context) {
137  var element,
138  id = id.replace('#', '');
139
140  if ('jQuery' in window && context) {
141    element = $('#' + id, context)[0];
142  } else {
143    element = document.getElementById(id);
144  };
145
146  return element;
147};
148
149var findAbsolutePosition = function(obj)  {
150  var curleft = 0,
151      curtop = 0;
152
153  if (obj.offsetParent) {
154    do {
155      curleft += obj.offsetLeft;
156      curtop += obj.offsetTop;
157    } while (obj = obj.offsetParent);
158  }
159
160  return [curleft, curtop];
161};
162
163var GMaps = (function(global) {
164  "use strict";
165
166  var doc = document;
167
168  var GMaps = function(options) {
169    if (!this) return new GMaps(options);
170
171    options.zoom = options.zoom || 15;
172    options.mapType = options.mapType || 'roadmap';
173
174    var self = this,
175        i,
176        events_that_hide_context_menu = [
177          'bounds_changed', 'center_changed', 'click', 'dblclick', 'drag',
178          'dragend', 'dragstart', 'idle', 'maptypeid_changed', 'projection_changed',
179          'resize', 'tilesloaded', 'zoom_changed'
180        ],
181        events_that_doesnt_hide_context_menu = ['mousemove', 'mouseout', 'mouseover'],
182        options_to_be_deleted = ['el', 'lat', 'lng', 'mapType', 'width', 'height', 'markerClusterer', 'enableNewStyle'],
183        identifier = options.el || options.div,
184        markerClustererFunction = options.markerClusterer,
185        mapType = google.maps.MapTypeId[options.mapType.toUpperCase()],
186        map_center = new google.maps.LatLng(options.lat, options.lng),
187        zoomControl = options.zoomControl || true,
188        zoomControlOpt = options.zoomControlOpt || {
189          style: 'DEFAULT',
190          position: 'TOP_LEFT'
191        },
192        zoomControlStyle = zoomControlOpt.style || 'DEFAULT',
193        zoomControlPosition = zoomControlOpt.position || 'TOP_LEFT',
194        panControl = options.panControl || true,
195        mapTypeControl = options.mapTypeControl || true,
196        scaleControl = options.scaleControl || true,
197        streetViewControl = options.streetViewControl || true,
198        overviewMapControl = overviewMapControl || true,
199        map_options = {},
200        map_base_options = {
201          zoom: this.zoom,
202          center: map_center,
203          mapTypeId: mapType
204        },
205        map_controls_options = {
206          panControl: panControl,
207          zoomControl: zoomControl,
208          zoomControlOptions: {
209            style: google.maps.ZoomControlStyle[zoomControlStyle],
210            position: google.maps.ControlPosition[zoomControlPosition]
211          },
212          mapTypeControl: mapTypeControl,
213          scaleControl: scaleControl,
214          streetViewControl: streetViewControl,
215          overviewMapControl: overviewMapControl
216        };
217
218      if (typeof(options.el) === 'string' || typeof(options.div) === 'string') {
219
220          if (identifier.indexOf("#") > -1) {
221              this.el = getElementById(identifier, options.context);
222          } else {
223              this.el = getElementsByClassName.apply(this, [identifier, options.context]);
224          }
225
226      } else {
227          this.el = identifier;
228      }
229
230    if (typeof(this.el) === 'undefined' || this.el === null) {
231      throw 'No element defined.';
232    }
233
234    window.context_menu = window.context_menu || {};
235    window.context_menu[self.el.id] = {};
236
237    this.controls = [];
238    this.overlays = [];
239    this.layers = []; // array with kml/georss and fusiontables layers, can be as many
240    this.singleLayers = {}; // object with the other layers, only one per layer
241    this.markers = [];
242    this.polylines = [];
243    this.routes = [];
244    this.polygons = [];
245    this.infoWindow = null;
246    this.overlay_el = null;
247    this.zoom = options.zoom;
248    this.registered_events = {};
249
250    this.el.style.width = options.width || this.el.scrollWidth || this.el.offsetWidth;
251    this.el.style.height = options.height || this.el.scrollHeight || this.el.offsetHeight;
252
253    google.maps.visualRefresh = options.enableNewStyle;
254
255    for (i = 0; i < options_to_be_deleted.length; i++) {
256      delete options[options_to_be_deleted[i]];
257    }
258
259    if(options.disableDefaultUI != true) {
260      map_base_options = extend_object(map_base_options, map_controls_options);
261    }
262
263    map_options = extend_object(map_base_options, options);
264
265    for (i = 0; i < events_that_hide_context_menu.length; i++) {
266      delete map_options[events_that_hide_context_menu[i]];
267    }
268
269    for (i = 0; i < events_that_doesnt_hide_context_menu.length; i++) {
270      delete map_options[events_that_doesnt_hide_context_menu[i]];
271    }
272
273    this.map = new google.maps.Map(this.el, map_options);
274
275    if (markerClustererFunction) {
276      this.markerClusterer = markerClustererFunction.apply(this, [this.map]);
277    }
278
279    var buildContextMenuHTML = function(control, e) {
280      var html = '',
281          options = window.context_menu[self.el.id][control];
282
283      for (var i in options){
284        if (options.hasOwnProperty(i)) {
285          var option = options[i];
286
287          html += '<li><a id="' + control + '_' + i + '" href="#">
287' + option.title + '</a></li>';
288        }
289      }
290
291      if (!getElementById('gmaps_context_menu')) return;
292
293      var context_menu_element = getElementById('gmaps_context_menu');
294      
295      context_menu_element.innerHTML = html;
296
297      var context_menu_items = context_menu_element.getElementsByTagName('a'),
298          context_menu_items_count = context_menu_items.length,
299          i;
300
301      for (i = 0; i < context_menu_items_count; i++) {
302        var context_menu_item = context_menu_items[i];
303
304        var assign_menu_item_action = function(ev){
305          ev.preventDefault();
306
307          options[this.id.replace(control + '_', '')].action.apply(self, [e]);
308          self.hideContextMenu();
309        };
310
311        google.maps.event.clearListeners(context_menu_item, 'click');
312        google.maps.event.addDomListenerOnce(context_menu_item, 'click', assign_menu_item_action, false);
313      }
314
315      var position = findAbsolutePosition.apply(this, [self.el]),
316          left = position[0] + e.pixel.x - 15,
317          top = position[1] + e.pixel.y- 15;
318
319      context_menu_element.style.left = left + "px";
320      context_menu_element.style.top = top + "px";
321
322      context_menu_element.style.display = 'block';
323    };
324
325    this.buildContextMenu = function(control, e) {
326      if (control === 'marker') {
327        e.pixel = {};
328
329        var overlay = new google.maps.OverlayView();
330        overlay.setMap(self.map);
331        
332        overlay.draw = function() {
333          var projection = overlay.getProjection(),
334              position = e.marker.getPosition();
335          
336          e.pixel = projection.fromLatLngToContainerPixel(position);
337
338          buildContextMenuHTML(control, e);
339        };
340      }
341      else {
342        buildContextMenuHTML(control, e);
343      }
344    };
345
346    this.setContextMenu = function(options) {
347      window.context_menu[self.el.id][options.control] = {};
348
349      var i,
350          ul = doc.createElement('ul');
351
352      for (i in options.options) {
353        if (options.options.hasOwnProperty(i)) {
354          var option = options.options[i];
355
356          window.context_menu[self.el.id][options.control][option.name] = {
357            title: option.title,
358            action: option.action
359          };
360        }
361      }
362
363      ul.id = 'gmaps_context_menu';
364      ul.style.display = 'none';
365      ul.style.position = 'absolute';
366      ul.style.minWidth = '100px';
367      ul.style.background = 'white';
368      ul.style.listStyle = 'none';
369      ul.style.padding = '8px';
370      ul.style.boxShadow = '2px 2px 6px #ccc';
371
372      doc.body.appendChild(ul);
373
374      var context_menu_element = getElementById('gmaps_context_menu')
375
376      google.maps.event.addDomListener(context_menu_element, 'mouseout', function(ev) {
377        if (!ev.relatedTarget || !this.contains(ev.relatedTarget)) {
378          window.setTimeout(function(){
379            context_menu_element.style.display = 'none';
380          }, 400);
381        }
382      }, false);
383    };
384
385    this.hideContextMenu = function() {
386      var context_menu_element = getElementById('gmaps_context_menu');
387
388      if (context_menu_element) {
389        context_menu_element.style.display = 'none';
390      }
391    };
392
393    var setupListener = function(object, name) {
394      google.maps.event.addListener(object, name, function(e){
395        if (e == undefined) {
396          e = this;
397        }
398
399        options[name].apply(this, [e]);
400
401        self.hideContextMenu();
402      });
403    };
404
405    //google.maps.event.addListener(this.map, 'idle', this.hideContextMenu);
406    google.maps.event.addListener(this.map, 'zoom_changed', this.hideContextMenu);
407
408    for (var ev = 0; ev < events_that_hide_context_menu.length; ev++) {
409      var name = events_that_hide_context_menu[ev];
410
411      if (name in options) {
412        setupListener(this.map, name);
413      }
414    }
415
416    for (var ev = 0; ev < events_that_doesnt_hide_context_menu.length; ev++) {
417      var name = events_that_doesnt_hide_context_menu[ev];
418
419      if (name in options) {
420        setupListener(this.map, name);
421      }
422    }
423
424    google.maps.event.addListener(this.map, 'rightclick', function(e) {
425      if (options.rightclick) {
426        options.rightclick.apply(this, [e]);
427      }
428
429      if(window.context_menu[self.el.id]['map'] != undefined) {
430        self.buildContextMenu('map', e);
431      }
432    });
433
434    this.refresh = function() {
435      google.maps.event.trigger(this.map, 'resize');
436    };
437
438    this.fitZoom = function() {
439      var latLngs = [],
440          markers_length = this.markers.length,
441          i;
442
443      for (i = 0; i < markers_length; i++) {
444        if(typeof(this.markers[i].visible) === 'boolean' && this.markers[i].visible) {
445          latLngs.push(this.markers[i].getPosition());
446        }
447      }
448
449      this.fitLatLngBounds(latLngs);
450    };
451
452    this.fitLatLngBounds = function(latLngs) {
453      var total = latLngs.length,
454          bounds = new google.maps.LatLngBounds(),
455          i;
456
457      for(i = 0; i < total; i++) {
458        bounds.extend(latLngs[i]);
459      }
460
461      this.map.fitBounds(bounds);
462    };
463
464    this.setCenter = function(lat, lng, callback) {
465      this.map.panTo(new google.maps.LatLng(lat, lng));
466
467      if (callback) {
468        callback();
469      }
470    };
471
472    this.getElement = function() {
473      return this.el;
474    };
475
476    this.zoomIn = function(value) {
477      value = value || 1;
478
479      this.zoom = this.map.getZoom() + value;
480      this.map.setZoom(this.zoom);
481    };
482
483    this.zoomOut = function(value) {
484      value = value || 1;
485
486      this.zoom = this.map.getZoom() - value;
487      this.map.setZoom(this.zoom);
488    };
489
490    var native_methods = [],
491        method;
492
493    for (method in this.map) {
494      if (typeof(this.map[method]) == 'function' && !this[method]) {
495        native_methods.push(method);
496      }
497    }
498
499    for (i = 0; i < native_methods.length; i++) {
500      (function(gmaps, scope, method_name) {
501        gmaps[method_name] = function(){
502          return scope[method_name].apply(scope, arguments);
503        };
504      })(this, this.map, native_methods[i]);
505    }
506  };
507
508  return GMaps;
509})(this);
510
511GMaps.prototype.createControl = function(options) {
512  var control = document.createElement('div');
513
514  control.style.cursor = 'pointer';
515  
516  if (options.disableDefaultStyles !== true) {
517    control.style.fontFamily = 'Roboto, Arial, sans-serif';
518    control.style.fontSize = '11px';
519    control.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px';
520  }
521
522  for (var option in options.style) {
523    control.style[option] = options.style[option];
524  }
525
526  if (options.id) {
527    control.id = options.id;
528  }
529
530  if (options.classes) {
531    control.className = options.classes;
532  }
533
534  if (options.content) {
535    if (typeof options.content === 'string') {
536      control.innerHTML = options.content;
537    }
538    else if (options.content instanceof HTMLElement) {
539      control.appendChild(options.content);
540    }
541  }
542
543  if (options.position) {
544    control.position = google.maps.ControlPosition[options.position.toUpperCase()];
545  }
546
547  for (var ev in options.events) {
548    (function(object, name) {
549      google.maps.event.addDomListener(object, name, function(){
550        options.events[name].apply(this, [this]);
551      });
552    })(control, ev);
553  }
554
555  control.index = 1;
556
557  return control;
558};
559
560GMaps.prototype.addControl = function(options) {
561  var control = this.createControl(options);
562  
563  this.controls.push(control);
564  this.map.controls[control.position].push(control);
565
566  return control;
567};
568
569GMaps.prototype.removeControl = function(control) {
570  var position = null,
571      i;
572
573  for (i = 0; i < this.controls.length; i++) {
574    if (this.controls[i] == control) {
575      position = this.controls[i].position;
576      this.controls.splice(i, 1);
577    }
578  }
579
580  if (position) {
581    for (i = 0; i < this.map.controls.length; i++) {
582      var controlsForPosition = this.map.controls[control.position];
583
584      if (controlsForPosition.getAt(i) == control) {
585        controlsForPosition.removeAt(i);
586
587        break;
588      }
589    }
590  }
591
592  return control;
593};
594
595GMaps.prototype.createMarker = function(options) {
596  if (options.lat == undefined && options.lng == undefined && options.position == undefined) {
597    throw 'No latitude or longitude defined.';
598  }
599
600  var self = this,
601      details = options.details,
602      fences = options.fences,
603      outside = options.outside,
604      base_options = {
605        position: new google.maps.LatLng(options.lat, options.lng),
606        map: null
607      },
608      marker_options = extend_object(base_options, options);
609
610  delete marker_options.lat;
611  delete marker_options.lng;
612  delete marker_options.fences;
613  delete marker_options.outside;
614
615  var marker = new google.maps.Marker(marker_options);
616
617  marker.fences = fences;
618
619  if (options.infoWindow) {
620    marker.infoWindow = new google.maps.InfoWindow(options.infoWindow);
621
622    var info_window_events = ['closeclick', 'content_changed', 'domready', 'position_changed', 'zindex_changed'];
623
624    for (var ev = 0; ev < info_window_events.length; ev++) {
625      (function(object, name) {
626        if (options.infoWindow[name]) {
627          google.maps.event.addListener(object, name, function(e){
628            options.infoWindow[name].apply(this, [e]);
629          });
630        }
631      })(marker.infoWindow, info_window_events[ev]);
632    }
633  }
634
635  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'];
636
637  var marker_events_with_mouse = ['dblclick', 'drag', 'dragend', 'dragstart', 'mousedown', 'mouseout', 'mouseover', 'mouseup'];
638
639  for (var ev = 0; ev < marker_events.length; ev++) {
640    (function(object, name) {
641      if (options[name]) {
642        google.maps.event.addListener(object, name, function(){
643          options[name].apply(this, [this]);
644        });
645      }
646    })(marker, marker_events[ev]);
647  }
648
649  for (var ev = 0; ev < marker_events_with_mouse.length; ev++) {
650    (function(map, object, name) {
651      if (options[name]) {
652        google.maps.event.addListener(object, name, function(me){
653          if(!me.pixel){
654            me.pixel = map.getProjection().fromLatLngToPoint(me.latLng)
655          }
656          
657          options[name].apply(this, [me]);
658        });
659      }
660    })(this.map, marker, marker_events_with_mouse[ev]);
661  }
662
663  google.maps.event.addListener(marker, 'click', function() {
664    this.details = details;
665
666    if (options.click) {
667      options.click.apply(this, [this]);
668    }
669
670    if (marker.infoWindow) {
671      self.hideInfoWindows();
672      marker.infoWindow.open(self.map, marker);
673    }
674  });
675
676  google.maps.event.addListener(marker, 'rightclick', function(e) {
677    e.marker = this;
678
679    if (options.rightclick) {
680      options.rightclick.apply(this, [e]);
681    }
682
683    if (window.context_menu[self.el.id]['marker'] != undefined) {
684      self.buildContextMenu('marker', e);
685    }
686  });
687
688  if (marker.fences) {
689    google.maps.event.addListener(marker, 'dragend', function() {
690      self.checkMarkerGeofence(marker, function(m, f) {
691        outside(m, f);
692      });
693    });
694  }
695
696  return marker;
697};
698
699GMaps.prototype.addMarker = function(options) {
700  var marker;
701  if(options.hasOwnProperty('gm_accessors_')) {
702    // Native google.maps.Marker object
703    marker = options;
704  }
705  else {
706    if ((options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) || options.position) {
707      marker = this.createMarker(options);
708    }
709    else {
710      throw 'No latitude or longitude defined.';
711    }
712  }
713
714  marker.setMap(this.map);
715
716  if(this.markerClusterer) {
717    this.markerClusterer.addMarker(marker);
718  }
719
720  this.markers.push(marker);
721
722  GMaps.fire('marker_added', marker, this);
723
724  return marker;
725};
726
727GMaps.prototype.addMarkers = function(array) {
728  for (var i = 0, marker; marker=array[i]; i++) {
729    this.addMarker(marker);
730  }
731
732  return this.markers;
733};
734
735GMaps.prototype.hideInfoWindows = function() {
736  for (var i = 0, marker; marker = this.markers[i]; i++){
737    if (marker.infoWindow) {
738      marker.infoWindow.close();
739    }
740  }
741};
742
743GMaps.prototype.removeMarker = function(marker) {
744  for (var i = 0; i < this.markers.length; i++) {
745    if (this.markers[i] === marker) {
746      this.markers[i].setMap(null);
747      this.markers.splice(i, 1);
748
749      if(this.markerClusterer) {
750        this.markerClusterer.removeMarker(marker);
751      }
752
753      GMaps.fire('marker_removed', marker, this);
754
755      break;
756    }
757  }
758
759  return marker;
760};
761
762GMaps.prototype.removeMarkers = function (collection) {
763  var new_markers = [];
764
765  if (typeof collection == 'undefined') {
766    for (var i = 0; i < this.markers.length; i++) {
767      var marker = this.markers[i];
768      marker.setMap(null);
769
770      if(this.markerClusterer) {
771        this.markerClusterer.removeMarker(marker);
772      }
773
774      GMaps.fire('marker_removed', marker, this);
775    }
776    
777    this.markers = new_markers;
778  }
779  else {
780    for (var i = 0; i < collection.length; i++) {
781      var index = this.markers.indexOf(collection[i]);
782
783      if (index > -1) {
784        var marker = this.markers[index];
785        marker.setMap(null);
786
787        if(this.markerClusterer) {
788          this.markerClusterer.removeMarker(marker);
789        }
790
791        GMaps.fire('marker_removed', marker, this);
792      }
793    }
794
795    for (var i = 0; i < this.markers.length; i++) {
796      var marker = this.markers[i];
797      if (marker.getMap() != null) {
798        new_markers.push(marker);
799      }
800    }
801
802    this.markers = new_markers;
803  }
804};
805
806GMaps.prototype.drawOverlay = function(options) {
807  var overlay = new google.maps.OverlayView(),
808      auto_show = true;
809
810  overlay.setMap(this.map);
811
812  if (options.auto_show != null) {
813    auto_show = options.auto_show;
814  }
815
816  overlay.onAdd = function() {
817    var el = document.createElement('div');
818
819    el.style.borderStyle = "none";
820    el.style.borderWidth = "0px";
821    el.style.position = "absolute";
822    el.style.zIndex = 100;
823    el.innerHTML = options.content;
824
825    overlay.el = el;
826
827    if (!options.layer) {
828      options.layer = 'overlayLayer';
829    }
830    
831    var panes = this.getPanes(),
832        overlayLayer = panes[options.layer],
833        stop_overlay_events = ['contextmenu', 'DOMMouseScroll', 'dblclick', 'mousedown'];
834
835    overlayLayer.appendChild(el);
836
837    for (var ev = 0; ev < stop_overlay_events.length; ev++) {
838      (function(object, name) {
839        google.maps.event.addDomListener(object, name, function(e){
840          if (navigator.userAgent.toLowerCase().indexOf('msie') != -1 && document.all) {
841            e.cancelBubble = true;
842            e.returnValue = false;
843          }
844          else {
845            e.stopPropagation();
846          }
847        });
848      })(el, stop_overlay_events[ev]);
849    }
850
851    if (options.click) {
852      panes.overlayMouseTarget.appendChild(overlay.el);
853      google.maps.event.addDomListener(overlay.el, 'click', function() {
854        options.click.apply(overlay, [overlay]);
855      });
856    }
857
858    google.maps.event.trigger(this, 'ready');
859  };
860
861  overlay.draw = function() {
862    var projection = this.getProjection(),
863        pixel = projection.fromLatLngToDivPixel(new google.maps.LatLng(options.lat, options.lng));
864
865    options.horizontalOffset = options.horizontalOffset || 0;
866    options.verticalOffset = options.verticalOffset || 0;
867
868    var el = overlay.el,
869        content = el.children[0],
870        content_height = content.clientHeight,
871        content_width = content.clientWidth;
872
873    switch (options.verticalAlign) {
874      case 'top':
875        el.style.top = (pixel.y - content_height + options.verticalOffset) + 'px';
876        break;
877      default:
878      case 'middle':
879        el.style.top = (pixel.y - (content_height / 2) + options.verticalOffset) + 'px';
880        break;
881      case 'bottom':
882        el.style.top = (pixel.y + options.verticalOffset) + 'px';
883        break;
884    }
885
886    switch (options.horizontalAlign) {
887      case 'left':
888        el.style.left = (pixel.x - content_width + options.horizontalOffset) + 'px';
889        break;
890      default:
891      case 'center':
892        el.style.left = (pixel.x - (content_width / 2) + options.horizontalOffset) + 'px';
893        break;
894      case 'right':
895        el.style.left = (pixel.x + options.horizontalOffset) + 'px';
896        break;
897    }
898
899    el.style.display = auto_show ? 'block' : 'none';
900
901    if (!auto_show) {
902      options.show.apply(this, [el]);
903    }
904  };
905
906  overlay.onRemove = function() {
907    var el = overlay.el;
908
909    if (options.remove) {
910      options.remove.apply(this, [el]);
911    }
912    else {
913      overlay.el.parentNode.removeChild(overlay.el);
914      overlay.el = null;
915    }
916  };
917
918  this.overlays.push(overlay);
919  return overlay;
920};
921
922GMaps.prototype.removeOverlay = function(overlay) {
923  for (var i = 0; i < this.overlays.length; i++) {
924    if (this.overlays[i] === overlay) {
925      this.overlays[i].setMap(null);
926      this.overlays.splice(i, 1);
927
928      break;
929    }
930  }
931};
932
933GMaps.prototype.removeOverlays = function() {
934  for (var i = 0, item; item = this.overlays[i]; i++) {
935    item.setMap(null);
936  }
937
938  this.overlays = [];
939};
940
941GMaps.prototype.drawPolyline = function(options) {
942  var path = [],
943      points = options.path;
944
945  if (points.length) {
946    if (points[0][0] === undefined) {
947      path = points;
948    }
949    else {
950      for (var i = 0, latlng; latlng = points[i]; i++) {
951        path.push(new google.maps.LatLng(latlng[0], latlng[1]));
952      }
953    }
954  }
955
956  var polyline_options = {
957    map: this.map,
958    path: path,
959    strokeColor: options.strokeColor,
960    strokeOpacity: options.strokeOpacity,
961    strokeWeight: options.strokeWeight,
962    geodesic: options.geodesic,
963    clickable: true,
964    editable: false,
965    visible: true
966  };
967
968  if (options.hasOwnProperty("clickable")) {
969    polyline_options.clickable = options.clickable;
970  }
971
972  if (options.hasOwnProperty("editable")) {
973    polyline_options.editable = options.editable;
974  }
975
976  if (options.hasOwnProperty("icons")) {
977    polyline_options.icons = options.icons;
978  }
979
980  if (options.hasOwnProperty("zIndex")) {
981    polyline_options.zIndex = options.zIndex;
982  }
983
984  var polyline = new google.maps.Polyline(polyline_options);
985
986  var polyline_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
987
988  for (var ev = 0; ev < polyline_events.length; ev++) {
989    (function(object, name) {
990      if (options[name]) {
991        google.maps.event.addListener(object, name, function(e){
992          options[name].apply(this, [e]);
993        });
994      }
995    })(polyline, polyline_events[ev]);
996  }
997
998  this.polylines.push(polyline);
999
1000  GMaps.fire('polyline_added', polyline, this);
1001
1002  return polyline;
1003};
1004
1005GMaps.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
1018GMaps.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
1026GMaps.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
1053GMaps.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
1083GMaps.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
1126GMaps.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
1139GMaps.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
1147GMaps.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
1168GMaps.prototype.loadFromFusionTables = function(options) {
1169  var layer = this.getFromFusionTables(options);
1170  layer.setMap(this.map);
1171
1172  return layer;
1173};
1174
1175GMaps.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
1198GMaps.prototype.loadFromKML = function(options) {
1199  var layer = this.getFromKML(options);
1200  layer.setMap(this.map);
1201
1202  return layer;
1203};
1204
1205GMaps.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
1288GMaps.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
1306var travelMode, unitSystem;
1307
1308GMaps.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
1370GMaps.prototype.removeRoutes = function() {
1371  this.routes = [];
1372};
1373
1374GMaps.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
1417GMaps.prototype.cleanRoute = GMaps.prototype.removePolylines;
1418
1419GMaps.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
1452GMaps.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
1497GMaps.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
1567GMaps.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
1592GMaps.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
1611GMaps.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
1624GMaps.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
1637GMaps.prototype.checkGeofence = function(lat, lng, fence) {
1638  return fence.containsLatLng(new google.maps.LatLng(lat, lng));
1639};
1640
1641GMaps.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
1652GMaps.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
1684GMaps.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
1898GMaps.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
1911GMaps.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
1924GMaps.prototype.removeOverlayMapType = function(overlayMapTypeIndex) {
1925  this.map.overlayMapTypes.removeAt(overlayMapTypeIndex);
1926};
1927
1928GMaps.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
1934GMaps.prototype.setStyle = function(mapTypeId) {
1935  this.map.setMapTypeId(mapTypeId);
1936};
1937
1938GMaps.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
1951GMaps.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
1983GMaps.prototype.on = function(event_name, handler) {
1984  return GMaps.on(event_name, this, handler);
1985};
1986
1987GMaps.prototype.off = function(event_name) {
1988  GMaps.off(event_name, this);
1989};
1990
1991GMaps.custom_events = ['marker_added', 'marker_removed', 'polyline_added', 'polyline_removed', 'polygon_added', 'polygon_removed', 'geolocated', 'geolocation_failed'];
1992
1993GMaps.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
2011GMaps.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
2021GMaps.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
2038GMaps.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
2065GMaps.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
2086if (!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
2103if (!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
2140if (!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
2151google.maps.LatLngBounds.prototype.containsLatLng = function(latLng) {
2152  return this.contains(latLng);
2153};
2154
2155google.maps.Marker.prototype.setFences = function(fences) {
2156  this.fences = fences;
2157};
2158
2159google.maps.Marker.prototype.addFence = function(fence) {
2160  this.fences.push(fence);
2161};
2162
2163google.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
2170if (!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  
2203return GMaps;
2204}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.