commit d68f7bbdefa5503cee44b8f0c7d513a00a35be2c
Author: Jan Kohlíček <kohlicekjan@gmail.com>
Date:   Wed May 9 19:38:27 2018 +0200

    refs #6754: Zobrazení detektorů na mapě, na vybranou lokalitu se zoomuje, kliknutím na marker se zobrazí info

diff --git a/frontend/app.js b/frontend/app.js
index baa4bbc..e6aad9b 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -4,10 +4,12 @@ app.constant('config', {
     APP_NAME: 'PVPK',
     APP_VERSION: 1.0,
     API_URL: API_URL,
-    API_TOKEN: API_TOKEN
+    API_TOKEN: API_TOKEN,
+    DEFAULT_POSITION: {LAT: 49.53, LNG: 13.3},
+    DEFAULT_ZOOM: 10
 });
 
-
+//PRIPRAVA PRO REFAKTORING
 // app.config(function($stateProvider, $locationProvider) {
 //     // $stateProvider
 //     // .state('report',{
@@ -29,10 +31,6 @@ app.constant('config', {
 //    $locationProvider.html5Mode(true);
 // });
 
-app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
-
-}]);
-
 
 app.controller('mainController', function ($rootScope, $scope, $http, $window) {
 
@@ -73,7 +71,11 @@ app.controller('searchController', function ($rootScope, $scope, $location, conf
         var toTime = new Date();
         toTime.setHours(16, 0, 0, 0);
 
-        var toDate = new Date();
+
+        var toDate = new Date(new Date().getTime() - (1 * 24 * 60 * 60 * 1000));
+
+        //DODELAT OMEZENI
+        $scope.maxDate = toDate;
         var fromDate = new Date(toDate.getTime() - (30 * 24 * 60 * 60 * 1000));
 
         var params = $location.search();
@@ -88,129 +90,221 @@ app.controller('searchController', function ($rootScope, $scope, $location, conf
         };
 
         $scope.locations = [];
-        $scope.showLocationsLoading = false;
+        $scope.showSearchLoading = false;
 
-        if (params.location!=null && params.location.length > 2) {
+        if (params.location != null && params.location.length > 2) {
             $scope.searchLocations(false);
         }
     };
 
 
     $scope.searchLocations = function (saveToUrl) {
-        $scope.showLocationsLoading = true;
+        if (!$scope.search.location || $scope.search.location.length <= 1) {
+            $scope.locations = [];
+            return;
+        }
+
+        $scope.showSearchLoading = true;
 
         if (saveToUrl)
             $location.search({
                 location: $scope.search.location,
-                fromDate: $scope.search.fromDate.getTime(),
-                toDate: $scope.search.toDate.getTime(),
-                fromTime: $scope.search.fromTime.getTime(),
-                toTime: $scope.search.toTime.getTime(),
+                // fromDate: $scope.search.fromDate.getTime(),
+                // toDate: $scope.search.toDate.getTime(),
+                // fromTime: $scope.search.fromTime.getTime(),
+                // toTime: $scope.search.toTime.getTime(),
                 direction: $scope.search.direction ? 1 : 0
             });
 
-        Device({jwt: config.API_TOKEN}).query({
+        Device.query({
             address: $scope.search.location,
             showDirection: $scope.search.direction ? 1 : 0
         }, function (data) {
             $scope.locations = data;
-            $scope.showLocationsLoading = false;
+            $scope.showSearchLoading = false;
 
             var params = $location.search();
-            if(!saveToUrl && jQuery.grep($scope.locations, function(e){ return e.id === params.deviceId; }).length>0){
+            if (!saveToUrl && jQuery.grep($scope.locations, function (e) {
+                return e.id === params.deviceId;
+            }).length > 0) {
                 $scope.selectDevice(params.deviceId);
             }
 
         }, function (response) {
-            $scope.showLocationsLoading = false;
+            $scope.showSearchLoading = false;
             console.log('Error api all Devices');
             $rootScope.handleErrorResponse(response);
         });
     };
 
     $scope.selectDevice = function (id) {
-        $scope.deviceId = id;
-
         var searchObject = $location.search();
         searchObject.deviceId = id;
         $location.search(searchObject);
 
-        Device({jwt: config.API_TOKEN}).get({
-            id: id
+        $rootScope.$emit('activeMarker', {id: id});
+        $rootScope.$emit('infoLocation', {id: id});
+    };
+
+});
+
+
+app.controller('infoController', function ($rootScope, $scope, config, Device, Vehicle) {
+
+    this.$onInit = function () {
+        $rootScope.selectDevice = null;
+        $scope.vehicles = [];
+        $scope.showInfoLoading = false;
+
+        Vehicle.query(null, function (data) {
+            $scope.vehicles = data;
+        }, function (response) {
+            $rootScope.graphShow = false;
+            console.log('Error api all Vehicles');
+            $rootScope.handleErrorResponse(response);
+        });
+    };
+
+    $rootScope.$on('infoLocation', function (event, args) {
+        $scope.showInfoLoading = true;
+
+        Device.get({
+            id: args.id
             // dateFrom: $scope.search.fromDate.getTime(),
             // dateTo: $scope.search.toDate.getTime(),
             // timeFrom: $scope.search.fromTime.getTime(),
             // timeTo: $scope.search.toTime.getTime(),
             // direction: $scope.search.direction ? 1 : 0
         }, function (data) {
-            $rootScope.$emit('renderGraph', data);
+            $rootScope.selectDevice = data;
+            $scope.showInfoLoading = false;
         }, function (response) {
+            $rootScope.selectDevice = null;
+            $scope.showInfoLoading = false;
             console.log('Error api get Devices');
             $rootScope.handleErrorResponse(response);
         });
-    };
 
+    });
+
+    $scope.infoClose = function () {
+        $rootScope.selectDevice = null;
+
+        $rootScope.$emit('setDefaultMap', null);
+    };
 });
 
 
-app.controller('graphController', function ($rootScope, $scope, config, Vehicle) {
+app.controller('mapController', function ($rootScope, $scope, config, Device) {
 
     this.$onInit = function () {
-        $rootScope.graphShow = false;
-        $scope.vehicles = [];
-    };
 
-    $rootScope.$on("renderGraph", function (event, args) {
-        $rootScope.graphShow = true;
-
-        Vehicle({jwt: config.API_TOKEN}).query(null, function (data) {
-            $scope.vehicles = data;
+        $scope.map = new GMaps({
+            div: '#map',
+            zoomControl: true,
+            mapTypeControl: false,
+            scaleControl: false,
+            streetViewControl: false,
+            rotateControl: false,
+            fullscreenControl: false,
+            mapTypeId: 'roadmap',
+            zoom: config.DEFAULT_ZOOM,
+            lat: config.DEFAULT_POSITION.LAT,
+            lng: config.DEFAULT_POSITION.LNG
+        });
 
+        Device.query({showDirection: 0}, function (data) {
+            $scope.createMarkerNext(data, 0);
         }, function (response) {
-            $rootScope.graphShow = false;
-            console.log('Error api all Vehicles');
+            console.log('Error api all Devices');
             $rootScope.handleErrorResponse(response);
         });
-    });
+    };
 
-});
+    $scope.createMarkerNext = function (data, i) {
+        var lctn = data[i];
+
+        GMaps.geocode({
+            address: lctn.street + ', ' + lctn.town + ', Plzeňský kraj',
+            callback: function (results, status) {
+                if (status === 'OK') {
+                    latlng = results[0].geometry.location;
+
+                    var marker = $scope.map.addMarker({
+                        lat: latlng.lat(),
+                        lng: latlng.lng(),
+                        title: lctn.name,
+                        label: 'U',
+                        click: function (e) {
+                            $rootScope.$emit('infoLocation', {id: lctn.id});
+                            //alert("asdfas");
+                        },
+                        infoWindow: {
+                            content: '<h6 class="mb-1">' + lctn.name + '</h6>'
+                            + '<address>' + lctn.street + ', ' + lctn.town + '</address>'
+                        },
+                        id: lctn.id
+                    });
+
+                } else if (status === 'ZERO_RESULTS') {
+                    console.log('No results found address');
+                }
+
+                i++;
+                if (i < data.length) {
+                    setTimeout(function () {
+                        $scope.createMarkerNext(data, i);
+                    }, 900);
+                }
+            }
+        });
 
-app.controller('mapController', function ($scope) {
+    };
 
-    this.$onInit = function () {
+    $rootScope.$on('activeMarker', function (event, args) {
+        var id = args.id;
+        for (var i = 0, marker; marker = $scope.map.markers[i]; i++) {
+            if (marker.id && marker.id === id && marker.infoWindow) {
+                $scope.map.setCenter(marker.position.lat(), marker.position.lng());
+                $scope.map.setZoom(12);
+                marker.infoWindow.open($scope.map, marker);
+                return;
+            }
+        }
+    });
 
-    };
+    $rootScope.$on('setDefaultMap', function (event, args) {
+        $scope.map.setCenter(config.DEFAULT_POSITION.LAT, config.DEFAULT_POSITION.LNG);
+        $scope.map.setZoom(config.DEFAULT_ZOOM);
+        $scope.map.hideInfoWindows();
+    });
 
 });
 
 
 app.factory("Device", function ($resource, config) {
-    return function (headers) {
-        return $resource(config.API_URL + "/devices/:id", {id: '@id'}, {
-            'get': {
-                url: config.API_URL + '/devices/:id',
-                method: 'GET',
-                headers: headers || {}
-            },
-            'query': {
-                url: config.API_URL + '/devices',
-                method: 'GET',
-                isArray: true,
-                headers: headers || {}
-            }
-        });
-    };
+    return $resource(config.API_URL + "/devices/:id", {id: '@id'}, {
+        'get': {
+            url: config.API_URL + '/devices/:id',
+            method: 'GET',
+            headers: {jwt: config.API_TOKEN}
+        },
+        'query': {
+            url: config.API_URL + '/devices',
+            method: 'GET',
+            isArray: true,
+            headers: {jwt: config.API_TOKEN}
+        }
+    });
 });
 
 app.factory("Vehicle", function ($resource, config) {
-    return function (headers) {
-        return $resource(config.API_URL + "/vehicles", null, {
-            'query': {
-                url: config.API_URL + '/vehicles',
-                method: 'GET',
-                isArray: true,
-                headers: headers || {}
-            }
-        });
-    };
+    return $resource(config.API_URL + "/vehicles", null, {
+        'query': {
+            url: config.API_URL + '/vehicles',
+            method: 'GET',
+            isArray: true,
+            headers: {jwt: config.API_TOKEN}
+        }
+    });
 });
\ No newline at end of file
diff --git a/frontend/assets/css/main.css b/frontend/assets/css/main.css
index 43ee447..9db2e1e 100644
--- a/frontend/assets/css/main.css
+++ b/frontend/assets/css/main.css
@@ -31,7 +31,7 @@ html, body {
     float: left;
 }
 
-#graph {
+#info {
     min-height: 100%;
     box-shadow: 0px 0px 10px 0px rgba(50, 50, 50, 0.75);
     z-index: 10;
diff --git a/frontend/assets/libs/gmaps.min.js b/frontend/assets/libs/gmaps.min.js
new file mode 100644
index 0000000..e73ed15
--- /dev/null
+++ b/frontend/assets/libs/gmaps.min.js
@@ -0,0 +1,2 @@
+"use strict";!function(a,b){"object"==typeof exports?module.exports=b():"function"==typeof define&&define.amd?define(["jquery","googlemaps!"],b):a.GMaps=b()}(this,function(){var a=function(a,b){var c;if(a===b)return a;for(c in b)void 0!==b[c]&&(a[c]=b[c]);return a},b=function(a,b){var c,d=Array.prototype.slice.call(arguments,2),e=[],f=a.length;if(Array.prototype.map&&a.map===Array.prototype.map)e=Array.prototype.map.call(a,function(a){var c=d.slice(0);return c.splice(0,0,a),b.apply(this,c)});else for(c=0;c<f;c++)callback_params=d,callback_params.splice(0,0,a[c]),e.push(b.apply(this,callback_params));return e},c=function(a){var b,c=[];for(b=0;b<a.length;b++)c=c.concat(a[b]);return c},d=function(a,b){var c=a[0],d=a[1];return b&&(c=a[1],d=a[0]),new google.maps.LatLng(c,d)},f=function(a,b){var c;for(c=0;c<a.length;c++)a[c]instanceof google.maps.LatLng||(a[c].length>0&&"object"==typeof a[c][0]?a[c]=f(a[c],b):a[c]=d(a[c],b));return a},g=function(a,b){var c=a.replace(".","");return"jQuery"in this&&b?$("."+c,b)[0]:document.getElementsByClassName(c)[0]},h=function(a,b){var a=a.replace("#","");return"jQuery"in window&&b?$("#"+a,b)[0]:document.getElementById(a)},i=function(a){var b=0,c=0;if(a.getBoundingClientRect){var d=a.getBoundingClientRect(),e=-(window.scrollX?window.scrollX:window.pageXOffset),f=-(window.scrollY?window.scrollY:window.pageYOffset);return[d.left-e,d.top-f]}if(a.offsetParent)do b+=a.offsetLeft,c+=a.offsetTop;while(a=a.offsetParent);return[b,c]},j=function(b){var c=document,d=function(b){if("object"!=typeof window.google||!window.google.maps)return"object"==typeof window.console&&window.console.error&&console.error("Google Maps API is required. Please register the following JavaScript library https://maps.googleapis.com/maps/api/js."),function(){};if(!this)return new d(b);b.zoom=b.zoom||15,b.mapType=b.mapType||"roadmap";var e,f=function(a,b){return void 0===a?b:a},j=this,k=["bounds_changed","center_changed","click","dblclick","drag","dragend","dragstart","idle","maptypeid_changed","projection_changed","resize","tilesloaded","zoom_changed"],l=["mousemove","mouseout","mouseover"],m=["el","lat","lng","mapType","width","height","markerClusterer","enableNewStyle"],n=b.el||b.div,o=b.markerClusterer,p=google.maps.MapTypeId[b.mapType.toUpperCase()],q=new google.maps.LatLng(b.lat,b.lng),r=f(b.zoomControl,!0),s=b.zoomControlOpt||{style:"DEFAULT",position:"TOP_LEFT"},t=s.style||"DEFAULT",u=s.position||"TOP_LEFT",v=f(b.panControl,!0),w=f(b.mapTypeControl,!0),x=f(b.scaleControl,!0),y=f(b.streetViewControl,!0),z=f(z,!0),A={},B={zoom:this.zoom,center:q,mapTypeId:p},C={panControl:v,zoomControl:r,zoomControlOptions:{style:google.maps.ZoomControlStyle[t],position:google.maps.ControlPosition[u]},mapTypeControl:w,scaleControl:x,streetViewControl:y,overviewMapControl:z};if("string"==typeof b.el||"string"==typeof b.div?n.indexOf("#")>-1?this.el=h(n,b.context):this.el=g.apply(this,[n,b.context]):this.el=n,void 0===this.el||null===this.el)throw"No element defined.";for(window.context_menu=window.context_menu||{},window.context_menu[j.el.id]={},this.controls=[],this.overlays=[],this.layers=[],this.singleLayers={},this.markers=[],this.polylines=[],this.routes=[],this.polygons=[],this.infoWindow=null,this.overlay_el=null,this.zoom=b.zoom,this.registered_events={},this.el.style.width=b.width||this.el.scrollWidth||this.el.offsetWidth,this.el.style.height=b.height||this.el.scrollHeight||this.el.offsetHeight,google.maps.visualRefresh=b.enableNewStyle,e=0;e<m.length;e++)delete b[m[e]];for(1!=b.disableDefaultUI&&(B=a(B,C)),A=a(B,b),e=0;e<k.length;e++)delete A[k[e]];for(e=0;e<l.length;e++)delete A[l[e]];this.map=new google.maps.Map(this.el,A),o&&(this.markerClusterer=o.apply(this,[this.map]));var D=function(a,b){var c="",d=window.context_menu[j.el.id][a];for(var e in d)if(d.hasOwnProperty(e)){var f=d[e];c+='<li><a id="'+a+"_"+e+'" href="#">'+f.title+"</a></li>"}if(h("gmaps_context_menu")){var g=h("gmaps_context_menu");g.innerHTML=c;var e,k=g.getElementsByTagName("a"),l=k.length;for(e=0;e<l;e++){var m=k[e],n=function(c){c.preventDefault(),d[this.id.replace(a+"_","")].action.apply(j,[b]),j.hideContextMenu()};google.maps.event.clearListeners(m,"click"),google.maps.event.addDomListenerOnce(m,"click",n,!1)}var o=i.apply(this,[j.el]),p=o[0]+b.pixel.x-15,q=o[1]+b.pixel.y-15;g.style.left=p+"px",g.style.top=q+"px"}};this.buildContextMenu=function(a,b){if("marker"===a){b.pixel={};var c=new google.maps.OverlayView;c.setMap(j.map),c.draw=function(){var d=c.getProjection(),e=b.marker.getPosition();b.pixel=d.fromLatLngToContainerPixel(e),D(a,b)}}else D(a,b);var d=h("gmaps_context_menu");setTimeout(function(){d.style.display="block"},0)},this.setContextMenu=function(a){window.context_menu[j.el.id][a.control]={};var b,d=c.createElement("ul");for(b in a.options)if(a.options.hasOwnProperty(b)){var e=a.options[b];window.context_menu[j.el.id][a.control][e.name]={title:e.title,action:e.action}}d.id="gmaps_context_menu",d.style.display="none",d.style.position="absolute",d.style.minWidth="100px",d.style.background="white",d.style.listStyle="none",d.style.padding="8px",d.style.boxShadow="2px 2px 6px #ccc",h("gmaps_context_menu")||c.body.appendChild(d);var f=h("gmaps_context_menu");google.maps.event.addDomListener(f,"mouseout",function(a){a.relatedTarget&&this.contains(a.relatedTarget)||window.setTimeout(function(){f.style.display="none"},400)},!1)},this.hideContextMenu=function(){var a=h("gmaps_context_menu");a&&(a.style.display="none")};var E=function(a,c){google.maps.event.addListener(a,c,function(a){void 0==a&&(a=this),b[c].apply(this,[a]),j.hideContextMenu()})};google.maps.event.addListener(this.map,"zoom_changed",this.hideContextMenu);for(var F=0;F<k.length;F++){var G=k[F];G in b&&E(this.map,G)}for(var F=0;F<l.length;F++){var G=l[F];G in b&&E(this.map,G)}google.maps.event.addListener(this.map,"rightclick",function(a){b.rightclick&&b.rightclick.apply(this,[a]),void 0!=window.context_menu[j.el.id].map&&j.buildContextMenu("map",a)}),this.refresh=function(){google.maps.event.trigger(this.map,"resize")},this.fitZoom=function(){var a,b=[],c=this.markers.length;for(a=0;a<c;a++)"boolean"==typeof this.markers[a].visible&&this.markers[a].visible&&b.push(this.markers[a].getPosition());this.fitLatLngBounds(b)},this.fitLatLngBounds=function(a){var b,c=a.length,d=new google.maps.LatLngBounds;for(b=0;b<c;b++)d.extend(a[b]);this.map.fitBounds(d)},this.setCenter=function(a,b,c){this.map.panTo(new google.maps.LatLng(a,b)),c&&c()},this.getElement=function(){return this.el},this.zoomIn=function(a){a=a||1,this.zoom=this.map.getZoom()+a,this.map.setZoom(this.zoom)},this.zoomOut=function(a){a=a||1,this.zoom=this.map.getZoom()-a,this.map.setZoom(this.zoom)};var H,I=[];for(H in this.map)"function"!=typeof this.map[H]||this[H]||I.push(H);for(e=0;e<I.length;e++)!function(a,b,c){a[c]=function(){return b[c].apply(b,arguments)}}(this,this.map,I[e])};return d}(this);j.prototype.createControl=function(a){var b=document.createElement("div");b.style.cursor="pointer",a.disableDefaultStyles!==!0&&(b.style.fontFamily="Roboto, Arial, sans-serif",b.style.fontSize="11px",b.style.boxShadow="rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px");for(var c in a.style)b.style[c]=a.style[c];a.id&&(b.id=a.id),a.title&&(b.title=a.title),a.classes&&(b.className=a.classes),a.content&&("string"==typeof a.content?b.innerHTML=a.content:a.content instanceof HTMLElement&&b.appendChild(a.content)),a.position&&(b.position=google.maps.ControlPosition[a.position.toUpperCase()]);for(var d in a.events)!function(b,c){google.maps.event.addDomListener(b,c,function(){a.events[c].apply(this,[this])})}(b,d);return b.index=1,b},j.prototype.addControl=function(a){var b=this.createControl(a);return this.controls.push(b),this.map.controls[b.position].push(b),b},j.prototype.removeControl=function(a){var b,c=null;for(b=0;b<this.controls.length;b++)this.controls[b]==a&&(c=this.controls[b].position,this.controls.splice(b,1));if(c)for(b=0;b<this.map.controls.length;b++){var d=this.map.controls[a.position];if(d.getAt(b)==a){d.removeAt(b);break}}return a},j.prototype.createMarker=function(b){if(void 0==b.lat&&void 0==b.lng&&void 0==b.position)throw"No latitude or longitude defined.";var c=this,d=b.details,e=b.fences,f=b.outside,g={position:new google.maps.LatLng(b.lat,b.lng),map:null},h=a(g,b);delete h.lat,delete h.lng,delete h.fences,delete h.outside;var i=new google.maps.Marker(h);if(i.fences=e,b.infoWindow){i.infoWindow=new google.maps.InfoWindow(b.infoWindow);for(var j=["closeclick","content_changed","domready","position_changed","zindex_changed"],k=0;k<j.length;k++)!function(a,c){b.infoWindow[c]&&google.maps.event.addListener(a,c,function(a){b.infoWindow[c].apply(this,[a])})}(i.infoWindow,j[k])}for(var l=["animation_changed","clickable_changed","cursor_changed","draggable_changed","flat_changed","icon_changed","position_changed","shadow_changed","shape_changed","title_changed","visible_changed","zindex_changed"],m=["dblclick","drag","dragend","dragstart","mousedown","mouseout","mouseover","mouseup"],k=0;k<l.length;k++)!function(a,c){b[c]&&google.maps.event.addListener(a,c,function(){b[c].apply(this,[this])})}(i,l[k]);for(var k=0;k<m.length;k++)!function(a,c,d){b[d]&&google.maps.event.addListener(c,d,function(c){c.pixel||(c.pixel=a.getProjection().fromLatLngToPoint(c.latLng)),b[d].apply(this,[c])})}(this.map,i,m[k]);return google.maps.event.addListener(i,"click",function(){this.details=d,b.click&&b.click.apply(this,[this]),i.infoWindow&&(c.hideInfoWindows(),i.infoWindow.open(c.map,i))}),google.maps.event.addListener(i,"rightclick",function(a){a.marker=this,b.rightclick&&b.rightclick.apply(this,[a]),void 0!=window.context_menu[c.el.id].marker&&c.buildContextMenu("marker",a)}),i.fences&&google.maps.event.addListener(i,"dragend",function(){c.checkMarkerGeofence(i,function(a,b){f(a,b)})}),i},j.prototype.addMarker=function(a){var b;if(a.hasOwnProperty("gm_accessors_"))b=a;else{if(!(a.hasOwnProperty("lat")&&a.hasOwnProperty("lng")||a.position))throw"No latitude or longitude defined.";b=this.createMarker(a)}return b.setMap(this.map),this.markerClusterer&&this.markerClusterer.addMarker(b),this.markers.push(b),j.fire("marker_added",b,this),b},j.prototype.addMarkers=function(a){for(var b,c=0;b=a[c];c++)this.addMarker(b);return this.markers},j.prototype.hideInfoWindows=function(){for(var a,b=0;a=this.markers[b];b++)a.infoWindow&&a.infoWindow.close()},j.prototype.removeMarker=function(a){for(var b=0;b<this.markers.length;b++)if(this.markers[b]===a){this.markers[b].setMap(null),this.markers.splice(b,1),this.markerClusterer&&this.markerClusterer.removeMarker(a),j.fire("marker_removed",a,this);break}return a},j.prototype.removeMarkers=function(a){var b=[];if(void 0===a){for(var c=0;c<this.markers.length;c++){var d=this.markers[c];d.setMap(null),j.fire("marker_removed",d,this)}this.markerClusterer&&this.markerClusterer.clearMarkers&&this.markerClusterer.clearMarkers(),this.markers=b}else{for(var c=0;c<a.length;c++){var e=this.markers.indexOf(a[c]);if(e>-1){var d=this.markers[e];d.setMap(null),this.markerClusterer&&this.markerClusterer.removeMarker(d),j.fire("marker_removed",d,this)}}for(var c=0;c<this.markers.length;c++){var d=this.markers[c];null!=d.getMap()&&b.push(d)}this.markers=b}},j.prototype.drawOverlay=function(a){var b=new google.maps.OverlayView,c=!0;return b.setMap(this.map),null!=a.auto_show&&(c=a.auto_show),b.onAdd=function(){var c=document.createElement("div");c.style.borderStyle="none",c.style.borderWidth="0px",c.style.position="absolute",c.style.zIndex=100,c.innerHTML=a.content,b.el=c,a.layer||(a.layer="overlayLayer");var d=this.getPanes(),e=d[a.layer],f=["contextmenu","DOMMouseScroll","dblclick","mousedown"];e.appendChild(c);for(var g=0;g<f.length;g++)!function(a,b){google.maps.event.addDomListener(a,b,function(a){navigator.userAgent.toLowerCase().indexOf("msie")!=-1&&document.all?(a.cancelBubble=!0,a.returnValue=!1):a.stopPropagation()})}(c,f[g]);a.click&&(d.overlayMouseTarget.appendChild(b.el),google.maps.event.addDomListener(b.el,"click",function(){a.click.apply(b,[b])})),google.maps.event.trigger(this,"ready")},b.draw=function(){var d=this.getProjection(),e=d.fromLatLngToDivPixel(new google.maps.LatLng(a.lat,a.lng));a.horizontalOffset=a.horizontalOffset||0,a.verticalOffset=a.verticalOffset||0;var f=b.el,g=f.children[0],h=g.clientHeight,i=g.clientWidth;switch(a.verticalAlign){case"top":f.style.top=e.y-h+a.verticalOffset+"px";break;default:case"middle":f.style.top=e.y-h/2+a.verticalOffset+"px";break;case"bottom":f.style.top=e.y+a.verticalOffset+"px"}switch(a.horizontalAlign){case"left":f.style.left=e.x-i+a.horizontalOffset+"px";break;default:case"center":f.style.left=e.x-i/2+a.horizontalOffset+"px";break;case"right":f.style.left=e.x+a.horizontalOffset+"px"}f.style.display=c?"block":"none",c||a.show.apply(this,[f])},b.onRemove=function(){var c=b.el;a.remove?a.remove.apply(this,[c]):(b.el.parentNode.removeChild(b.el),b.el=null)},this.overlays.push(b),b},j.prototype.removeOverlay=function(a){for(var b=0;b<this.overlays.length;b++)if(this.overlays[b]===a){this.overlays[b].setMap(null),this.overlays.splice(b,1);break}},j.prototype.removeOverlays=function(){for(var a,b=0;a=this.overlays[b];b++)a.setMap(null);this.overlays=[]},j.prototype.drawPolyline=function(a){var b=[],c=a.path;if(c.length)if(void 0===c[0][0])b=c;else for(var d,e=0;d=c[e];e++)b.push(new google.maps.LatLng(d[0],d[1]));var f={map:this.map,path:b,strokeColor:a.strokeColor,strokeOpacity:a.strokeOpacity,strokeWeight:a.strokeWeight,geodesic:a.geodesic,clickable:!0,editable:!1,visible:!0};a.hasOwnProperty("clickable")&&(f.clickable=a.clickable),a.hasOwnProperty("editable")&&(f.editable=a.editable),a.hasOwnProperty("icons")&&(f.icons=a.icons),a.hasOwnProperty("zIndex")&&(f.zIndex=a.zIndex);for(var g=new google.maps.Polyline(f),h=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","rightclick"],i=0;i<h.length;i++)!function(b,c){a[c]&&google.maps.event.addListener(b,c,function(b){a[c].apply(this,[b])})}(g,h[i]);return this.polylines.push(g),j.fire("polyline_added",g,this),g},j.prototype.removePolyline=function(a){for(var b=0;b<this.polylines.length;b++)if(this.polylines[b]===a){this.polylines[b].setMap(null),this.polylines.splice(b,1),j.fire("polyline_removed",a,this);break}},j.prototype.removePolylines=function(){for(var a,b=0;a=this.polylines[b];b++)a.setMap(null);this.polylines=[]},j.prototype.drawCircle=function(b){b=a({map:this.map,center:new google.maps.LatLng(b.lat,b.lng)},b),delete b.lat,delete b.lng;for(var c=new google.maps.Circle(b),d=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","rightclick"],e=0;e<d.length;e++)!function(a,c){b[c]&&google.maps.event.addListener(a,c,function(a){b[c].apply(this,[a])})}(c,d[e]);return this.polygons.push(c),c},j.prototype.drawRectangle=function(b){b=a({map:this.map},b);var c=new google.maps.LatLngBounds(new google.maps.LatLng(b.bounds[0][0],b.bounds[0][1]),new google.maps.LatLng(b.bounds[1][0],b.bounds[1][1]));b.bounds=c;for(var d=new google.maps.Rectangle(b),e=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","rightclick"],f=0;f<e.length;f++)!function(a,c){b[c]&&google.maps.event.addListener(a,c,function(a){b[c].apply(this,[a])})}(d,e[f]);return this.polygons.push(d),d},j.prototype.drawPolygon=function(d){var e=!1;d.hasOwnProperty("useGeoJSON")&&(e=d.useGeoJSON),delete d.useGeoJSON,d=a({map:this.map},d),0==e&&(d.paths=[d.paths.slice(0)]),d.paths.length>0&&d.paths[0].length>0&&(d.paths=c(b(d.paths,f,e)));for(var g=new google.maps.Polygon(d),h=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","rightclick"],i=0;i<h.length;i++)!function(a,b){d[b]&&google.maps.event.addListener(a,b,function(a){d[b].apply(this,[a])})}(g,h[i]);return this.polygons.push(g),j.fire("polygon_added",g,this),g},j.prototype.removePolygon=function(a){for(var b=0;b<this.polygons.length;b++)if(this.polygons[b]===a){this.polygons[b].setMap(null),this.polygons.splice(b,1),j.fire("polygon_removed",a,this);break}},j.prototype.removePolygons=function(){for(var a,b=0;a=this.polygons[b];b++)a.setMap(null);this.polygons=[]},j.prototype.getFromFusionTables=function(a){var b=a.events;delete a.events;var c=a,d=new google.maps.FusionTablesLayer(c);for(var e in b)!function(a,c){google.maps.event.addListener(a,c,function(a){b[c].apply(this,[a])})}(d,e);return this.layers.push(d),d},j.prototype.loadFromFusionTables=function(a){var b=this.getFromFusionTables(a);return b.setMap(this.map),b},j.prototype.getFromKML=function(a){var b=a.url,c=a.events;delete a.url,delete a.events;var d=a,e=new google.maps.KmlLayer(b,d);for(var f in c)!function(a,b){google.maps.event.addListener(a,b,function(a){c[b].apply(this,[a])})}(e,f);return this.layers.push(e),e},j.prototype.loadFromKML=function(a){var b=this.getFromKML(a);return b.setMap(this.map),b},j.prototype.addLayer=function(a,b){b=b||{};var c;switch(a){case"weather":this.singleLayers.weather=c=new google.maps.weather.WeatherLayer;break;case"clouds":this.singleLayers.clouds=c=new google.maps.weather.CloudLayer;break;case"traffic":this.singleLayers.traffic=c=new google.maps.TrafficLayer;break;case"transit":this.singleLayers.transit=c=new google.maps.TransitLayer;break;case"bicycling":this.singleLayers.bicycling=c=new google.maps.BicyclingLayer;break;case"panoramio":this.singleLayers.panoramio=c=new google.maps.panoramio.PanoramioLayer,c.setTag(b.filter),delete b.filter,b.click&&google.maps.event.addListener(c,"click",function(a){b.click(a),delete b.click});break;case"places":if(this.singleLayers.places=c=new google.maps.places.PlacesService(this.map),b.search||b.nearbySearch||b.radarSearch){var d={bounds:b.bounds||null,keyword:b.keyword||null,location:b.location||null,name:b.name||null,radius:b.radius||null,rankBy:b.rankBy||null,types:b.types||null};b.radarSearch&&c.radarSearch(d,b.radarSearch),b.search&&c.search(d,b.search),b.nearbySearch&&c.nearbySearch(d,b.nearbySearch)}if(b.textSearch){var e={bounds:b.bounds||null,location:b.location||null,query:b.query||null,radius:b.radius||null};c.textSearch(e,b.textSearch)}}if(void 0!==c)return"function"==typeof c.setOptions&&c.setOptions(b),"function"==typeof c.setMap&&c.setMap(this.map),c},j.prototype.removeLayer=function(a){if("string"==typeof a&&void 0!==this.singleLayers[a])this.singleLayers[a].setMap(null),delete this.singleLayers[a];else for(var b=0;b<this.layers.length;b++)if(this.layers[b]===a){this.layers[b].setMap(null),this.layers.splice(b,1);break}};var k,l;return j.prototype.getRoutes=function(b){switch(b.travelMode){case"bicycling":k=google.maps.TravelMode.BICYCLING;break;case"transit":k=google.maps.TravelMode.TRANSIT;break;case"driving":k=google.maps.TravelMode.DRIVING;break;default:k=google.maps.TravelMode.WALKING}l="imperial"===b.unitSystem?google.maps.UnitSystem.IMPERIAL:google.maps.UnitSystem.METRIC;var c={avoidHighways:!1,avoidTolls:!1,optimizeWaypoints:!1,waypoints:[]},d=a(c,b);d.origin=/string/.test(typeof b.origin)?b.origin:new google.maps.LatLng(b.origin[0],b.origin[1]),d.destination=/string/.test(typeof b.destination)?b.destination:new google.maps.LatLng(b.destination[0],b.destination[1]),d.travelMode=k,d.unitSystem=l,delete d.callback,delete d.error;var e=[];(new google.maps.DirectionsService).route(d,function(a,c){if(c===google.maps.DirectionsStatus.OK){for(var d in a.routes)a.routes.hasOwnProperty(d)&&e.push(a.routes[d]);b.callback&&b.callback(e,a,c)}else b.error&&b.error(a,c)})},j.prototype.removeRoutes=function(){this.routes.length=0},j.prototype.getElevations=function(d){d=a({locations:[],path:!1,samples:256},d),d.locations.length>0&&d.locations[0].length>0&&(d.locations=c(b([d.locations],f,!1)));var e=d.callback;delete d.callback;var g=new google.maps.ElevationService;if(d.path){var h={path:d.locations,samples:d.samples};g.getElevationAlongPath(h,function(a,b){e&&"function"==typeof e&&e(a,b)})}else delete d.path,delete d.samples,g.getElevationForLocations(d,function(a,b){e&&"function"==typeof e&&e(a,b)})},j.prototype.cleanRoute=j.prototype.removePolylines,j.prototype.renderRoute=function(b,c){var d,e="string"==typeof c.panel?document.getElementById(c.panel.replace("#","")):c.panel;c.panel=e,c=a({map:this.map},c),d=new google.maps.DirectionsRenderer(c),this.getRoutes({origin:b.origin,destination:b.destination,travelMode:b.travelMode,waypoints:b.waypoints,unitSystem:b.unitSystem,error:b.error,avoidHighways:b.avoidHighways,avoidTolls:b.avoidTolls,optimizeWaypoints:b.optimizeWaypoints,callback:function(a,b,c){c===google.maps.DirectionsStatus.OK&&d.setDirections(b)}})},j.prototype.drawRoute=function(a){var b=this;this.getRoutes({origin:a.origin,destination:a.destination,travelMode:a.travelMode,waypoints:a.waypoints,unitSystem:a.unitSystem,error:a.error,avoidHighways:a.avoidHighways,avoidTolls:a.avoidTolls,optimizeWaypoints:a.optimizeWaypoints,callback:function(c){if(c.length>0){var d={path:c[c.length-1].overview_path,strokeColor:a.strokeColor,strokeOpacity:a.strokeOpacity,strokeWeight:a.strokeWeight};a.hasOwnProperty("icons")&&(d.icons=a.icons),b.drawPolyline(d),a.callback&&a.callback(c[c.length-1])}}})},j.prototype.travelRoute=function(a){if(a.origin&&a.destination)this.getRoutes({origin:a.origin,destination:a.destination,travelMode:a.travelMode,waypoints:a.waypoints,unitSystem:a.unitSystem,error:a.error,callback:function(b){if(b.length>0&&a.start&&a.start(b[b.length-1]),b.length>0&&a.step){var c=b[b.length-1];if(c.legs.length>0)for(var d,e=c.legs[0].steps,f=0;d=e[f];f++)d.step_number=f,a.step(d,c.legs[0].steps.length-1)}b.length>0&&a.end&&a.end(b[b.length-1])}});else if(a.route&&a.route.legs.length>0)for(var b,c=a.route.legs[0].steps,d=0;b=c[d];d++)b.step_number=d,a.step(b)},j.prototype.drawSteppedRoute=function(a){var b=this;if(a.origin&&a.destination)this.getRoutes({origin:a.origin,destination:a.destination,travelMode:a.travelMode,waypoints:a.waypoints,error:a.error,callback:function(c){if(c.length>0&&a.start&&a.start(c[c.length-1]),c.length>0&&a.step){var d=c[c.length-1];if(d.legs.length>0)for(var e,f=d.legs[0].steps,g=0;e=f[g];g++){e.step_number=g;var h={path:e.path,strokeColor:a.strokeColor,strokeOpacity:a.strokeOpacity,strokeWeight:a.strokeWeight};a.hasOwnProperty("icons")&&(h.icons=a.icons),b.drawPolyline(h),a.step(e,d.legs[0].steps.length-1)}}c.length>0&&a.end&&a.end(c[c.length-1])}});else if(a.route&&a.route.legs.length>0)for(var c,d=a.route.legs[0].steps,e=0;c=d[e];e++){c.step_number=e;var f={path:c.path,strokeColor:a.strokeColor,strokeOpacity:a.strokeOpacity,strokeWeight:a.strokeWeight};a.hasOwnProperty("icons")&&(f.icons=a.icons),b.drawPolyline(f),a.step(c)}},j.Route=function(a){this.origin=a.origin,this.destination=a.destination,this.waypoints=a.waypoints,this.map=a.map,this.route=a.route,this.step_count=0,this.steps=this.route.legs[0].steps,this.steps_length=this.steps.length;var b={path:new google.maps.MVCArray,strokeColor:a.strokeColor,strokeOpacity:a.strokeOpacity,strokeWeight:a.strokeWeight};a.hasOwnProperty("icons")&&(b.icons=a.icons),this.polyline=this.map.drawPolyline(b).getPath()},j.Route.prototype.getRoute=function(a){var b=this;this.map.getRoutes({origin:this.origin,destination:this.destination,travelMode:a.travelMode,waypoints:this.waypoints||[],error:a.error,callback:function(){b.route=e[0],a.callback&&a.callback.call(b)}})},j.Route.prototype.back=function(){if(this.step_count>0){this.step_count--;var a=this.route.legs[0].steps[this.step_count].path;for(var b in a)a.hasOwnProperty(b)&&this.polyline.pop()}},j.Route.prototype.forward=function(){if(this.step_count<this.steps_length){var a=this.route.legs[0].steps[this.step_count].path;for(var b in a)a.hasOwnProperty(b)&&this.polyline.push(a[b]);this.step_count++}},j.prototype.checkGeofence=function(a,b,c){return c.containsLatLng(new google.maps.LatLng(a,b))},j.prototype.checkMarkerGeofence=function(a,b){if(a.fences)for(var c,d=0;c=a.fences[d];d++){var e=a.getPosition();this.checkGeofence(e.lat(),e.lng(),c)||b(a,c)}},j.prototype.toImage=function(a){var a=a||{},b={};if(b.size=a.size||[this.el.clientWidth,this.el.clientHeight],b.lat=this.getCenter().lat(),b.lng=this.getCenter().lng(),this.markers.length>0){b.markers=[];for(var c=0;c<this.markers.length;c++)b.markers.push({lat:this.markers[c].getPosition().lat(),lng:this.markers[c].getPosition().lng()})}if(this.polylines.length>0){var d=this.polylines[0];b.polyline={},b.polyline.path=google.maps.geometry.encoding.encodePath(d.getPath()),b.polyline.strokeColor=d.strokeColor,b.polyline.strokeOpacity=d.strokeOpacity,b.polyline.strokeWeight=d.strokeWeight}return j.staticMapURL(b)},j.staticMapURL=function(a){function b(a,b){if("#"===a[0]&&(a=a.replace("#","0x"),b)){if(b=parseFloat(b),0===(b=Math.min(1,Math.max(b,0))))return"0x00000000";b=(255*b).toString(16),1===b.length&&(b+=b),a=a.slice(0,8)+b}return a}var c,d=[],e=("file:"===location.protocol?"http:":location.protocol)+"//maps.googleapis.com/maps/api/staticmap";a.url&&(e=a.url,delete a.url),e+="?";var f=a.markers;delete a.markers,!f&&a.marker&&(f=[a.marker],delete a.marker);var g=a.styles;delete a.styles;var h=a.polyline;if(delete a.polyline,a.center)d.push("center="+a.center),delete a.center;else if(a.address)d.push("center="+a.address),delete a.address;else if(a.lat)d.push(["center=",a.lat,",",a.lng].join("")),delete a.lat,delete a.lng;else if(a.visible){var i=encodeURI(a.visible.join("|"));d.push("visible="+i)}var j=a.size;j?(j.join&&(j=j.join("x")),delete a.size):j="630x300",d.push("size="+j),a.zoom||a.zoom===!1||(a.zoom=15);var k=!a.hasOwnProperty("sensor")||!!a.sensor;delete a.sensor,d.push("sensor="+k);for(var l in a)a.hasOwnProperty(l)&&d.push(l+"="+a[l]);if(f)for(var m,n,o=0;c=f[o];o++){m=[],c.size&&"normal"!==c.size?(m.push("size:"+c.size),delete c.size):c.icon&&(m.push("icon:"+encodeURI(c.icon)),delete c.icon),c.color&&(m.push("color:"+c.color.replace("#","0x")),delete c.color),c.label&&(m.push("label:"+c.label[0].toUpperCase()),delete c.label),n=c.address?c.address:c.lat+","+c.lng,delete c.address,delete c.lat,delete c.lng;for(var l in c)c.hasOwnProperty(l)&&m.push(l+":"+c[l]);m.length||0===o?(m.push(n),m=m.join("|"),d.push("markers="+encodeURI(m))):(m=d.pop()+encodeURI("|"+n),d.push(m))}if(g)for(var o=0;o<g.length;o++){var p=[];g[o].featureType&&p.push("feature:"+g[o].featureType.toLowerCase()),g[o].elementType&&p.push("element:"+g[o].elementType.toLowerCase());for(var q=0;q<g[o].stylers.length;q++)for(var r in g[o].stylers[q]){var s=g[o].stylers[q][r];"hue"!=r&&"color"!=r||(s="0x"+s.substring(1)),p.push(r+":"+s)}var t=p.join("|");""!=t&&d.push("style="+t)}if(h){if(c=h,h=[],c.strokeWeight&&h.push("weight:"+parseInt(c.strokeWeight,10)),c.strokeColor){var u=b(c.strokeColor,c.strokeOpacity);h.push("color:"+u)}if(c.fillColor){var v=b(c.fillColor,c.fillOpacity);h.push("fillcolor:"+v)}var w=c.path;if(w.join)for(var x,q=0;x=w[q];q++)h.push(x.join(","));else h.push("enc:"+w);h=h.join("|"),d.push("path="+encodeURI(h))}var y=window.devicePixelRatio||1;return d.push("scale="+y),d=d.join("&"),e+d},j.prototype.addMapType=function(a,b){if(!b.hasOwnProperty("getTileUrl")||"function"!=typeof b.getTileUrl)throw"'getTileUrl' function required.";b.tileSize=b.tileSize||new google.maps.Size(256,256);var c=new google.maps.ImageMapType(b);this.map.mapTypes.set(a,c)},j.prototype.addOverlayMapType=function(a){if(!a.hasOwnProperty("getTile")||"function"!=typeof a.getTile)throw"'getTile' function required.";var b=a.index;delete a.index,this.map.overlayMapTypes.insertAt(b,a)},j.prototype.removeOverlayMapType=function(a){this.map.overlayMapTypes.removeAt(a)},j.prototype.addStyle=function(a){var b=new google.maps.StyledMapType(a.styles,{name:a.styledMapName});this.map.mapTypes.set(a.mapTypeId,b)},j.prototype.setStyle=function(a){this.map.setMapTypeId(a)},j.prototype.createPanorama=function(a){return a.hasOwnProperty("lat")&&a.hasOwnProperty("lng")||(a.lat=this.getCenter().lat(),a.lng=this.getCenter().lng()),this.panorama=j.createPanorama(a),this.map.setStreetView(this.panorama),this.panorama},j.createPanorama=function(b){var c=h(b.el,b.context);b.position=new google.maps.LatLng(b.lat,b.lng),delete b.el,delete b.context,delete b.lat,delete b.lng;for(var d=["closeclick","links_changed","pano_changed","position_changed","pov_changed","resize","visible_changed"],e=a({visible:!0},b),f=0;f<d.length;f++)delete e[d[f]];for(var g=new google.maps.StreetViewPanorama(c,e),f=0;f<d.length;f++)!function(a,c){b[c]&&google.maps.event.addListener(a,c,function(){b[c].apply(this)})}(g,d[f]);return g},j.prototype.on=function(a,b){return j.on(a,this,b)},j.prototype.off=function(a){j.off(a,this)},j.prototype.once=function(a,b){return j.once(a,this,b)},j.custom_events=["marker_added","marker_removed","polyline_added","polyline_removed","polygon_added","polygon_removed","geolocated","geolocation_failed"],j.on=function(a,b,c){if(j.custom_events.indexOf(a)==-1)return b instanceof j&&(b=b.map),google.maps.event.addListener(b,a,c);var d={handler:c,eventName:a};return b.registered_events[a]=b.registered_events[a]||[],b.registered_events[a].push(d),d},j.off=function(a,b){j.custom_events.indexOf(a)==-1?(b instanceof j&&(b=b.map),google.maps.event.clearListeners(b,a)):b.registered_events[a]=[]},j.once=function(a,b,c){if(j.custom_events.indexOf(a)==-1)return b instanceof j&&(b=b.map),google.maps.event.addListenerOnce(b,a,c)},j.fire=function(a,b,c){if(j.custom_events.indexOf(a)==-1)google.maps.event.trigger(b,a,Array.prototype.slice.apply(arguments).slice(2));else if(a in c.registered_events)for(var d=c.registered_events[a],e=0;e<d.length;e++)!function(a,b,c){a.apply(b,[c])}(d[e].handler,c,b)},j.geolocate=function(a){var b=a.always||a.complete;navigator.geolocation?navigator.geolocation.getCurrentPosition(function(c){a.success(c),b&&b()},function(c){a.error(c),b&&b()},a.options):(a.not_supported(),b&&b())},j.geocode=function(a){this.geocoder=new google.maps.Geocoder;var b=a.callback;a.hasOwnProperty("lat")&&a.hasOwnProperty("lng")&&(a.latLng=new google.maps.LatLng(a.lat,a.lng)),delete a.lat,delete a.lng,delete a.callback,this.geocoder.geocode(a,function(a,c){b(a,c)})},"object"==typeof window.google&&window.google.maps&&(google.maps.Polygon.prototype.getBounds||(google.maps.Polygon.prototype.getBounds=function(a){for(var b,c=new google.maps.LatLngBounds,d=this.getPaths(),e=0;e<d.getLength();e++){b=d.getAt(e);for(var f=0;f<b.getLength();f++)c.extend(b.getAt(f))}return c}),google.maps.Polygon.prototype.containsLatLng||(google.maps.Polygon.prototype.containsLatLng=function(a){var b=this.getBounds();if(null!==b&&!b.contains(a))return!1;for(var c=!1,d=this.getPaths().getLength(),e=0;e<d;e++)for(var f=this.getPaths().getAt(e),g=f.getLength(),h=g-1,i=0;i<g;i++){var j=f.getAt(i),k=f.getAt(h);(j.lng()<a.lng()&&k.lng()>=a.lng()||k.lng()<a.lng()&&j.lng()>=a.lng())&&j.lat()+(a.lng()-j.lng())/(k.lng()-j.lng())*(k.lat()-j.lat())<a.lat()&&(c=!c),h=i}return c}),google.maps.Circle.prototype.containsLatLng||(google.maps.Circle.prototype.containsLatLng=function(a){return!google.maps.geometry||google.maps.geometry.spherical.computeDistanceBetween(this.getCenter(),a)<=this.getRadius()}),google.maps.Rectangle.prototype.containsLatLng=function(a){return this.getBounds().contains(a)},google.maps.LatLngBounds.prototype.containsLatLng=function(a){return this.contains(a)},google.maps.Marker.prototype.setFences=function(a){this.fences=a},google.maps.Marker.prototype.addFence=function(a){this.fences.push(a)},google.maps.Marker.prototype.getId=function(){return this.__gm_id}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){if(null==this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=0;if(arguments.length>1&&(d=Number(arguments[1]),d!=d?d=0:0!=d&&d!=1/0&&d!=-(1/0)&&(d=(d>0||-1)*Math.floor(Math.abs(d)))),d>=c)return-1;for(var e=d>=0?d:Math.max(c-Math.abs(d),0);e<c;e++)if(e in b&&b[e]===a)return e;return-1}),j});
+//# sourceMappingURL=gmaps.min.js.map
\ No newline at end of file
diff --git a/frontend/index.php b/frontend/index.php
index 5f2981b..d032d1c 100644
--- a/frontend/index.php
+++ b/frontend/index.php
@@ -27,7 +27,7 @@
     <script>
         <?php
         /* JEN PRO TESTOVANI, POZDEJI SE ODSTRANIT, A NAHRADIT NASDILENOU KNIHOVNOU */
-        $base_url = 'http://localhost/pvpk/backend/public/api/v1';
+        $base_url = './../backend/public/api/v1';
 
         include_once '../backend/lib/generateToken.php';
         $token = generateToken();
@@ -44,14 +44,14 @@
 <body ng-controller="mainController" class="container-fluid">
 
 
-
-
 <div id="loadingScreen" ng-show="showLoadingScreen">
     <div class="loading"></div>
 </div>
 <div class="row h-100">
 
-    <section class="search col-12 col-sm-6 col-lg-3" id="search" ng-controller="searchController">
+    <!--SEARCH section-->
+    <section class="search col-12 col-sm-6 col-lg-3" id="search" ng-controller="searchController"
+             ng-class="{ 'col-sm-12': $root.selectDevice==null, 'col-sm-6': $root.selectDevice!=null }">
 
         <div class="w-100 searchWrapper">
             <header class="mt-2">
@@ -67,7 +67,7 @@
                     <input type="search" id="searchLocation" name="location"
                            class="form-control form-control-sm" placeholder="Město, ulice, ..."
                            ng-model="search.location" required maxlength="255" autocomplete="off"
-                           ng-change="search.location.length>2 && searchLocations(true)">
+                           ng-change="searchLocations(true)">
                 </div>
 
                 <div class="custom-control custom-checkbox mb-3">
@@ -82,7 +82,8 @@
                         <label for="searchFromDate">Období</label>
                         <input type="date" id="searchFromDate" name="fromDate"
                                class="form-control form-control-sm" ng-model="search.fromDate" required
-                               ng-class="{ 'is-invalid': search.fromDate>search.toDate}">
+                               ng-class="{ 'is-invalid': search.fromDate>search.toDate}"
+                               max="{{maxDate | date:'yyyy-MM-dd'}}">
                         <div class="invalid-feedback">
                             Tento datum musí být menší.
                         </div>
@@ -92,7 +93,8 @@
                         <label for="searchToDate" class="invisible">Období</label>
                         <input type="date" id="searchToDate" name="toDateTime"
                                class="form-control form-control-sm" ng-model="search.toDate" required
-                               ng-class="{ 'is-invalid': search.fromDate>search.toDate}">
+                               ng-class="{ 'is-invalid': search.fromDate>search.toDate}"
+                               max="{{maxDate | date:'yyyy-MM-dd'}}">
                         <div class="invalid-feedback">
                             Tento datum musí být vetší.
                         </div>
@@ -120,22 +122,19 @@
                         </div>
                     </div>
                 </div>
-
-                <!--<input type="submit" value="Vyhledat" class="btn btn-primary btn-block"-->
-                <!--ng-disabled="search.fromDate>search.toDate || search.fromTime>search.toTime">-->
             </form>
 
 
             <div class="result-locations mb-5 mt-5">
                 <h5>Lokality</h5>
 
-                <div class="list-group" ng-show="locations.length>0 && !showLocationsLoading">
+                <div class="list-group" ng-show="locations.length>0 && !showSearchLoading">
                     <!-- class = active -->
                     <a href="" id="location-{{location.id}}"
                        class="list-group-item list-group-item-action flex-column align-items-start"
                        ng-repeat="location in locations"
                        ng-click="selectDevice(location.id)"
-                       ng-class="{'active': deviceId == location.id}">
+                       ng-class="{'active': $root.selectDevice.id == location.id}">
 
                         <div class="d-flex w-100 justify-content-between">
                             <h6 class="mb-1">{{location.name}}</h6>
@@ -148,11 +147,11 @@
                     </a>
                 </div>
 
-                <div ng-show="locations.length==0 && !showLocationsLoading">
+                <div ng-show="locations.length==0 && !showSearchLoading">
                     <small class="form-text text-muted text-center">Žádná lokalita</small>
                 </div>
 
-                <div class="loading" ng-show="showLocationsLoading"></div>
+                <div class="loading" ng-show="showSearchLoading"></div>
 
             </div>
 
@@ -163,83 +162,47 @@
     </section>
 
 
-    <!--graph section-->
-    <section class="graph col-12 col-sm-6 col-lg-3" id="graph" ng-show="$root.graphShow"
-             ng-controller="graphController">
+    <!--INFO section-->
+    <section class="info col-12 col-sm-6 col-lg-3" id="info" ng-show="$root.selectDevice!=null"
+             ng-controller="infoController">
 
         <header class="mt-2">
 
-            <h4>Grafy
-                <button type="button" class="close" aria-label="Close" ng-click="$root.graphShow = !$root.graphShow">
+            <h5>{{$root.selectDevice.name}}
+                <button type="button" class="close" aria-label="Close" ng-click="infoClose()">
                     <span aria-hidden="true">&times;</span>
                 </button>
-            </h4>
+            </h5>
+            <small>
+                <address>{{$root.selectDevice.street}}, {{$root.selectDevice.town}}</address>
+            </small>
         </header>
 
-        <form>
-            <div class="form-group">
-                <label for="searchVehicle">Vozidla</label>
-                <select id="searchVehicle" class="custom-select custom-select-sm" ng-model="search.vehicle">
-                    <option value="">Všechna vozidla</option>
-                    <option ng-repeat="vehicle in vehicles" value="{{vehicle.id}}">{{vehicle.name}}</option>
-                </select>
-            </div>
+        <div class="loading" ng-show="showInfoLoading"></div>
 
-        </form>
-
-        <div class="loading"></div>
-
-        <!--<canvas id="myChart" width="100%" height="60"></canvas>-->
-        <!--<script>-->
-        <!--var ctx = document.getElementById("myChart").getContext('2d');-->
-        <!--var myChart = new Chart(ctx, {-->
-        <!--type: 'bar',-->
-        <!--data: {-->
-        <!--labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],-->
-        <!--datasets: [{-->
-        <!--label: '# of Votes',-->
-        <!--data: [12, 19, 3, 5, 2, 3],-->
-        <!--backgroundColor: [-->
-        <!--'rgba(255, 99, 132, 0.2)',-->
-        <!--'rgba(54, 162, 235, 0.2)',-->
-        <!--'rgba(255, 206, 86, 0.2)',-->
-        <!--'rgba(75, 192, 192, 0.2)',-->
-        <!--'rgba(153, 102, 255, 0.2)',-->
-        <!--'rgba(255, 159, 64, 0.2)'-->
-        <!--],-->
-        <!--borderColor: [-->
-        <!--'rgba(255,99,132,1)',-->
-        <!--'rgba(54, 162, 235, 1)',-->
-        <!--'rgba(255, 206, 86, 1)',-->
-        <!--'rgba(75, 192, 192, 1)',-->
-        <!--'rgba(153, 102, 255, 1)',-->
-        <!--'rgba(255, 159, 64, 1)'-->
-        <!--],-->
-        <!--borderWidth: 1-->
-        <!--}]-->
-        <!--},-->
-        <!--options: {-->
-        <!--scales: {-->
-        <!--yAxes: [{-->
-        <!--ticks: {-->
-        <!--beginAtZero: true-->
-        <!--}-->
-        <!--}]-->
-        <!--}-->
-        <!--}-->
-        <!--});-->
-        <!--</script>-->
 
-    </section>
+        <h3 class="mt-5">Graf #1</h3>
+
+        <h3 class="mt-5">Graf #2</h3>
+        <div class="form-group">
+            <label for="searchVehicle">Vozidla</label>
+            <select id="searchVehicle" class="custom-select custom-select-sm" ng-model="search.vehicle">
+                <option value="">Všechna vozidla</option>
+                <option ng-repeat="vehicle in vehicles" value="{{vehicle.id}}">{{vehicle.name}}</option>
+            </select>
+        </div>
 
+        <h3 class="mt-5">Graf #3</h3>
 
-    <!--map section-->
-    <!-- ng-class="textType" -->
+
+    </section>
+
+    <!--MAP section-->
     <section class="map col-12 col-sm-12" id="map"
-             ng-class="{ 'col-lg-9': !$root.graphShow, 'col-lg-6': $root.graphShow }" ng-controller="mapController">
+             ng-class="{ 'col-lg-9': $root.selectDevice==null, 'col-lg-6': $root.selectDevice!=null }"
+             ng-controller="mapController">
+
 
-        <iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d1326226.1771813703!2d11.996870042985256!3d49.51688547407959!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x470abf4bb3db6569%3A0x100af0f6614a850!2zUGx6ZcWIc2vDvSBrcmFq!5e0!3m2!1scs!2scz!4v1523814169200"
-                width="100%" height="100%" frameborder="0" style="border:0" allowfullscreen></iframe>
     </section>
 
 
@@ -270,6 +233,12 @@
         integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm"
         crossorigin="anonymous"></script>
 
+<!--async defer-->
+<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCSx7hyAzQiG5uocJTeZgf1Z3lpDy4kpEk"
+        type="text/javascript"></script>
+
+<script type="text/javascript" src="./assets/libs/gmaps.min.js"></script>
+
 
 </body>
 </html>
\ No newline at end of file
