diff --git a/geolet.js b/geolet.js new file mode 100644 index 0000000..a668815 --- /dev/null +++ b/geolet.js @@ -0,0 +1,318 @@ +/* +GEOLET version 20.12.31 +Copyright (c) 2020 Ruben Holthuijsen; available under the MIT license +https://github.com/rhlt/leaflet-geolet/ +*/ + +(function () { + + var _consolePrefix = '[GEOLET] '; + var L = window.L; + + if (!L || !L.map) { + //Was Leaflet loaded correctly? + console.error(_consolePrefix + 'Missing Leaflet'); + return; + } + + var _merge = function (object1, object2) { + //Merge all properties from object 2 into object 1, recursively for plain objects + + if (typeof object2 != 'object' || object2 === null) + return object1; + if (typeof object1 != 'object' || object1 === null) + object1 = {}; + + for (key in object2) if (({}).hasOwnProperty.call(object2, key)) { + if (typeof object2[key] == 'object' && object2[key] !== null && Object.getPrototypeOf(object2[key]) == Object.getPrototypeOf({})) { + //Plain object: merge the next level + object1[key] = _merge(object1[key], object2[key]); + } else { + //Anything else (including typed objects): just assign it + object1[key] = object2[key]; + } + } + + return object1; + }; + + var _defaultMarker = function (controlOptions) { + //Generate a default marker + return L.marker(null, { + icon: L.divIcon({ + html: '', + iconAnchor: [10, 10], + className: '' + }), + attribution: 'Geolet', + zIndexOffset: 1000 + }); + }; + + + L.Geolet = L.Control.extend({ + + options: { + title: 'Find current location', + className: null, + activeClassName: null, + style: { display: 'flex', color: '' }, + activeStyle: { display: 'flex', color: '#E00' }, + html: '', + geoOptions: { enableHighAccuracy: true, maximumAge: 30000, timeout: 27000 }, + marker: null, + popup: null, + popupContent: null, + updatePopupWhenOpen: true, + autoPan: true, + minZoom: 9 + }, + + _a: null, + _map: null, + _watchId: null, + _latLng: null, + _popupContent: null, + _first: false, + + marker: null, + popup: null, + + initialize: function (options) { + //Set control options + if (typeof options == 'object' && options !== null) + L.setOptions(this, options); + }, + + isActive: function () { + //Is the control active (are we displaying the current location, or trying to obtain it)? + return this._watchId ? true : false; + }, + + getLatLng: function () { + //Returns the coordinates of the location currently displayed on the map (if any) -- might still be null if isActive() + return this.isActive() ? this._latLng : null; + }, + + updatePopup: function () { + //Update the popup with new content + var popupContent; + if (!this._popup) + return; + if (typeof this.options.popupContent == 'undefined' || this.options.popupContent === null) { + popupContent = '' + L.Geolet.formatLatLng(this.getLatLng()) + ''; + } else if (typeof this.options.popupContent == 'function') { + popupContent = this.options.popupContent.call(this, this.getLatLng()); + } else if (this.options.popupContent) { + popupContent = this.options.popupContent; + } + if (popupContent !== null) + this._popup.setContent(popupContent); + }, + + activate: function () { + //Activate the current location display + + if (!L.Geolet.browserSupport) + return; + + this.styleAnchor(true); + + var control = this; + + var geoSuccessCallback = function (data) { + var first = !!control._first; + control._latLng = L.latLng(data.coords.latitude, data.coords.longitude, data.coords.altitude); + control._popupContent = control.options.popupContent; + if (control._popup) { + if (control._popup.isOpen() && control.options.updatePopupWhenOpen) + control.updatePopup(); + } + if (control._marker) { + control._marker.setLatLng(control._latLng); + control._marker.addTo(control._map); + } + if (control._first) { + control._first = false; + if (control.options.autoPan) + control._map.setView(control._latLng, control.options.minZoom ? Math.max(control._map.getZoom(), control.options.minZoom) : control._map.getZoom()); + } + control._map.fire('geolet_success', { + control: control, + first: first, + marker: control._marker, + latlng: control._latLng, //"latlng" all-lowercase for consistency with Leaflet's MouseEvent + raw: data + }); + }; + + var geoErrorCallback = function (data) { + control.deactivate(); + console.warn(_consolePrefix + data.message); + control._map.fire('geolet_error', { + control: control, + raw: data + }); + }; + + this._first = true; + this._watchId = navigator.geolocation.watchPosition(geoSuccessCallback, geoErrorCallback, this.options.geoOptions); + }, + + deactivate: function () { + //Deactivate current location display + + this.styleAnchor(false); + + if (this.isActive()) { + navigator.geolocation.clearWatch(this._watchId); + this._watchId = null; + } + + this._latLng = null; + + if (this._marker) + this._marker.remove(); + if (this._popup) + this._popup.remove(); + }, + + styleAnchor: function (active) { + //Apply CSS classes and styles to the button's element + + var className = []; + if (!this._a) + return; + if (this.options.className) + className.push(this.options.className); + if (active && this.options.activeClassName) + className.push(this.options.activeClassName); + if (className.length) { + this._a.className = className.join(' '); + } else { + this._a.className = ''; + } + if (active && this.options.activeStyle) + _merge(this._a.style, this.options.activeStyle); + if (!active && this.options.style) + _merge(this._a.style, this.options.style); + }, + + onAdd: function (map) { + //Initialize everything when the control is added + + var control = this; + var el = L.DomUtil.create('div'); + this._map = map; + + if (!L.Geolet.browserSupport) { + console.warn(_consolePrefix + 'Browser does not support Geolocation'); + el.style.display = 'none'; + return el; + } + + if (this._marker) + this._marker.remove(); + if (typeof this.options.marker == 'function') { + this._marker = this.options.marker.call(this, map); + } else if (this.options.marker) { + this._marker = this.options.marker; + } else if (typeof this.options.marker == 'undefined' || this.options.marker === null) { + this._marker = _defaultMarker(); + } else { + this._marker = null; + } + + if (this._popup) + this._popup.remove(); + if (typeof this.options.popup == 'function') { + this._popup = this.options.popup.call(this, map); + } else if (this.options.popup) { + this._popup = this.options.popup; + } else if (typeof this.options.popup == 'undefined' || this.options.popup === null) { + this._popup = L.popup({ autoPan: this.options.autoPan }); + } else { + this._popup = null; + } + + if (this._marker && this._popup) { + this._marker.bindPopup(this._popup); + this._marker.on('popupopen', function () { control.updatePopup(); }); + } + + el.className = 'leaflet-bar leaflet-control'; + + this._a = document.createElement('a'); + this._a.setAttribute('href', '#'); + if (this.options.title) + this._a.setAttribute('title', this.options.title); + + this.styleAnchor(); + + this._a.addEventListener('click', function (event) { + if (!control.isActive()) { + //Currently inactive + control.activate(); + } else { + //Currently active + control.deactivate(); + } + event.preventDefault(); + event.stopPropagation(); + }); + + this._a.addEventListener('dblclick', function (event) { + //Ignore double clicks + event.stopPropagation(); + }); + + if (this.options.html) + this._a.innerHTML = this.options.html; + el.appendChild(this._a); + + return el; + }, + + onRemove: function (map) { + //Deinitialize everything when the control is removed + this.deactivate(); + this._a = null; + this._map = null; + this._popup = null; + this._marker = null; + } + }); + + + //Browser support test + L.Geolet.browserSupport = !!(navigator && navigator.geolocation && navigator.geolocation.watchPosition); + + //Format coordinates (for use in the default pop up) + L.Geolet.formatSymbols = {}; + L.Geolet.formatLatLng = function (latLng, l, a) { + + latLng = L.latLng(latLng, l, a); + if (!latLng) + return; + + var result = []; + var symbols = _merge({ deg: '°', min: '’', sec: '”', N: 'N', E: 'E', S: 'S', W: 'W', space: ' ', comma: ', ' }, L.Geolet.formatSymbols); + + ['lat', 'lng'].forEach(function (key) { + var dir = (key == 'lat' ? (latLng[key] < 0 ? symbols.S : symbols.N) : (latLng[key] < 0 ? symbols.W : symbols.E)); + var val = Math.round(Math.abs(latLng[key]) * 3600) / 3600; + var deg = Math.floor(val) + symbols.deg; + var min = Math.floor(val * 60) % 60 + symbols.min; + var sec = Math.floor(val * 3600) % 60 + symbols.sec; + result.push([deg, min, sec, dir].join(symbols.space)); + }); + + return result.join(symbols.comma); + }; + + //Factory function + L.geolet = function (options) { + return new L.Geolet(options); + }; + +})() diff --git a/index.html b/index.html index 1d92e39..496e11e 100644 --- a/index.html +++ b/index.html @@ -6,11 +6,40 @@ Dashboard + + -
+
\ No newline at end of file diff --git a/toilet.svg b/toilet.svg new file mode 100644 index 0000000..34dcc09 --- /dev/null +++ b/toilet.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file