X-Git-Url: https://git.openstreetmap.org/rails.git/blobdiff_plain/74293c89468019254b2b5b298b8fae9d7abfc78b..3eda7be919f5b3bd88b5cf85ba1a3c1f72550d6f:/vendor/assets/leaflet/leaflet.locate.js diff --git a/vendor/assets/leaflet/leaflet.locate.js b/vendor/assets/leaflet/leaflet.locate.js index 77754d84d..822b24da2 100644 --- a/vendor/assets/leaflet/leaflet.locate.js +++ b/vendor/assets/leaflet/leaflet.locate.js @@ -1,16 +1,23 @@ +/* +Copyright (c) 2014 Dominik Moritz + +This file is part of the leaflet locate control. It is licensed under the MIT license. +You can find the project at: https://github.com/domoritz/leaflet-locatecontrol +*/ L.Control.Locate = L.Control.extend({ options: { position: 'topleft', drawCircle: true, follow: false, // follow with zoom and pan the user's location + stopFollowingOnDrag: false, // if follow is true, stop following when map is dragged (deprecated) // range circle circleStyle: { - color: '#136AEC', - fillColor: '#136AEC', - fillOpacity: 0.15, - weight: 2, - opacity: 0.5 - }, + color: '#136AEC', + fillColor: '#136AEC', + fillOpacity: 0.15, + weight: 2, + opacity: 0.5 + }, // inner marker markerStyle: { color: '#136AEC', @@ -18,106 +25,183 @@ L.Control.Locate = L.Control.extend({ fillOpacity: 0.7, weight: 2, opacity: 0.9, - radius: 4 + radius: 5 + }, + // changes to range circle and inner marker while following + // it is only necessary to provide the things that should change + followCircleStyle: {}, + followMarkerStyle: { + //color: '#FFA500', + //fillColor: '#FFB000' }, + icon: 'icon-location', // icon-location or icon-direction + iconLoading: 'icon-spinner animate-spin', + circlePadding: [0, 0], metric: true, - debug: false, onLocationError: function(err) { + // this event is called in case of any location error + // that is not a time out error. alert(err.message); }, - title: "Show me where I am", - popupText: ["You are within ", " from this point"], + onLocationOutsideMapBounds: function(control) { + // this event is repeatedly called when the location changes + control.stopLocate(); + alert(context.options.strings.outsideMapBoundsMsg); + }, setView: true, // automatically sets the map view to the user's location - locateOptions: {} + // keep the current map zoom level when displaying the user's location. (if 'false', use maxZoom) + keepCurrentZoomLevel: false, + strings: { + title: "Show me where I am", + popup: "You are within {distance} {unit} from this point", + outsideMapBoundsMsg: "You seem located outside the boundaries of the map" + }, + locateOptions: { + maxZoom: Infinity, + watch: true // if you overwrite this, visualization cannot be updated + } }, onAdd: function (map) { - var className = 'control-locate', - container = L.DomUtil.create('div', className); + var container = L.DomUtil.create('div', 'control-locate'); var self = this; this._layer = new L.LayerGroup(); this._layer.addTo(map); this._event = undefined; - // nested extend so that the first can overwrite the second - // and the second can overwrite the third - this._locateOptions = L.extend(L.extend({ - 'setView': false // have to set this to false because we have to - // do setView manually - }, this.options.locateOptions), { - 'watch': true // if you overwrite this, visualization cannot be updated + + this._locateOptions = this.options.locateOptions; + L.extend(this._locateOptions, this.options.locateOptions); + L.extend(this._locateOptions, { + setView: false // have to set this to false because we have to + // do setView manually }); - var link = L.DomUtil.create('a', 'control-button', container); + // extend the follow marker style and circle from the normal style + var tmp = {}; + L.extend(tmp, this.options.markerStyle, this.options.followMarkerStyle); + this.options.followMarkerStyle = tmp; + tmp = {}; + L.extend(tmp, this.options.circleStyle, this.options.followCircleStyle); + this.options.followCircleStyle = tmp; + + var link = L.DomUtil.create('a', 'control-button ' + this.options.icon, container); link.innerHTML = ""; link.href = '#'; - link.title = this.options.title; - - var _log = function(data) { - if (self.options.debug) { - console.log(data); - } - }; + link.title = this.options.strings.title; L.DomEvent .on(link, 'click', L.DomEvent.stopPropagation) .on(link, 'click', L.DomEvent.preventDefault) .on(link, 'click', function() { - if (self._active && (map.getBounds().contains(self._event.latlng) || !self.options.setView)) { + if (self._active && (self._event === undefined || map.getBounds().contains(self._event.latlng) || !self.options.setView || + isOutsideMapBounds())) { stopLocate(); } else { - if (self.options.setView) { - self._locateOnNextLocationFound = true; - } - if(!self._active) { - map.locate(self._locateOptions); - } - self._active = true; - if (!self._event) { - L.DomUtil.addClass(self._container, "requesting"); - } else { - visualizeLocation(); - } + locate(); } }) .on(link, 'dblclick', L.DomEvent.stopPropagation); - var onLocationFound = function (e) { - _log('onLocationFound'); - + var locate = function () { + if (self.options.setView) { + self._locateOnNextLocationFound = true; + } + if(!self._active) { + map.locate(self._locateOptions); + } self._active = true; + if (self.options.follow) { + startFollowing(); + } + if (!self._event) { + setClasses('requesting'); + } else { + visualizeLocation(); + } + }; + var onLocationFound = function (e) { + // no need to do anything if the location has not changed if (self._event && - (self._event.latlng.lat != e.latlng.lat || - self._event.latlng.lng != e.latlng.lng)) { - _log('location has changed'); + (self._event.latlng.lat === e.latlng.lat && + self._event.latlng.lng === e.latlng.lng && + self._event.accuracy === e.accuracy)) { + return; + } + + if (!self._active) { + return; } self._event = e; - if (self.options.follow) { + if (self.options.follow && self._following) { self._locateOnNextLocationFound = true; } visualizeLocation(); }; - var visualizeLocation = function() { - _log('visualizeLocation,' + 'setView:' + self._locateOnNextLocationFound); + var startFollowing = function() { + map.fire('startfollowing', self); + self._following = true; + if (self.options.stopFollowingOnDrag) { + map.on('dragstart', stopFollowing); + } + }; - var radius = self._event.accuracy / 2; + var stopFollowing = function() { + map.fire('stopfollowing', self); + self._following = false; + if (self.options.stopFollowingOnDrag) { + map.off('dragstart', stopFollowing); + } + visualizeLocation(); + }; + + var isOutsideMapBounds = function () { + if (self._event === undefined) + return false; + return map.options.maxBounds && + !map.options.maxBounds.contains(self._event.latlng); + }; + + var visualizeLocation = function() { + if (self._event.accuracy === undefined) + self._event.accuracy = 0; + var radius = self._event.accuracy; if (self._locateOnNextLocationFound) { - map.fitBounds(self._event.bounds); + if (isOutsideMapBounds()) { + self.options.onLocationOutsideMapBounds(self); + } else { + map.fitBounds(self._event.bounds, { + padding: self.options.circlePadding, + maxZoom: self.options.keepCurrentZoomLevel ? map.getZoom() : self._locateOptions.maxZoom + }); + } self._locateOnNextLocationFound = false; } - self._layer.clearLayers(); - // circle with the radius of the location's accuracy + var style, o; if (self.options.drawCircle) { - L.circle(self._event.latlng, radius, self.options.circleStyle) - .addTo(self._layer); + if (self._following) { + style = self.options.followCircleStyle; + } else { + style = self.options.circleStyle; + } + + if (!self._circle) { + self._circle = L.circle(self._event.latlng, radius, style) + .addTo(self._layer); + } else { + self._circle.setLatLng(self._event.latlng).setRadius(radius); + for (o in style) { + self._circle.options[o] = style[o]; + } + } } var distance, unit; @@ -130,43 +214,86 @@ L.Control.Locate = L.Control.extend({ } // small inner marker - var t = self.options.popupText; - L.circleMarker(self._event.latlng, self.options.markerStyle) - .bindPopup(t[0] + distance + " " + unit + t[1]) - .addTo(self._layer); + var mStyle; + if (self._following) { + mStyle = self.options.followMarkerStyle; + } else { + mStyle = self.options.markerStyle; + } + + var t = self.options.strings.popup; + if (!self._circleMarker) { + self._circleMarker = L.circleMarker(self._event.latlng, mStyle) + .bindPopup(L.Util.template(t, {distance: distance, unit: unit})) + .addTo(self._layer); + } else { + self._circleMarker.setLatLng(self._event.latlng) + .bindPopup(L.Util.template(t, {distance: distance, unit: unit})) + ._popup.setLatLng(self._event.latlng); + for (o in mStyle) { + self._circleMarker.options[o] = mStyle[o]; + } + } if (!self._container) return; - - L.DomUtil.removeClass(self._container, "requesting"); - L.DomUtil.addClass(self._container, "active"); + if (self._following) { + setClasses('following'); + } else { + setClasses('active'); + } }; + var setClasses = function(state) { + if (state == 'requesting') { + L.DomUtil.removeClasses(self._container, "active following"); + L.DomUtil.addClasses(self._container, "requesting"); + + L.DomUtil.removeClasses(link, self.options.icon); + L.DomUtil.addClasses(link, self.options.iconLoading); + } else if (state == 'active') { + L.DomUtil.removeClasses(self._container, "requesting following"); + L.DomUtil.addClasses(self._container, "active"); + + L.DomUtil.removeClasses(link, self.options.iconLoading); + L.DomUtil.addClasses(link, self.options.icon); + } else if (state == 'following') { + L.DomUtil.removeClasses(self._container, "requesting"); + L.DomUtil.addClasses(self._container, "active following"); + + L.DomUtil.removeClasses(link, self.options.iconLoading); + L.DomUtil.addClasses(link, self.options.icon); + } + } + var resetVariables = function() { self._active = false; - self._locateOnNextLocationFound = true; + self._locateOnNextLocationFound = self.options.setView; + self._following = false; }; resetVariables(); var stopLocate = function() { - _log('stopLocate'); map.stopLocate(); + map.off('dragstart', stopFollowing); + if (self.options.follow && self._following) { + stopFollowing(); + } L.DomUtil.removeClass(self._container, "requesting"); L.DomUtil.removeClass(self._container, "active"); - + L.DomUtil.removeClass(self._container, "following"); resetVariables(); self._layer.clearLayers(); + self._circleMarker = undefined; + self._circle = undefined; }; - var onLocationError = function (err) { - _log('onLocationError'); - - // ignore timeout error if the location is watched - if (err.code==3 && this._locateOptions.watch) { + // ignore time out error if the location is watched + if (err.code == 3 && self._locateOptions.watch) { return; } @@ -178,6 +305,11 @@ L.Control.Locate = L.Control.extend({ map.on('locationfound', onLocationFound, self); map.on('locationerror', onLocationError, self); + // make locate functions available to outside world + this.locate = locate; + this.stopLocate = stopLocate; + this.stopFollowing = stopFollowing; + return container; } }); @@ -192,3 +324,17 @@ L.Map.addInitHook(function () { L.control.locate = function (options) { return new L.Control.Locate(options); }; + +(function(){ + // leaflet.js raises bug when trying to addClass / removeClass multiple classes at once + // Let's create a wrapper on it which fixes it. + var LDomUtilApplyClassesMethod = function(method, element, classNames) { + classNames = classNames.split(' '); + classNames.forEach(function(className) { + L.DomUtil[method].call(this, element, className); + }); + }; + + L.DomUtil.addClasses = function(el, names) { LDomUtilApplyClassesMethod('addClass', el, names); } + L.DomUtil.removeClasses = function(el, names) { LDomUtilApplyClassesMethod('removeClass', el, names); } +})();