JAVASCRIPT   16

gmaps.js

Guest on 18th July 2021 05:12:21 PM

  1. /*!
  2.  * GMaps.js v0.3
  3.  * http://hpneo.github.com/gmaps/
  4.  *
  5.  * Copyright 2012, Gustavo Leon
  6.  * Released under the MIT License.
  7.  */
  8.  
  9. if(window.google && window.google.maps){
  10.  
  11.   var GMaps = (function(global) {
  12.     "use strict";
  13.  
  14.     var doc = document;
  15.     var getElementById = function(id, context) {
  16.       var ele
  17.       if('jQuery' in global && context){
  18.         ele = $("#"+id.replace('#', ''), context)[0]
  19.       } else {
  20.         ele = doc.getElementById(id.replace('#', ''));
  21.       };
  22.       return ele;
  23.     };
  24.  
  25.     var GMaps = function(options) {
  26.       var self = this;
  27.  
  28.       var events_that_hide_context_menu = ['bounds_changed', 'center_changed', 'click', 'dblclick', 'drag', 'dragend', 'dragstart', 'idle', 'maptypeid_changed', 'projection_changed', 'resize', 'tilesloaded', 'zoom_changed'];
  29.       var events_that_doesnt_hide_context_menu = ['mousemove', 'mouseout', 'mouseover'];
  30.  
  31.       window.context_menu = {};
  32.  
  33.       if (typeof(options.el) === 'string' || typeof(options.div) === 'string') {
  34.         this.el = getElementById(options.el || options.div, options.context);
  35.       } else {
  36.         this.el = options.el || options.div;
  37.       };
  38.  
  39.       if (typeof(this.el) === 'undefined' || this.el === null) {
  40.         throw 'No element defined.';
  41.       }
  42.  
  43.       this.el.style.width = options.width || this.el.scrollWidth || this.el.offsetWidth;
  44.       this.el.style.height = options.height || this.el.scrollHeight || this.el.offsetHeight;
  45.  
  46.       this.controls = [];
  47.       this.overlays = [];
  48.       this.layers = []; // array with kml and ft layers, can be as many
  49.       this.singleLayers = {}; // object with the other layers, only one per layer
  50.       this.markers = [];
  51.       this.polylines = [];
  52.       this.routes = [];
  53.       this.polygons = [];
  54.       this.infoWindow = null;
  55.       this.overlay_el = null;
  56.       this.zoom = options.zoom || 15;
  57.       this.registered_events = {};
  58.  
  59.       var markerClusterer = options.markerClusterer;
  60.  
  61.       //'Hybrid', 'Roadmap', 'Satellite' or 'Terrain'
  62.       var mapType;
  63.  
  64.       if (options.mapType) {
  65.         mapType = google.maps.MapTypeId[options.mapType.toUpperCase()];
  66.       }
  67.       else {
  68.         mapType = google.maps.MapTypeId.ROADMAP;
  69.       }
  70.  
  71.       var map_center = new google.maps.LatLng(options.lat, options.lng);
  72.  
  73.       delete options.el;
  74.       delete options.lat;
  75.       delete options.lng;
  76.       delete options.mapType;
  77.       delete options.width;
  78.       delete options.height;
  79.       delete options.markerClusterer;
  80.  
  81.       var zoomControlOpt = options.zoomControlOpt || {
  82.         style: 'DEFAULT',
  83.         position: 'TOP_LEFT'
  84.       };
  85.  
  86.       var zoomControl = options.zoomControl || true,
  87.           zoomControlStyle = zoomControlOpt.style || 'DEFAULT',
  88.           zoomControlPosition = zoomControlOpt.position || 'TOP_LEFT',
  89.           panControl = options.panControl || true,
  90.           mapTypeControl = options.mapTypeControl || true,
  91.           scaleControl = options.scaleControl || true,
  92.           streetViewControl = options.streetViewControl || true,
  93.           overviewMapControl = overviewMapControl || true;
  94.  
  95.       var map_options = {};
  96.  
  97.       var map_base_options = {
  98.         zoom: this.zoom,
  99.         center: map_center,
  100.         mapTypeId: mapType
  101.       };
  102.  
  103.       var map_controls_options = {
  104.         panControl: panControl,
  105.         zoomControl: zoomControl,
  106.         zoomControlOptions: {
  107.           style: google.maps.ZoomControlStyle[zoomControlStyle], // DEFAULT LARGE SMALL
  108.           position: google.maps.ControlPosition[zoomControlPosition]
  109.         },
  110.         mapTypeControl: mapTypeControl,
  111.         scaleControl: scaleControl,
  112.         streetViewControl: streetViewControl,
  113.         overviewMapControl: overviewMapControl
  114.       }
  115.  
  116.       if(options.disableDefaultUI != true)
  117.         map_base_options = extend_object(map_base_options, map_controls_options);
  118.  
  119.       map_options = extend_object(map_base_options, options);
  120.  
  121.       for(var i = 0; i < events_that_hide_context_menu.length; i++) {
  122.         delete map_options[events_that_hide_context_menu[i]];
  123.       }
  124.  
  125.       for(var i = 0; i < events_that_doesnt_hide_context_menu.length; i++) {
  126.         delete map_options[events_that_doesnt_hide_context_menu[i]];
  127.       }
  128.  
  129.       this.map = new google.maps.Map(this.el, map_options);
  130.  
  131.       if(markerClusterer) {
  132.         this.markerClusterer = markerClusterer.apply(this, [this.map]);
  133.       }
  134.  
  135.       // finds absolute position of an element
  136.       var findAbsolutePosition = function(obj)  {
  137.         var curleft = 0;
  138.         var curtop = 0;
  139.         if (obj.offsetParent) {
  140.           do {
  141.             curleft += obj.offsetLeft;
  142.             curtop += obj.offsetTop;
  143.           } while (obj = obj.offsetParent);
  144.         }
  145.         return [curleft,curtop];
  146.       //returns an array
  147.       }
  148.  
  149.  
  150.       // Context menus
  151.       var buildContextMenuHTML = function(control, e) {
  152.         var html = '';
  153.         var options = window.context_menu[control];
  154.         for (var i in options){
  155.           if (options.hasOwnProperty(i)){
  156.             var option = options[i];
  157.             html += '<li><a id="' + control + '_' + i + '" href="#">' +
  158.               option.title + '</a></li>';
  159.           }
  160.         }
  161.  
  162.         if(!getElementById('gmaps_context_menu')) return;
  163.  
  164.         var context_menu_element = getElementById('gmaps_context_menu');
  165.         context_menu_element.innerHTML = html;
  166.  
  167.         var context_menu_items = context_menu_element.getElementsByTagName('a');
  168.  
  169.         var context_menu_items_count = context_menu_items.length;
  170.  
  171.         for(var i = 0; i < context_menu_items_count; i++){
  172.           var context_menu_item = context_menu_items[i];
  173.  
  174.           var assign_menu_item_action = function(ev){
  175.             ev.preventDefault();
  176.  
  177.             options[this.id.replace(control + '_', '')].action.apply(self, [e]);
  178.             self.hideContextMenu();
  179.           };
  180.  
  181.           google.maps.event.clearListeners(context_menu_item, 'click');
  182.           google.maps.event.addDomListenerOnce(context_menu_item, 'click', assign_menu_item_action, false);
  183.         }
  184.  
  185.         var position = findAbsolutePosition.apply(this, [self.el]);
  186.         var left = position[0] + e.pixel.x - 15;
  187.         var top = position[1] + e.pixel.y- 15;
  188.  
  189.         context_menu_element.style.left = left + "px";
  190.         context_menu_element.style.top = top + "px";
  191.  
  192.         context_menu_element.style.display = 'block';
  193.       };
  194.  
  195.       var buildContextMenu = function(control, e) {
  196.         if (control === 'marker') {
  197.           e.pixel = {};
  198.           var overlay = new google.maps.OverlayView();
  199.           overlay.setMap(self.map);
  200.           overlay.draw = function() {
  201.             var projection = overlay.getProjection();
  202.             var position = e.marker.getPosition();
  203.             e.pixel = projection.fromLatLngToContainerPixel(position);
  204.  
  205.             buildContextMenuHTML(control, e);
  206.           };
  207.         }
  208.         else {
  209.           buildContextMenuHTML(control, e);
  210.         }
  211.       };
  212.  
  213.       this.setContextMenu = function(options) {
  214.         window.context_menu[options.control] = {};
  215.  
  216.         for (var i in options.options){
  217.           if (options.options.hasOwnProperty(i)){
  218.             var option = options.options[i];
  219.             window.context_menu[options.control][option.name] = {
  220.               title: option.title,
  221.               action: option.action
  222.             };
  223.           }
  224.         }
  225.  
  226.         var ul = doc.createElement('ul');
  227.  
  228.         ul.id = 'gmaps_context_menu';
  229.         ul.style.display = 'none';
  230.         ul.style.position = 'absolute';
  231.         ul.style.minWidth = '100px';
  232.         ul.style.background = 'white';
  233.         ul.style.listStyle = 'none';
  234.         ul.style.padding = '8px';
  235.         ul.style.boxShadow = '2px 2px 6px #ccc';
  236.  
  237.         doc.body.appendChild(ul);
  238.  
  239.         var context_menu_element = getElementById('gmaps_context_menu');
  240.  
  241.         google.maps.event.addDomListener(context_menu_element, 'mouseout', function(ev) {
  242.           if(!ev.relatedTarget || !this.contains(ev.relatedTarget)){
  243.             window.setTimeout(function(){
  244.               context_menu_element.style.display = 'none';
  245.             }, 400);
  246.           }
  247.         }, false);
  248.       };
  249.  
  250.       this.hideContextMenu = function() {
  251.         var context_menu_element = getElementById('gmaps_context_menu');
  252.         if(context_menu_element)
  253.           context_menu_element.style.display = 'none';
  254.       };
  255.  
  256.       //Events
  257.  
  258.       var setupListener = function(object, name) {
  259.         google.maps.event.addListener(object, name, function(e){
  260.           if(e == undefined) {
  261.             e = this;
  262.           }
  263.  
  264.           options[name].apply(this, [e]);
  265.  
  266.           self.hideContextMenu();
  267.         });
  268.       }
  269.  
  270.       for (var ev = 0; ev < events_that_hide_context_menu.length; ev++) {
  271.         var name = events_that_hide_context_menu[ev];
  272.  
  273.         if (name in options) {
  274.           setupListener(this.map, name);
  275.         }
  276.       }
  277.  
  278.       for (var ev = 0; ev < events_that_doesnt_hide_context_menu.length; ev++) {
  279.         var name = events_that_doesnt_hide_context_menu[ev];
  280.  
  281.         if (name in options) {
  282.           setupListener(this.map, name);
  283.         }
  284.       }
  285.  
  286.       google.maps.event.addListener(this.map, 'rightclick', function(e) {
  287.         if (options.rightclick) {
  288.           options.rightclick.apply(this, [e]);
  289.         }
  290.  
  291.         if(window.context_menu['map'] != undefined) {
  292.           buildContextMenu('map', e);
  293.         }
  294.       });
  295.  
  296.       this.refresh = function() {
  297.         google.maps.event.trigger(this.map, 'resize');
  298.       };
  299.  
  300.       this.fitZoom = function() {
  301.         var latLngs = [];
  302.         var markers_length = this.markers.length;
  303.  
  304.         for(var i=0; i < markers_length; i++) {
  305.           latLngs.push(this.markers[i].getPosition());
  306.         }
  307.  
  308.         this.fitLatLngBounds(latLngs);
  309.       };
  310.  
  311.       this.fitLatLngBounds = function(latLngs) {
  312.         var total = latLngs.length;
  313.         var bounds = new google.maps.LatLngBounds();
  314.  
  315.         for(var i=0; i < total; i++) {
  316.           bounds.extend(latLngs[i]);
  317.         }
  318.  
  319.         this.map.fitBounds(bounds);
  320.       };
  321.  
  322.       // Map methods
  323.       this.setCenter = function(lat, lng, callback) {
  324.         this.map.panTo(new google.maps.LatLng(lat, lng));
  325.         if (callback) {
  326.           callback();
  327.         }
  328.       };
  329.  
  330.       this.getElement = function() {
  331.         return this.el;
  332.       };
  333.  
  334.       this.zoomIn = function(value) {
  335.         this.zoom = this.map.getZoom() + value;
  336.         this.map.setZoom(this.zoom);
  337.       };
  338.  
  339.       this.zoomOut = function(value) {
  340.         this.zoom = this.map.getZoom() - value;
  341.         this.map.setZoom(this.zoom);
  342.       };
  343.  
  344.       var native_methods = [];
  345.  
  346.       for(var method in this.map){
  347.         if(typeof(this.map[method]) == 'function' && !this[method]){
  348.           native_methods.push(method);
  349.         }
  350.       }
  351.  
  352.       for(var i=0; i < native_methods.length; i++){
  353.         (function(gmaps, scope, method_name) {
  354.           gmaps[method_name] = function(){
  355.             return scope[method_name].apply(scope, arguments);
  356.           };
  357.         })(this, this.map, native_methods[i]);
  358.       }
  359.  
  360.       this.createControl = function(options) {
  361.         var control = doc.createElement('div');
  362.  
  363.         control.style.cursor = 'pointer';
  364.         control.style.fontFamily = 'Arial, sans-serif';
  365.         control.style.fontSize = '13px';
  366.         control.style.boxShadow = 'rgba(0, 0, 0, 0.398438) 0px 2px 4px';
  367.  
  368.         for(var option in options.style)
  369.           control.style[option] = options.style[option];
  370.  
  371.         if(options.id) {
  372.           control.id = options.id;
  373.         }
  374.  
  375.         if(options.classes) {
  376.           control.className = options.classes;
  377.         }
  378.  
  379.         if(options.content) {
  380.           control.innerHTML = options.content;
  381.         }
  382.  
  383.         for (var ev in options.events) {
  384.           (function(object, name) {
  385.             google.maps.event.addDomListener(object, name, function(){
  386.               options.events[name].apply(this, [this]);
  387.             });
  388.           })(control, ev);
  389.         }
  390.  
  391.         control.index = 1;
  392.  
  393.         return control;
  394.       };
  395.  
  396.       this.addControl = function(options) {
  397.         var position = google.maps.ControlPosition[options.position.toUpperCase()];
  398.  
  399.         delete options.position;
  400.  
  401.         var control = this.createControl(options);
  402.         this.controls.push(control);
  403.         this.map.controls[position].push(control);
  404.  
  405.         return control;
  406.       };
  407.  
  408.       // Markers
  409.       this.createMarker = function(options) {
  410.         if ((options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) || options.position) {
  411.           var self = this;
  412.           var details = options.details;
  413.           var fences = options.fences;
  414.           var outside = options.outside;
  415.  
  416.           var base_options = {
  417.             position: new google.maps.LatLng(options.lat, options.lng),
  418.             map: null
  419.           };
  420.  
  421.           delete options.lat;
  422.           delete options.lng;
  423.           delete options.fences;
  424.           delete options.outside;
  425.  
  426.           var marker_options = extend_object(base_options, options);
  427.  
  428.           var marker = new google.maps.Marker(marker_options);
  429.  
  430.           marker.fences = fences;
  431.  
  432.           if (options.infoWindow) {
  433.             marker.infoWindow = new google.maps.InfoWindow(options.infoWindow);
  434.  
  435.             var info_window_events = ['closeclick', 'content_changed', 'domready', 'position_changed', 'zindex_changed'];
  436.  
  437.             for (var ev = 0; ev < info_window_events.length; ev++) {
  438.               (function(object, name) {
  439.                 google.maps.event.addListener(object, name, function(e){
  440.                   if (options.infoWindow[name])
  441.                     options.infoWindow[name].apply(this, [e]);
  442.                 });
  443.               })(marker.infoWindow, info_window_events[ev]);
  444.             }
  445.           }
  446.  
  447.           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'];
  448.  
  449.           var marker_events_with_mouse = ['dblclick', 'drag', 'dragend', 'dragstart', 'mousedown', 'mouseout', 'mouseover', 'mouseup'];
  450.  
  451.           for (var ev = 0; ev < marker_events.length; ev++) {
  452.             (function(object, name) {
  453.               google.maps.event.addListener(object, name, function(){
  454.                 if (options[name])
  455.                   options[name].apply(this, [this]);
  456.               });
  457.             })(marker, marker_events[ev]);
  458.           }
  459.  
  460.           for (var ev = 0; ev < marker_events_with_mouse.length; ev++) {
  461.             (function(map, object, name) {
  462.               google.maps.event.addListener(object, name, function(me){
  463.                 if(!me.pixel){
  464.                   me.pixel = map.getProjection().fromLatLngToPoint(me.latLng)
  465.                 }
  466.                 if (options[name])
  467.                   options[name].apply(this, [me]);
  468.               });
  469.             })(this.map, marker, marker_events_with_mouse[ev]);
  470.           }
  471.  
  472.           google.maps.event.addListener(marker, 'click', function() {
  473.             this.details = details;
  474.  
  475.             if (options.click) {
  476.               options.click.apply(this, [this]);
  477.             }
  478.  
  479.             if (marker.infoWindow) {
  480.               self.hideInfoWindows();
  481.               marker.infoWindow.open(self.map, marker);
  482.             }
  483.           });
  484.  
  485.           google.maps.event.addListener(marker, 'rightclick', function(e) {
  486.             e.marker = this;
  487.  
  488.             if (options.rightclick) {
  489.               options.rightclick.apply(this, [e]);
  490.             }
  491.  
  492.             if (window.context_menu['marker'] != undefined) {
  493.               buildContextMenu('marker', e);
  494.             }
  495.           });
  496.  
  497.           if (options.dragend || marker.fences) {
  498.             google.maps.event.addListener(marker, 'dragend', function() {
  499.               if (marker.fences) {
  500.                 self.checkMarkerGeofence(marker, function(m, f) {
  501.                   outside(m, f);
  502.                 });
  503.               }
  504.             });
  505.           }
  506.  
  507.           return marker;
  508.         }
  509.         else {
  510.           throw 'No latitude or longitude defined.';
  511.         }
  512.       };
  513.  
  514.       this.addMarker = function(options) {
  515.         var marker;
  516.         if(options.hasOwnProperty('gm_accessors_')) {
  517.           // Native google.maps.Marker object
  518.           marker = options;
  519.         }
  520.         else {
  521.           if ((options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) || options.position) {
  522.             marker = this.createMarker(options);
  523.           }
  524.           else {
  525.             throw 'No latitude or longitude defined.';
  526.           }
  527.         }
  528.  
  529.         marker.setMap(this.map);
  530.  
  531.         if(this.markerClusterer) {
  532.           this.markerClusterer.addMarker(marker);
  533.         }
  534.  
  535.         this.markers.push(marker);
  536.  
  537.         GMaps.fire('marker_added', marker, this);
  538.  
  539.         return marker;
  540.       };
  541.  
  542.       this.addMarkers = function(array) {
  543.         for (var i=0, marker; marker=array[i]; i++) {
  544.           this.addMarker(marker);
  545.         }
  546.         return this.markers;
  547.       };
  548.  
  549.       this.hideInfoWindows = function() {
  550.         for (var i=0, marker; marker=this.markers[i]; i++){
  551.           if (marker.infoWindow){
  552.             marker.infoWindow.close();
  553.           }
  554.         }
  555.       };
  556.  
  557.       this.removeMarker = function(marker) {
  558.         for(var i = 0; i < this.markers.length; i++) {
  559.           if(this.markers[i] === marker) {
  560.             this.markers[i].setMap(null);
  561.             this.markers.splice(i, 1);
  562.  
  563.             GMaps.fire('marker_removed', marker, this);
  564.  
  565.             break;
  566.           }
  567.         }
  568.  
  569.         return marker;
  570.       };
  571.  
  572.       this.removeMarkers = function(collection) {
  573.         var collection = (collection || this.markers);
  574.  
  575.         for(var i=0;i < this.markers.length; i++){
  576.           if(this.markers[i] === collection[i])
  577.             this.markers[i].setMap(null);
  578.         }
  579.  
  580.         var new_markers = [];
  581.  
  582.         for(var i=0;i < this.markers.length; i++){
  583.           if(this.markers[i].getMap() != null)
  584.             new_markers.push(this.markers[i]);
  585.         }
  586.  
  587.         this.markers = new_markers;
  588.       };
  589.  
  590.       // Overlays
  591.  
  592.       this.drawOverlay = function(options) {
  593.         var overlay = new google.maps.OverlayView();
  594.         overlay.setMap(self.map);
  595.  
  596.         var auto_show = true;
  597.  
  598.         if(options.auto_show != null)
  599.           auto_show = options.auto_show;
  600.  
  601.         overlay.onAdd = function() {
  602.           var el = doc.createElement('div');
  603.           el.style.borderStyle = "none";
  604.           el.style.borderWidth = "0px";
  605.           el.style.position = "absolute";
  606.           el.style.zIndex = 100;
  607.           el.innerHTML = options.content;
  608.  
  609.           overlay.el = el;
  610.  
  611.           var panes = this.getPanes();
  612.           if (!options.layer) {
  613.             options.layer = 'overlayLayer';
  614.           }
  615.           var overlayLayer = panes[options.layer];
  616.           overlayLayer.appendChild(el);
  617.  
  618.           var stop_overlay_events = ['contextmenu', 'DOMMouseScroll', 'dblclick', 'mousedown'];
  619.  
  620.           for (var ev = 0; ev < stop_overlay_events.length; ev++) {
  621.             (function(object, name) {
  622.               google.maps.event.addDomListener(object, name, function(e){
  623.                 if(navigator.userAgent.toLowerCase().indexOf('msie') != -1 && document.all) {
  624.                   e.cancelBubble = true;
  625.                   e.returnValue = false;
  626.                 }
  627.                 else {
  628.                   e.stopPropagation();
  629.                 }
  630.               });
  631.             })(el, stop_overlay_events[ev]);
  632.           }
  633.  
  634.           google.maps.event.trigger(this, 'ready');
  635.         };
  636.  
  637.         overlay.draw = function() {
  638.           var projection = this.getProjection();
  639.           var pixel = projection.fromLatLngToDivPixel(new google.maps.LatLng(options.lat, options.lng));
  640.  
  641.           options.horizontalOffset = options.horizontalOffset || 0;
  642.           options.verticalOffset = options.verticalOffset || 0;
  643.  
  644.           var el = overlay.el;
  645.           var content = el.children[0];
  646.  
  647.           var content_height = content.clientHeight;
  648.           var content_width = content.clientWidth;
  649.  
  650.           switch (options.verticalAlign) {
  651.             case 'top':
  652.               el.style.top = (pixel.y - content_height + options.verticalOffset) + 'px';
  653.               break;
  654.             default:
  655.             case 'middle':
  656.               el.style.top = (pixel.y - (content_height / 2) + options.verticalOffset) + 'px';
  657.               break;
  658.             case 'bottom':
  659.               el.style.top = (pixel.y + options.verticalOffset) + 'px';
  660.               break;
  661.           }
  662.  
  663.           switch (options.horizontalAlign) {
  664.             case 'left':
  665.               el.style.left = (pixel.x - content_width + options.horizontalOffset) + 'px';
  666.               break;
  667.             default:
  668.             case 'center':
  669.               el.style.left = (pixel.x - (content_width / 2) + options.horizontalOffset) + 'px';
  670.               break;
  671.             case 'right':
  672.               el.style.left = (pixel.x + options.horizontalOffset) + 'px';
  673.               break;
  674.           }
  675.  
  676.           el.style.display = auto_show ? 'block' : 'none';
  677.  
  678.           if(!auto_show){
  679.             options.show.apply(this, [el]);
  680.           }
  681.         };
  682.  
  683.         overlay.onRemove = function() {
  684.           var el = overlay.el;
  685.  
  686.           if(options.remove){
  687.             options.remove.apply(this, [el]);
  688.           }
  689.           else {
  690.             overlay.el.parentNode.removeChild(overlay.el);
  691.             overlay.el = null;
  692.           }
  693.         };
  694.  
  695.         self.overlays.push(overlay);
  696.         return overlay;
  697.       };
  698.  
  699.       this.removeOverlay = function(overlay) {
  700.         for(var i = 0; i < this.overlays.length; i++) {
  701.           if(this.overlays[i] === overlay) {
  702.             this.overlays[i].setMap(null);
  703.             this.overlays.splice(i, 1);
  704.  
  705.             break;
  706.           }
  707.         }
  708.       };
  709.  
  710.       this.removeOverlays = function() {
  711.         for (var i=0, item; item=self.overlays[i]; i++){
  712.           item.setMap(null);
  713.         }
  714.         self.overlays = [];
  715.       };
  716.  
  717.       // Geometry
  718.  
  719.       this.drawPolyline = function(options) {
  720.         var path = [];
  721.         var points = options.path;
  722.  
  723.         if (points.length){
  724.           if (points[0][0] === undefined){
  725.             path = points;
  726.           }
  727.           else {
  728.             for (var i=0, latlng; latlng=points[i]; i++){
  729.               path.push(new google.maps.LatLng(latlng[0], latlng[1]));
  730.             }
  731.           }
  732.         }
  733.  
  734.         var polyline_options = {
  735.           map: this.map,
  736.           path: path,
  737.           strokeColor: options.strokeColor,
  738.           strokeOpacity: options.strokeOpacity,
  739.           strokeWeight: options.strokeWeight,
  740.           geodesic: options.geodesic,
  741.           clickable: true,
  742.           editable: false,
  743.           visible: true
  744.         };
  745.  
  746.         if(options.hasOwnProperty("clickable"))
  747.           polyline_options.clickable = options.clickable;
  748.  
  749.         if(options.hasOwnProperty("editable"))
  750.           polyline_options.editable = options.editable;
  751.  
  752.         if(options.hasOwnProperty("icons"))
  753.           polyline_options.icons = options.icons;
  754.  
  755.         if(options.hasOwnProperty("zIndex"))
  756.           polyline_options.zIndex = options.zIndex;
  757.  
  758.         var polyline = new google.maps.Polyline(polyline_options);
  759.  
  760.         var polyline_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
  761.  
  762.         for (var ev = 0; ev < polyline_events.length; ev++) {
  763.           (function(object, name) {
  764.             google.maps.event.addListener(object, name, function(e){
  765.               if (options[name])
  766.                 options[name].apply(this, [e]);
  767.             });
  768.           })(polyline, polyline_events[ev]);
  769.         }
  770.  
  771.         this.polylines.push(polyline);
  772.  
  773.         GMaps.fire('polyline_added', polyline, this);
  774.  
  775.         return polyline;
  776.       };
  777.  
  778.       this.removePolyline = function(polyline) {
  779.         for(var i = 0; i < this.polylines.length; i++) {
  780.           if(this.polylines[i] === polyline) {
  781.             this.polylines[i].setMap(null);
  782.             this.polylines.splice(i, 1);
  783.  
  784.             GMaps.fire('polyline_removed', polyline, this);
  785.  
  786.             break;
  787.           }
  788.         }
  789.       };
  790.  
  791.       this.removePolylines = function() {
  792.         for (var i=0, item; item=self.polylines[i]; i++){
  793.           item.setMap(null);
  794.         }
  795.         self.polylines = [];
  796.       };
  797.  
  798.       this.drawCircle = function(options) {
  799.         options =  extend_object({
  800.           map: this.map,
  801.           center: new google.maps.LatLng(options.lat, options.lng)
  802.         }, options);
  803.  
  804.         delete options.lat;
  805.         delete options.lng;
  806.         var polygon = new google.maps.Circle(options);
  807.  
  808.         var polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
  809.  
  810.         for (var ev = 0; ev < polygon_events.length; ev++) {
  811.           (function(object, name) {
  812.             google.maps.event.addListener(object, name, function(e){
  813.               if (options[name])
  814.                 options[name].apply(this, [e]);
  815.             });
  816.           })(polygon, polygon_events[ev]);
  817.         }
  818.  
  819.         this.polygons.push(polygon);
  820.  
  821.         return polygon;
  822.       };
  823.  
  824.       this.drawRectangle = function(options) {
  825.         options = extend_object({
  826.           map: this.map
  827.         }, options);
  828.  
  829.         var latLngBounds = new google.maps.LatLngBounds(
  830.           new google.maps.LatLng(options.bounds[0][0], options.bounds[0][1]),
  831.           new google.maps.LatLng(options.bounds[1][0], options.bounds[1][1])
  832.         );
  833.  
  834.         options.bounds = latLngBounds;
  835.  
  836.         var polygon = new google.maps.Rectangle(options);
  837.  
  838.         var polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
  839.  
  840.         for (var ev = 0; ev < polygon_events.length; ev++) {
  841.           (function(object, name) {
  842.             google.maps.event.addListener(object, name, function(e){
  843.               if (options[name])
  844.                 options[name].apply(this, [e]);
  845.             });
  846.           })(polygon, polygon_events[ev]);
  847.         }
  848.  
  849.         this.polygons.push(polygon);
  850.  
  851.         return polygon;
  852.       };
  853.  
  854.       this.drawPolygon = function(options) {
  855.         var useGeoJSON = false;
  856.         if(options.hasOwnProperty("useGeoJSON"))
  857.           useGeoJSON = options.useGeoJSON;
  858.  
  859.         delete options.useGeoJSON;
  860.  
  861.         options = extend_object({
  862.           map: this.map
  863.         }, options);
  864.  
  865.         if(useGeoJSON == false)
  866.           options.paths = [options.paths.slice(0)];
  867.  
  868.         if(options.paths.length > 0) {
  869.           if(options.paths[0].length > 0) {
  870.             options.paths = array_flat(array_map(options.paths, arrayToLatLng, useGeoJSON));
  871.           }
  872.         }
  873.  
  874.         var polygon = new google.maps.Polygon(options);
  875.  
  876.         var polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
  877.  
  878.         for (var ev = 0; ev < polygon_events.length; ev++) {
  879.           (function(object, name) {
  880.             google.maps.event.addListener(object, name, function(e){
  881.               if (options[name])
  882.                 options[name].apply(this, [e]);
  883.             });
  884.           })(polygon, polygon_events[ev]);
  885.         }
  886.  
  887.         this.polygons.push(polygon);
  888.  
  889.         GMaps.fire('polygon_added', polygon, this);
  890.  
  891.         return polygon;
  892.       };
  893.  
  894.       this.removePolygon = function(polygon) {
  895.         for(var i = 0; i < this.polygons.length; i++) {
  896.           if(this.polygons[i] === polygon) {
  897.             this.polygons[i].setMap(null);
  898.             this.polygons.splice(i, 1);
  899.  
  900.             GMaps.fire('polygon_removed', polygon, this);
  901.  
  902.             break;
  903.           }
  904.         }
  905.       };
  906.  
  907.       this.removePolygons = function() {
  908.         for (var i=0, item; item=self.polygons[i]; i++){
  909.           item.setMap(null);
  910.         }
  911.         self.polygons = [];
  912.       };
  913.  
  914.       // Fusion Tables
  915.  
  916.       this.getFromFusionTables = function(options) {
  917.         var events = options.events;
  918.  
  919.         delete options.events;
  920.  
  921.         var fusion_tables_options = options;
  922.  
  923.         var layer = new google.maps.FusionTablesLayer(fusion_tables_options);
  924.  
  925.         for (var ev in events) {
  926.           (function(object, name) {
  927.             google.maps.event.addListener(object, name, function(e){
  928.               events[name].apply(this, [e]);
  929.             });
  930.           })(layer, ev);
  931.         }
  932.  
  933.         this.layers.push(layer);
  934.  
  935.         return layer;
  936.       };
  937.  
  938.       this.loadFromFusionTables = function(options) {
  939.         var layer = this.getFromFusionTables(options);
  940.         layer.setMap(this.map);
  941.  
  942.         return layer;
  943.       };
  944.  
  945.       // KML
  946.  
  947.       this.getFromKML = function(options) {
  948.         var url = options.url;
  949.         var events = options.events;
  950.  
  951.         delete options.url;
  952.         delete options.events;
  953.  
  954.         var kml_options = options;
  955.  
  956.         var layer = new google.maps.KmlLayer(url, kml_options);
  957.  
  958.         for (var ev in events) {
  959.           (function(object, name) {
  960.             google.maps.event.addListener(object, name, function(e){
  961.               events[name].apply(this, [e]);
  962.             });
  963.           })(layer, ev);
  964.         }
  965.  
  966.         this.layers.push(layer);
  967.  
  968.         return layer;
  969.       };
  970.  
  971.       this.loadFromKML = function(options) {
  972.         var layer = this.getFromKML(options);
  973.         layer.setMap(this.map);
  974.  
  975.         return layer;
  976.       };
  977.  
  978.       // Routes
  979.  
  980.       var travelMode, unitSystem;
  981.       this.getRoutes = function(options) {
  982.         switch (options.travelMode) {
  983.         case 'bicycling':
  984.           travelMode = google.maps.TravelMode.BICYCLING;
  985.           break;
  986.         case 'transit':
  987.           travelMode = google.maps.TravelMode.TRANSIT;
  988.           break;
  989.         case 'driving':
  990.           travelMode = google.maps.TravelMode.DRIVING;
  991.           break;
  992.         // case 'walking':
  993.         default:
  994.           travelMode = google.maps.TravelMode.WALKING;
  995.           break;
  996.         }
  997.  
  998.         if (options.unitSystem === 'imperial') {
  999.           unitSystem = google.maps.UnitSystem.IMPERIAL;
  1000.         }
  1001.         else {
  1002.           unitSystem = google.maps.UnitSystem.METRIC;
  1003.         }
  1004.  
  1005.         var base_options = {
  1006.           avoidHighways: false,
  1007.           avoidTolls: false,
  1008.           optimizeWaypoints: false,
  1009.           waypoints: []
  1010.         };
  1011.  
  1012.         var request_options =  extend_object(base_options, options);
  1013.  
  1014.         request_options.origin = /string/.test(typeof options.origin) ? options.origin : new google.maps.LatLng(options.origin[0], options.origin[1]);
  1015.         request_options.destination = new google.maps.LatLng(options.destination[0], options.destination[1]);
  1016.         request_options.travelMode = travelMode;
  1017.         request_options.unitSystem = unitSystem;
  1018.  
  1019.         delete request_options.callback;
  1020.  
  1021.         var self = this;
  1022.         var service = new google.maps.DirectionsService();
  1023.  
  1024.         service.route(request_options, function(result, status) {
  1025.           if (status === google.maps.DirectionsStatus.OK) {
  1026.             for (var r in result.routes) {
  1027.               if (result.routes.hasOwnProperty(r)) {
  1028.                 self.routes.push(result.routes[r]);
  1029.               }
  1030.             }
  1031.           }
  1032.           if (options.callback) {
  1033.             options.callback(self.routes);
  1034.           }
  1035.         });
  1036.       };
  1037.  
  1038.       this.removeRoutes = function() {
  1039.         this.routes = [];
  1040.       };
  1041.  
  1042.       this.getElevations = function(options) {
  1043.         options = extend_object({
  1044.           locations: [],
  1045.           path : false,
  1046.           samples : 256
  1047.         }, options);
  1048.  
  1049.         if(options.locations.length > 0) {
  1050.           if(options.locations[0].length > 0) {
  1051.             options.locations = array_flat(array_map([options.locations], arrayToLatLng,  false));
  1052.           }
  1053.         }
  1054.  
  1055.         var callback = options.callback;
  1056.         delete options.callback;
  1057.  
  1058.         var service = new google.maps.ElevationService();
  1059.  
  1060.         //location request
  1061.         if (!options.path) {
  1062.           delete options.path;
  1063.           delete options.samples;
  1064.           service.getElevationForLocations(options, function(result, status){
  1065.             if (callback && typeof(callback) === "function") {
  1066.               callback(result, status);
  1067.             }
  1068.           });
  1069.         //path request
  1070.         } else {
  1071.           var pathRequest = {
  1072.             path : options.locations,
  1073.             samples : options.samples
  1074.           };
  1075.  
  1076.           service.getElevationAlongPath(pathRequest, function(result, status){
  1077.            if (callback && typeof(callback) === "function") {
  1078.               callback(result, status);
  1079.             }
  1080.           });
  1081.         }
  1082.       };
  1083.  
  1084.       // Alias for the method "drawRoute"
  1085.       this.cleanRoute = this.removePolylines;
  1086.  
  1087.       this.drawRoute = function(options) {
  1088.         var self = this;
  1089.         this.getRoutes({
  1090.           origin: options.origin,
  1091.           destination: options.destination,
  1092.           travelMode: options.travelMode,
  1093.           waypoints: options.waypoints,
  1094.           unitSystem: options.unitSystem,
  1095.           callback: function(e) {
  1096.             if (e.length > 0) {
  1097.               self.drawPolyline({
  1098.                 path: e[e.length - 1].overview_path,
  1099.                 strokeColor: options.strokeColor,
  1100.                 strokeOpacity: options.strokeOpacity,
  1101.                 strokeWeight: options.strokeWeight
  1102.               });
  1103.               if (options.callback) {
  1104.                 options.callback(e[e.length - 1]);
  1105.               }
  1106.             }
  1107.           }
  1108.         });
  1109.       };
  1110.  
  1111.       this.travelRoute = function(options) {
  1112.         if (options.origin && options.destination) {
  1113.           this.getRoutes({
  1114.             origin: options.origin,
  1115.             destination: options.destination,
  1116.             travelMode: options.travelMode,
  1117.             waypoints : options.waypoints,
  1118.             callback: function(e) {
  1119.               //start callback
  1120.               if (e.length > 0 && options.start) {
  1121.                 options.start(e[e.length - 1]);
  1122.               }
  1123.  
  1124.               //step callback
  1125.               if (e.length > 0 && options.step) {
  1126.                 var route = e[e.length - 1];
  1127.                 if (route.legs.length > 0) {
  1128.                   var steps = route.legs[0].steps;
  1129.                   for (var i=0, step; step=steps[i]; i++) {
  1130.                     step.step_number = i;
  1131.                     options.step(step, (route.legs[0].steps.length - 1));
  1132.                   }
  1133.                 }
  1134.               }
  1135.  
  1136.               //end callback
  1137.               if (e.length > 0 && options.end) {
  1138.                  options.end(e[e.length - 1]);
  1139.               }
  1140.             }
  1141.           });
  1142.         }
  1143.         else if (options.route) {
  1144.           if (options.route.legs.length > 0) {
  1145.             var steps = options.route.legs[0].steps;
  1146.             for (var i=0, step; step=steps[i]; i++) {
  1147.               step.step_number = i;
  1148.               options.step(step);
  1149.             }
  1150.           }
  1151.         }
  1152.       };
  1153.  
  1154.       this.drawSteppedRoute = function(options) {
  1155.         if (options.origin && options.destination) {
  1156.           this.getRoutes({
  1157.             origin: options.origin,
  1158.             destination: options.destination,
  1159.             travelMode: options.travelMode,
  1160.             waypoints : options.waypoints,
  1161.             callback: function(e) {
  1162.               //start callback
  1163.               if (e.length > 0 && options.start) {
  1164.                 options.start(e[e.length - 1]);
  1165.               }
  1166.  
  1167.               //step callback
  1168.               if (e.length > 0 && options.step) {
  1169.                 var route = e[e.length - 1];
  1170.                 if (route.legs.length > 0) {
  1171.                   var steps = route.legs[0].steps;
  1172.                   for (var i=0, step; step=steps[i]; i++) {
  1173.                     step.step_number = i;
  1174.                     self.drawPolyline({
  1175.                       path: step.path,
  1176.                       strokeColor: options.strokeColor,
  1177.                       strokeOpacity: options.strokeOpacity,
  1178.                       strokeWeight: options.strokeWeight
  1179.                     });
  1180.                     options.step(step, (route.legs[0].steps.length - 1));
  1181.                   }
  1182.                 }
  1183.               }
  1184.  
  1185.               //end callback
  1186.               if (e.length > 0 && options.end) {
  1187.                  options.end(e[e.length - 1]);
  1188.               }
  1189.             }
  1190.           });
  1191.         }
  1192.         else if (options.route) {
  1193.           if (options.route.legs.length > 0) {
  1194.             var steps = options.route.legs[0].steps;
  1195.             for (var i=0, step; step=steps[i]; i++) {
  1196.               step.step_number = i;
  1197.               self.drawPolyline({
  1198.                 path: step.path,
  1199.                 strokeColor: options.strokeColor,
  1200.                 strokeOpacity: options.strokeOpacity,
  1201.                 strokeWeight: options.strokeWeight
  1202.               });
  1203.               options.step(step);
  1204.             }
  1205.           }
  1206.         }
  1207.       };
  1208.  
  1209.       // Geofence
  1210.  
  1211.       this.checkGeofence = function(lat, lng, fence) {
  1212.         return fence.containsLatLng(new google.maps.LatLng(lat, lng));
  1213.       };
  1214.  
  1215.       this.checkMarkerGeofence = function(marker, outside_callback) {
  1216.         if (marker.fences) {
  1217.           for (var i=0, fence; fence=marker.fences[i]; i++) {
  1218.             var pos = marker.getPosition();
  1219.             if (!self.checkGeofence(pos.lat(), pos.lng(), fence)) {
  1220.               outside_callback(marker, fence);
  1221.             }
  1222.           }
  1223.         }
  1224.       };
  1225.  
  1226.       // Layers
  1227.  
  1228.       this.addLayer = function(layerName, options) {
  1229.         //var default_layers = ['weather', 'clouds', 'traffic', 'transit', 'bicycling', 'panoramio', 'places'];
  1230.         options = options || {};
  1231.         var layer;
  1232.  
  1233.         switch(layerName) {
  1234.           case 'weather': this.singleLayers.weather = layer = new google.maps.weather.WeatherLayer();
  1235.             break;
  1236.           case 'clouds': this.singleLayers.clouds = layer = new google.maps.weather.CloudLayer();
  1237.             break;
  1238.           case 'traffic': this.singleLayers.traffic = layer = new google.maps.TrafficLayer();
  1239.             break;
  1240.           case 'transit': this.singleLayers.transit = layer = new google.maps.TransitLayer();
  1241.             break;
  1242.           case 'bicycling': this.singleLayers.bicycling = layer = new google.maps.BicyclingLayer();
  1243.             break;
  1244.           case 'panoramio':
  1245.               this.singleLayers.panoramio = layer = new google.maps.panoramio.PanoramioLayer();
  1246.               layer.setTag(options.filter);
  1247.               delete options.filter;
  1248.  
  1249.               //click event
  1250.               if(options.click) {
  1251.                 google.maps.event.addListener(layer, 'click', function(event) {
  1252.                   options.click(event);
  1253.                   delete options.click;
  1254.                 });
  1255.               }
  1256.             break;
  1257.             case 'places':
  1258.               this.singleLayers.places = layer = new google.maps.places.PlacesService(this.map);
  1259.  
  1260.               //search and  nearbySearch callback, Both are the same
  1261.               if(options.search || options.nearbySearch) {
  1262.                 var placeSearchRequest  = {
  1263.                   bounds : options.bounds || null,
  1264.                   keyword : options.keyword || null,
  1265.                   location : options.location || null,
  1266.                   name : options.name || null,
  1267.                   radius : options.radius || null,
  1268.                   rankBy : options.rankBy || null,
  1269.                   types : options.types || null
  1270.                 };
  1271.  
  1272.                 if(options.search) {
  1273.                   layer.search(placeSearchRequest, options.search);
  1274.                 }
  1275.  
  1276.                 if(options.nearbySearch) {
  1277.                   layer.nearbySearch(placeSearchRequest, options.nearbySearch);
  1278.                 }
  1279.               }
  1280.  
  1281.               //textSearch callback
  1282.               if(options.textSearch) {
  1283.                 var textSearchRequest  = {
  1284.                   bounds : options.bounds || null,
  1285.                   location : options.location || null,
  1286.                   query : options.query || null,
  1287.                   radius : options.radius || null
  1288.                 };
  1289.  
  1290.                 layer.textSearch(textSearchRequest, options.textSearch);
  1291.               }
  1292.             break;
  1293.         }
  1294.  
  1295.         if(layer !== undefined) {
  1296.           if(typeof layer.setOptions == 'function') {
  1297.             layer.setOptions(options);
  1298.           }
  1299.           if(typeof layer.setMap == 'function') {
  1300.             layer.setMap(this.map);
  1301.           }
  1302.  
  1303.           return layer;
  1304.         }
  1305.       };
  1306.  
  1307.       this.removeLayer = function(layerName) {
  1308.         if(this.singleLayers[layerName] !== undefined) {
  1309.            this.singleLayers[layerName].setMap(null);
  1310.            delete this.singleLayers[layerName];
  1311.         }
  1312.       };
  1313.  
  1314.       // Static Maps
  1315.  
  1316.       this.toImage = function(options) {
  1317.         var options = options || {};
  1318.         var static_map_options = {};
  1319.         static_map_options['size'] = options['size'] || [this.el.clientWidth, this.el.clientHeight];
  1320.         static_map_options['lat'] = this.getCenter().lat();
  1321.         static_map_options['lng'] = this.getCenter().lng();
  1322.  
  1323.         if(this.markers.length > 0) {
  1324.           static_map_options['markers'] = [];
  1325.           for(var i=0; i < this.markers.length; i++) {
  1326.             static_map_options['markers'].push({
  1327.               lat: this.markers[i].getPosition().lat(),
  1328.               lng: this.markers[i].getPosition().lng()
  1329.             });
  1330.           }
  1331.         }
  1332.  
  1333.         if(this.polylines.length > 0) {
  1334.           var polyline = this.polylines[0];
  1335.           static_map_options['polyline'] = {};
  1336.           static_map_options['polyline']['path'] = google.maps.geometry.encoding.encodePath(polyline.getPath());
  1337.           static_map_options['polyline']['strokeColor'] = polyline.strokeColor
  1338.           static_map_options['polyline']['strokeOpacity'] = polyline.strokeOpacity
  1339.           static_map_options['polyline']['strokeWeight'] = polyline.strokeWeight
  1340.         }
  1341.  
  1342.         return GMaps.staticMapURL(static_map_options);
  1343.       };
  1344.  
  1345.       // Map Types
  1346.  
  1347.       this.addMapType = function(mapTypeId, options) {
  1348.         if(options.hasOwnProperty("getTileUrl") && typeof(options["getTileUrl"]) == "function") {
  1349.           options.tileSize = options.tileSize || new google.maps.Size(256, 256);
  1350.  
  1351.           var mapType = new google.maps.ImageMapType(options);
  1352.  
  1353.           this.map.mapTypes.set(mapTypeId, mapType);
  1354.         }
  1355.         else {
  1356.           throw "'getTileUrl' function required.";
  1357.         }
  1358.       };
  1359.  
  1360.       this.addOverlayMapType = function(options) {
  1361.         if(options.hasOwnProperty("getTile") && typeof(options["getTile"]) == "function") {
  1362.           var overlayMapTypeIndex = options.index;
  1363.  
  1364.           delete options.index;
  1365.  
  1366.           this.map.overlayMapTypes.insertAt(overlayMapTypeIndex, options);
  1367.         }
  1368.         else {
  1369.           throw "'getTile' function required.";
  1370.         }
  1371.       };
  1372.  
  1373.       this.removeOverlayMapType = function(overlayMapTypeIndex) {
  1374.         this.map.overlayMapTypes.removeAt(overlayMapTypeIndex);
  1375.       };
  1376.  
  1377.       // Styles
  1378.  
  1379.       this.addStyle = function(options) {
  1380.         var styledMapType = new google.maps.StyledMapType(options.styles, options.styledMapName);
  1381.  
  1382.         this.map.mapTypes.set(options.mapTypeId, styledMapType);
  1383.       };
  1384.  
  1385.       this.setStyle = function(mapTypeId) {
  1386.         this.map.setMapTypeId(mapTypeId);
  1387.       };
  1388.  
  1389.       // StreetView
  1390.  
  1391.       this.createPanorama = function(streetview_options) {
  1392.         if (!streetview_options.hasOwnProperty('lat') || !streetview_options.hasOwnProperty('lng')) {
  1393.           streetview_options.lat = this.getCenter().lat();
  1394.           streetview_options.lng = this.getCenter().lng();
  1395.         }
  1396.  
  1397.         this.panorama = GMaps.createPanorama(streetview_options);
  1398.  
  1399.         this.map.setStreetView(this.panorama);
  1400.  
  1401.         return this.panorama;
  1402.       };
  1403.  
  1404.       // Events
  1405.  
  1406.       this.on = function(event_name, handler) {
  1407.         return GMaps.on(event_name, this, handler);
  1408.       };
  1409.  
  1410.       this.off = function(event_name) {
  1411.         GMaps.off(event_name, this);
  1412.       };
  1413.     };
  1414.  
  1415.     GMaps.createPanorama = function(options) {
  1416.       var el = getElementById(options.el, options.context);
  1417.  
  1418.       options.position = new google.maps.LatLng(options.lat, options.lng);
  1419.  
  1420.       delete options.el;
  1421.       delete options.context;
  1422.       delete options.lat;
  1423.       delete options.lng;
  1424.  
  1425.       var streetview_events = ['closeclick', 'links_changed', 'pano_changed', 'position_changed', 'pov_changed', 'resize', 'visible_changed'];
  1426.  
  1427.       var streetview_options = extend_object({visible : true}, options);
  1428.  
  1429.       for(var i = 0; i < streetview_events.length; i++) {
  1430.         delete streetview_options[streetview_events[i]];
  1431.       }
  1432.  
  1433.       var panorama = new google.maps.StreetViewPanorama(el, streetview_options);
  1434.  
  1435.       for(var i = 0; i < streetview_events.length; i++) {
  1436.         (function(object, name) {
  1437.           if (options[name]) {
  1438.             google.maps.event.addListener(object, name, function(){
  1439.               options[name].apply(this);
  1440.             });
  1441.           }
  1442.         })(panorama, streetview_events[i]);
  1443.       }
  1444.  
  1445.       return panorama;
  1446.     };
  1447.  
  1448.     GMaps.Route = function(options) {
  1449.       this.map = options.map;
  1450.       this.route = options.route;
  1451.       this.step_count = 0;
  1452.       this.steps = this.route.legs[0].steps;
  1453.       this.steps_length = this.steps.length;
  1454.  
  1455.       this.polyline = this.map.drawPolyline({
  1456.         path: new google.maps.MVCArray(),
  1457.         strokeColor: options.strokeColor,
  1458.         strokeOpacity: options.strokeOpacity,
  1459.         strokeWeight: options.strokeWeight
  1460.       }).getPath();
  1461.  
  1462.       this.back = function() {
  1463.         if (this.step_count > 0) {
  1464.           this.step_count--;
  1465.           var path = this.route.legs[0].steps[this.step_count].path;
  1466.           for (var p in path){
  1467.             if (path.hasOwnProperty(p)){
  1468.               this.polyline.pop();
  1469.             }
  1470.           }
  1471.         }
  1472.       };
  1473.  
  1474.       this.forward = function() {
  1475.         if (this.step_count < this.steps_length) {
  1476.           var path = this.route.legs[0].steps[this.step_count].path;
  1477.           for (var p in path){
  1478.             if (path.hasOwnProperty(p)){
  1479.               this.polyline.push(path[p]);
  1480.             }
  1481.           }
  1482.           this.step_count++;
  1483.         }
  1484.       };
  1485.     };
  1486.  
  1487.     // Geolocation (Modern browsers only)
  1488.  
  1489.     GMaps.geolocate = function(options) {
  1490.       var complete_callback = options.always || options.complete;
  1491.  
  1492.       if (navigator.geolocation) {
  1493.         navigator.geolocation.getCurrentPosition(function(position) {
  1494.           options.success(position);
  1495.  
  1496.           if (complete_callback) {
  1497.             complete_callback();
  1498.           }
  1499.         }, function(error) {
  1500.           options.error(error);
  1501.  
  1502.           if (complete_callback) {
  1503.             complete_callback();
  1504.           }
  1505.         }, options.options);
  1506.       }
  1507.       else {
  1508.         options.not_supported();
  1509.  
  1510.         if (complete_callback) {
  1511.           complete_callback();
  1512.         }
  1513.       }
  1514.     };
  1515.  
  1516.     // Geocoding
  1517.  
  1518.     GMaps.geocode = function(options) {
  1519.       this.geocoder = new google.maps.Geocoder();
  1520.       var callback = options.callback;
  1521.       if (options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) {
  1522.         options.latLng = new google.maps.LatLng(options.lat, options.lng);
  1523.       }
  1524.  
  1525.       delete options.lat;
  1526.       delete options.lng;
  1527.       delete options.callback;
  1528.       this.geocoder.geocode(options, function(results, status) {
  1529.         callback(results, status);
  1530.       });
  1531.     };
  1532.  
  1533.     // Static maps
  1534.  
  1535.     GMaps.staticMapURL = function(options){
  1536.       var parameters = [];
  1537.       var data;
  1538.  
  1539.       var static_root = 'http://maps.googleapis.com/maps/api/staticmap';
  1540.       if (options.url){
  1541.         static_root = options.url;
  1542.         delete options.url;
  1543.       }
  1544.       static_root += '?';
  1545.  
  1546.       var markers = options.markers;
  1547.       delete options.markers;
  1548.       if (!markers && options.marker){
  1549.         markers = [options.marker];
  1550.         delete options.marker;
  1551.       }
  1552.  
  1553.       var polyline = options.polyline;
  1554.       delete options.polyline;
  1555.  
  1556.       /** Map options **/
  1557.       if (options.center){
  1558.         parameters.push('center=' + options.center);
  1559.         delete options.center;
  1560.       }
  1561.       else if (options.address){
  1562.         parameters.push('center=' + options.address);
  1563.         delete options.address;
  1564.       }
  1565.       else if (options.lat){
  1566.         parameters.push(['center=', options.lat, ',', options.lng].join(''));
  1567.         delete options.lat;
  1568.         delete options.lng;
  1569.       }
  1570.       else if (options.visible){
  1571.         var visible = encodeURI(options.visible.join('|'));
  1572.         parameters.push('visible=' + visible);
  1573.       }
  1574.  
  1575.       var size = options.size;
  1576.       if (size){
  1577.         if (size.join){
  1578.           size = size.join('x');
  1579.         }
  1580.         delete options.size;
  1581.       }
  1582.       else {
  1583.         size = '630x300';
  1584.       }
  1585.       parameters.push('size=' + size);
  1586.  
  1587.       if (!options.zoom){
  1588.         options.zoom = 15;
  1589.       }
  1590.  
  1591.       var sensor = options.hasOwnProperty('sensor') ? !!options.sensor : true;
  1592.       delete options.sensor;
  1593.       parameters.push('sensor=' + sensor);
  1594.  
  1595.       for (var param in options){
  1596.         if (options.hasOwnProperty(param)){
  1597.           parameters.push(param + '=' + options[param]);
  1598.         }
  1599.       }
  1600.  
  1601.       /** Markers **/
  1602.       if (markers){
  1603.         var marker, loc;
  1604.  
  1605.         for (var i=0; data=markers[i]; i++){
  1606.           marker = [];
  1607.  
  1608.           if (data.size && data.size !== 'normal'){
  1609.             marker.push('size:' + data.size);
  1610.           }
  1611.           else if (data.icon){
  1612.             marker.push('icon:' + encodeURI(data.icon));
  1613.           }
  1614.  
  1615.           if (data.color){
  1616.             marker.push('color:' + data.color.replace('#', '0x'));
  1617.           }
  1618.  
  1619.           if (data.label){
  1620.             marker.push('label:' + data.label[0].toUpperCase());
  1621.           }
  1622.  
  1623.           loc = (data.address ? data.address : data.lat + ',' + data.lng);
  1624.  
  1625.           if (marker.length || i === 0){
  1626.             marker.push(loc);
  1627.             marker = marker.join('|');
  1628.             parameters.push('markers=' + encodeURI(marker));
  1629.           }
  1630.           // New marker without styles
  1631.           else {
  1632.             marker = parameters.pop() + encodeURI('|' + loc);
  1633.             parameters.push(marker);
  1634.           }
  1635.         }
  1636.       }
  1637.  
  1638.       /** Polylines **/
  1639.       function parseColor(color, opacity){
  1640.         if (color[0] === '#'){
  1641.           color = color.replace('#', '0x');
  1642.  
  1643.           if (opacity){
  1644.             opacity = parseFloat(opacity);
  1645.             opacity = Math.min(1, Math.max(opacity, 0));
  1646.             if (opacity === 0){
  1647.               return '0x00000000';
  1648.             }
  1649.             opacity = (opacity * 255).toString(16);
  1650.             if (opacity.length === 1){
  1651.               opacity += opacity;
  1652.             }
  1653.  
  1654.             color = color.slice(0,8) + opacity;
  1655.           }
  1656.         }
  1657.         return color;
  1658.       }
  1659.  
  1660.       if (polyline){
  1661.         data = polyline;
  1662.         polyline = [];
  1663.  
  1664.         if (data.strokeWeight){
  1665.           polyline.push('weight:' + parseInt(data.strokeWeight, 10));
  1666.         }
  1667.  
  1668.         if (data.strokeColor){
  1669.           var color = parseColor(data.strokeColor, data.strokeOpacity);
  1670.           polyline.push('color:' + color);
  1671.         }
  1672.  
  1673.         if (data.fillColor){
  1674.           var fillcolor = parseColor(data.fillColor, data.fillOpacity);
  1675.           polyline.push('fillcolor:' + fillcolor);
  1676.         }
  1677.  
  1678.         var path = data.path;
  1679.         if (path.join){
  1680.           for (var j=0, pos; pos=path[j]; j++){
  1681.             polyline.push(pos.join(','));
  1682.           }
  1683.         }
  1684.         else {
  1685.           polyline.push('enc:' + path);
  1686.         }
  1687.  
  1688.         polyline = polyline.join('|');
  1689.         parameters.push('path=' + encodeURI(polyline));
  1690.       }
  1691.  
  1692.       parameters = parameters.join('&');
  1693.       return static_root + parameters;
  1694.     };
  1695.  
  1696.     // Events
  1697.  
  1698.     GMaps.custom_events = ['marker_added', 'marker_removed', 'polyline_added', 'polyline_removed', 'polygon_added', 'polygon_removed', 'geolocated', 'geolocation_failed'];
  1699.  
  1700.     GMaps.on = function(event_name, object, handler) {
  1701.       if (GMaps.custom_events.indexOf(event_name) == -1) {
  1702.         return google.maps.event.addListener(object, event_name, handler);
  1703.       }
  1704.       else {
  1705.         var registered_event = {
  1706.           handler : handler,
  1707.           eventName : event_name
  1708.         };
  1709.  
  1710.         object.registered_events[event_name] = object.registered_events[event_name] || [];
  1711.         object.registered_events[event_name].push(registered_event);
  1712.  
  1713.         return registered_event;
  1714.       }
  1715.     };
  1716.  
  1717.     GMaps.off = function(event_name, object) {
  1718.       if (GMaps.custom_events.indexOf(event_name) == -1) {
  1719.         google.maps.event.clearListeners(object, event_name);
  1720.       }
  1721.       else {
  1722.         object.registered_events[event_name] = [];
  1723.       }
  1724.     };
  1725.  
  1726.     GMaps.fire = function(event_name, object, scope) {
  1727.       if (GMaps.custom_events.indexOf(event_name) == -1) {
  1728.         google.maps.event.trigger(object, event_name, Array.prototype.slice.apply(arguments).slice(2));
  1729.       }
  1730.       else {
  1731.         if(event_name in scope.registered_events) {
  1732.           var firing_events = scope.registered_events[event_name];
  1733.  
  1734.           for(var i = 0; i < firing_events.length; i++) {
  1735.             (function(handler, scope, object) {
  1736.               handler.apply(scope, [object]);
  1737.             })(firing_events[i]['handler'], scope, object);
  1738.           }
  1739.         }
  1740.       }
  1741.     };
  1742.  
  1743.     //==========================
  1744.     // Polygon containsLatLng
  1745.     // https://github.com/tparkin/Google-Maps-Point-in-Polygon
  1746.     // Poygon getBounds extension - google-maps-extensions
  1747.     // http://code.google.com/p/google-maps-extensions/source/browse/google.maps.Polygon.getBounds.js
  1748.     if (!google.maps.Polygon.prototype.getBounds) {
  1749.       google.maps.Polygon.prototype.getBounds = function(latLng) {
  1750.         var bounds = new google.maps.LatLngBounds();
  1751.         var paths = this.getPaths();
  1752.         var path;
  1753.  
  1754.         for (var p = 0; p < paths.getLength(); p++) {
  1755.           path = paths.getAt(p);
  1756.           for (var i = 0; i < path.getLength(); i++) {
  1757.             bounds.extend(path.getAt(i));
  1758.           }
  1759.         }
  1760.  
  1761.         return bounds;
  1762.       };
  1763.     }
  1764.  
  1765.     if (!google.maps.Polygon.prototype.containsLatLng) {
  1766.       // Polygon containsLatLng - method to determine if a latLng is within a polygon
  1767.       google.maps.Polygon.prototype.containsLatLng = function(latLng) {
  1768.         // Exclude points outside of bounds as there is no way they are in the poly
  1769.         var bounds = this.getBounds();
  1770.  
  1771.         if (bounds !== null && !bounds.contains(latLng)) {
  1772.           return false;
  1773.         }
  1774.  
  1775.         // Raycast point in polygon method
  1776.         var inPoly = false;
  1777.  
  1778.         var numPaths = this.getPaths().getLength();
  1779.         for (var p = 0; p < numPaths; p++) {
  1780.           var path = this.getPaths().getAt(p);
  1781.           var numPoints = path.getLength();
  1782.           var j = numPoints - 1;
  1783.  
  1784.           for (var i = 0; i < numPoints; i++) {
  1785.             var vertex1 = path.getAt(i);
  1786.             var vertex2 = path.getAt(j);
  1787.  
  1788.             if (vertex1.lng() < latLng.lng() && vertex2.lng() >= latLng.lng() || vertex2.lng() < latLng.lng() && vertex1.lng() >= latLng.lng()) {
  1789.               if (vertex1.lat() + (latLng.lng() - vertex1.lng()) / (vertex2.lng() - vertex1.lng()) * (vertex2.lat() - vertex1.lat()) < latLng.lat()) {
  1790.                 inPoly = !inPoly;
  1791.               }
  1792.             }
  1793.  
  1794.             j = i;
  1795.           }
  1796.         }
  1797.  
  1798.         return inPoly;
  1799.       };
  1800.     }
  1801.  
  1802.     google.maps.LatLngBounds.prototype.containsLatLng = function(latLng) {
  1803.       return this.contains(latLng);
  1804.     };
  1805.  
  1806.     google.maps.Marker.prototype.setFences = function(fences) {
  1807.       this.fences = fences;
  1808.     };
  1809.  
  1810.     google.maps.Marker.prototype.addFence = function(fence) {
  1811.       this.fences.push(fence);
  1812.     };
  1813.  
  1814.     return GMaps;
  1815.   }(this));
  1816.  
  1817.   var coordsToLatLngs = function(coords, useGeoJSON) {
  1818.     var first_coord = coords[0];
  1819.     var second_coord = coords[1];
  1820.  
  1821.     if(useGeoJSON) {
  1822.       first_coord = coords[1];
  1823.       second_coord = coords[0];
  1824.     }
  1825.  
  1826.     return new google.maps.LatLng(first_coord, second_coord);
  1827.   };
  1828.  
  1829.   var arrayToLatLng = function(coords, useGeoJSON) {
  1830.     for(var i=0; i < coords.length; i++) {
  1831.       if(coords[i].length > 0 && typeof(coords[i][0]) != "number") {
  1832.         coords[i] = arrayToLatLng(coords[i], useGeoJSON);
  1833.       }
  1834.       else {
  1835.         coords[i] = coordsToLatLngs(coords[i], useGeoJSON);
  1836.       }
  1837.     }
  1838.  
  1839.     return coords;
  1840.   };
  1841.  
  1842.   var extend_object = function(obj, new_obj) {
  1843.     if(obj === new_obj) return obj;
  1844.  
  1845.     for(var name in new_obj) {
  1846.       obj[name] = new_obj[name];
  1847.     }
  1848.  
  1849.     return obj;
  1850.   };
  1851.  
  1852.   var replace_object = function(obj, replace) {
  1853.     if(obj === replace) return obj;
  1854.  
  1855.     for(var name in replace) {
  1856.       if(obj[name] != undefined)
  1857.         obj[name] = replace[name];
  1858.     }
  1859.  
  1860.     return obj;
  1861.   };
  1862.  
  1863.   var array_map = function(array, callback) {
  1864.     var original_callback_params = Array.prototype.slice.call(arguments, 2);
  1865.  
  1866.     if (Array.prototype.map && array.map === Array.prototype.map) {
  1867.       return Array.prototype.map.call(array, function(item) {
  1868.         callback_params = original_callback_params;
  1869.         callback_params.splice(0, 0, item);
  1870.  
  1871.         return callback.apply(this, callback_params);
  1872.       });
  1873.     }
  1874.     else {
  1875.       var array_return = [];
  1876.       var array_length = array.length;
  1877.  
  1878.       for(var i = 0; i < array_length; i++) {
  1879.         callback_params = original_callback_params;
  1880.         callback_params = callback_params.splice(0, 0, array[i]);
  1881.         array_return.push(callback.apply(this, callback_params));
  1882.       }
  1883.  
  1884.       return array_return;
  1885.     }
  1886.   };
  1887.  
  1888.   var array_flat = function(array) {
  1889.     new_array = [];
  1890.  
  1891.     for(var i=0; i < array.length; i++) {
  1892.       new_array = new_array.concat(array[i]);
  1893.     }
  1894.  
  1895.     return new_array;
  1896.   };
  1897. }
  1898.  
  1899. else {
  1900.   throw 'Google Maps API is required. Please register the following JavaScript library http://maps.google.com/maps/api/js?sensor=true.'
  1901. }

Raw Paste


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