JAVASCRIPT   87
gmaps.js
Guest on 19th April 2022 01:03:19 AM


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

Raw Paste

Login or Register to edit or fork this paste. It's free.