ngmap
Version:
The Simplest AngularJS Google Maps V3 Directive
294 lines (263 loc) • 10.4 kB
JavaScript
/**
* @ngdoc controller
* @name MapController
*/
(function() {
'use strict';
var Attr2MapOptions;
var __MapController = function(
$scope, $element, $attrs, $parse, $interpolate, _Attr2MapOptions_, NgMap, NgMapPool, escapeRegExp
) {
Attr2MapOptions = _Attr2MapOptions_;
var vm = this;
var exprStartSymbol = $interpolate.startSymbol();
var exprEndSymbol = $interpolate.endSymbol();
vm.mapOptions; /** @memberof __MapController */
vm.mapEvents; /** @memberof __MapController */
vm.eventListeners; /** @memberof __MapController */
/**
* Add an object to the collection of group
* @memberof __MapController
* @function addObject
* @param groupName the name of collection that object belongs to
* @param obj an object to add into a collection, i.e. marker, shape
*/
vm.addObject = function(groupName, obj) {
if (vm.map) {
vm.map[groupName] = vm.map[groupName] || {};
var len = Object.keys(vm.map[groupName]).length;
vm.map[groupName][obj.id || len] = obj;
if (vm.map instanceof google.maps.Map) {
//infoWindow.setMap works like infoWindow.open
if (groupName != "infoWindows" && obj.setMap) {
obj.setMap && obj.setMap(vm.map);
}
if (obj.centered && obj.position) {
vm.map.setCenter(obj.position);
}
(groupName == 'markers') && vm.objectChanged('markers');
(groupName == 'customMarkers') && vm.objectChanged('customMarkers');
}
}
};
/**
* Delete an object from the collection and remove from map
* @memberof __MapController
* @function deleteObject
* @param {Array} objs the collection of objects. i.e., map.markers
* @param {Object} obj the object to be removed. i.e., marker
*/
vm.deleteObject = function(groupName, obj) {
/* delete from group */
if (obj.map) {
var objs = obj.map[groupName];
for (var name in objs) {
if (objs[name] === obj) {
console.log('Deleting', groupName, obj);
google.maps.event.clearInstanceListeners(obj);
delete objs[name];
}
}
/* delete from map */
obj.map && obj.setMap && obj.setMap(null);
(groupName == 'markers') && vm.objectChanged('markers');
(groupName == 'customMarkers') && vm.objectChanged('customMarkers');
}
};
/**
* @memberof __MapController
* @function observeAttrSetObj
* @param {Hash} orgAttrs attributes before its initialization
* @param {Hash} attrs attributes after its initialization
* @param {Object} obj map object that an action is to be done
* @description watch changes of attribute values and
* do appropriate action based on attribute name
*/
vm.observeAttrSetObj = function(orgAttrs, attrs, obj) {
if (attrs.noWatcher) {
return false;
}
var attrsToObserve = Attr2MapOptions.getAttrsToObserve(orgAttrs);
for (var i=0; i<attrsToObserve.length; i++) {
var attrName = attrsToObserve[i];
attrs.$observe(attrName, NgMap.observeAndSet(attrName, obj));
}
};
/**
* @memberof __MapController
* @function zoomToIncludeMarkers
*/
vm.zoomToIncludeMarkers = function() {
// Only fit to bounds if we have any markers
// object.keys is supported in all major browsers (IE9+)
if ((vm.map.markers != null && Object.keys(vm.map.markers).length > 0) || (vm.map.customMarkers != null && Object.keys(vm.map.customMarkers).length > 0)) {
var bounds = new google.maps.LatLngBounds();
for (var k1 in vm.map.markers) {
bounds.extend(vm.map.markers[k1].getPosition());
}
for (var k2 in vm.map.customMarkers) {
bounds.extend(vm.map.customMarkers[k2].getPosition());
}
if (vm.mapOptions.maximumZoom) {
vm.enableMaximumZoomCheck = true; //enable zoom check after resizing for markers
}
vm.map.fitBounds(bounds);
}
};
/**
* @memberof __MapController
* @function objectChanged
* @param {String} group name of group e.g., markers
*/
vm.objectChanged = function(group) {
if ( vm.map &&
(group == 'markers' || group == 'customMarkers') &&
vm.map.zoomToIncludeMarkers == 'auto'
) {
vm.zoomToIncludeMarkers();
}
};
/**
* @memberof __MapController
* @function initializeMap
* @description
* . initialize Google map on <div> tag
* . set map options, events, and observers
* . reset zoom to include all (custom)markers
*/
vm.initializeMap = function() {
var mapOptions = vm.mapOptions,
mapEvents = vm.mapEvents;
var lazyInitMap = vm.map; //prepared for lazy init
vm.map = NgMapPool.getMapInstance($element[0]);
NgMap.setStyle($element[0]);
// set objects for lazyInit
if (lazyInitMap) {
/**
* rebuild mapOptions for lazyInit
* because attributes values might have been changed
*/
var filtered = Attr2MapOptions.filter($attrs);
var options = Attr2MapOptions.getOptions(filtered);
var controlOptions = Attr2MapOptions.getControlOptions(filtered);
mapOptions = angular.extend(options, controlOptions);
console.log('map options', mapOptions);
for (var group in lazyInitMap) {
var groupMembers = lazyInitMap[group]; //e.g. markers
if (typeof groupMembers == 'object') {
for (var id in groupMembers) {
vm.addObject(group, groupMembers[id]);
}
}
}
vm.map.showInfoWindow = vm.showInfoWindow;
vm.map.hideInfoWindow = vm.hideInfoWindow;
}
// set options
mapOptions.zoom = (mapOptions.zoom && !isNaN(mapOptions.zoom)) ? +mapOptions.zoom : 15;
var center = mapOptions.center;
var exprRegExp = new RegExp(escapeRegExp(exprStartSymbol) + '.*' + escapeRegExp(exprEndSymbol));
if (!mapOptions.center ||
((typeof center === 'string') && center.match(exprRegExp))
) {
mapOptions.center = new google.maps.LatLng(0, 0);
} else if( (typeof center === 'string') && center.match(/^[0-9.-]*,[0-9.-]*$/) ){
var lat = parseFloat(center.split(',')[0]);
var lng = parseFloat(center.split(',')[1]);
mapOptions.center = new google.maps.LatLng(lat, lng);
} else if (!(center instanceof google.maps.LatLng)) {
var geoCenter = mapOptions.center;
delete mapOptions.center;
NgMap.getGeoLocation(geoCenter, mapOptions.geoLocationOptions).
then(function (latlng) {
vm.map.setCenter(latlng);
var geoCallback = mapOptions.geoCallback;
geoCallback && $parse(geoCallback)($scope);
}, function () {
if (mapOptions.geoFallbackCenter) {
vm.map.setCenter(mapOptions.geoFallbackCenter);
}
});
}
vm.map.setOptions(mapOptions);
// set events
for (var eventName in mapEvents) {
var event = mapEvents[eventName];
var listener = google.maps.event.addListener(vm.map, eventName, event);
vm.eventListeners[eventName] = listener;
}
// set observers
vm.observeAttrSetObj(orgAttrs, $attrs, vm.map);
vm.singleInfoWindow = mapOptions.singleInfoWindow;
google.maps.event.trigger(vm.map, 'resize');
google.maps.event.addListenerOnce(vm.map, "idle", function () {
NgMap.addMap(vm);
if (mapOptions.zoomToIncludeMarkers) {
vm.zoomToIncludeMarkers();
}
//TODO: it's for backward compatibiliy. will be removed
$scope.map = vm.map;
$scope.$emit('mapInitialized', vm.map);
//callback
if ($attrs.mapInitialized) {
$parse($attrs.mapInitialized)($scope, {map: vm.map});
}
});
//add maximum zoom listeners if zoom-to-include-markers and and maximum-zoom are valid attributes
if (mapOptions.zoomToIncludeMarkers && mapOptions.maximumZoom) {
google.maps.event.addListener(vm.map, 'zoom_changed', function() {
if (vm.enableMaximumZoomCheck == true) {
vm.enableMaximumZoomCheck = false;
google.maps.event.addListenerOnce(vm.map, 'bounds_changed', function() {
vm.map.setZoom(Math.min(mapOptions.maximumZoom, vm.map.getZoom()));
});
}
});
}
};
$scope.google = google; //used by $scope.eval to avoid eval()
/**
* get map options and events
*/
var orgAttrs = Attr2MapOptions.orgAttributes($element);
var filtered = Attr2MapOptions.filter($attrs);
var options = Attr2MapOptions.getOptions(filtered, {scope: $scope});
var controlOptions = Attr2MapOptions.getControlOptions(filtered);
var mapOptions = angular.extend(options, controlOptions);
var mapEvents = Attr2MapOptions.getEvents($scope, filtered);
console.log('ng-map Options', mapOptions);
Object.keys(mapEvents).length && console.log('ng-map Events', mapEvents);
vm.mapOptions = mapOptions;
vm.mapEvents = mapEvents;
vm.eventListeners = {};
if (options.lazyInit) { // allows controlled initialization
// parse angular expression for dynamic ids
if (!!$attrs.id &&
// starts with, at position 0
$attrs.id.indexOf(exprStartSymbol, 0) === 0 &&
// ends with
$attrs.id.indexOf(exprEndSymbol, $attrs.id.length - exprEndSymbol.length) !== -1) {
var idExpression = $attrs.id.slice(2,-2);
var mapId = $parse(idExpression)($scope);
} else {
var mapId = $attrs.id;
}
vm.map = {id: mapId}; //set empty, not real, map
NgMap.addMap(vm);
} else {
vm.initializeMap();
}
//Trigger Resize
if(options.triggerResize) {
google.maps.event.trigger(vm.map, 'resize');
}
$element.bind('$destroy', function() {
NgMapPool.returnMapInstance(vm.map);
NgMap.deleteMap(vm);
});
}; // __MapController
__MapController.$inject = [
'$scope', '$element', '$attrs', '$parse', '$interpolate', 'Attr2MapOptions', 'NgMap', 'NgMapPool', 'escapeRegexpFilter'
];
angular.module('ngMap').controller('__MapController', __MapController);
})();