2 Copyright (c) 2013 Dominik Moritz
 
   4 This file is part of the leaflet locate control. It is licensed under the MIT license.
 
   5 You can find the project at: https://github.com/domoritz/leaflet-locatecontrol
 
   7 L.Control.Locate = L.Control.extend({
 
  11         follow: false,  // follow with zoom and pan the user's location
 
  12         stopFollowingOnDrag: false, // if follow is true, stop following when map is dragged
 
  30         // changes to range circle and inner marker while following
 
  31         // it is only necessary to provide the things that should change
 
  32         followCircleStyle: {},
 
  35             //fillColor: '#FFB000'
 
  38         onLocationError: function(err) {
 
  39             // this event is called in case of any location error
 
  40             // that is not a time out error.
 
  43         onLocationOutsideMapBounds: function(context) {
 
  44             // this event is repeatedly called when the location changes
 
  45             alert(context.options.strings.outsideMapBoundsMsg);
 
  47         setView: true, // automatically sets the map view to the user's location
 
  49             title: "Show me where I am",
 
  50             popup: "You are within {distance} {unit} from this point",
 
  51             outsideMapBoundsMsg: "You seem located outside the boundaries of the map"
 
  56     onAdd: function (map) {
 
  57         var container = L.DomUtil.create('div', 'control-locate');
 
  60         this._layer = new L.LayerGroup();
 
  61         this._layer.addTo(map);
 
  62         this._event = undefined;
 
  64         this._locateOptions = {
 
  65             watch: true  // if you overwrite this, visualization cannot be updated
 
  67         L.extend(this._locateOptions, this.options.locateOptions);
 
  68         L.extend(this._locateOptions, {
 
  69             setView: false // have to set this to false because we have to
 
  70                            // do setView manually
 
  73         // extend the follow marker style and circle from the normal style
 
  75         L.extend(tmp, this.options.markerStyle, this.options.followMarkerStyle);
 
  76         this.options.followMarkerStyle = tmp;
 
  78         L.extend(tmp, this.options.circleStyle, this.options.followCircleStyle);
 
  79         this.options.followCircleStyle = tmp;
 
  81         var link = L.DomUtil.create('a', 'control-button', container);
 
  82         link.innerHTML = "<span class='icon geolocate'></span>";
 
  84         link.title = this.options.strings.title;
 
  87             .on(link, 'click', L.DomEvent.stopPropagation)
 
  88             .on(link, 'click', L.DomEvent.preventDefault)
 
  89             .on(link, 'click', function() {
 
  90                 if (self._active && (map.getBounds().contains(self._event.latlng) || !self.options.setView ||
 
  91                     isOutsideMapBounds())) {
 
  94                     if (self.options.setView) {
 
  95                         self._locateOnNextLocationFound = true;
 
  98                         map.locate(self._locateOptions);
 
 101                     if (self.options.follow) {
 
 105                         L.DomUtil.addClass(self._container, "requesting");
 
 106                         L.DomUtil.removeClass(self._container, "active");
 
 107                         L.DomUtil.removeClass(self._container, "following");
 
 113             .on(link, 'dblclick', L.DomEvent.stopPropagation);
 
 115         var onLocationFound = function (e) {
 
 116             // no need to do anything if the location has not changed
 
 118                 (self._event.latlng.lat == e.latlng.lat &&
 
 119                  self._event.latlng.lng == e.latlng.lng)) {
 
 129             if (self.options.follow && self._following) {
 
 130                 self._locateOnNextLocationFound = true;
 
 136         var startFollowing = function() {
 
 137             self._following = true;
 
 138             if (self.options.stopFollowingOnDrag) {
 
 139                 map.on('dragstart', stopFollowing);
 
 143         var stopFollowing = function() {
 
 144             self._following = false;
 
 145             if (self.options.stopFollowingOnDrag) {
 
 146                 map.off('dragstart', stopFollowing);
 
 151         var isOutsideMapBounds = function () {
 
 152             if (self._event === undefined)
 
 154             return map.options.maxBounds &&
 
 155                 !map.options.maxBounds.contains(self._event.latlng);
 
 158         var visualizeLocation = function() {
 
 159             if (self._event.accuracy === undefined)
 
 160                 self._event.accuracy = 0;
 
 162             var radius = self._event.accuracy;
 
 163             if (self._locateOnNextLocationFound) {
 
 164                 if (isOutsideMapBounds()) {
 
 165                     self.options.onLocationOutsideMapBounds(self);
 
 167                     map.fitBounds(self._event.bounds);
 
 169                 self._locateOnNextLocationFound = false;
 
 172             // circle with the radius of the location's accuracy
 
 174             if (self.options.drawCircle) {
 
 175                 if (self._following) {
 
 176                     style = self.options.followCircleStyle;
 
 178                     style = self.options.circleStyle;
 
 182                     self._circle = L.circle(self._event.latlng, radius, style)
 
 185                     self._circle.setLatLng(self._event.latlng).setRadius(radius);
 
 190             if (self.options.metric) {
 
 191                 distance = radius.toFixed(0);
 
 194                 distance = (radius * 3.2808399).toFixed(0);
 
 198             // small inner marker
 
 200             if (self._following) {
 
 201                 m = self.options.followMarkerStyle;
 
 203                 m = self.options.markerStyle;
 
 206             var t = self.options.strings.popup;
 
 207             if (!self._circleMarker) {
 
 208                 self._circleMarker = L.circleMarker(self._event.latlng, m)
 
 209                     .bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
 
 212                 self._circleMarker.setLatLng(self._event.latlng)
 
 213                     .bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
 
 214                     ._popup.setLatLng(self._event.latlng);
 
 217             if (!self._container)
 
 219             if (self._following) {
 
 220                 L.DomUtil.removeClass(self._container, "requesting");
 
 221                 L.DomUtil.addClass(self._container, "active");
 
 222                 L.DomUtil.addClass(self._container, "following");
 
 224                 L.DomUtil.removeClass(self._container, "requesting");
 
 225                 L.DomUtil.addClass(self._container, "active");
 
 226                 L.DomUtil.removeClass(self._container, "following");
 
 230         var resetVariables = function() {
 
 231             self._active = false;
 
 232             self._locateOnNextLocationFound = self.options.setView;
 
 233             self._following = false;
 
 238         var stopLocate = function() {
 
 240             map.off('dragstart', stopFollowing);
 
 242             L.DomUtil.removeClass(self._container, "requesting");
 
 243             L.DomUtil.removeClass(self._container, "active");
 
 244             L.DomUtil.removeClass(self._container, "following");
 
 247             self._layer.clearLayers();
 
 248             self._circleMarker = undefined;
 
 249             self._circle = undefined;
 
 252         var onLocationError = function (err) {
 
 253             // ignore time out error if the location is watched
 
 254             if (err.code == 3 && this._locateOptions.watch) {
 
 259             self.options.onLocationError(err);
 
 263         map.on('locationfound', onLocationFound, self);
 
 264         map.on('locationerror', onLocationError, self);
 
 270 L.Map.addInitHook(function () {
 
 271     if (this.options.locateControl) {
 
 272         this.locateControl = L.control.locate();
 
 273         this.addControl(this.locateControl);
 
 277 L.control.locate = function (options) {
 
 278     return new L.Control.Locate(options);