PageSourceSearch

https://dramavarna.com/templates/drama/js/maps.js

js dramavarna.com collected 2026-09-26 17:16:05 UTC 69,751 bytes, 2,396 lines download raw bytes

vendor: 69,751 bytes, lines 1-2396
1"use strict";
2(function (root, factory) {
3  if (typeof exports === 'object') {
4    module.exports = factory();
5  } else if (typeof define === 'function' && define.amd) {
6    define(['jquery', 'googlemaps!'], factory);
7  } else {
8    root.GMaps = factory();
9  }
10
11
12}(this, function () {
13
14  /*!
15   * GMaps.js v0.4.25
16   * http://hpneo.github.com/gmaps/
17   *
18   * Copyright 2017, Gustavo Leon
19   * Released under the MIT License.
20   */
21
22  var extend_object = function (obj, new_obj) {
23    var name;
24
25    if (obj === new_obj) {
26      return obj;
27    }
28
29    for (name in new_obj) {
30      if (new_obj[name] !== undefined) {
31        obj[name] = new_obj[name];
32      }
33    }
34
35    return obj;
36  };
37
38  var replace_object = function (obj, replace) {
39    var name;
40
41    if (obj === replace) {
42      return obj;
43    }
44
45    for (name in replace) {
46      if (obj[name] != undefined) {
47        obj[name] = replace[name];
48      }
49    }
50
51    return obj;
52  };
53
54  var array_map = function (array, callback) {
55    var original_callback_params = Array.prototype.slice.call(arguments, 2),
56      array_return = [],
57      array_length = array.length,
58      i;
59
60    if (Array.prototype.map && array.map === Array.prototype.map) {
61      array_return = Array.prototype.map.call(array, function (item) {
62        var callback_params = original_callback_params.slice(0);
63        callback_params.splice(0, 0, item);
64
65        return callback.apply(this, callback_params);
66      });
67    } else {
68      for (i = 0; i < array_length; i++) {
69        callback_params = original_callback_params;
70        callback_params.splice(0, 0, array[i]);
71        array_return.push(callback.apply(this, callback_params));
72      }
73    }
74
75    return array_return;
76  };
77
78  var array_flat = function (array) {
79    var new_array = [],
80      i;
81
82    for (i = 0; i < array.length; i++) {
83      new_array = new_array.concat(array[i]);
84    }
85
86    return new_array;
87  };
88
89  var coordsToLatLngs = function (coords, useGeoJSON) {
90    var first_coord = coords[0],
91      second_coord = coords[1];
92
93    if (useGeoJSON) {
94      first_coord = coords[1];
95      second_coord = coords[0];
96    }
97
98    return new google.maps.LatLng(first_coord, second_coord);
99  };
100
101  var arrayToLatLng = function (coords, useGeoJSON) {
102    var i;
103
104    for (i = 0; i < coords.length; i++) {
105      if (!(coords[i] instanceof google.maps.LatLng)) {
106        if (coords[i].length > 0 && typeof (coords[i][0]) === "object") {
107          coords[i] = arrayToLatLng(coords[i], useGeoJSON);
108        } else {
109          coords[i] = coordsToLatLngs(coords[i], useGeoJSON);
110        }
111      }
112    }
113
114    return coords;
115  };
116
117  var getElementsByClassName = function (class_name, context) {
118    var element,
119      _class = class_name.replace('.', '');
120
121    if ('jQuery' in this && context) {
122      element = $("." + _class, context)[0];
123    } else {
124      element = document.getElementsByClassName(_class)[0];
125    }
126    return element;
127
128  };
129
130  var getElementById = function (id, context) {
131    var element,
132      id = id.replace('#', '');
133
134    if ('jQuery' in window && context) {
135      element = $('#' + id, context)[0];
136    } else {
137      element = document.getElementById(id);
138    };
139
140    return element;
141  };
142
143  var findAbsolutePosition = function (obj) {
144    var curleft = 0,
145      curtop = 0;
146
147    if (obj.getBoundingClientRect) {
148      var rect = obj.getBoundingClientRect();
149      var sx = -(window.scrollX ? window.scrollX : window.pageXOffset);
150      var sy = -(window.scrollY ? window.scrollY : window.pageYOffset);
151
152      return [(rect.left - sx), (rect.top - sy)];
153    }
154
155    if (obj.offsetParent) {
156      do {
157        curleft += obj.offsetLeft;
158        curtop += obj.offsetTop;
159      } while (obj = obj.offsetParent);
160    }
161
162    return [curleft, curtop];
163  };
164
165  var GMaps = (function (global) {
166    "use strict";
167
168    var doc = document;
169    /**
170     * Creates a new GMaps instance, including a Google Maps map.
171     * @class GMaps
172     * @constructs
173     * @param {object} options - `options` accepts all the [MapOptions](https://developers.google.com/maps/documentation/javascript/reference#MapOptions) and [events](https://developers.google.com/maps/documentation/javascript/reference#Map) listed in the Google Maps API. Also accepts:
174     * * `lat` (number): Latitude of the map's center
175     * * `lng` (number): Longitude of the map's center
176     * * `el` (string or HTMLElement): container where the map will be rendered
177     * * `markerClusterer` (function): A function to create a marker cluster. You can use MarkerClusterer or MarkerClustererPlus.
178     */
179    var GMaps = function (options) {
180
181      if (!(typeof window.google === 'object' && window.google.maps)) {
182        if (typeof window.console === 'object' && window.console.error) {
183          console.error('Google Maps API is required. Please register the following JavaScript library https://maps.googleapis.com/maps/api/js.');
184        }
185
186        return function () {};
187      }
188
189      if (!this) return new GMaps(options);
190
191      options.zoom = options.zoom || 15;
192      options.mapType = options.mapType || 'roadmap';
193
194      var valueOrDefault = function (value, defaultValue) {
195        return value === undefined ? defaultValue : value;
196      };
197
198      var self = this,
199        i,
200        events_that_hide_context_menu = [
201          'bounds_changed', 'center_changed', 'click', 'dblclick', 'drag',
202          'dragend', 'dragstart', 'idle', 'maptypeid_changed', 'projection_changed',
203          'resize', 'tilesloaded', 'zoom_changed'
204        ],
205        events_that_doesnt_hide_context_menu = ['mousemove', 'mouseout', 'mouseover'],
206        options_to_be_deleted = ['el', 'lat', 'lng', 'mapType', 'width', 'height', 'markerClusterer', 'enableNewStyle'],
207        identifier = options.el || options.div,
208        markerClustererFunction = options.markerClusterer,
209        mapType = google.maps.MapTypeId[options.mapType.toUpperCase()],
210        map_center = new google.maps.LatLng(options.lat, options.lng),
211        zoomControl = valueOrDefault(options.zoomControl, true),
212        zoomControlOpt = options.zoomControlOpt || {
213          style: 'DEFAULT',
214          position: 'TOP_LEFT'
215        },
216        zoomControlStyle = zoomControlOpt.style || 'DEFAULT',
217        zoomControlPosition = zoomControlOpt.position || 'TOP_LEFT',
218        panControl = valueOrDefault(options.panControl, true),
219        mapTypeControl = valueOrDefault(options.mapTypeControl, true),
220        scaleControl = valueOrDefault(options.scaleControl, true),
221        streetViewControl = valueOrDefault(options.streetViewControl, true),
222        overviewMapControl = valueOrDefault(overviewMapControl, true),
223        map_options = {},
224        map_base_options = {
225          zoom: this.zoom,
226          center: map_center,
227          mapTypeId: mapType
228        },
229        map_controls_options = {
230          panControl: panControl,
231          zoomControl: zoomControl,
232          zoomControlOptions: {
233            style: google.maps.ZoomControlStyle[zoomControlStyle],
234            position: google.maps.ControlPosition[zoomControlPosition]
235          },
236          mapTypeControl: mapTypeControl,
237          scaleControl: scaleControl,
238          streetViewControl: streetViewControl,
239          overviewMapControl: overviewMapControl
240        };
241
242      if (typeof (options.el) === 'string' || typeof (options.div) === 'string') {
243        if (identifier.indexOf("#") > -1) {
244          /**
245           * Container element
246           *
247           * @type {HTMLElement}
248           */
249          this.el = getElementById(identifier, options.context);
250        } else {
251          this.el = getElementsByClassName.apply(this, [identifier, options.context]);
252        }
253      } else {
254        this.el = identifier;
255      }
256
257      if (typeof (this.el) === 'undefined' || this.el === null) {
258        throw 'No element defined.';
259      }
260
261      window.context_menu = window.context_menu || {};
262      window.context_menu[self.el.id] = {};
263
264      /**
265       * Collection of custom controls in the map UI
266       *
267       * @type {array}
268       */
269      this.controls = [];
270      /**
271       * Collection of map's overlays
272       *
273       * @type {array}
274       */
275      this.overlays = [];
276      /**
277       * Collection of KML/GeoRSS and FusionTable layers
278       *
279       * @type {array}
280       */
281      this.layers = [];
282      /**
283       * Collection of data layers (See {@link GMaps#addLayer})
284       *
285       * @type {object}
286       */
287      this.singleLayers = {};
288      /**
289       * Collection of map's markers
290       *
291       * @type {array}
292       */
293      this.markers = [];
294      /**
295       * Collection of map's lines
296       *
297       * @type {array}
298       */
299      this.polylines = [];
300      /**
301       * Collection of map's routes requested by {@link GMaps#getRoutes}, {@link GMaps#renderRoute}, {@link GMaps#drawRoute}, {@link GMaps#travelRoute} or {@link GMaps#drawSteppedRoute}
302       *
303       * @type {array}
304       */
305      this.routes = [];
306      /**
307       * Collection of map's polygons
308       *
309       * @type {array}
310       */
311      this.polygons = [];
312      this.infoWindow = null;
313      this.overlay_el = null;
314      /**
315       * Current map's zoom
316       *
317       * @type {number}
318       */
319      this.zoom = options.zoom;
320      this.registered_events = {};
321
322      this.el.style.width = options.width || this.el.scrollWidth || this.el.offsetWidth;
323      this.el.style.height = options.height || this.el.scrollHeight || this.el.offsetHeight;
324
325      google.maps.visualRefresh = options.enableNewStyle;
326
327      for (i = 0; i < options_to_be_deleted.length; i++) {
328        delete options[options_to_be_deleted[i]];
329      }
330
331      if (options.disableDefaultUI != true) {
332        map_base_options = extend_object(map_base_options, map_controls_options);
333      }
334
335      map_options = extend_object(map_base_options, options);
336
337      for (i = 0; i < events_that_hide_context_menu.length; i++) {
338        delete map_options[events_that_hide_context_menu[i]];
339      }
340
341      for (i = 0; i < events_that_doesnt_hide_context_menu.length; i++) {
342        delete map_options[events_that_doesnt_hide_context_menu[i]];
343      }
344
345      /**
346       * Google Maps map instance
347       *
348       * @type {google.maps.Map}
349       */
350      this.map = new google.maps.Map(this.el, map_options);
351
352      if (markerClustererFunction) {
353        /**
354         * Marker Clusterer instance
355         *
356         * @type {object}
357         */
358        this.markerClusterer = markerClustererFunction.apply(this, [this.map]);
359      }
360
361      var buildContextMenuHTML = function (control, e) {
362        var html = '',
363          options = window.context_menu[self.el.id][control];
364
365        for (var i in options) {
366          if (options.hasOwnProperty(i)) {
367            var option = options[i];
368
369            html += '<li><a id="' + control + '_' + i + '" href="#">' + option.title + '</a></li>';
370          }
371        }
372
373        if (!getElementById('gmaps_context_menu')) return;
374
375        var context_menu_element = getElementById('gmaps_context_menu');
376
377        context_menu_element.innerHTML = html;
378
379        var context_menu_items = context_menu_element.getElementsByTagName('a'),
380          context_menu_items_count = context_menu_items.length,
381          i;
382
383        for (i = 0; i < context_menu_items_count; i++) {
384          var context_menu_item = context_menu_items[i];
385
386          var assign_menu_item_action = function (ev) {
387            ev.preventDefault();
388
389            options[this.id.replace(control + '_', '')].action.apply(self, [e]);
390            self.hideContextMenu();
391          };
392
393          google.maps.event.clearListeners(context_menu_item, 'click');
394          google.maps.event.addDomListenerOnce(context_menu_item, 'click', assign_menu_item_action, false);
395        }
396
397        var position = findAbsolutePosition.apply(this, [self.el]),
398          left = position[0] + e.pixel.x - 15,
399          top = position[1] + e.pixel.y - 15;
400
401        context_menu_element.style.left = left + "px";
402        context_menu_element.style.top = top + "px";
403
404        // context_menu_element.style.display = 'block';
405      };
406
407      this.buildContextMenu = function (control, e) {
408        if (control === 'marker') {
409          e.pixel = {};
410
411          var overlay = new google.maps.OverlayView();
412          overlay.setMap(self.map);
413
414          overlay.draw = function () {
415            var projection = overlay.getProjection(),
416              position = e.marker.getPosition();
417
418            e.pixel = projection.fromLatLngToContainerPixel(position);
419
420            buildContextMenuHTML(control, e);
421          };
422        } else {
423          buildContextMenuHTML(control, e);
424        }
425
426        var context_menu_element = getElementById('gmaps_context_menu');
427
428        setTimeout(function () {
429          context_menu_element.style.display = 'block';
430        }, 0);
431      };
432
433      /**
434       * Add a context menu for a map or a marker.
435       *
436       * @param {object} options - The `options` object should contain:
437       * * `control` (string): Kind of control the context menu will be attached. Can be "map" or "marker".
438       * * `options` (array): A collection of context menu items:
439       *   * `title` (string): Item's title shown in the context menu.
440       *   * `name` (string): Item's identifier.
441       *   * `action` (function): Function triggered after selecting the context menu item.
442       */
443      this.setContextMenu = function (options) {
444        window.context_menu[self.el.id][options.control] = {};
445
446        var i,
447          ul = doc.createElement('ul');
448
449        for (i in options.options) {
450          if (options.options.hasOwnProperty(i)) {
451            var option = options.options[i];
452
453            window.context_menu[self.el.id][options.control][option.name] = {
454              title: option.title,
455              action: option.action
456            };
457          }
458        }
459
460        ul.id = 'gmaps_context_menu';
461        ul.style.display = 'none';
462        ul.style.position = 'absolute';
463        ul.style.minWidth = '100px';
464        ul.style.background = 'white';
465        ul.style.listStyle = 'none';
466        ul.style.padding = '8px';
467        ul.style.boxShadow = '2px 2px 6px #ccc';
468
469        if (!getElementById('gmaps_context_menu')) {
470          doc.body.appendChild(ul);
471        }
472
473        var context_menu_element = getElementById('gmaps_context_menu');
474
475        google.maps.event.addDomListener(context_menu_element, 'mouseout', function (ev) {
476          if (!ev.relatedTarget || !this.contains(ev.relatedTarget)) {
477            window.setTimeout(function () {
478              context_menu_element.style.display = 'none';
479            }, 400);
480          }
481        }, false);
482      };
483
484      /**
485       * Hide the current context menu
486       */
487      this.hideContextMenu = function () {
488        var context_menu_element = getElementById('gmaps_context_menu');
489
490        if (context_menu_element) {
491          context_menu_element.style.display = 'none';
492        }
493      };
494
495      var setupListener = function (object, name) {
496        google.maps.event.addListener(object, name, function (e) {
497          if (e == undefined) {
498            e = this;
499          }
500
501          options[name].apply(this, [e]);
502
503          self.hideContextMenu();
504        });
505      };
506
507      //google.maps.event.addListener(this.map, 'idle', this.hideContextMenu);
508      google.maps.event.addListener(this.map, 'zoom_changed', this.hideContextMenu);
509
510      for (var ev = 0; ev < events_that_hide_context_menu.length; ev++) {
511        var name = events_that_hide_context_menu[ev];
512
513        if (name in options) {
514          setupListener(this.map, name);
515        }
516      }
517
518      for (var ev = 0; ev < events_that_doesnt_hide_context_menu.length; ev++) {
519        var name = events_that_doesnt_hide_context_menu[ev];
520
521        if (name in options) {
522          setupListener(this.map, name);
523        }
524      }
525
526      google.maps.event.addListener(this.map, 'rightclick', function (e) {
527        if (options.rightclick) {
528          options.rightclick.apply(this, [e]);
529        }
530
531        if (window.context_menu[self.el.id]['map'] != undefined) {
532          self.buildContextMenu('map', e);
533        }
534      });
535
536      /**
537       * Trigger a `resize` event, useful if you need to repaint the current map (for changes in the viewport or display / hide actions).
538       */
539      this.refresh = function () {
540        google.maps.event.trigger(this.map, 'resize');
541      };
542
543      /**
544       * Adjust the map zoom to include all the markers added in the map.
545       */
546      this.fitZoom = function () {
547        var latLngs = [],
548          markers_length = this.markers.length,
549          i;
550
551        for (i = 0; i < markers_length; i++) {
552          if (typeof (this.markers[i].visible) === 'boolean' && this.markers[i].visible) {
553            latLngs.push(this.markers[i].getPosition());
554          }
555        }
556
557        this.fitLatLngBounds(latLngs);
558      };
559
560      /**
561       * Adjust the map zoom to include all the coordinates in the `latLngs` array.
562       *
563       * @param {array} latLngs - Collection of `google.maps.LatLng` objects.
564       */
565      this.fitLatLngBounds = function (latLngs) {
566        var total = latLngs.length,
567          bounds = new google.maps.LatLngBounds(),
568          i;
569
570        for (i = 0; i < total; i++) {
571          bounds.extend(latLngs[i]);
572        }
573
574        this.map.fitBounds(bounds);
575      };
576
577      /**
578       * Center the map using the `lat` and `lng` coordinates.
579       *
580       * @param {number} lat - Latitude of the coordinate.
581       * @param {number} lng - Longitude of the coordinate.
582       * @param {function} [callback] - Callback that will be executed after the map is centered.
583       */
584      this.setCenter = function (lat, lng, callback) {
585        this.map.panTo(new google.maps.LatLng(lat, lng));
586
587        if (callback) {
588          callback();
589        }
590      };
591
592      /**
593       * Return the HTML element container of the map.
594       *
595       * @returns {HTMLElement} the element container.
596       */
597      this.getElement = function () {
598        return this.el;
599      };
600
601      /**
602       * Increase the map's zoom.
603       *
604       * @param {number} [magnitude] - The number of times the map will be zoomed in.
605       */
606      this.zoomIn = function (value) {
607        value = value || 1;
608
609        this.zoom = this.map.getZoom() + value;
610        this.map.setZoom(this.zoom);
611      };
612
613      /**
614       * Decrease the map's zoom.
615       *
616       * @param {number} [magnitude] - The number of times the map will be zoomed out.
617       */
618      this.zoomOut = function (value) {
619        value = value || 1;
620
621        this.zoom = this.map.getZoom() - value;
622        this.map.setZoom(this.zoom);
623      };
624
625      var native_methods = [],
626        method;
627
628      for (method in this.map) {
629        if (typeof (this.map[method]) == 'function' && !this[method]) {
630          native_methods.push(method);
631        }
632      }
633
634      for (i = 0; i < native_methods.length; i++) {
635        (function (gmaps, scope, method_name) {
636          gmaps[method_name] = function () {
637            return scope[method_name].apply(scope, arguments);
638          };
639        })(this, this.map, native_methods[i]);
640      }
641    };
642
643    return GMaps;
644  })(this);
645
646  GMaps.prototype.createControl = function (options) {
647    var control = document.createElement('div');
648
649    control.style.cursor = 'pointer';
650
651    if (options.disableDefaultStyles !== true) {
652      control.style.fontFamily = 'Roboto, Arial, sans-serif';
653      control.style.fontSize = '11px';
654      control.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px';
655    }
656
657    for (var option in options.style) {
658      control.style[option] = options.style[option];
659    }
660
661    if (options.id) {
662      control.id = options.id;
663    }
664
665    if (options.title) {
666      control.title = options.title;
667    }
668
669    if (options.classes) {
670      control.className = options.classes;
671    }
672
673    if (options.content) {
674      if (typeof options.content === 'string') {
675        control.innerHTML = options.content;
676      } else if (options.content instanceof HTMLElement) {
677        control.appendChild(options.content);
678      }
679    }
680
681    if (options.position) {
682      control.position = google.maps.ControlPosition[options.position.toUpperCase()];
683    }
684
685    for (var ev in options.events) {
686      (function (object, name) {
687        google.maps.event.addDomListener(object, name, function () {
688          options.events[name].apply(this, [this]);
689        });
690      })(control, ev);
691    }
692
693    control.index = 1;
694
695    return control;
696  };
697
698  /**
699   * Add a custom control to the map UI.
700   *
701   * @param {object} options - The `options` object should contain:
702   * * `style` (object): The keys and values of this object should be valid CSS properties and values.
703   * * `id` (string): The HTML id for the custom control.
704   * * `classes` (string): A string containing all the HTML classes for the custom control.
705   * * `content` (string or HTML element): The content of the custom control.
706   * * `position` (string): Any valid [`google.maps.ControlPosition`](https://developers.google.com/maps/documentation/javascript/controls#ControlPositioning) value, in lower or upper case.
707   * * `events` (object): The keys of this object should be valid DOM events. The values should be functions.
708   * * `disableDefaultStyles` (boolean): If false, removes the default styles for the controls like font (family and size), and box shadow.
709   * @returns {HTMLElement}
710   */
711  GMaps.prototype.addControl = function (options) {
712    var control = this.createControl(options);
713
714    this.controls.push(control);
715    this.map.controls[control.position].push(control);
716
717    return control;
718  };
719
720  /**
721   * Remove a control from the map. `control` should be a control returned by `addControl()`.
722   *
723   * @param {HTMLElement} control - One of the controls returned by `addControl()`.
724   * @returns {HTMLElement} the removed control.
725   */
726  GMaps.prototype.removeControl = function (control) {
727    var position = null,
728      i;
729
730    for (i = 0; i < this.controls.length; i++) {
731      if (this.controls[i] == control) {
732        position = this.controls[i].position;
733        this.controls.splice(i, 1);
734      }
735    }
736
737    if (position) {
738      for (i = 0; i < this.map.controls.length; i++) {
739        var controlsForPosition = this.map.controls[control.position];
740
741        if (controlsForPosition.getAt(i) == control) {
742          controlsForPosition.removeAt(i);
743
744          break;
745        }
746      }
747    }
748
749    return control;
750  };
751
752  GMaps.prototype.createMarker = function (options) {
753    if (options.lat == undefined && options.lng == undefined && options.position == undefined) {
754      throw 'No latitude or longitude defined.';
755    }
756
757    var self = this,
758      details = options.details,
759      fences = options.fences,
760      outside = options.outside,
761      base_options = {
762        position: new google.maps.LatLng(options.lat, options.lng),
763        map: null
764      },
765      marker_options = extend_object(base_options, options);
766
767    delete marker_options.lat;
768    delete marker_options.lng;
769    delete marker_options.fences;
770    delete marker_options.outside;
771
772    var marker = new google.maps.Marker(marker_options);
773
774    marker.fences = fences;
775
776    if (options.infoWindow) {
777      marker.infoWindow = new google.maps.InfoWindow(options.infoWindow);
778
779      var info_window_events = ['closeclick', 'content_changed', 'domready', 'position_changed', 'zindex_changed'];
780
781      for (var ev = 0; ev < info_window_events.length; ev++) {
782        (function (object, name) {
783          if (options.infoWindow[name]) {
784            google.maps.event.addListener(object, name, function (e) {
785              options.infoWindow[name].apply(this, [e]);
786            });
787          }
788        })(marker.infoWindow, info_window_events[ev]);
789      }
790    }
791
792    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'];
793
794    var marker_events_with_mouse = ['dblclick', 'drag', 'dragend', 'dragstart', 'mousedown', 'mouseout', 'mouseover', 'mouseup'];
795
796    for (var ev = 0; ev < marker_events.length; ev++) {
797      (function (object, name) {
798        if (options[name]) {
799          google.maps.event.addListener(object, name, function () {
800            options[name].apply(this, [this]);
801          });
802        }
803      })(marker, marker_events[ev]);
804    }
805
806    for (var ev = 0; ev < marker_events_with_mouse.length; ev++) {
807      (function (map, object, name) {
808        if (options[name]) {
809          google.maps.event.addListener(object, name, function (me) {
810            if (!me.pixel) {
811              me.pixel = map.getProjection().fromLatLngToPoint(me.latLng)
812            }
813
814            options[name].apply(this, [me]);
815          });
816        }
817      })(this.map, marker, marker_events_with_mouse[ev]);
818    }
819
820    google.maps.event.addListener(marker, 'click', function () {
821      this.details = details;
822
823      if (options.click) {
824        options.click.apply(this, [this]);
825      }
826
827      if (marker.infoWindow) {
828        self.hideInfoWindows();
829        marker.infoWindow.open(self.map, marker);
830      }
831    });
832
833    google.maps.event.addListener(marker, 'rightclick', function (e) {
834      e.marker = this;
835
836      if (options.rightclick) {
837        options.rightclick.apply(this, [e]);
838      }
839
840      if (window.context_menu[self.el.id]['marker'] != undefined) {
841        self.buildContextMenu('marker', e);
842      }
843    });
844
845    if (marker.fences) {
846      google.maps.event.addListener(marker, 'dragend', function () {
847        self.checkMarkerGeofence(marker, function (m, f) {
848          outside(m, f);
849        });
850      });
851    }
852
853    return marker;
854  };
855
856  GMaps.prototype.addMarker = function (options) {
857    var marker;
858    if (options.hasOwnProperty('gm_accessors_')) {
859      // Native google.maps.Marker object
860      marker = options;
861    } else {
862      if ((options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) || options.position) {
863        marker = this.createMarker(options);
864      } else {
865        throw 'No latitude or longitude defined.';
866      }
867    }
868
869    marker.setMap(this.map);
870
871    if (this.markerClusterer) {
872      this.markerClusterer.addMarker(marker);
873    }
874
875    this.markers.push(marker);
876
877    GMaps.fire('marker_added', marker, this);
878
879    return marker;
880  };
881
882  GMaps.prototype.addMarkers = function (array) {
883    for (var i = 0, marker; marker = array[i]; i++) {
884      this.addMarker(marker);
885    }
886
887    return this.markers;
888  };
889
890  GMaps.prototype.hideInfoWindows = function () {
891    for (var i = 0, marker; marker = this.markers[i]; i++) {
892      if (marker.infoWindow) {
893        marker.infoWindow.close();
894      }
895    }
896  };
897
898  GMaps.prototype.removeMarker = function (marker) {
899    for (var i = 0; i < this.markers.length; i++) {
900      if (this.markers[i] === marker) {
901        this.markers[i].setMap(null);
902        this.markers.splice(i, 1);
903
904        if (this.markerClusterer) {
905          this.markerClusterer.removeMarker(marker);
906        }
907
908        GMaps.fire('marker_removed', marker, this);
909
910        break;
911      }
912    }
913
914    return marker;
915  };
916
917  GMaps.prototype.removeMarkers = function (collection) {
918    var new_markers = [];
919
920    if (typeof collection == 'undefined') {
921      for (var i = 0; i < this.markers.length; i++) {
922        var marker = this.markers[i];
923        marker.setMap(null);
924
925        GMaps.fire('marker_removed', marker, this);
926      }
927
928      if (this.markerClusterer && this.markerClusterer.clearMarkers) {
929        this.markerClusterer.clearMarkers();
930      }
931
932      this.markers = new_markers;
933    } else {
934      for (var i = 0; i < collection.length; i++) {
935        var index = this.markers.indexOf(collection[i]);
936
937        if (index > -1) {
938          var marker = this.markers[index];
939          marker.setMap(null);
940
941          if (this.markerClusterer) {
942            this.markerClusterer.removeMarker(marker);
943          }
944
945          GMaps.fire('marker_removed', marker, this);
946        }
947      }
948
949      for (var i = 0; i < this.markers.length; i++) {
950        var marker = this.markers[i];
951        if (marker.getMap() != null) {
952          new_markers.push(marker);
953        }
954      }
955
956      this.markers = new_markers;
957    }
958  };
959
960  GMaps.prototype.drawOverlay = function (options) {
961    var overlay = new google.maps.OverlayView(),
962      auto_show = true;
963
964    overlay.setMap(this.map);
965
966    if (options.auto_show != null) {
967      auto_show = options.auto_show;
968    }
969
970    overlay.onAdd = function () {
971      var el = document.createElement('div');
972
973      el.style.borderStyle = "none";
974      el.style.borderWidth = "0px";
975      el.style.position = "absolute";
976      el.style.zIndex = 100;
977      el.innerHTML = options.content;
978
979      overlay.el = el;
980
981      if (!options.layer) {
982        options.layer = 'overlayLayer';
983      }
984
985      var panes = this.getPanes(),
986        overlayLayer = panes[options.layer],
987        stop_overlay_events = ['contextmenu', 'DOMMouseScroll', 'dblclick', 'mousedown'];
988
989      overlayLayer.appendChild(el);
990
991      for (var ev = 0; ev < stop_overlay_events.length; ev++) {
992        (function (object, name) {
993          google.maps.event.addDomListener(object, name, function (e) {
994            if (navigator.userAgent.toLowerCase().indexOf('msie') != -1 && document.all) {
995              e.cancelBubble = true;
996              e.returnValue = false;
997            } else {
998              e.stopPropagation();
999            }
1000          });
1001        })(el, stop_overlay_events[ev]);
1002      }
1003
1004      if (options.click) {
1005        panes.overlayMouseTarget.appendChild(overlay.el);
1006        google.maps.event.addDomListener(overlay.el, 'click', function () {
1007          options.click.apply(overlay, [overlay]);
1008        });
1009      }
1010
1011      google.maps.event.trigger(this, 'ready');
1012    };
1013
1014    overlay.draw = function () {
1015      var projection = this.getProjection(),
1016        pixel = projection.fromLatLngToDivPixel(new google.maps.LatLng(options.lat, options.lng));
1017
1018      options.horizontalOffset = options.horizontalOffset || 0;
1019      options.verticalOffset = options.verticalOffset || 0;
1020
1021      var el = overlay.el,
1022        content = el.children[0],
1023        content_height = content.clientHeight,
1024        content_width = content.clientWidth;
1025
1026      switch (options.verticalAlign) {
1027        case 'top':
1028          el.style.top = (pixel.y - content_height + options.verticalOffset) + 'px';
1029          break;
1030        default:
1031        case 'middle':
1032          el.style.top = (pixel.y - (content_height / 2) + options.verticalOffset) + 'px';
1033          break;
1034        case 'bottom':
1035          el.style.top = (pixel.y + options.verticalOffset) + 'px';
1036          break;
1037      }
1038
1039      switch (options.horizontalAlign) {
1040        case 'left':
1041          el.style.left = (pixel.x - content_width + options.horizontalOffset) + 'px';
1042          break;
1043        default:
1044        case 'center':
1045          el.style.left = (pixel.x - (content_width / 2) + options.horizontalOffset) + 'px';
1046          break;
1047        case 'right':
1048          el.style.left = (pixel.x + options.horizontalOffset) + 'px';
1049          break;
1050      }
1051
1052      el.style.display = auto_show ? 'block' : 'none';
1053
1054      if (!auto_show) {
1055        options.show.apply(this, [el]);
1056      }
1057    };
1058
1059    overlay.onRemove = function () {
1060      var el = overlay.el;
1061
1062      if (options.remove) {
1063        options.remove.apply(this, [el]);
1064      } else {
1065        overlay.el.parentNode.removeChild(overlay.el);
1066        overlay.el = null;
1067      }
1068    };
1069
1070    this.overlays.push(overlay);
1071    return overlay;
1072  };
1073
1074  GMaps.prototype.removeOverlay = function (overlay) {
1075    for (var i = 0; i < this.overlays.length; i++) {
1076      if (this.overlays[i] === overlay) {
1077        this.overlays[i].setMap(null);
1078        this.overlays.splice(i, 1);
1079
1080        break;
1081      }
1082    }
1083  };
1084
1085  GMaps.prototype.removeOverlays = function () {
1086    for (var i = 0, item; item = this.overlays[i]; i++) {
1087      item.setMap(null);
1088    }
1089
1090    this.overlays = [];
1091  };
1092
1093  GMaps.prototype.drawPolyline = function (options) {
1094    var path = [],
1095      points = options.path;
1096
1097    if (points.length) {
1098      if (points[0][0] === undefined) {
1099        path = points;
1100      } else {
1101        for (var i = 0, latlng; latlng = points[i]; i++) {
1102          path.push(new google.maps.LatLng(latlng[0], latlng[1]));
1103        }
1104      }
1105    }
1106
1107    var polyline_options = {
1108      map: this.map,
1109      path: path,
1110      strokeColor: options.strokeColor,
1111      strokeOpacity: options.strokeOpacity,
1112      strokeWeight: options.strokeWeight,
1113      geodesic: options.geodesic,
1114      clickable: true,
1115      editable: false,
1116      visible: true
1117    };
1118
1119    if (options.hasOwnProperty("clickable")) {
1120      polyline_options.clickable = options.clickable;
1121    }
1122
1123    if (options.hasOwnProperty("editable")) {
1124      polyline_options.editable = options.editable;
1125    }
1126
1127    if (options.hasOwnProperty("icons")) {
1128      polyline_options.icons = options.icons;
1129    }
1130
1131    if (options.hasOwnProperty("zIndex")) {
1132      polyline_options.zIndex = options.zIndex;
1133    }
1134
1135    var polyline = new google.maps.Polyline(polyline_options);
1136
1137    var polyline_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
1138
1139    for (var ev = 0; ev < polyline_events.length; ev++) {
1140      (function (object, name) {
1141        if (options[name]) {
1142          google.maps.event.addListener(object, name, function (e) {
1143            options[name].apply(this, [e]);
1144          });
1145        }
1146      })(polyline, polyline_events[ev]);
1147    }
1148
1149    this.polylines.push(polyline);
1150
1151    GMaps.fire('polyline_added', polyline, this);
1152
1153    return polyline;
1154  };
1155
1156  GMaps.prototype.removePolyline = function (polyline) {
1157    for (var i = 0; i < this.polylines.length; i++) {
1158      if (this.polylines[i] === polyline) {
1159        this.polylines[i].setMap(null);
1160        this.polylines.splice(i, 1);
1161
1162        GMaps.fire('polyline_removed', polyline, this);
1163
1164        break;
1165      }
1166    }
1167  };
1168
1169  GMaps.prototype.removePolylines = function () {
1170    for (var i = 0, item; item = this.polylines[i]; i++) {
1171      item.setMap(null);
1172    }
1173
1174    this.polylines = [];
1175  };
1176
1177  GMaps.prototype.drawCircle = function (options) {
1178    options = extend_object({
1179      map: this.map,
1180      center: new google.maps.LatLng(options.lat, options.lng)
1181    }, options);
1182
1183    delete options.lat;
1184    delete options.lng;
1185
1186    var polygon = new google.maps.Circle(options),
1187      polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
1188
1189    for (var ev = 0; ev < polygon_events.length; ev++) {
1190      (function (object, name) {
1191        if (options[name]) {
1192          google.maps.event.addListener(object, name, function (e) {
1193            options[name].apply(this, [e]);
1194          });
1195        }
1196      })(polygon, polygon_events[ev]);
1197    }
1198
1199    this.polygons.push(polygon);
1200
1201    return polygon;
1202  };
1203
1204  GMaps.prototype.drawRectangle = function (options) {
1205    options = extend_object({
1206      map: this.map
1207    }, options);
1208
1209    var latLngBounds = new google.maps.LatLngBounds(
1210      new google.maps.LatLng(options.bounds[0][0], options.bounds[0][1]),
1211      new google.maps.LatLng(options.bounds[1][0], options.bounds[1][1])
1212    );
1213
1214    options.bounds = latLngBounds;
1215
1216    var polygon = new google.maps.Rectangle(options),
1217      polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
1218
1219    for (var ev = 0; ev < polygon_events.length; ev++) {
1220      (function (object, name) {
1221        if (options[name]) {
1222          google.maps.event.addListener(object, name, function (e) {
1223            options[name].apply(this, [e]);
1224          });
1225        }
1226      })(polygon, polygon_events[ev]);
1227    }
1228
1229    this.polygons.push(polygon);
1230
1231    return polygon;
1232  };
1233
1234  GMaps.prototype.drawPolygon = function (options) {
1235    var useGeoJSON = false;
1236
1237    if (options.hasOwnProperty("useGeoJSON")) {
1238      useGeoJSON = options.useGeoJSON;
1239    }
1240
1241    delete options.useGeoJSON;
1242
1243    options = extend_object({
1244      map: this.map
1245    }, options);
1246
1247    if (useGeoJSON == false) {
1248      options.paths = [options.paths.slice(0)];
1249    }
1250
1251    if (options.paths.length > 0) {
1252      if (options.paths[0].length > 0) {
1253        options.paths = array_flat(array_map(options.paths, arrayToLatLng, useGeoJSON));
1254      }
1255    }
1256
1257    var polygon = new google.maps.Polygon(options),
1258      polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
1259
1260    for (var ev = 0; ev < polygon_events.length; ev++) {
1261      (function (object, name) {
1262        if (options[name]) {
1263          google.maps.event.addListener(object, name, function (e) {
1264            options[name].apply(this, [e]);
1265          });
1266        }
1267      })(polygon, polygon_events[ev]);
1268    }
1269
1270    this.polygons.push(polygon);
1271
1272    GMaps.fire('polygon_added', polygon, this);
1273
1274    return polygon;
1275  };
1276
1277  GMaps.prototype.removePolygon = function (polygon) {
1278    for (var i = 0; i < this.polygons.length; i++) {
1279      if (this.polygons[i] === polygon) {
1280        this.polygons[i].setMap(null);
1281        this.polygons.splice(i, 1);
1282
1283        GMaps.fire('polygon_removed', polygon, this);
1284
1285        break;
1286      }
1287    }
1288  };
1289
1290  GMaps.prototype.removePolygons = function () {
1291    for (var i = 0, item; item = this.polygons[i]; i++) {
1292      item.setMap(null);
1293    }
1294
1295    this.polygons = [];
1296  };
1297
1298  GMaps.prototype.getFromFusionTables = function (options) {
1299    var events = options.events;
1300
1301    delete options.events;
1302
1303    var fusion_tables_options = options,
1304      layer = new google.maps.FusionTablesLayer(fusion_tables_options);
1305
1306    for (var ev in events) {
1307      (function (object, name) {
1308        google.maps.event.addListener(object, name, function (e) {
1309          events[name].apply(this, [e]);
1310        });
1311      })(layer, ev);
1312    }
1313
1314    this.layers.push(layer);
1315
1316    return layer;
1317  };
1318
1319  GMaps.prototype.loadFromFusionTables = function (options) {
1320    var layer = this.getFromFusionTables(options);
1321    layer.setMap(this.map);
1322
1323    return layer;
1324  };
1325
1326  GMaps.prototype.getFromKML = function (options) {
1327    var url = options.url,
1328      events = options.events;
1329
1330    delete options.url;
1331    delete options.events;
1332
1333    var kml_options = options,
1334      layer = new google.maps.KmlLayer(url, kml_options);
1335
1336    for (var ev in events) {
1337      (function (object, name) {
1338        google.maps.event.addListener(object, name, function (e) {
1339          events[name].apply(this, [e]);
1340        });
1341      })(layer, ev);
1342    }
1343
1344    this.layers.push(layer);
1345
1346    return layer;
1347  };
1348
1349  GMaps.prototype.loadFromKML = function (options) {
1350    var layer = this.getFromKML(options);
1351    layer.setMap(this.map);
1352
1353    return layer;
1354  };
1355
1356  GMaps.prototype.addLayer = function (layerName, options) {
1357    //var default_layers = ['weather', 'clouds', 'traffic', 'transit', 'bicycling', 'panoramio', 'places'];
1358    options = options || {};
1359    var layer;
1360
1361    switch (layerName) {
1362      case 'weather':
1363        this.singleLayers.weather = layer = new google.maps.weather.WeatherLayer();
1364        break;
1365      case 'clouds':
1366        this.singleLayers.clouds = layer = new google.maps.weather.CloudLayer();
1367        break;
1368      case 'traffic':
1369        this.singleLayers.traffic = layer = new google.maps.TrafficLayer();
1370        break;
1371      case 'transit':
1372        this.singleLayers.transit = layer = new google.maps.TransitLayer();
1373        break;
1374      case 'bicycling':
1375        this.singleLayers.bicycling = layer = new google.maps.BicyclingLayer();
1376        break;
1377      case 'panoramio':
1378        this.singleLayers.panoramio = layer = new google.maps.panoramio.PanoramioLayer();
1379        layer.setTag(options.filter);
1380        delete options.filter;
1381
1382        //click event
1383        if (options.click) {
1384          google.maps.event.addListener(layer, 'click', function (event) {
1385            options.click(event);
1386            delete options.click;
1387          });
1388        }
1389        break;
1390      case 'places':
1391        this.singleLayers.places = layer = new google.maps.places.PlacesService(this.map);
1392
1393        //search, nearbySearch, radarSearch callback, Both are the same
1394        if (options.search || options.nearbySearch || options.radarSearch) {
1395          var placeSearchRequest = {
1396            bounds: options.bounds || null,
1397            keyword: options.keyword || null,
1398            location: options.location || null,
1399            name: options.name || null,
1400            radius: options.radius || null,
1401            rankBy: options.rankBy || null,
1402            types: options.types || null
1403          };
1404
1405          if (options.radarSearch) {
1406            layer.radarSearch(placeSearchRequest, options.radarSearch);
1407          }
1408
1409          if (options.search) {
1410            layer.search(placeSearchRequest, options.search);
1411          }
1412
1413          if (options.nearbySearch) {
1414            layer.nearbySearch(placeSearchRequest, options.nearbySearch);
1415          }
1416        }
1417
1418        //textSearch callback
1419        if (options.textSearch) {
1420          var textSearchRequest = {
1421            bounds: options.bounds || null,
1422            location: options.location || null,
1423            query: options.query || null,
1424            radius: options.radius || null
1425          };
1426
1427          layer.textSearch(textSearchRequest, options.textSearch);
1428        }
1429        break;
1430    }
1431
1432    if (layer !== undefined) {
1433      if (typeof layer.setOptions == 'function') {
1434        layer.setOptions(options);
1435      }
1436      if (typeof layer.setMap == 'function') {
1437        layer.setMap(this.map);
1438      }
1439
1440      return layer;
1441    }
1442  };
1443
1444  GMaps.prototype.removeLayer = function (layer) {
1445    if (typeof (layer) == "string" && this.singleLayers[layer] !== undefined) {
1446      this.singleLayers[layer].setMap(null);
1447
1448      delete this.singleLayers[layer];
1449    } else {
1450      for (var i = 0; i < this.layers.length; i++) {
1451        if (this.layers[i] === layer) {
1452          this.layers[i].setMap(null);
1453          this.layers.splice(i, 1);
1454
1455          break;
1456        }
1457      }
1458    }
1459  };
1460
1461  var travelMode, unitSystem;
1462
1463  GMaps.prototype.getRoutes = function (options) {
1464    switch (options.travelMode) {
1465      case 'bicycling':
1466        travelMode = google.maps.TravelMode.BICYCLING;
1467        break;
1468      case 'transit':
1469        travelMode = google.maps.TravelMode.TRANSIT;
1470        break;
1471      case 'driving':
1472        travelMode = google.maps.TravelMode.DRIVING;
1473        break;
1474      default:
1475        travelMode = google.maps.TravelMode.WALKING;
1476        break;
1477    }
1478
1479    if (options.unitSystem === 'imperial') {
1480      unitSystem = google.maps.UnitSystem.IMPERIAL;
1481    } else {
1482      unitSystem = google.maps.UnitSystem.METRIC;
1483    }
1484
1485    var base_options = {
1486        avoidHighways: false,
1487        avoidTolls: false,
1488        optimizeWaypoints: false,
1489        waypoints: []
1490      },
1491      request_options = extend_object(base_options, options);
1492
1493    request_options.origin = /string/.test(typeof options.origin) ? options.origin : new google.maps.LatLng(options.origin[0], options.origin[1]);
1494    request_options.destination = /string/.test(typeof options.destination) ? options.destination : new google.maps.LatLng(options.destination[0], options.destination[1]);
1495    request_options.travelMode = travelMode;
1496    request_options.unitSystem = unitSystem;
1497
1498    delete request_options.callback;
1499    delete request_options.error;
1500
1501    var self = this,
1502      routes = [],
1503      service = new google.maps.DirectionsService();
1504
1505    service.route(request_options, function (result, status) {
1506      if (status === google.maps.DirectionsStatus.OK) {
1507        for (var r in result.routes) {
1508          if (result.routes.hasOwnProperty(r)) {
1509            routes.push(result.routes[r]);
1510          }
1511        }
1512
1513        if (options.callback) {
1514          options.callback(routes, result, status);
1515        }
1516      } else {
1517        if (options.error) {
1518          options.error(result, status);
1519        }
1520      }
1521    });
1522  };
1523
1524  GMaps.prototype.removeRoutes = function () {
1525    this.routes.length = 0;
1526  };
1527
1528  GMaps.prototype.getElevations = function (options) {
1529    options = extend_object({
1530      locations: [],
1531      path: false,
1532      samples: 256
1533    }, options);
1534
1535    if (options.locations.length > 0) {
1536      if (options.locations[0].length > 0) {
1537        options.locations = array_flat(array_map([options.locations], arrayToLatLng, false));
1538      }
1539    }
1540
1541    var callback = options.callback;
1542    delete options.callback;
1543
1544    var service = new google.maps.ElevationService();
1545
1546    //location request
1547    if (!options.path) {
1548      delete options.path;
1549      delete options.samples;
1550
1551      service.getElevationForLocations(options, function (result, status) {
1552        if (callback && typeof (callback) === "function") {
1553          callback(result, status);
1554        }
1555      });
1556      //path request
1557    } else {
1558      var pathRequest = {
1559        path: options.locations,
1560        samples: options.samples
1561      };
1562
1563      service.getElevationAlongPath(pathRequest, function (result, status) {
1564        if (callback && typeof (callback) === "function") {
1565          callback(result, status);
1566        }
1567      });
1568    }
1569  };
1570
1571  GMaps.prototype.cleanRoute = GMaps.prototype.removePolylines;
1572
1573  GMaps.prototype.renderRoute = function (options, renderOptions) {
1574    var self = this,
1575      panel = ((typeof renderOptions.panel === 'string') ? document.getElementById(renderOptions.panel.replace('#', '')) : renderOptions.panel),
1576      display;
1577
1578    renderOptions.panel = panel;
1579    renderOptions = extend_object({
1580      map: this.map
1581    }, renderOptions);
1582    display = new google.maps.DirectionsRenderer(renderOptions);
1583
1584    this.getRoutes({
1585      origin: options.origin,
1586      destination: options.destination,
1587      travelMode: options.travelMode,
1588      waypoints: options.waypoints,
1589      unitSystem: options.unitSystem,
1590      error: options.error,
1591      avoidHighways: options.avoidHighways,
1592      avoidTolls: options.avoidTolls,
1593      optimizeWaypoints: options.optimizeWaypoints,
1594      callback: function (routes, response, status) {
1595        if (status === google.maps.DirectionsStatus.OK) {
1596          display.setDirections(response);
1597        }
1598      }
1599    });
1600  };
1601
1602  GMaps.prototype.drawRoute = function (options) {
1603    var self = this;
1604
1605    this.getRoutes({
1606      origin: options.origin,
1607      destination: options.destination,
1608      travelMode: options.travelMode,
1609      waypoints: options.waypoints,
1610      unitSystem: options.unitSystem,
1611      error: options.error,
1612      avoidHighways: options.avoidHighways,
1613      avoidTolls: options.avoidTolls,
1614      optimizeWaypoints: options.optimizeWaypoints,
1615      callback: function (routes) {
1616        if (routes.length > 0) {
1617          var polyline_options = {
1618            path: routes[routes.length - 1].overview_path,
1619            strokeColor: options.strokeColor,
1620            strokeOpacity: options.strokeOpacity,
1621            strokeWeight: options.strokeWeight
1622          };
1623
1624          if (options.hasOwnProperty("icons")) {
1625            polyline_options.icons = options.icons;
1626          }
1627
1628          self.drawPolyline(polyline_options);
1629
1630          if (options.callback) {
1631            options.callback(routes[routes.length - 1]);
1632          }
1633        }
1634      }
1635    });
1636  };
1637
1638  GMaps.prototype.travelRoute = function (options) {
1639    if (options.origin && options.destination) {
1640      this.getRoutes({
1641        origin: options.origin,
1642        destination: options.destination,
1643        travelMode: options.travelMode,
1644        waypoints: options.waypoints,
1645        unitSystem: options.unitSystem,
1646        error: options.error,
1647        callback: function (e) {
1648          //start callback
1649          if (e.length > 0 && options.start) {
1650            options.start(e[e.length - 1]);
1651          }
1652
1653          //step callback
1654          if (e.length > 0 && options.step) {
1655            var route = e[e.length - 1];
1656            if (route.legs.length > 0) {
1657              var steps = route.legs[0].steps;
1658              for (var i = 0, step; step = steps[i]; i++) {
1659                step.step_number = i;
1660                options.step(step, (route.legs[0].steps.length - 1));
1661              }
1662            }
1663          }
1664
1665          //end callback
1666          if (e.length > 0 && options.end) {
1667            options.end(e[e.length - 1]);
1668          }
1669        }
1670      });
1671    } else if (options.route) {
1672      if (options.route.legs.length > 0) {
1673        var steps = options.route.legs[0].steps;
1674        for (var i = 0, step; step = steps[i]; i++) {
1675          step.step_number = i;
1676          options.step(step);
1677        }
1678      }
1679    }
1680  };
1681
1682  GMaps.prototype.drawSteppedRoute = function (options) {
1683    var self = this;
1684
1685    if (options.origin && options.destination) {
1686      this.getRoutes({
1687        origin: options.origin,
1688        destination: options.destination,
1689        travelMode: options.travelMode,
1690        waypoints: options.waypoints,
1691        error: options.error,
1692        callback: function (e) {
1693          //start callback
1694          if (e.length > 0 && options.start) {
1695            options.start(e[e.length - 1]);
1696          }
1697
1698          //step callback
1699          if (e.length > 0 && options.step) {
1700            var route = e[e.length - 1];
1701            if (route.legs.length > 0) {
1702              var steps = route.legs[0].steps;
1703              for (var i = 0, step; step = steps[i]; i++) {
1704                step.step_number = i;
1705                var polyline_options = {
1706                  path: step.path,
1707                  strokeColor: options.strokeColor,
1708                  strokeOpacity: options.strokeOpacity,
1709                  strokeWeight: options.strokeWeight
1710                };
1711
1712                if (options.hasOwnProperty("icons")) {
1713                  polyline_options.icons = options.icons;
1714                }
1715
1716                self.drawPolyline(polyline_options);
1717                options.step(step, (route.legs[0].steps.length - 1));
1718              }
1719            }
1720          }
1721
1722          //end callback
1723          if (e.length > 0 && options.end) {
1724            options.end(e[e.length - 1]);
1725          }
1726        }
1727      });
1728    } else if (options.route) {
1729      if (options.route.legs.length > 0) {
1730        var steps = options.route.legs[0].steps;
1731        for (var i = 0, step; step = steps[i]; i++) {
1732          step.step_number = i;
1733          var polyline_options = {
1734            path: step.path,
1735            strokeColor: options.strokeColor,
1736            strokeOpacity: options.strokeOpacity,
1737            strokeWeight: options.strokeWeight
1738          };
1739
1740          if (options.hasOwnProperty("icons")) {
1741            polyline_options.icons = options.icons;
1742          }
1743
1744          self.drawPolyline(polyline_options);
1745          options.step(step);
1746        }
1747      }
1748    }
1749  };
1750
1751  GMaps.Route = function (options) {
1752    this.origin = options.origin;
1753    this.destination = options.destination;
1754    this.waypoints = options.waypoints;
1755
1756    this.map = options.map;
1757    this.route = options.route;
1758    this.step_count = 0;
1759    this.steps = this.route.legs[0].steps;
1760    this.steps_length = this.steps.length;
1761
1762    var polyline_options = {
1763      path: new google.maps.MVCArray(),
1764      strokeColor: options.strokeColor,
1765      strokeOpacity: options.strokeOpacity,
1766      strokeWeight: options.strokeWeight
1767    };
1768
1769    if (options.hasOwnProperty("icons")) {
1770      polyline_options.icons = options.icons;
1771    }
1772
1773    this.polyline = this.map.drawPolyline(polyline_options).getPath();
1774  };
1775
1776  GMaps.Route.prototype.getRoute = function (options) {
1777    var self = this;
1778
1779    this.map.getRoutes({
1780      origin: this.origin,
1781      destination: this.destination,
1782      travelMode: options.travelMode,
1783      waypoints: this.waypoints || [],
1784      error: options.error,
1785      callback: function () {
1786        self.route = e[0];
1787
1788        if (options.callback) {
1789          options.callback.call(self);
1790        }
1791      }
1792    });
1793  };
1794
1795  GMaps.Route.prototype.back = function () {
1796    if (this.step_count > 0) {
1797      this.step_count--;
1798      var path = this.route.legs[0].steps[this.step_count].path;
1799
1800      for (var p in path) {
1801        if (path.hasOwnProperty(p)) {
1802          this.polyline.pop();
1803        }
1804      }
1805    }
1806  };
1807
1808  GMaps.Route.prototype.forward = function () {
1809    if (this.step_count < this.steps_length) {
1810      var path = this.route.legs[0].steps[this.step_count].path;
1811
1812      for (var p in path) {
1813        if (path.hasOwnProperty(p)) {
1814          this.polyline.push(path[p]);
1815        }
1816      }
1817      this.step_count++;
1818    }
1819  };
1820
1821  GMaps.prototype.checkGeofence = function (lat, lng, fence) {
1822    return fence.containsLatLng(new google.maps.LatLng(lat, lng));
1823  };
1824
1825  GMaps.prototype.checkMarkerGeofence = function (marker, outside_callback) {
1826    if (marker.fences) {
1827      for (var i = 0, fence; fence = marker.fences[i]; i++) {
1828        var pos = marker.getPosition();
1829        if (!this.checkGeofence(pos.lat(), pos.lng(), fence)) {
1830          outside_callback(marker, fence);
1831        }
1832      }
1833    }
1834  };
1835
1836  GMaps.prototype.toImage = function (options) {
1837    var options = options || {},
1838      static_map_options = {};
1839
1840    static_map_options['size'] = options['size'] || [this.el.clientWidth, this.el.clientHeight];
1841    static_map_options['lat'] = this.getCenter().lat();
1842    static_map_options['lng'] = this.getCenter().lng();
1843
1844    if (this.markers.length > 0) {
1845      static_map_options['markers'] = [];
1846
1847      for (var i = 0; i < this.markers.length; i++) {
1848        static_map_options['markers'].push({
1849          lat: this.markers[i].getPosition().lat(),
1850          lng: this.markers[i].getPosition().lng()
1851        });
1852      }
1853    }
1854
1855    if (this.polylines.length > 0) {
1856      var polyline = this.polylines[0];
1857
1858      static_map_options['polyline'] = {};
1859      static_map_options['polyline']['path'] = google.maps.geometry.encoding.encodePath(polyline.getPath());
1860      static_map_options['polyline']['strokeColor'] = polyline.strokeColor
1861      static_map_options['polyline']['strokeOpacity'] = polyline.strokeOpacity
1862      static_map_options['polyline']['strokeWeight'] = polyline.strokeWeight
1863    }
1864
1865    return GMaps.staticMapURL(static_map_options);
1866  };
1867
1868  GMaps.staticMapURL = function (options) {
1869    var parameters = [],
1870      data,
1871      static_root = (location.protocol === 'file:' ? 'http:' : location.protocol) + '//maps.googleapis.com/maps/api/staticmap';
1872
1873    if (options.url) {
1874      static_root = options.url;
1875      delete options.url;
1876    }
1877
1878    static_root += '?';
1879
1880    var markers = options.markers;
1881
1882    delete options.markers;
1883
1884    if (!markers && options.marker) {
1885      markers = [options.marker];
1886      delete options.marker;
1887    }
1888
1889    var styles = options.styles;
1890
1891    delete options.styles;
1892
1893    var polyline = options.polyline;
1894    delete options.polyline;
1895
1896    /** Map options **/
1897    if (options.center) {
1898      parameters.push('center=' + options.center);
1899      delete options.center;
1900    } else if (options.address) {
1901      parameters.push('center=' + options.address);
1902      delete options.address;
1903    } else if (options.lat) {
1904      parameters.push(['center=', options.lat, ',', options.lng].join(''));
1905      delete options.lat;
1906      delete options.lng;
1907    } else if (options.visible) {
1908      var visible = encodeURI(options.visible.join('|'));
1909      parameters.push('visible=' + visible);
1910    }
1911
1912    var size = options.size;
1913    if (size) {
1914      if (size.join) {
1915        size = size.join('x');
1916      }
1917      delete options.size;
1918    } else {
1919      size = '630x300';
1920    }
1921    parameters.push('size=' + size);
1922
1923    if (!options.zoom && options.zoom !== false) {
1924      options.zoom = 15;
1925    }
1926
1927    var sensor = options.hasOwnProperty('sensor') ? !!options.sensor : true;
1928    delete options.sensor;
1929    parameters.push('sensor=' + sensor);
1930
1931    for (var param in options) {
1932      if (options.hasOwnProperty(param)) {
1933        parameters.push(param + '=' + options[param]);
1934      }
1935    }
1936
1937    /** Markers **/
1938    if (markers) {
1939      var marker, loc;
1940
1941      for (var i = 0; data = markers[i]; i++) {
1942        marker = [];
1943
1944        if (data.size && data.size !== 'normal') {
1945          marker.push('size:' + data.size);
1946          delete data.size;
1947        } else if (data.icon) {
1948          marker.push('icon:' + encodeURI(data.icon));
1949          delete data.icon;
1950        }
1951
1952        if (data.color) {
1953          marker.push('color:' + data.color.replace('#', '0x'));
1954          delete data.color;
1955        }
1956
1957        if (data.label) {
1958          marker.push('label:' + data.label[0].toUpperCase());
1959          delete data.label;
1960        }
1961
1962        loc = (data.address ? data.address : data.lat + ',' + data.lng);
1963        delete data.address;
1964        delete data.lat;
1965        delete data.lng;
1966
1967        for (var param in data) {
1968          if (data.hasOwnProperty(param)) {
1969            marker.push(param + ':' + data[param]);
1970          }
1971        }
1972
1973        if (marker.length || i === 0) {
1974          marker.push(loc);
1975          marker = marker.join('|');
1976          parameters.push('markers=' + encodeURI(marker));
1977        }
1978        // New marker without styles
1979        else {
1980          marker = parameters.pop() + encodeURI('|' + loc);
1981          parameters.push(marker);
1982        }
1983      }
1984    }
1985
1986    /** Map Styles **/
1987    if (styles) {
1988      for (var i = 0; i < styles.length; i++) {
1989        var styleRule = [];
1990        if (styles[i].featureType) {
1991          styleRule.push('feature:' + styles[i].featureType.toLowerCase());
1992        }
1993
1994        if (styles[i].elementType) {
1995          styleRule.push('element:' + styles[i].elementType.toLowerCase());
1996        }
1997
1998        for (var j = 0; j < styles[i].stylers.length; j++) {
1999          for (var p in styles[i].stylers[j]) {
2000            var ruleArg = styles[i].stylers[j][p];
2001            if (p == 'hue' || p == 'color') {
2002              ruleArg = '0x' + ruleArg.substring(1);
2003            }
2004            styleRule.push(p + ':' + ruleArg);
2005          }
2006        }
2007
2008        var rule = styleRule.join('|');
2009        if (rule != '') {
2010          parameters.push('style=' + rule);
2011        }
2012      }
2013    }
2014
2015    /** Polylines **/
2016    function parseColor(color, opacity) {
2017      if (color[0] === '#') {
2018        color = color.replace('#', '0x');
2019
2020        if (opacity) {
2021          opacity = parseFloat(opacity);
2022          opacity = Math.min(1, Math.max(opacity, 0));
2023          if (opacity === 0) {
2024            return '0x00000000';
2025          }
2026          opacity = (opacity * 255).toString(16);
2027          if (opacity.length === 1) {
2028            opacity += opacity;
2029          }
2030
2031          color = color.slice(0, 8) + opacity;
2032        }
2033      }
2034      return color;
2035    }
2036
2037    if (polyline) {
2038      data = polyline;
2039      polyline = [];
2040
2041      if (data.strokeWeight) {
2042        polyline.push('weight:' + parseInt(data.strokeWeight, 10));
2043      }
2044
2045      if (data.strokeColor) {
2046        var color = parseColor(data.strokeColor, data.strokeOpacity);
2047        polyline.push('color:' + color);
2048      }
2049
2050      if (data.fillColor) {
2051        var fillcolor = parseColor(data.fillColor, data.fillOpacity);
2052        polyline.push('fillcolor:' + fillcolor);
2053      }
2054
2055      var path = data.path;
2056      if (path.join) {
2057        for (var j = 0, pos; pos = path[j]; j++) {
2058          polyline.push(pos.join(','));
2059        }
2060      } else {
2061        polyline.push('enc:' + path);
2062      }
2063
2064      polyline = polyline.join('|');
2065      parameters.push('path=' + encodeURI(polyline));
2066    }
2067
2068    /** Retina support **/
2069    var dpi = window.devicePixelRatio || 1;
2070    parameters.push('scale=' + dpi);
2071
2072    parameters = parameters.join('&');
2073    return static_root + parameters;
2074  };
2075
2076  GMaps.prototype.addMapType = function (mapTypeId, options) {
2077    if (options.hasOwnProperty("getTileUrl") && typeof (options["getTileUrl"]) == "function") {
2078      options.tileSize = options.tileSize || new google.maps.Size(256, 256);
2079
2080      var mapType = new google.maps.ImageMapType(options);
2081
2082      this.map.mapTypes.set(mapTypeId, mapType);
2083    } else {
2084      throw "'getTileUrl' function required.";
2085    }
2086  };
2087
2088  GMaps.prototype.addOverlayMapType = function (options) {
2089    if (options.hasOwnProperty("getTile") && typeof (options["getTile"]) == "function") {
2090      var overlayMapTypeIndex = options.index;
2091
2092      delete options.index;
2093
2094      this.map.overlayMapTypes.insertAt(overlayMapTypeIndex, options);
2095    } else {
2096      throw "'getTile' function required.";
2097    }
2098  };
2099
2100  GMaps.prototype.removeOverlayMapType = function (overlayMapTypeIndex) {
2101    this.map.overlayMapTypes.removeAt(overlayMapTypeIndex);
2102  };
2103
2104  GMaps.prototype.addStyle = function (options) {
2105    var styledMapType = new google.maps.StyledMapType(options.styles, {
2106      name: options.styledMapName
2107    });
2108
2109    this.map.mapTypes.set(options.mapTypeId, styledMapType);
2110  };
2111
2112  GMaps.prototype.setStyle = function (mapTypeId) {
2113    this.map.setMapTypeId(mapTypeId);
2114  };
2115
2116  GMaps.prototype.createPanorama = function (streetview_options) {
2117    if (!streetview_options.hasOwnProperty('lat') || !streetview_options.hasOwnProperty('lng')) {
2118      streetview_options.lat = this.getCenter().lat();
2119      streetview_options.lng = this.getCenter().lng();
2120    }
2121
2122    this.panorama = GMaps.createPanorama(streetview_options);
2123
2124    this.map.setStreetView(this.panorama);
2125
2126    return this.panorama;
2127  };
2128
2129  GMaps.createPanorama = function (options) {
2130    var el = getElementById(options.el, options.context);
2131
2132    options.position = new google.maps.LatLng(options.lat, options.lng);
2133
2134    delete options.el;
2135    delete options.context;
2136    delete options.lat;
2137    delete options.lng;
2138
2139    var streetview_events = ['closeclick', 'links_changed', 'pano_changed', 'position_changed', 'pov_changed', 'resize', 'visible_changed'],
2140      streetview_options = extend_object({
2141        visible: true
2142      }, options);
2143
2144    for (var i = 0; i < streetview_events.length; i++) {
2145      delete streetview_options[streetview_events[i]];
2146    }
2147
2148    var panorama = new google.maps.StreetViewPanorama(el, streetview_options);
2149
2150    for (var i = 0; i < streetview_events.length; i++) {
2151      (function (object, name) {
2152        if (options[name]) {
2153          google.maps.event.addListener(object, name, function () {
2154            options[name].apply(this);
2155          });
2156        }
2157      })(panorama, streetview_events[i]);
2158    }
2159
2160    return panorama;
2161  };
2162
2163  GMaps.prototype.on = function (event_name, handler) {
2164    return GMaps.on(event_name, this, handler);
2165  };
2166
2167  GMaps.prototype.off = function (event_name) {
2168    GMaps.off(event_name, this);
2169  };
2170
2171  GMaps.prototype.once = function (event_name, handler) {
2172    return GMaps.once(event_name, this, handler);
2173  };
2174
2175  GMaps.custom_events = ['marker_added', 'marker_removed', 'polyline_added', 'polyline_removed', 'polygon_added', 'polygon_removed', 'geolocated', 'geolocation_failed'];
2176
2177  GMaps.on = function (event_name, object, handler) {
2178    if (GMaps.custom_events.indexOf(event_name) == -1) {
2179      if (object instanceof GMaps) object = object.map;
2180      return google.maps.event.addListener(object, event_name, handler);
2181    } else {
2182      var registered_event = {
2183        handler: handler,
2184        eventName: event_name
2185      };
2186
2187      object.registered_events[event_name] = object.registered_events[event_name] || [];
2188      object.registered_events[event_name].push(registered_event);
2189
2190      return registered_event;
2191    }
2192  };
2193
2194  GMaps.off = function (event_name, object) {
2195    if (GMaps.custom_events.indexOf(event_name) == -1) {
2196      if (object instanceof GMaps) object = object.map;
2197      google.maps.event.clearListeners(object, event_name);
2198    } else {
2199      object.registered_events[event_name] = [];
2200    }
2201  };
2202
2203  GMaps.once = function (event_name, object, handler) {
2204    if (GMaps.custom_events.indexOf(event_name) == -1) {
2205      if (object instanceof GMaps) object = object.map;
2206      return google.maps.event.addListenerOnce(object, event_name, handler);
2207    }
2208  };
2209
2210  GMaps.fire = function (event_name, object, scope) {
2211    if (GMaps.custom_events.indexOf(event_name) == -1) {
2212      google.maps.event.trigger(object, event_name, Array.prototype.slice.apply(arguments).slice(2));
2213    } else {
2214      if (event_name in scope.registered_events) {
2215        var firing_events = scope.registered_events[event_name];
2216
2217        for (var i = 0; i < firing_events.length; i++) {
2218          (function (handler, scope, object) {
2219            handler.apply(scope, [object]);
2220          })(firing_events[i]['handler'], scope, object);
2221        }
2222      }
2223    }
2224  };
2225
2226  GMaps.geolocate = function (options) {
2227    var complete_callback = options.always || options.complete;
2228
2229    if (navigator.geolocation) {
2230      navigator.geolocation.getCurrentPosition(function (position) {
2231        options.success(position);
2232
2233        if (complete_callback) {
2234          complete_callback();
2235        }
2236      }, function (error) {
2237        options.error(error);
2238
2239        if (complete_callback) {
2240          complete_callback();
2241        }
2242      }, options.options);
2243    } else {
2244      options.not_supported();
2245
2246      if (complete_callback) {
2247        complete_callback();
2248      }
2249    }
2250  };
2251
2252  GMaps.geocode = function (options) {
2253    this.geocoder = new google.maps.Geocoder();
2254    var callback = options.callback;
2255    if (options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) {
2256      options.latLng = new google.maps.LatLng(options.lat, options.lng);
2257    }
2258
2259    delete options.lat;
2260    delete options.lng;
2261    delete options.callback;
2262
2263    this.geocoder.geocode(options, function (results, status) {
2264      callback(results, status);
2265    });
2266  };
2267
2268  if (typeof window.google === 'object' && window.google.maps) {
2269    //==========================
2270    // Polygon containsLatLng
2271    // https://github.com/tparkin/Google-Maps-Point-in-Polygon
2272    // Poygon getBounds extension - google-maps-extensions
2273    // http://code.google.com/p/google-maps-extensions/source/browse/google.maps.Polygon.getBounds.js
2274    if (!google.maps.Polygon.prototype.getBounds) {
2275      google.maps.Polygon.prototype.getBounds = function (latLng) {
2276        var bounds = new google.maps.LatLngBounds();
2277        var paths = this.getPaths();
2278        var path;
2279
2280        for (var p = 0; p < paths.getLength(); p++) {
2281          path = paths.getAt(p);
2282          for (var i = 0; i < path.getLength(); i++) {
2283            bounds.extend(path.getAt(i));
2284          }
2285        }
2286
2287        return bounds;
2288      };
2289    }
2290
2291    if (!google.maps.Polygon.prototype.containsLatLng) {
2292      // Polygon containsLatLng - method to determine if a latLng is within a polygon
2293      google.maps.Polygon.prototype.containsLatLng = function (latLng) {
2294        // Exclude points outside of bounds as there is no way they are in the poly
2295        var bounds = this.getBounds();
2296
2297        if (bounds !== null && !bounds.contains(latLng)) {
2298          return false;
2299        }
2300
2301        // Raycast point in polygon method
2302        var inPoly = false;
2303
2304        var numPaths = this.getPaths().getLength();
2305        for (var p = 0; p < numPaths; p++) {
2306          var path = this.getPaths().getAt(p);
2307          var numPoints = path.getLength();
2308          var j = numPoints - 1;
2309
2310          for (var i = 0; i < numPoints; i++) {
2311            var vertex1 = path.getAt(i);
2312            var vertex2 = path.getAt(j);
2313
2314            if (vertex1.lng() < latLng.lng() && vertex2.lng() >= latLng.lng() || vertex2.lng() < latLng.lng() && vertex1.lng() >= latLng.lng()) {
2315              if (vertex1.lat() + (latLng.lng() - vertex1.lng()) / (vertex2.lng() - vertex1.lng()) * (vertex2.lat() - vertex1.lat()) < latLng.lat()) {
2316                inPoly = !inPoly;
2317              }
2318            }
2319
2320            j = i;
2321          }
2322        }
2323
2324        return inPoly;
2325      };
2326    }
2327
2328    if (!google.maps.Circle.prototype.containsLatLng) {
2329      google.maps.Circle.prototype.containsLatLng = function (latLng) {
2330        if (google.maps.geometry) {
2331          return google.maps.geometry.spherical.computeDistanceBetween(this.getCenter(), latLng) <= this.getRadius();
2332        } else {
2333          return true;
2334        }
2335      };
2336    }
2337
2338    google.maps.Rectangle.prototype.containsLatLng = function (latLng) {
2339      return this.getBounds().contains(latLng);
2340    };
2341
2342    google.maps.LatLngBounds.prototype.containsLatLng = function (latLng) {
2343      return this.contains(latLng);
2344    };
2345
2346    google.maps.Marker.prototype.setFences = function (fences) {
2347      this.fences = fences;
2348    };
2349
2350    google.maps.Marker.prototype.addFence = function (fence) {
2351      this.fences.push(fence);
2352    };
2353
2354    google.maps.Marker.prototype.getId = function () {
2355      return this['__gm_id'];
2356    };
2357  }
2358
2359  //==========================
2360  // Array indexOf
2361  // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf
2362  if (!Array.prototype.indexOf) {
2363    Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
2364      "use strict";
2365      if (this == null) {
2366        throw new TypeError();
2367      }
2368      var t = Object(this);
2369      var len = t.length >>> 0;
2370      if (len === 0) {
2371        return -1;
2372      }
2373      var n = 0;
2374      if (arguments.length > 1) {
2375        n = Number(arguments[1]);
2376        if (n != n) { // shortcut for verifying if it's NaN
2377          n = 0;
2378        } else if (n != 0 && n != Infinity && n != -Infinity) {
2379          n = (n > 0 || -1) * Math.floor(Math.abs(n));
2380        }
2381      }
2382      if (n >= len) {
2383        return -1;
2384      }
2385      var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
2386      for (; k < len; k++) {
2387        if (k in t && t[k] === searchElement) {
2388          return k;
2389        }
2390      }
2391      return -1;
2392    }
2393  }
2394
2395  return GMaps;
2396}));

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.