UNPKG

ang-google-maps

Version:
946 lines (681 loc) 30.7 kB
(function(ang, g) { var indexOfPacContainer = 0, Characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", labelIndex = 0; 'use strict'; ang.module('ang-google-maps', []) .factory('Map', function() { return MapFactory; }) .factory('GetPosition', function() { return GetPosition; }) .factory('GetMarker', function() { return GetMarker; }) .factory('Geocode', function() { return Geocode(); }) .factory('Direction', ['$q', '$rootScope', direction_factory]) .directive('mapTemp', ['$window', '$document', '$http', mapTempFactory]) .directive('autoCompleteTemp', ['$window', '$http', '$rootScope', autoCompleteTempFactory]); /** * Gets the overall paths from $Legs. */ function getOverallPaths($Legs, $Options) { var $Legs = $Legs || [], $Options = $Options || {}, $Paths = []; // First iteration on legs for ( var index = 0, length = $Legs.length; index < length; index++) { var $Steps = ('steps' in $Legs[index]) ? $Legs[index]['steps'] : []; // Second iteration on steps (function($S) { for (var index2 = 0, length2 = $S.length; index2 < length2; index2++) { var $Path = ('path' in $S[index2]) ? $S[index2]['path'] : []; // Third iteration on path (function($P) { for (var index3 = 0, length3 = $P.length; index3 < length3; index3++) { var $Data = $P[index3]; $Paths.push({ lat: (typeof $Data['lat'] == 'function') ? $Data['lat']() : $Data['lat'], lng: (typeof $Data['lng'] == 'function') ? $Data['lng']() : $Data['lng'] }); } }($Path)); } }($Steps)); if ($Options['isSkipLast'] && index + 1 == length - 1) return $Paths; if (index + 1 == length) return $Paths; } } function direction_factory($q, $rootScope) { var $DirectionService = new g.maps.DirectionsService, $DirectionDisplay = new g.maps.DirectionsRenderer({ draggable: true, hideRouteList: true, suppressMarkers: true, suppressPolylines: true //infoWindow: new google.maps.InfoWindow }); $DirectionDisplay.addListener('directions_changed', function(res) { var $Directions = this.directions, $Map = this.map, $Leg = $Directions.routes[0].legs[0], $ProcessedLeg = processLegs($Directions, $Map); if (typeof $Map.ondirectionchange == 'function') $Map.ondirectionchange({$Leg: $ProcessedLeg, $parentScope: $rootScope, $Directions: $Directions}); $rootScope.$digest(); }); return Direction({ $DirectionService: $DirectionService, $DirectionDisplay: $DirectionDisplay },$q); } function processLegs($Directions, $Map) { var $Routes = $Directions.routes[0], $Legs = $Routes.legs, $Result = {waypoints: [], totalDuration: 0, totalDistance: 0}; if (!$Legs.length) return $Results; $Result['current'] = { coords: $Legs[0]['start_location'], name: $Legs[0]['start_address'], next_coords: $Legs[0]['end_location'], next_name: $Legs[0]['end_address'] }; if ($Legs.length == 1) { $Result['destination'] = { coords: $Legs[0]['end_location'], name: $Legs[0]['end_address'], prev_coords: $Legs[0]['start_location'], prev_name: $Legs[0]['start_address'] } } else { for (var index = 0, length = $Legs.length; index < length; index++) { if (index + 1 == length) { $Result['destination'] = { coords: $Legs[index]['end_location'], name: $Legs[index]['end_address'], prev_coords: $Legs[index]['start_location'], prev_name: $Legs[index]['start_address'] }; } else { $Result['waypoints'].push($Legs[index]); } } $Result['waypoints'] = $Result['waypoints'].reverse(); } for (var index = 0, length = $Legs.length; index < length; index++) { if ($Map.isCurrentSameDestination && index + 1 == length) break; $Result['totalDistance'] += $Legs[index]['distance']['value']; $Result['totalDuration'] += $Legs[index]['duration']['value']; } return $Result; } /** * Factory of google map's marker */ function MapFactory(options) { var $Map = new g.maps.Map( document.getElementById('main-map'), options ); return $Map; } /** * Factory of google map's marker */ function GetMarker(options) { return new g.maps.Marker({ animation: google.maps.Animation.DROP, map: options['map'] || null, draggable: true, animation: google.maps.Animation.DROP || '', title: options['title'] || '', label: options['label'] || '', position: { lat: options['lat'], lng: options['lng'] }, fillColor: '#cc1' }); }; /** * Factory of google map's position * * @param {Float} lat * @param {Float} lng */ function GetPosition(lat, lng) { return new g.maps.LatLng(lat || 0, lng || 0); } /** * Creates the factory of mapTemp. * * @param {Object} $window * @param {Object} $http */ function mapTempFactory($window, $document, $http) { return { controller: mapTempCtrl, link: mapTempLink, scope: { id: "=id", configs: "=", ondirectionchange: "&ondirectionchange", onmapclick: "&onmapclick", }, template: '<div class="filter" ng-transclude=""></div>', transclude: true, }; } mapTempFactory.$inject = ['$window', '$document', '$http']; /** * Creates the factory of autoCompleteTemp. * * @param {Object} $window * @param {Object} $http */ function autoCompleteTempFactory($window, $http, $rootScope) { return { controller: autoCompleteTempCtrl, link: function($scope, element, $atts, $ctrls) { autoCompleteTempLink.call(this, $scope, element, $atts, $ctrls, $rootScope); }, scope: { map: '=map', nameofinput: '@', onfill: '&onfill', ondrop: '&ondrop', ondragstart: '&ondragstart', onmarkerclick: '&onmarkerclick', markerlabel: '@' } }; } autoCompleteTempFactory.$inject = ['$window', '$http', '$rootScope']; /** * Handles map-temp directive's controller * * @param {Object} $scope */ function mapTempCtrl($scope, GetPosition) { $scope.mapOptions = { center: { lat: parseFloat($scope.configs.lat), lng: parseFloat($scope.configs.lng), }, zoom: $scope.configs.zoom || 18, scrollwheel: $scope.configs.scrollwheel || false, }; $scope.createMapContainer = createMapContainer; } mapTempCtrl.$inject = ['$scope', 'GetPosition']; /** * Handles map-temp directive's link * * @param {Object} $scope * @param {Object} element * @param {Object} attrs * @param {Array} ctlrs */ function mapTempLink($scope, element, attrs, ctrls) { // Append map's container into element element $scope.mapContainer = $scope.createMapContainer(); element[0].appendChild($scope.mapContainer); // Define Methods $scope.renderWithGeolocation = renderWithGeolocation; $scope.renderWithDefault = renderWithDefault; initMap.call($scope, element); } mapTempLink.$inject = ['$scope', 'element', 'attrs', 'ctrls']; /** * Creates map's container * * @return {Object} div */ function createMapContainer() { var div = document.createElement('div'); // Set the class div.className = "container"; div.setAttribute('id', this.configs.id || ''); return div; } function deletingMarker(name) { var markerName = name || '', indexOfMarker = (markerName) ? findByName(this.markers, markerName) : -1; if (indexOfMarker == -1) return; // Set marker into map null this.markers[indexOfMarker]['marker'].setMap(null); if(this['O'+name]) { this['O'+name].remove() delete this['O'+name]; } } /** * Extends maps's object. * * @param Object $Maps. */ function ExtendsMaps($Maps) { var $Maps = $Maps || {}, $RefMaps = { $Maps: $Maps }; $RefMaps.searchMarkers = function(name) { var $Data = { code: -1 }; for (var index = 0, length = this.$Maps.markers.length; index < length; index++) { if (this.$Maps.markers[index]['name'] == name) { $Data = { code: index, $E: this.$Maps.markers[index], index: index }; break; } } return $Data; }; // Attaches getOverallPaths into $Maps. $Maps.getOverallPaths = getOverallPaths; /** * Assign _$Marker service into map's object. */ $Maps._$Marker = { get: function(name) { var $Query = $RefMaps.searchMarkers(name); if ($Query.code < 0) return -1; return $Query.$E; }, /** * Deletes single marker from the maps' canvas. */ delete: function(name) { try { var $Query = $RefMaps.searchMarkers(name); if ($Query.code < 0) return false; if(typeof $Query.$E.marker == 'object') if(typeof $Query.$E.marker.setMap == 'function') { $Query.$E.marker.setMap(null); if($RefMaps.$Maps.markers[$Query.index]['marker']) delete $RefMaps.$Maps.markers[$Query.index]['marker']; $RefMaps.$Maps.markers.splice($Query.index, 1); } return true; } catch (error) { return false; } }, updateAll: function(callback) { var callback = callback || function() {}; for (var index = 0, length = $RefMaps.$Maps.markers.length; index < length; index++) { (function(i, $O) { callback($O); }(index, $RefMaps.$Maps.markers[index])); } } }; $Maps._$Polyline = { /** * Draws the polyline into the map. */ draw: function() { }, /** * Processes steps object. * * @param Array $Steps [{}, {}] * * @return Array $Paths [{lat: , lng: }, {lat: , lng: }] */ processingSteps: function($Steps) { var $Steps = $Steps || undefined, $Paths = []; if (!$Steps.length) throw new Error("$Steps can't be empty / undefined"); for (var index = 0, length = $Steps.length; index < length; index++) { var $Path = (typeof $Steps[index] == 'object') ? $Steps[index]['path'] : undefined; if (!$Path) throw new Error("$Path must be object"); for (var index2 = 0, length2 = $Path.length; index2 < length2; index2++) { $Paths.push({ lat: (typeof $Path[index2]['lat'] == 'function') ? $Path[index2]['lat']() : $Path[index2]['lat'] || 0, lng: (typeof $Path[index2]['lng'] == 'function') ? $Path[index2]['lng']() : $Path[index2]['lng'] || 0, }); } } return $Paths; }, /** * Gets the paths from the $Directions. * * @param Object $Directions * @param String latlng {32.49129291,8.23124124} * * @return Array $Paths. */ getPaths: function($Directions, latlng) { var $Routes = $Directions.routes; if (!$Routes.length) throw new Error("Routes can't be empty / undefined @ getPaths method"); var $Legs = ($Routes[0]) ? $Routes[0].legs : undefined; if (!$Legs.length) throw new Error("Legs can't be empty / undefined @ getPaths method"); for (var index = 0 , length = $Legs.length; index < length; index++) { var $StartLocation = $Legs[index]['start_location'] || {}; if ('start_location' in $Legs[index]) { var lat = (typeof $StartLocation.lat == 'function') ? $StartLocation.lat() : $StartLocation.lat || 0, lng = (typeof $StartLocation.lng == 'function') ? $StartLocation.lng() : $StartLocation.lng || 0, LatLng = lat + ',' + lng; if (latlng == LatLng) { //console.log("Matched"); //console.log("Do Something"); return this.processingSteps($Legs[index]['steps']); } } } }, /** * Gets the polyline's object. */ getPolyline: function(name) { var $Data = { code: -1 }; if ($Maps.O$polyline) { if ($Maps.O$polyline[name]) $Data = { code: 1, $E: $Maps.O$polyline[name] }; } return $Data; }, /** * Clears the polyline from the map. * * @param Object $Polyline. */ clearPolyline: function($Polyline) { var $Polyline = $Polyline || {}; if ('setMap' in $Polyline && typeof $Polyline.setMap == 'function' ) $Polyline.setMap(null); }, /** * Draws the polyline into the map. * */ addPolyline: function($Configs) { if ($Maps['O$polyline'][$Configs['name']]) { $Maps['O$polyline'][$Configs['name']].setMap(null); delete $Maps['O$polyline'][$Configs['name']]; } var $Configs = $Configs || [], $PathPolyline = new google.maps.Polyline({ path: $Configs['paths'] || [], geodesic: $Configs['geodesic'] || true, strokeColor: $Configs['strokeColor'] || '#19CA93', strokeOpacity: $Configs['strokeOpacity'] || 1, strokeWeight: $Configs['strokeWeight'] || 4, map: $Maps, zIndex: $Configs['zIndex'] || 1, }); if ($Configs['name']) $Maps['O$polyline'][$Configs['name']] = $PathPolyline; return $PathPolyline; } }; } function initMapModel() { window.map = this.$parent.map = this.map = new g.maps.Map(this.mapContainer, this.mapOptions); this.mapOptions['disableDefaultUI'] = true; this.map.markers = []; this.map.id = this.configs.id; this.map.location = {}; this.map.O$polyline = {}; this.map.addingMarker = addingMarker; this.map.ondirectionchange = this.ondirectionchange || undefined; //this.map.location = // Attach deleting marker method into the map's object. this.map.deletingMarker = deletingMarker; attachMapEvents.call(this); ExtendsMaps(this.map); } // For incoming features function attachMapEvents() { var $Self = this; $Self.map.addListener('click', function($Event) { $Self.onmapclick({ $Coords: $Event.latLng, $Pixel: $Event.pixel, $Za: $Event.za, $Event: $Event }); }); /* $Self.map.addListener('dblclick', function($Event) { // $Event.stopPropagation(); console.log($Event); console.log("Double click"); }); */ } function renderWithGeolocation($Position) { this.map.setCenter({ lat: $Position.coords.latitude, lng: $Position.coords.longitude }); } function renderWithDefault($Error) { this.map.setCenter({ lat: 13.736717 , lng: 100.523186 }); } /** * Initializes Google map into map's content. * */ function initMap(element) { var $Obj = this; if ('geolocation' in navigator) { navigator.geolocation.getCurrentPosition(function($Position) { $Obj.renderWithGeolocation($Position); }, function($Error) { $Obj.renderWithDefault($Error); }); } initMapModel.call($Obj); } /** * Handles auto-complete-temp directive's controller * * @param {Object} $scope */ function autoCompleteTempCtrl($scope) { $scope.model = { label: '', name: $scope.nameofinput, show: 0 }; $scope.fillInAddress = fillInAddress; } autoCompleteTempCtrl.$inject = ['$scope']; /** * Handles auto-complete-temp directive's link. * * @param {Object} $scope * @param {Object} element * @param {Object} attrs * @param {Array} ctlrs */ function autoCompleteTempLink($scope, element, attrs, ctrls, $rootScope) { // Create autocomplete object $scope.autocomplete = new g.maps.places.Autocomplete( // Dom element element[0] || null, // Type { type: 'geocode' } ); // Assign DOM element of input's field. $scope.element = $scope.map["O" + $scope['nameofinput']] = element[0]; // Assign DOM's index of pac-container which is auto-generated when the user is typing the address. if($scope.element) $scope.element.setAttribute('pac-element-index', indexOfPacContainer); // Assign nameofinput to watch for the further changes. $rootScope[$scope['nameofinput']] = ''; $rootScope.$watch($scope['nameofinput'], function(newValue, oldValue) { //console.log("Something is being changed"); //console.log(arguments); if(newValue) $scope.element.value = newValue; return newValue; }); // Attach an event on user's typing of the address which is handled by Google Places API. g.maps.event.addListener($scope.autocomplete, 'place_changed', function() { $scope.fillInAddress(); }); // The index number for the auto-generated pac-container from Google Places API. indexOfPacContainer += 1; } autoCompleteTempLink.$inject = ['$scope', 'element', 'attrs', 'ctrls']; function findInList(List, $Obj) { var List = List || [], $Obj = $Obj || {key: null, value: null}; for (var index = 0, length = List.length; index < length; index++) { if ( List[index][$Obj['key']] == $Obj['value'] ) return index; } return -1; } function extendObj($Obj, $NewObj) { var $FormedObj = {}; for (var item in $NewObj) { $FormedObj[item] = $NewObj[item]; } for (var item in $Obj) { $FormedObj[item] = $Obj[item]; } return $FormedObj; } function appendMarker(List, model) { var position = findInList(List || [], {key: 'name', value: model['name']}), index = position; if (position >= 0) { List[position] = extendObj(List[position], model); } else { List.push(model); index = List.length - 1;} return index; } function fillInAddress() { var place = this.autocomplete.getPlace(), lat = place.geometry.location.lat() || 0, lng = place.geometry.location.lng() || 0, $CtrlScope = this.$parent, $Self = this, $Position = { lat: parseFloat(lat), lng: parseFloat(lng) }, indexOfMarker = findByName($Self.map.markers, $Self['model']['name']); if (indexOfMarker < 0) indexOfMarker = appendMarker($Self.map.markers, $Self['model']); try { $Self.map.panTo($Position); // Drop pin if(!$Self.map.markers[indexOfMarker]['marker']) { $Self.map.markers[indexOfMarker]['marker'] = GetMarker({ animation: google.maps.Animation.DROP, map: $Self.map, lat: lat, lng: lng, label: $Self['markerlabel'] || Characters[indexOfMarker], }); } else { $Self.map.markers[indexOfMarker]['marker'].setMap($Self.map); $Self.map.markers[indexOfMarker]['marker'].setPosition({ lat: lat, lng: lng }); } var $Marker = $Self.map.markers[indexOfMarker]['marker']; $Marker.addListener('dragend', function($Event) { if(typeof $Self.ondrop == 'function') $Self.ondrop({$Event: $Event, $Model: $Self.model, $AutoCompScope: $Self, $Marker: $Marker}); }); $Marker.addListener('dragstart', function($Event) { if(typeof $Self.ondragstart == 'function') $Self.ondragstart({$Event: $Event, $Model: $Self.model, $AutoCompScope: $Self, $Marker: $Marker}); }); $Marker.addListener('click', function($Event) { if (typeof $Self.onmarkerclick == 'function') $Self.onmarkerclick({$Event: $Event, $Model: $Self.model, $AutoCompScope: $Self, $Marker: $Marker}); }); // Push a new marker into markers list appendMarker($Self.map.markers, $Self.model); } catch (error) { console.log(error.message); } if(typeof $Self.onfill == 'function') $Self.onfill({$Position: $Position, $Model: $Self.model, $CoreModel: place, $Element: $Self.element, $Marker: $Marker}); } function clearAllMarkers($map) { var $Markers = $map.markers || []; for (var index = 0, length = $Markers.length; index < length; index++) { if('marker' in $Markers[index]) $Markers[index]['marker'].setMap(null); } } function handleMakerClick($Event) { this.$InfoWindow = new g.maps.InfoWindow({ content: this.model.name || '' }); this.$InfoWindow.open(this.map, this.model.marker); } function findByName(list, name) { var list = list || []; for (var index = 0, length = list.length; index < length; index++) { if (list[index]['name'] == name) return index; } return -1; } function Direction(configs, $q) { var $DirectionService = configs['$DirectionService'], $DirectionDisplay = configs['$DirectionDisplay']; window.resetRoute = function() { $DirectionDisplay.setMap(null); }; return { setRoute: function(options) { var $Defered = $q.defer(); if (!g) return; if (options['map']) { //clearAllMarkers(options['map']); if (!$DirectionDisplay.getMap()) $DirectionDisplay.setMap(options['map']); $DirectionService.route({ destination: (options['destination']) ? options['destination'] : { location: {lat: 0, lng: 0 }}, origin: (options['current']) ? options['current'] : { location: {lat: 0, lng: 0 }}, provideRouteAlternatives : (options['provideRouteAlternatives']) ? (options['provideRouteAlternatives']) : false, optimizeWaypoints : (options['optimized']) ? options['optimized'] : false, waypoints: (options['dropOffs']) ? options['dropOffs'] : [], avoidHighways: (options['avoidHighways']) ? options['avoidHighways'] : false, avoidTolls: (options['avoidTolls']) ? (options['avoidTolls']) : false, travelMode: g.maps.TravelMode.DRIVING, unitSystem: g.maps.UnitSystem.IMPERIAL }, function(response, status) { if (status == g.maps.DirectionsStatus.OK) { // console.log(response); $DirectionDisplay.setDirections(response); $Defered.resolve(response); } else { $Defered.reject({ message: "There something wrong @ setRoute, please check if you pass appropriate parameters into this function", code: 0 }); } }); } return $Defered.promise; }, resetRoute: function(options) { $DirectionDisplay.setMap(null); } }; } /** * Adds marker into map * * @param {Options} */ function addingMarker(options) { var indexOfCurrentMarker = findInList(this.markers || [], {key: 'name', value: options['name'] || ''}), options = options || {}, isOnDragStart = (typeof options['ondragstart'] == 'function'), isOnDragEndFunc = (typeof options['ondragend'] == 'function'), isOnInitFunc = (typeof options['oninit'] == 'function'), isOnClickFunc = (typeof options['onclick'] == 'function'); if (indexOfCurrentMarker >= 0 && this.markers[indexOfCurrentMarker]['marker']) { var $Marker = this.markers[indexOfCurrentMarker]['marker'], isOnInitFunc = typeof options['oninit'] == 'function'; $Marker.setPosition(options['position'] || {lat: 0, lng: 0}); $Marker.setMap(this); if (isOnInitFunc) options['oninit']({latLng: options['position']}, $Marker); return; } var $Marker = new g.maps.Marker({ path: 0, position: options['position'], draggable: true, animation: google.maps.Animation.DROP }), model = { name: options['name'], marker: $Marker }; if (!$Marker.position) throw new Error("Ops, the position is not being passed") if (!model['name']) throw new Error('Please provide the name for the marker'); if (isOnInitFunc) options['oninit']({latLng: options['position']}, $Marker); // Attach marker into the map $Marker.setMap(this); var indexOfMarker = appendMarker(this.markers, model); this.markers[indexOfMarker]['marker'].setLabel(options['markerlabel'] || Characters[indexOfMarker]); if (isOnDragEndFunc) $Marker.addListener('dragend', function($Event) { options['ondragend']($Event, $Marker); }); if (isOnClickFunc) $Marker.addListener('click', function($Event) { //console.log('Clicking on the marker'); options['onclick']($Event, $Marker); }); if (isOnDragStart) $Marker.addListener('dragstart', function($Event) { options['ondragstart']($Event, $Marker); }); } }(angular, google));