From a7ec161ba216eae5ce40d8d923aaf11cf56d3dde Mon Sep 17 00:00:00 2001 From: Raruto Date: Wed, 16 Mar 2022 11:38:53 +0100 Subject: [PATCH] upgrade plugins --- .gitignore | 5 +- build/rollup.config.js | 2 + .../leaflet.fullscreenicon-fullscreen.svg | 1 + ...flet.locatecontrollocation-arrow-solid.svg | 1 + .../leaflet.locatecontrolspinner-solid.svg | 1 + dist/leaflet-ui-src.js | 874 ++++++++++-------- dist/leaflet-ui-src.js.map | 2 +- dist/leaflet-ui.css | 2 +- dist/leaflet-ui.js | 20 +- dist/leaflet-ui.js.map | 2 +- package-lock.json | 30 +- package.json | 12 +- src/leaflet-ui.js | 91 +- 13 files changed, 586 insertions(+), 457 deletions(-) create mode 100644 dist/images/leaflet.fullscreenicon-fullscreen.svg create mode 100644 dist/images/leaflet.locatecontrollocation-arrow-solid.svg create mode 100644 dist/images/leaflet.locatecontrolspinner-solid.svg diff --git a/.gitignore b/.gitignore index 47d49f7..06dd67c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules/ npm-debug.log -src/*.min.js -src/*.min.css +src/**/*.min.js +src/**/*.min.css +build/**/*.min.js diff --git a/build/rollup.config.js b/build/rollup.config.js index 367bd96..e06de1d 100644 --- a/build/rollup.config.js +++ b/build/rollup.config.js @@ -31,6 +31,7 @@ export default [ input: input, output: output, plugins: plugins, + moduleContext: { "node_modules/leaflet.fullscreen/Control.FullScreen.js": "window" }, }, //** "leaflet-ui.js" **// @@ -40,6 +41,7 @@ export default [ file: "dist/" + plugin.name + ".js" }), plugins: plugins.concat(terser()), + moduleContext: { "node_modules/leaflet.fullscreen/Control.FullScreen.js": "window" }, }, //** "leaflet-ui.css" **// diff --git a/dist/images/leaflet.fullscreenicon-fullscreen.svg b/dist/images/leaflet.fullscreenicon-fullscreen.svg new file mode 100644 index 0000000..6107d8c --- /dev/null +++ b/dist/images/leaflet.fullscreenicon-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/images/leaflet.locatecontrollocation-arrow-solid.svg b/dist/images/leaflet.locatecontrollocation-arrow-solid.svg new file mode 100644 index 0000000..9fea156 --- /dev/null +++ b/dist/images/leaflet.locatecontrollocation-arrow-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/images/leaflet.locatecontrolspinner-solid.svg b/dist/images/leaflet.locatecontrolspinner-solid.svg new file mode 100644 index 0000000..f795980 --- /dev/null +++ b/dist/images/leaflet.locatecontrolspinner-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/leaflet-ui-src.js b/dist/leaflet-ui-src.js index a624d7b..da9002b 100644 --- a/dist/leaflet-ui-src.js +++ b/dist/leaflet-ui-src.js @@ -1115,7 +1115,7 @@ if (e.shiftKey) { e.preventDefault(); this._map.scrollWheelZoom.disable(); - this._map.setBearing((this._map._bearing * L.DomUtil.RAD_TO_DEG) + e.deltaY); + this._map.setBearing((this._map._bearing * L.DomUtil.RAD_TO_DEG) + Math.sign(e.deltaY) * 5); } else { this._map.scrollWheelZoom.enable(); } @@ -1372,21 +1372,21 @@ window.L.Control.Locate = factory(L); } } (function (L) { - var LDomUtilApplyClassesMethod = function(method, element, classNames) { + const LDomUtilApplyClassesMethod = (method, element, classNames) => { classNames = classNames.split(' '); classNames.forEach(function(className) { L.DomUtil[method].call(this, element, className); }); }; - var addClasses = function(el, names) { LDomUtilApplyClassesMethod('addClass', el, names); }; - var removeClasses = function(el, names) { LDomUtilApplyClassesMethod('removeClass', el, names); }; + const addClasses = (el, names) => LDomUtilApplyClassesMethod('addClass', el, names); + const removeClasses = (el, names) => LDomUtilApplyClassesMethod('removeClass', el, names); /** * Compatible with L.Circle but a true marker instead of a path */ - var LocationMarker = L.Marker.extend({ - initialize: function (latlng, options) { + const LocationMarker = L.Marker.extend({ + initialize(latlng, options) { L.Util.setOptions(this, options); this._latlng = latlng; this.createIcon(); @@ -1395,28 +1395,28 @@ /** * Create a styled circle location marker */ - createIcon: function() { - var opt = this.options; + createIcon() { + const opt = this.options; - var style = ''; + let style = ''; if (opt.color !== undefined) { - style += 'stroke:'+opt.color+';'; + style += `stroke:${opt.color};`; } if (opt.weight !== undefined) { - style += 'stroke-width:'+opt.weight+';'; + style += `stroke-width:${opt.weight};`; } if (opt.fillColor !== undefined) { - style += 'fill:'+opt.fillColor+';'; + style += `fill:${opt.fillColor};`; } if (opt.fillOpacity !== undefined) { - style += 'fill-opacity:'+opt.fillOpacity+';'; + style += `fill-opacity:${opt.fillOpacity};`; } if (opt.opacity !== undefined) { - style += 'opacity:'+opt.opacity+';'; + style += `opacity:${opt.opacity};`; } - var icon = this._getIconSVG(opt, style); + const icon = this._getIconSVG(opt, style); this._locationIcon = L.divIcon({ className: icon.className, @@ -1432,63 +1432,63 @@ * * Split so can be easily overridden */ - _getIconSVG: function(options, style) { - var r = options.radius; - var w = options.weight; - var s = r + w; - var s2 = s * 2; - var svg = '' + + _getIconSVG(options, style) { + const r = options.radius; + const w = options.weight; + const s = r + w; + const s2 = s * 2; + const svg = `` + '' + ''; return { className: 'leaflet-control-locate-location', - svg: svg, + svg, w: s2, h: s2 }; }, - setStyle: function(style) { + setStyle(style) { L.Util.setOptions(this, style); this.createIcon(); } }); - var CompassMarker = LocationMarker.extend({ - initialize: function (latlng, heading, options) { + const CompassMarker = LocationMarker.extend({ + initialize(latlng, heading, options) { L.Util.setOptions(this, options); this._latlng = latlng; this._heading = heading; this.createIcon(); }, - setHeading: function(heading) { + setHeading(heading) { this._heading = heading; }, /** * Create a styled arrow compass marker */ - _getIconSVG: function(options, style) { - var r = options.radius; - var w = (options.width + options.weight); - var h = (r+options.depth + options.weight)*2; - var path = 'M0,0 l'+(options.width/2)+','+options.depth+' l-'+(w)+',0 z'; - var svgstyle = 'transform: rotate('+this._heading+'deg)'; - var svg = ''+ + _getIconSVG(options, style) { + const r = options.radius; + const w = (options.width + options.weight); + const h = (r+options.depth + options.weight)*2; + const path = `M0,0 l${options.width/2},${options.depth} l-${w},0 z`; + const svgstyle = `transform: rotate(${this._heading}deg)`; + const svg = ``+ ''+ ''; return { className: 'leaflet-control-locate-heading', - svg: svg, - w: w, - h: h + svg, + w, + h }; }, }); - var LocateControl = L.Control.extend({ + const LocateControl = L.Control.extend({ options: { /** Position of the control */ position: 'topleft', @@ -1514,8 +1514,8 @@ setView: 'untilPanOrZoom', /** Keep the current map zoom level when setting the view and only pan. */ keepCurrentZoomLevel: false, - /** After activating the plugin by clicking on the icon, zoom to the selected zoom level, even when keepCurrentZoomLevel is true. Set to 'false' to disable this feature. */ - initialZoomLevel: false, + /** After activating the plugin by clicking on the icon, zoom to the selected zoom level, even when keepCurrentZoomLevel is true. Set to 'false' to disable this feature. */ + initialZoomLevel: false, /** * This callback can be used to override the viewport tracking * This function should return a LatLngBounds object. @@ -1526,7 +1526,7 @@ * return locationEvent.bounds.extend([-33.873085, 151.219273]); * }, */ - getLocationBounds: function (locationEvent) { + getLocationBounds(locationEvent) { return locationEvent.bounds; }, /** Smooth pan and zoom to the location of the marker. Only works in Leaflet 1.0+. */ @@ -1610,8 +1610,8 @@ }, followCompassStyle: {}, /** The CSS class for the icon. For example fa-location-arrow or fa-map-marker */ - icon: 'fa fa-map-marker', - iconLoading: 'fa fa-spinner fa-spin', + icon: 'leaflet-control-locate-location-arrow', + iconLoading: 'leaflet-control-locate-spinner', /** The element to be created for icons. For example span or i */ iconElementTag: 'span', /** The element to be created for the text. For example small or span */ @@ -1625,34 +1625,34 @@ * This is useful for DOM manipulation frameworks such as angular etc. * This function should return an object with HtmlElement for the button (link property) and the icon (icon property). */ - createButtonCallback: function (container, options) { - var link = L.DomUtil.create('a', 'leaflet-bar-part leaflet-bar-part-single', container); + createButtonCallback(container, options) { + const link = L.DomUtil.create('a', 'leaflet-bar-part leaflet-bar-part-single', container); link.title = options.strings.title; link.role = 'button'; link.href = '#'; - var icon = L.DomUtil.create(options.iconElementTag, options.icon, link); + const icon = L.DomUtil.create(options.iconElementTag, options.icon, link); if (options.strings.text !== undefined) { - var text = L.DomUtil.create(options.textElementTag, 'leaflet-locate-text', link); + const text = L.DomUtil.create(options.textElementTag, 'leaflet-locate-text', link); text.textContent = options.strings.text; - link.classList.add('leaflet-locate-text-active'); + link.classList.add('leaflet-locate-text-active'); link.parentNode.style.display = "flex"; if (options.icon.length > 0) { icon.classList.add('leaflet-locate-icon'); } } - return { link: link, icon: icon }; + return { link, icon }; }, /** This event is called in case of any location error that is not a time out error. */ - onLocationError: function(err, control) { + onLocationError(err, control) { alert(err.message); }, /** * This event is called when the user's location is outside the bounds set on the map. * The event is called repeatedly when the location changes. */ - onLocationOutsideMapBounds: function(control) { + onLocationOutsideMapBounds(control) { control.stop(); alert(control.options.strings.outsideMapBoundsMsg); }, @@ -1674,9 +1674,9 @@ } }, - initialize: function (options) { + initialize(options) { // set default options if nothing is set (merge one step deep) - for (var i in options) { + for (const i in options) { if (typeof this.options[i] === 'object') { L.extend(this.options[i], options[i]); } else { @@ -1693,9 +1693,9 @@ /** * Add control to map. Returns the container for the control. */ - onAdd: function (map) { - var container = L.DomUtil.create('div', - 'leaflet-control-locate leaflet-bar leaflet-control'); + onAdd(map) { + const container = L.DomUtil.create('div', + 'leaflet-control-locate leaflet-bar leaflet-control'); this._container = container; this._map = map; this._layer = this.options.layer || new L.LayerGroup(); @@ -1704,7 +1704,7 @@ this._compassHeading = null; this._prevBounds = null; - var linkAndIcon = this.options.createButtonCallback(container, this.options); + const linkAndIcon = this.options.createButtonCallback(container, this.options); this._link = linkAndIcon.link; this._icon = linkAndIcon.icon; @@ -1729,9 +1729,9 @@ /** * This method is called when the user clicks on the control. */ - _onClick: function() { + _onClick() { this._justClicked = true; - var wasFollowing = this._isFollowing(); + const wasFollowing = this._isFollowing(); this._userPanned = false; this._userZoomed = false; @@ -1739,8 +1739,8 @@ // click while requesting this.stop(); } else if (this._active) { - var behaviors = this.options.clickBehavior; - var behavior = behaviors.outOfView; + const behaviors = this.options.clickBehavior; + let behavior = behaviors.outOfView; if (this._map.getBounds().contains(this._event.latlng)) { behavior = wasFollowing ? behaviors.inView : behaviors.inViewNotFollowing; } @@ -1757,7 +1757,7 @@ case 'stop': this.stop(); if (this.options.returnToPrevBounds) { - var f = this.options.flyTo ? this._map.flyToBounds : this._map.fitBounds; + const f = this.options.flyTo ? this._map.flyToBounds : this._map.fitBounds; f.bind(this._map)(this._prevBounds); } break; @@ -1777,7 +1777,7 @@ * - activates the engine * - draws the marker (if coordinates available) */ - start: function() { + start() { this._activate(); if (this._event) { @@ -1797,7 +1797,7 @@ * - reinitializes the button * - removes the marker */ - stop: function() { + stop() { this._deactivate(); this._cleanClasses(); @@ -1809,7 +1809,7 @@ /** * Keep the control active but stop following the location */ - stopFollowing: function() { + stopFollowing() { this._userPanned = true; this._updateContainerStyle(); this._drawMarker(); @@ -1824,7 +1824,7 @@ * It should set the this._active to true and do nothing if * this._active is true. */ - _activate: function() { + _activate() { if (!this._active) { this._map.locate(this.options.locateOptions); this._map.fire('locateactivate', this); @@ -1837,10 +1837,10 @@ this._map.on('zoomstart', this._onZoom, this); this._map.on('zoomend', this._onZoomEnd, this); if (this.options.showCompass) { - var oriAbs = 'ondeviceorientationabsolute' in window; + const oriAbs = 'ondeviceorientationabsolute' in window; if (oriAbs || ('ondeviceorientation' in window)) { - var _this = this; - var deviceorientation = function () { + const _this = this; + const deviceorientation = function () { L.DomEvent.on(window, oriAbs ? 'deviceorientationabsolute' : 'deviceorientation', _this._onDeviceOrientation, _this); }; if (DeviceOrientationEvent && typeof DeviceOrientationEvent.requestPermission === 'function') { @@ -1862,7 +1862,7 @@ * * Override it to shutdown any functionalities you added on start. */ - _deactivate: function() { + _deactivate() { this._map.stopLocate(); this._map.fire('locatedeactivate', this); this._active = false; @@ -1890,48 +1890,47 @@ /** * Zoom (unless we should keep the zoom level) and an to the current view. */ - setView: function() { - this._drawMarker(); - if (this._isOutsideMapBounds()) { - this._event = undefined; // clear the current location so we can get back into the bounds - this.options.onLocationOutsideMapBounds(this); - } else { - if (this._justClicked && this.options.initialZoomLevel !== false) { - var f = this.options.flyTo ? this._map.flyTo : this._map.setView; - f.bind(this._map)([this._event.latitude, this._event.longitude], this.options.initialZoomLevel); - } else - if (this.options.keepCurrentZoomLevel) { - var f = this.options.flyTo ? this._map.flyTo : this._map.panTo; - f.bind(this._map)([this._event.latitude, this._event.longitude]); - } else { - var f = this.options.flyTo ? this._map.flyToBounds : this._map.fitBounds; - // Ignore zoom events while setting the viewport as these would stop following - this._ignoreEvent = true; - f.bind(this._map)(this.options.getLocationBounds(this._event), { - padding: this.options.circlePadding, - maxZoom: this.options.locateOptions.maxZoom - }); - L.Util.requestAnimFrame(function(){ - // Wait until after the next animFrame because the flyTo can be async - this._ignoreEvent = false; - }, this); + setView() { + this._drawMarker(); + if (this._isOutsideMapBounds()) { + this._event = undefined; // clear the current location so we can get back into the bounds + this.options.onLocationOutsideMapBounds(this); + } else { + if (this._justClicked && this.options.initialZoomLevel !== false) { + var f = this.options.flyTo ? this._map.flyTo : this._map.setView; + f.bind(this._map)([this._event.latitude, this._event.longitude], this.options.initialZoomLevel); + } else if (this.options.keepCurrentZoomLevel) { + var f = this.options.flyTo ? this._map.flyTo : this._map.panTo; + f.bind(this._map)([this._event.latitude, this._event.longitude]); + } else { + var f = this.options.flyTo ? this._map.flyToBounds : this._map.fitBounds; + // Ignore zoom events while setting the viewport as these would stop following + this._ignoreEvent = true; + f.bind(this._map)(this.options.getLocationBounds(this._event), { + padding: this.options.circlePadding, + maxZoom: this.options.initialZoomLevel || this.options.locateOptions.maxZoom + }); + L.Util.requestAnimFrame(function(){ + // Wait until after the next animFrame because the flyTo can be async + this._ignoreEvent = false; + }, this); - } - } - }, + } + } + }, /** * */ - _drawCompass: function() { + _drawCompass() { if (!this._event) { return; } - var latlng = this._event.latlng; + const latlng = this._event.latlng; if (this.options.showCompass && latlng && this._compassHeading !== null) { - var cStyle = this._isFollowing() ? this.options.followCompassStyle : this.options.compassStyle; + const cStyle = this._isFollowing() ? this.options.followCompassStyle : this.options.compassStyle; if (!this._compass) { this._compass = new this.options.compassClass(latlng, this._compassHeading, cStyle).addTo(this._layer); } else { @@ -1955,17 +1954,17 @@ * * Uses the event retrieved from onLocationFound from the map. */ - _drawMarker: function() { + _drawMarker() { if (this._event.accuracy === undefined) { this._event.accuracy = 0; } - var radius = this._event.accuracy; - var latlng = this._event.latlng; + const radius = this._event.accuracy; + const latlng = this._event.latlng; // circle with the radius of the location's accuracy if (this.options.drawCircle) { - var style = this._isFollowing() ? this.options.followCircleStyle : this.options.circleStyle; + const style = this._isFollowing() ? this.options.followCircleStyle : this.options.circleStyle; if (!this._circle) { this._circle = L.circle(latlng, radius, style).addTo(this._layer); @@ -1974,7 +1973,8 @@ } } - var distance, unit; + let distance; + let unit; if (this.options.metric) { distance = radius.toFixed(0); unit = this.options.strings.metersUnit; @@ -1985,7 +1985,7 @@ // small inner marker if (this.options.drawMarker) { - var mStyle = this._isFollowing() ? this.options.followMarkerStyle : this.options.markerStyle; + const mStyle = this._isFollowing() ? this.options.followMarkerStyle : this.options.markerStyle; if (!this._marker) { this._marker = new this.options.markerClass(latlng, mStyle).addTo(this._layer); } else { @@ -1999,12 +1999,12 @@ this._drawCompass(); - var t = this.options.strings.popup; + const t = this.options.strings.popup; function getPopupText() { if (typeof t === 'string') { - return L.Util.template(t, {distance: distance, unit: unit}); + return L.Util.template(t, {distance, unit}); } else if (typeof t === 'function') { - return t({distance: distance, unit: unit}); + return t({distance, unit}); } else { return t; } @@ -2024,7 +2024,7 @@ /** * Remove the marker from map. */ - _removeMarker: function() { + _removeMarker() { this._layer.clearLayers(); this._marker = undefined; this._circle = undefined; @@ -2034,7 +2034,7 @@ * Unload the plugin and all event listeners. * Kind of the opposite of onAdd. */ - _unload: function() { + _unload() { this.stop(); this._map.off('unload', this._unload, this); }, @@ -2042,7 +2042,7 @@ /** * Sets the compass heading */ - _setCompassHeading: function(angle) { + _setCompassHeading(angle) { if (!isNaN(parseFloat(angle)) && isFinite(angle)) { angle = Math.round(angle); @@ -2056,14 +2056,14 @@ /** * If the compass fails calibration just fail safely and remove the compass */ - _onCompassNeedsCalibration: function() { + _onCompassNeedsCalibration() { this._setCompassHeading(); }, /** * Process and normalise compass events */ - _onDeviceOrientation: function(e) { + _onDeviceOrientation(e) { if (!this._active) { return; } @@ -2080,7 +2080,7 @@ /** * Calls deactivate and dispatches an error. */ - _onLocationError: function(err) { + _onLocationError(err) { // ignore time out error if the location is watched if (err.code == 3 && this.options.locateOptions.watch) { return; @@ -2093,7 +2093,7 @@ /** * Stores the received event and updates the marker. */ - _onLocationFound: function(e) { + _onLocationFound(e) { // no need to do anything if the location has not changed if (this._event && (this._event.latlng.lat === e.latlng.lat && @@ -2139,7 +2139,7 @@ /** * When the user drags. Need a separate event so we can bind and unbind event listeners. */ - _onDrag: function() { + _onDrag() { // only react to drags once we have a location if (this._event && !this._ignoreEvent) { this._userPanned = true; @@ -2151,7 +2151,7 @@ /** * When the user zooms. Need a separate event so we can bind and unbind event listeners. */ - _onZoom: function() { + _onZoom() { // only react to drags once we have a location if (this._event && !this._ignoreEvent) { this._userZoomed = true; @@ -2163,7 +2163,7 @@ /** * After a zoom ends update the compass and handle sideways zooms */ - _onZoomEnd: function() { + _onZoomEnd() { if (this._event) { this._drawCompass(); } @@ -2181,7 +2181,7 @@ /** * Compute whether the map is following the user location with pan and zoom. */ - _isFollowing: function() { + _isFollowing() { if (!this._active) { return false; } @@ -2198,7 +2198,7 @@ /** * Check if location is in map bounds */ - _isOutsideMapBounds: function() { + _isOutsideMapBounds() { if (this._event === undefined) { return false; } @@ -2209,7 +2209,7 @@ /** * Toggles button class between following and active. */ - _updateContainerStyle: function() { + _updateContainerStyle() { if (!this._container) { return; } @@ -2229,7 +2229,7 @@ /** * Sets the CSS classes for the state. */ - _setClasses: function(state) { + _setClasses(state) { if (state == 'requesting') { removeClasses(this._container, "active following"); addClasses(this._container, "requesting"); @@ -2254,7 +2254,7 @@ /** * Removes all classes from button. */ - _cleanClasses: function() { + _cleanClasses() { L.DomUtil.removeClass(this._container, "requesting"); L.DomUtil.removeClass(this._container, "active"); L.DomUtil.removeClass(this._container, "following"); @@ -2266,7 +2266,7 @@ /** * Reinitializes state variables. */ - _resetVariables: function() { + _resetVariables() { // whether locate is active or not this._active = false; @@ -2282,241 +2282,354 @@ } }); - L.control.locate = function (options) { - return new L.Control.Locate(options); - }; + L.control.locate = (options) => new L.Control.Locate(options); return LocateControl; }, window)); - (function () { - - L.Control.FullScreen = L.Control.extend({ - options: { - position: 'topleft', - title: 'Full Screen', - titleCancel: 'Exit Full Screen', - forceSeparateButton: false, - forcePseudoFullscreen: false, - fullscreenElement: false - }, - - onAdd: function (map) { - var className = 'leaflet-control-zoom-fullscreen', container, content = ''; - - if (map.zoomControl && !this.options.forceSeparateButton) { - container = map.zoomControl._container; - } else { - container = L.DomUtil.create('div', 'leaflet-bar'); - } - - if (this.options.content) { - content = this.options.content; - } else { - className += ' fullscreen-icon'; + /*! + * Based on package 'screenfull' + * v5.1.0 - 2020-12-24 + * (c) Sindre Sorhus; MIT License + * Added definition for using screenfull as an amd module + * Must be placed before the definition of leaflet.fullscreen + * as it is required by that + */ + (function (root, factory) { + if (typeof define === 'function' && define.amd) { + define('screenfull', factory); + } else if (typeof module === 'object' && module.exports) { + module.exports.screenfull = factory(); + } else { + // Save 'screenfull' into global window variable + root.screenfull = factory(); + } + }(window, function () { + + var document = typeof window !== 'undefined' && typeof window.document !== 'undefined' ? window.document : {}; + + var fn = (function () { + var val; + + var fnMap = [ + [ + 'requestFullscreen', + 'exitFullscreen', + 'fullscreenElement', + 'fullscreenEnabled', + 'fullscreenchange', + 'fullscreenerror' + ], + // New WebKit + [ + 'webkitRequestFullscreen', + 'webkitExitFullscreen', + 'webkitFullscreenElement', + 'webkitFullscreenEnabled', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + // Old WebKit + [ + 'webkitRequestFullScreen', + 'webkitCancelFullScreen', + 'webkitCurrentFullScreenElement', + 'webkitCancelFullScreen', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + [ + 'mozRequestFullScreen', + 'mozCancelFullScreen', + 'mozFullScreenElement', + 'mozFullScreenEnabled', + 'mozfullscreenchange', + 'mozfullscreenerror' + ], + [ + 'msRequestFullscreen', + 'msExitFullscreen', + 'msFullscreenElement', + 'msFullscreenEnabled', + 'MSFullscreenChange', + 'MSFullscreenError' + ] + ]; + + var i = 0; + var l = fnMap.length; + var ret = {}; + + for (; i < l; i++) { + val = fnMap[i]; + if (val && val[1] in document) { + for (i = 0; i < val.length; i++) { + ret[fnMap[0][i]] = val[i]; + } + return ret; + } } - this._createButton(this.options.title, className, content, container, this.toggleFullScreen, this); - this._map.fullscreenControl = this; + return false; + })(); - this._map.on('enterFullscreen exitFullscreen', this._toggleTitle, this); + var eventNameMap = { + change: fn.fullscreenchange, + error: fn.fullscreenerror + }; - return container; - }, - - onRemove: function (map) { - L.DomEvent - .off(this.link, 'click', L.DomEvent.stopPropagation) - .off(this.link, 'click', L.DomEvent.preventDefault) - .off(this.link, 'click', this.toggleFullScreen, this); - - L.DomEvent - .off(this._container, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation) - .off(this._container, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault) - .off(this._container, fullScreenApi.fullScreenEventName, this._handleFullscreenChange, this); - - L.DomEvent - .off(document, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation) - .off(document, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault) - .off(document, fullScreenApi.fullScreenEventName, this._handleFullscreenChange, this); - }, - - _createButton: function (title, className, content, container, fn, context) { - this.link = L.DomUtil.create('a', className, container); - this.link.href = '#'; - this.link.title = title; - this.link.innerHTML = content; + var screenfull = { + request: function (element, options) { + return new Promise(function (resolve, reject) { + var onFullScreenEntered = function () { + this.off('change', onFullScreenEntered); + resolve(); + }.bind(this); - this.link.setAttribute('role', 'button'); - this.link.setAttribute('aria-label', title); + this.on('change', onFullScreenEntered); - L.DomEvent - .on(this.link, 'click', L.DomEvent.stopPropagation) - .on(this.link, 'click', L.DomEvent.preventDefault) - .on(this.link, 'click', fn, context); - - L.DomEvent - .on(container, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation) - .on(container, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault) - .on(container, fullScreenApi.fullScreenEventName, this._handleFullscreenChange, context); - - L.DomEvent - .on(document, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation) - .on(document, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault) - .on(document, fullScreenApi.fullScreenEventName, this._handleFullscreenChange, context); + element = element || document.documentElement; - return this.link; - }, - - toggleFullScreen: function () { - var map = this._map; - map._exitFired = false; - if (map._isFullscreen) { - if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) { - fullScreenApi.cancelFullScreen(); - } else { - L.DomUtil.removeClass(this.options.fullscreenElement ? this.options.fullscreenElement : map._container, 'leaflet-pseudo-fullscreen'); + var returnPromise = element[fn.requestFullscreen](options); + + if (returnPromise instanceof Promise) { + returnPromise.then(onFullScreenEntered).catch(reject); + } + }.bind(this)); + }, + exit: function () { + return new Promise(function (resolve, reject) { + if (!this.isFullscreen) { + resolve(); + return; + } + + var onFullScreenExit = function () { + this.off('change', onFullScreenExit); + resolve(); + }.bind(this); + + this.on('change', onFullScreenExit); + + var returnPromise = document[fn.exitFullscreen](); + + if (returnPromise instanceof Promise) { + returnPromise.then(onFullScreenExit).catch(reject); + } + }.bind(this)); + }, + toggle: function (element, options) { + return this.isFullscreen ? this.exit() : this.request(element, options); + }, + onchange: function (callback) { + this.on('change', callback); + }, + onerror: function (callback) { + this.on('error', callback); + }, + on: function (event, callback) { + var eventName = eventNameMap[event]; + if (eventName) { + document.addEventListener(eventName, callback, false); } - map.fire('exitFullscreen'); - map._exitFired = true; - map._isFullscreen = false; - } - else { - if (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) { - fullScreenApi.requestFullScreen(this.options.fullscreenElement ? this.options.fullscreenElement : map._container); - } else { - L.DomUtil.addClass(this.options.fullscreenElement ? this.options.fullscreenElement : map._container, 'leaflet-pseudo-fullscreen'); + }, + off: function (event, callback) { + var eventName = eventNameMap[event]; + if (eventName) { + document.removeEventListener(eventName, callback, false); } - map.fire('enterFullscreen'); - map._isFullscreen = true; - } - }, - - _toggleTitle: function () { - this.link.title = this._map._isFullscreen ? this.options.title : this.options.titleCancel; - }, - - _handleFullscreenChange: function () { - var map = this._map; - map.invalidateSize(); - if (!fullScreenApi.isFullScreen() && !map._exitFired) { - map.fire('exitFullscreen'); - map._exitFired = true; - map._isFullscreen = false; - } - } - }); + }, + raw: fn + }; - L.Map.include({ - toggleFullscreen: function () { - this.fullscreenControl.toggleFullScreen(); + if (!fn) { + return {isEnabled: false}; + } else { + Object.defineProperties(screenfull, { + isFullscreen: { + get: function () { + return Boolean(document[fn.fullscreenElement]); + } + }, + element: { + enumerable: true, + get: function () { + return document[fn.fullscreenElement]; + } + }, + isEnabled: { + enumerable: true, + get: function () { + // Coerce to boolean in case of old WebKit + return Boolean(document[fn.fullscreenEnabled]); + } + } + }); + return screenfull; } - }); + })); - L.Map.addInitHook(function () { - if (this.options.fullscreenControl) { - this.addControl(L.control.fullscreen(this.options.fullscreenControlOptions)); + /*! + * leaflet.fullscreen + */ + (function (root, factory) { + if (typeof define === 'function' && define.amd) { + // define an AMD module that requires 'leaflet' and 'screenfull' + // and resolve to an object containing leaflet and screenfull + define('leafletFullScreen', ['leaflet', 'screenfull'], factory); + } else if (typeof module === 'object' && module.exports) { + // define a CommonJS module that requires 'leaflet' and 'screenfull' + module.exports = factory(require('leaflet'), require('screenfull')); + } else { + // Assume 'leaflet' and 'screenfull' are loaded into global variable already + factory(root.L, root.screenfull); } - }); + }(window, function (leaflet, screenfull) { + + leaflet.Control.FullScreen = leaflet.Control.extend({ + options: { + position: 'topleft', + title: 'Full Screen', + titleCancel: 'Exit Full Screen', + forceSeparateButton: false, + forcePseudoFullscreen: false, + fullscreenElement: false + }, - L.control.fullscreen = function (options) { - return new L.Control.FullScreen(options); - }; + _screenfull: screenfull, - /* - Native FullScreen JavaScript API - ------------- - Assumes Mozilla naming conventions instead of W3C for now + onAdd: function (map) { + var className = 'leaflet-control-zoom-fullscreen', container, content = ''; - source : http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/ + if (map.zoomControl && !this.options.forceSeparateButton) { + container = map.zoomControl._container; + } else { + container = leaflet.DomUtil.create('div', 'leaflet-bar'); + } - */ + if (this.options.content) { + content = this.options.content; + } else { + className += ' fullscreen-icon'; + } + + this._createButton(this.options.title, className, content, container, this.toggleFullScreen, this); + this._map.fullscreenControl = this; - var - fullScreenApi = { - supportsFullScreen: false, - isFullScreen: function () { return false; }, - requestFullScreen: function () {}, - cancelFullScreen: function () {}, - fullScreenEventName: '', - prefix: '' + this._map.on('enterFullscreen exitFullscreen', this._toggleState, this); + + return container; }, - browserPrefixes = 'webkit moz o ms khtml'.split(' '); - - // check for native support - if (typeof document.exitFullscreen !== 'undefined') { - fullScreenApi.supportsFullScreen = true; - } else { - // check for fullscreen support by vendor prefix - for (var i = 0, il = browserPrefixes.length; i < il; i++) { - fullScreenApi.prefix = browserPrefixes[i]; - if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] !== 'undefined') { - fullScreenApi.supportsFullScreen = true; - break; + + onRemove: function () { + leaflet.DomEvent + .off(this.link, 'click', leaflet.DomEvent.stopPropagation) + .off(this.link, 'click', leaflet.DomEvent.preventDefault) + .off(this.link, 'click', this.toggleFullScreen, this); + + leaflet.DomEvent + .off(this._container, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.stopPropagation) + .off(this._container, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.preventDefault) + .off(this._container, this._screenfull.raw.fullscreenchange, this._handleFullscreenChange, this); + + leaflet.DomEvent + .off(document, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.stopPropagation) + .off(document, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.preventDefault) + .off(document, this._screenfull.raw.fullscreenchange, this._handleFullscreenChange, this); + }, + + _createButton: function (title, className, content, container, fn, context) { + this.link = leaflet.DomUtil.create('a', className, container); + this.link.href = '#'; + this.link.title = title; + this.link.innerHTML = content; + + this.link.setAttribute('role', 'button'); + this.link.setAttribute('aria-label', title); + + leaflet.DomEvent + .on(this.link, 'click', leaflet.DomEvent.stopPropagation) + .on(this.link, 'click', leaflet.DomEvent.preventDefault) + .on(this.link, 'click', fn, context); + + leaflet.DomEvent + .on(container, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.stopPropagation) + .on(container, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.preventDefault) + .on(container, this._screenfull.raw.fullscreenchange, this._handleFullscreenChange, context); + + leaflet.DomEvent + .on(document, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.stopPropagation) + .on(document, this._screenfull.raw.fullscreenchange, leaflet.DomEvent.preventDefault) + .on(document, this._screenfull.raw.fullscreenchange, this._handleFullscreenChange, context); + + return this.link; + }, + + toggleFullScreen: function () { + var map = this._map; + map._exitFired = false; + if (map._isFullscreen) { + if (this._screenfull.isEnabled && !this.options.forcePseudoFullscreen) { + this._screenfull.exit(); + } else { + leaflet.DomUtil.removeClass(this.options.fullscreenElement ? this.options.fullscreenElement : map._container, 'leaflet-pseudo-fullscreen'); + map.invalidateSize(); + } + map.fire('exitFullscreen'); + map._exitFired = true; + map._isFullscreen = false; + } + else { + if (this._screenfull.isEnabled && !this.options.forcePseudoFullscreen) { + this._screenfull.request(this.options.fullscreenElement ? this.options.fullscreenElement : map._container); + } else { + leaflet.DomUtil.addClass(this.options.fullscreenElement ? this.options.fullscreenElement : map._container, 'leaflet-pseudo-fullscreen'); + map.invalidateSize(); + } + map.fire('enterFullscreen'); + map._isFullscreen = true; + } + }, + + _toggleState: function () { + this.link.title = this._map._isFullscreen ? this.options.title : this.options.titleCancel; + this._map._isFullscreen ? L.DomUtil.removeClass(this.link, 'leaflet-fullscreen-on') : L.DomUtil.addClass(this.link, 'leaflet-fullscreen-on'); + }, + + _handleFullscreenChange: function () { + var map = this._map; + map.invalidateSize(); + if (!this._screenfull.isFullscreen && !map._exitFired) { + map.fire('exitFullscreen'); + map._exitFired = true; + map._isFullscreen = false; } } - if (typeof document['msExitFullscreen'] !== 'undefined') { - fullScreenApi.prefix = 'ms'; - fullScreenApi.supportsFullScreen = true; + }); + + leaflet.Map.include({ + toggleFullscreen: function () { + this.fullscreenControl.toggleFullScreen(); } - } - - // update methods to do something useful - if (fullScreenApi.supportsFullScreen) { - if (fullScreenApi.prefix === 'ms') { - fullScreenApi.fullScreenEventName = 'MSFullscreenChange'; - } else { - fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange'; + }); + + leaflet.Map.addInitHook(function () { + if (this.options.fullscreenControl) { + this.addControl(leaflet.control.fullscreen(this.options.fullscreenControlOptions)); } - fullScreenApi.isFullScreen = function () { - switch (this.prefix) { - case '': - return document.fullscreen; - case 'webkit': - return document.webkitIsFullScreen; - case 'ms': - return document.msFullscreenElement; - default: - return document[this.prefix + 'FullScreen']; - } - }; - fullScreenApi.requestFullScreen = function (el) { - switch (this.prefix) { - case '': - return el.requestFullscreen(); - case 'ms': - return el.msRequestFullscreen(); - default: - return el[this.prefix + 'RequestFullScreen'](); - } - }; - fullScreenApi.cancelFullScreen = function () { - switch (this.prefix) { - case '': - return document.exitFullscreen(); - case 'ms': - return document.msExitFullscreen(); - default: - return document[this.prefix + 'CancelFullScreen'](); - } - }; - } + }); - // jQuery plugin - if (typeof jQuery !== 'undefined') { - jQuery.fn.requestFullScreen = function () { - return this.each(function () { - var el = jQuery(this); - if (fullScreenApi.supportsFullScreen) { - fullScreenApi.requestFullScreen(el); - } - }); - }; - } + leaflet.control.fullscreen = function (options) { + return new leaflet.Control.FullScreen(options); + }; - // export api - window.fullScreenApi = fullScreenApi; - })(); + // must return an object containing also screenfull to make screenfull + // available outside of this package, if used as an amd module, + // as webpack cannot handle amd define with moduleid + return {leaflet: leaflet, screenfull: screenfull}; + })); /** * leaflet-pegman @@ -4182,9 +4295,9 @@ })(); /* - * Leaflet Control Search v2.9.9 - 2020-12-01 + * Leaflet Control Search v3.0.0 - 2021-08-18 * - * Copyright 2020 Stefano Cudini + * Copyright 2021 Stefano Cudini * stefano.cudini@gmail.com * https://opengeo.tech/ * @@ -4937,7 +5050,7 @@ L.DomUtil.addClass(this._container, 'search-load'); - if(this._layer) + if(this.options.layer) { //TODO _recordsFromLayer must return array of objects, formatted from _formatData this._recordsCache = this._recordsFromLayer(); @@ -4994,7 +5107,7 @@ var searchTips = this._tooltip.hasChildNodes() ? this._tooltip.childNodes : []; - for (i=0; i= (searchTips.length - 1))) {// If at end of list. @@ -5448,59 +5561,57 @@ L.Map.addInitHook('addHandler', 'visualClick', L.Map.VisualClick); - const currentScript = document.currentScript; - const currentVersion = version.split("+")[0].trim(); - - - var lazyLoader = { + (function() { - baseURL: 'https://unpkg.com/', + const currentScript = document.currentScript; + const currentVersion = version.split("+")[0].trim(); - // Sequentially download multiple scripts. - loadSyncScripts: function(urls) { - return urls.reduce((prev, curr) => prev.then(() => lazyLoader.loadAsyncScripts(curr)), Promise.resolve()); - }, + var lazyLoader = { - // Parallel download multiple scripts. - loadAsyncScripts: function(urls) { - return Promise.all(urls.map((url) => lazyLoader.loadScript(url))); - }, + baseURL: 'https://unpkg.com/', - // Dynamically load a single script. - loadScript: function(url) { - return new Promise((resolve, reject) => { + // Sequentially download multiple scripts. + loadSyncScripts: function(urls) { + return urls.reduce((prev, curr) => prev.then(() => lazyLoader.loadAsyncScripts(curr)), Promise.resolve()); + }, - let type = url.split('.').pop().split('?')[0]; - let tag = type == 'css' ? 'link' : 'script'; - let script = document.createElement(tag); - let head = document.head; - let root_script = (head.contains(currentScript) ? currentScript : head.lastChild) || head; - let prev_tag = lazyLoader["prev_" + tag] || (tag == 'script' && lazyLoader.prev_link ? lazyLoader.prev_link : root_script); - let base_url = (url.indexOf(".") === 0 || url.indexOf("/") === 0 || url.indexOf('http://') === 0 || url.indexOf('https://') === 0) ? '' : lazyLoader.baseURL; + // Parallel download multiple scripts. + loadAsyncScripts: function(urls) { + return Promise.all(urls.map((url) => lazyLoader.loadScript(url))); + }, - if (type == 'css') { - script.rel = 'stylesheet'; - } + // Dynamically load a single script. + loadScript: function(url) { + return new Promise((resolve, reject) => { - script.addEventListener('load', resolve, { - once: true - }); - script.setAttribute(type == 'css' ? 'href' : 'src', base_url + url); + let type = url.split('.').pop().split('?')[0]; + let tag = type == 'css' ? 'link' : 'script'; + let script = document.createElement(tag); + let head = document.head; + let root_script = (head.contains(currentScript) ? currentScript : head.lastChild) || head; + let prev_tag = lazyLoader["prev_" + tag] || (tag == 'script' && lazyLoader.prev_link ? lazyLoader.prev_link : root_script); + let base_url = (url.indexOf(".") === 0 || url.indexOf("/") === 0 || url.indexOf('http://') === 0 || url.indexOf('https://') === 0) ? '' : lazyLoader.baseURL; - if (prev_tag.parentNode && prev_tag.nextSibling) - prev_tag.parentNode.insertBefore(script, prev_tag.nextSibling); - else - head.appendChild(script); + if (type == 'css') { + script.rel = 'stylesheet'; + } - lazyLoader["prev_" + tag] = script; + script.addEventListener('load', resolve, { + once: true + }); + script.setAttribute(type == 'css' ? 'href' : 'src', base_url + url); - }); - } + if (prev_tag.parentNode && prev_tag.nextSibling) + prev_tag.parentNode.insertBefore(script, prev_tag.nextSibling); + else + head.appendChild(script); - }; + lazyLoader["prev_" + tag] = script; + }); + } - (function() { + }; // You can ovveride them by passing one of the following to leaflet map constructor. var default_options = { @@ -5567,7 +5678,7 @@ position: 'bottomright' }, rotateControl: { - position: 'bottomright' + position: 'bottomright', }, scaleControl: { width: 200, @@ -5682,6 +5793,7 @@ resizerControl: false, disableDefaultUI: false, includeLeafletCSS: true, + includeLeafletUICSS: true, apiKeys: undefined, // eg. { thunderforest: "", google: "", ... } _isMiniMap: false, // used to prevent infinite loops when loading the minimap control. }); @@ -5918,7 +6030,10 @@ // Load custom plugins. if (this.options.plugins) { if (!lazyLoader.loader) { - var core_plugins = ["leaflet-ui@" + currentVersion + "/dist/leaflet-ui.css"]; + var core_plugins = []; + if (this.options.includeLeafletUICSS) { + core_plugins.unshift("leaflet-ui@" + currentVersion + "/dist/leaflet-ui.css"); + } if (!window.L) { core_plugins.unshift("leaflet@1.3.4/dist/leaflet.css"); core_plugins.unshift("leaflet@1.3.4/dist/leaflet.js"); @@ -5950,10 +6065,7 @@ }); } - lazyLoader.loader - .then(function() { - this.fire('plugins_loaded'); - }.bind(this)); + lazyLoader.loader.then(() => this.fire('plugins_loaded')); } } diff --git a/dist/leaflet-ui-src.js.map b/dist/leaflet-ui-src.js.map index a50647d..03e5fe8 100644 --- a/dist/leaflet-ui-src.js.map +++ b/dist/leaflet-ui-src.js.map @@ -1 +1 @@ -{"version":3,"file":"leaflet-ui-src.js","sources":["../node_modules/leaflet-rotate/src/dom/DomUtil.js","../node_modules/leaflet-rotate/src/dom/Draggable.js","../node_modules/leaflet-rotate/src/geometry/Point.js","../node_modules/leaflet-rotate/src/layer/DivOverlay.js","../node_modules/leaflet-rotate/src/layer/Popup.js","../node_modules/leaflet-rotate/src/layer/Tooltip.js","../node_modules/leaflet-rotate/src/layer/marker/Icon.js","../node_modules/leaflet-rotate/src/layer/marker/Marker.js","../node_modules/leaflet-rotate/src/layer/tile/GridLayer.js","../node_modules/leaflet-rotate/src/layer/vector/Canvas.js","../node_modules/leaflet-rotate/src/layer/vector/Renderer.js","../node_modules/leaflet-rotate/src/layer/vector/SVG.js","../node_modules/leaflet-rotate/src/map/Map.js","../node_modules/leaflet-rotate/src/map/handler/CompassBearing.js","../node_modules/leaflet-rotate/src/map/handler/ContainerMutation.js","../node_modules/leaflet-rotate/src/map/handler/TouchGestures.js","../node_modules/leaflet-rotate/src/map/handler/TouchRotate.js","../node_modules/leaflet-rotate/src/map/handler/ShiftKeyRotate.js","../node_modules/leaflet-rotate/src/map/handler/TouchZoom.js","../node_modules/leaflet-rotate/src/control/Rotate.js","../node_modules/leaflet.locatecontrol/src/L.Control.Locate.js","../node_modules/leaflet.fullscreen/Control.FullScreen.js","../node_modules/leaflet-pegman/leaflet-pegman.js","../node_modules/@raruto/leaflet-gesture-handling/src/locales.js","../node_modules/@raruto/leaflet-gesture-handling/src/index.js","../node_modules/@raruto/leaflet-edit-osm/leaflet-edit-osm.js","../node_modules/leaflet-control-layers-inline/leaflet-control-layers-inline.js","../node_modules/leaflet-minimap/dist/Control.MiniMap.min.js","../node_modules/leaflet-loading/src/Control.Loading.js","../node_modules/leaflet-search/dist/leaflet-search.src.js","../node_modules/leaflet-easyprint/dist/bundle.js","../node_modules/leaflet.control.resizer/L.Control.Resizer.js","../node_modules/leaflet.visualclick/dist/L.VisualClick.js","../src/leaflet-ui.js"],"sourcesContent":["/**\n * L.DomUtil\n */\nconst domUtilProto = L.extend({}, L.DomUtil);\n\nL.extend(L.DomUtil, {\n\n setTransform: function(el, offset, scale, bearing, pivot) {\n var pos = offset || new L.Point(0, 0);\n\n if (!bearing) {\n offset = pos._round();\n return domUtilProto.setTransform.call(this, el, offset, scale);\n }\n\n pos = pos.rotateFrom(bearing, pivot);\n\n el.style[L.DomUtil.TRANSFORM] =\n 'translate3d(' + pos.x + 'px,' + pos.y + 'px' + ',0)' +\n (scale ? ' scale(' + scale + ')' : '') +\n ' rotate(' + bearing + 'rad)';\n },\n\n setPosition: function(el, point, bearing, pivot) { // (HTMLElement, Point[, Boolean])\n if (!bearing) {\n return domUtilProto.setPosition.call(this, el, point);\n }\n\n /*eslint-disable */\n el._leaflet_pos = point;\n /*eslint-enable */\n\n if (L.Browser.any3d) {\n L.DomUtil.setTransform(el, point, undefined, bearing, pivot);\n } else {\n el.style.left = point.x + 'px';\n el.style.top = point.y + 'px';\n }\n },\n\n // Constants for rotation\n DEG_TO_RAD: Math.PI / 180,\n RAD_TO_DEG: 180 / Math.PI,\n\n});\n","/**\n * L.Draggable\n */\nL.Draggable.include({\n\n updateMapBearing: function(mapBearing) {\n this._mapBearing = mapBearing;\n },\n\n});\n","/**\n * L.Point\n */\nL.extend(L.Point.prototype, {\n\n // Rotate around (0,0) by applying the 2D rotation matrix:\n // ⎡ x' ⎤ = ⎡ cos θ -sin θ ⎤ ⎡ x ⎤\n // ⎣ y' ⎦ ⎣ sin θ cos θ ⎦ ⎣ y ⎦\n // Theta must be given in radians.\n rotate: function(theta) {\n if (!theta) { return this; }\n var sinTheta = Math.sin(theta);\n var cosTheta = Math.cos(theta);\n\n return new L.Point(\n this.x * cosTheta - this.y * sinTheta,\n this.x * sinTheta + this.y * cosTheta\n );\n },\n\n // Rotate around (pivot.x, pivot.y) by:\n // 1. subtract (pivot.x, pivot.y)\n // 2. rotate around (0, 0)\n // 3. add (pivot.x, pivot.y) back\n // same as `this.subtract(pivot).rotate(theta).add(pivot)`\n rotateFrom: function(theta, pivot) {\n if (!theta) { return this; }\n var sinTheta = Math.sin(theta);\n var cosTheta = Math.cos(theta);\n var cx = pivot.x,\n cy = pivot.y;\n var x = this.x - cx,\n y = this.y - cy;\n\n return new L.Point(\n x * cosTheta - y * sinTheta + cx,\n x * sinTheta + y * cosTheta + cy\n );\n },\n\n});\n","/**\n * L.DivOverlay\n */\nconst divOverlayProto = L.extend({}, L.DivOverlay.prototype);\n\nL.DivOverlay.include({\n\n getEvents: function() {\n return L.extend(divOverlayProto.getEvents.call(this), { rotate: this._updatePosition });\n },\n\n _updatePosition: function() {\n if (!this._map) { return; }\n\n var pos = this._map.latLngToLayerPoint(this._latlng),\n offset = L.point(this.options.offset),\n anchor = this._getAnchor();\n\n if (this._zoomAnimated) {\n // TODO: use divOverlayProto._updatePosition\n if (this._map._rotate) {\n pos = this._map.rotatedPointToMapPanePoint(pos);\n }\n L.DomUtil.setPosition(this._container, pos.add(anchor));\n } else {\n offset = offset.add(pos).add(anchor);\n }\n\n var bottom = this._containerBottom = -offset.y,\n left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;\n\n // bottom position the popup in case the height of the popup changes (images loading etc)\n this._container.style.bottom = bottom + 'px';\n this._container.style.left = left + 'px';\n },\n\n});\n","/**\n * L.Popup\n */\nconst popupProto = L.extend({}, L.Popup.prototype);\n\nL.Popup.include({\n\n _animateZoom: function(e) {\n if (!this._map._rotate) {\n popupProto._animateZoom.call(this, e);\n }\n var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),\n anchor = this._getAnchor();\n\n pos = this._map.rotatedPointToMapPanePoint(pos);\n\n L.DomUtil.setPosition(this._container, pos.add(anchor));\n },\n\n _adjustPan: function() {\n if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }\n\n var map = this._map,\n marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,\n containerHeight = this._container.offsetHeight + marginBottom,\n containerWidth = this._containerWidth,\n layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);\n\n layerPos._add(L.DomUtil.getPosition(this._container));\n\n // var containerPos = map.layerPointToContainerPoint(layerPos);\n // TODO: use popupProto._adjustPan\n var containerPos = layerPos._add(this._map._getMapPanePos()),\n padding = L.point(this.options.autoPanPadding),\n paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),\n paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),\n size = map.getSize(),\n dx = 0,\n dy = 0;\n\n if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right\n dx = containerPos.x + containerWidth - size.x + paddingBR.x;\n }\n if (containerPos.x - dx - paddingTL.x < 0) { // left\n dx = containerPos.x - paddingTL.x;\n }\n if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom\n dy = containerPos.y + containerHeight - size.y + paddingBR.y;\n }\n if (containerPos.y - dy - paddingTL.y < 0) { // top\n dy = containerPos.y - paddingTL.y;\n }\n\n // @namespace Map\n // @section Popup events\n // @event autopanstart: Event\n // Fired when the map starts autopanning when opening a popup.\n if (dx || dy) {\n map\n .fire('autopanstart')\n .panBy([dx, dy]);\n }\n },\n\n});\n","/**\n * L.Tooltip\n */\nconst tooltipProto = L.extend({}, L.Tooltip.prototype);\n\nL.Tooltip.include({\n\n _updatePosition: function() {\n if (!this._map._rotate) {\n return tooltipProto._updatePosition.call(this);\n }\n var pos = this._map.latLngToLayerPoint(this._latlng);\n\n pos = this._map.rotatedPointToMapPanePoint(pos);\n this._setPosition(pos);\n },\n\n _animateZoom: function(e) {\n if (!this._map._rotate) {\n return tooltipProto._animateZoom.call(this, e);\n }\n var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);\n\n pos = this._map.rotatedPointToMapPanePoint(pos);\n this._setPosition(pos);\n },\n\n});\n","/**\n * L.Icon\n */\nconst iconProto = L.extend({}, L.Icon.prototype);\n\nL.Icon.include({\n\n _setIconStyles: function(img, name) {\n var options = this.options;\n var sizeOption = options[name + 'Size'];\n\n if (typeof sizeOption === 'number') {\n sizeOption = [sizeOption, sizeOption];\n }\n\n var size = L.point(sizeOption),\n anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||\n size && size.divideBy(2, true));\n\n img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');\n\n if (anchor) {\n img.style.marginLeft = (-anchor.x) + 'px';\n img.style.marginTop = (-anchor.y) + 'px';\n // TODO: use iconProto._setIconStyles\n img.style[L.DomUtil.TRANSFORM + \"Origin\"] = anchor.x + \"px \" + anchor.y + \"px 0px\";\n }\n\n if (size) {\n img.style.width = size.x + 'px';\n img.style.height = size.y + 'px';\n }\n },\n\n});\n","/**\n * L.Handler.MarkerDrag\n */\nvar markerDragProto;\n\nvar MarkerDrag = {\n\n _onDragStart: function() {\n if (!this._marker._map._rotate) {\n return markerDragProto._onDragStart.call(this)\n }\n this._draggable.updateMapBearing(this._marker._map._bearing);\n },\n\n _onDrag: function(e) {\n var marker = this._marker,\n // TODO: use markerDragProto._onDrag\n rotated_marker = marker.options.rotation || marker.options.rotateWithView,\n shadow = marker._shadow,\n iconPos = L.DomUtil.getPosition(marker._icon);\n\n // TODO: use markerDragProto._onDrag\n // update shadow position\n if (!rotated_marker && shadow) {\n L.DomUtil.setPosition(shadow, iconPos);\n }\n\n // TODO: use markerDragProto._onDrag\n if (marker._map._rotate) {\n // Reverse calculation from mapPane coordinates to rotatePane coordinates\n iconPos = marker._map.mapPanePointToRotatedPoint(iconPos);\n }\n var latlng = marker._map.layerPointToLatLng(iconPos);\n\n marker._latlng = latlng;\n e.latlng = latlng;\n e.oldLatLng = this._oldLatLng;\n\n // TODO: use markerDragProto._onDrag\n if (rotated_marker) marker.setLatLng(latlng); // use `setLatLng` to presisit rotation. low efficiency\n else marker.fire('move', e); // `setLatLng` will trig 'move' event. we imitate here.\n\n // @event drag: Event\n // Fired repeatedly while the user drags the marker.\n marker\n .fire('drag', e);\n },\n\n _onDragEnd: function(e) {\n if (this._marker._map._rotate) {\n this._marker.update();\n }\n markerDragProto._onDragEnd.call(this, e);\n },\n\n};\n\n/**\n * L.Marker\n */\nconst markerProto = L.extend({}, L.Marker.prototype);\n\nL.Marker.mergeOptions({\n\n // @option rotation: Number = 0\n // Rotation of this marker in rad\n rotation: 0,\n\n // @option rotateWithView: Boolean = false\n // Rotate this marker when map rotates\n rotateWithView: false,\n\n});\n\nL.Marker.include({\n\n getEvents: function() {\n return L.extend(markerProto.getEvents.call(this), { rotate: this.update });\n },\n\n onAdd: function(map) {\n markerProto.onAdd.call(this, map);\n map.on('rotate', this.update, this);\n },\n\n _initInteraction: function() {\n var ret = markerProto._initInteraction.call(this);\n if (this.dragging && this.dragging.enabled() && this._map && this._map._rotate) {\n // L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable\n markerDragProto = markerDragProto || Object.getPrototypeOf(this.dragging);\n this.dragging._onDragStart = MarkerDrag._onDragStart.bind(this.dragging);\n this.dragging._onDrag = MarkerDrag._onDrag.bind(this.dragging);\n this.dragging._onDragEnd = MarkerDrag._onDragEnd.bind(this.dragging);\n this.dragging.disable();\n this.dragging.enable();\n }\n return ret;\n },\n\n _setPos: function(pos) {\n\n // TODO: use markerProto._setPos\n if (this._map._rotate) {\n pos = this._map.rotatedPointToMapPanePoint(pos);\n }\n\n // TODO: use markerProto._setPos\n var bearing = this.options.rotation || 0;\n if (this.options.rotateWithView) {\n bearing += this._map._bearing;\n }\n\n // TODO: use markerProto._setPos\n L.DomUtil.setPosition(this._icon, pos, bearing, pos);\n\n // TODO: use markerProto._setPos\n if (this._shadow) {\n L.DomUtil.setPosition(this._shadow, pos, bearing, pos);\n }\n\n this._zIndex = pos.y + this.options.zIndexOffset;\n\n this._resetZIndex();\n },\n\n _updateZIndex: function(offset) {\n if (!this._map._rotate) {\n return markerProto._updateZIndex.call(this, offset)\n }\n this._icon.style.zIndex = Math.round(this._zIndex + offset);\n },\n\n setRotation: function(rotation) {\n this.options.rotation = rotation;\n this.update();\n },\n\n});\n","/**\n * L.GridLayer\n */\nconst gridLayerProto = L.extend({}, L.GridLayer.prototype);\n\nL.GridLayer.include({\n\n getEvents: function() {\n var events = gridLayerProto.getEvents.call(this);\n if (this._map._rotate && !this.options.updateWhenIdle) {\n if (!this._onRotate) {\n this._onRotate = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);\n }\n events.rotate = this._onRotate;\n }\n return events;\n },\n\n _getTiledPixelBounds: function(center) {\n if (!this._map._rotate) {\n return gridLayerProto._getTiledPixelBounds.call(this, center);\n }\n\n var map = this._map,\n mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),\n scale = map.getZoomScale(mapZoom, this._tileZoom),\n pixelCenter = map.project(center, this._tileZoom).floor(),\n size = map.getSize(),\n halfSize = new L.Bounds([\n map.containerPointToLayerPoint([0, 0]).floor(),\n map.containerPointToLayerPoint([size.x, 0]).floor(),\n map.containerPointToLayerPoint([0, size.y]).floor(),\n map.containerPointToLayerPoint([size.x, size.y]).floor()\n ]).getSize().divideBy(scale * 2);\n\n return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));\n },\n\n});\n","/**\n * L.Canvas\n */\nconst canvasProto = L.extend({}, L.Canvas.prototype);\n\nL.Canvas.include({\n\n onAdd: function() {\n canvasProto.onAdd.call(this);\n // When rotating the canvas itself, it is cleared by some weird reason, so redraw.\n this._map.on('rotate', this._redraw, this);\n },\n\n onRemove: function() {\n canvasProto.onRemove.call(this);\n this._map.off('rotate', this._redraw, this);\n },\n\n _update: function() {\n canvasProto._update.call(this);\n // Tell paths to redraw themselves\n this.fire('update')\n },\n\n});\n","/**\n * L.Renderer\n */\nconst rendererProto = L.extend({}, L.Renderer.prototype);\n\nL.Renderer.include({\n\n onAdd: function() {\n rendererProto.onAdd.call(this);\n // this._map.on('rotate', this._update, this);\n },\n\n onRemove: function() {\n rendererProto.onRemove.call(this);\n // this._map.off('rotate', this._update, this);\n },\n\n _updateTransform: function(center, zoom) {\n if (!this._map._rotate) {\n return rendererProto._updateTransform.call(this, center, zoom);\n }\n var scale = this._map.getZoomScale(zoom, this._zoom),\n offset = this._map._latLngToNewLayerPoint(this._topLeft, zoom, center);\n if (L.Browser.any3d) {\n L.DomUtil.setTransform(this._container, offset, scale);\n } else {\n L.DomUtil.setPosition(this._container, offset);\n }\n },\n\n _update: function() {\n if (!this._map._rotate) {\n return rendererProto._update.call(this);\n }\n // Update pixel bounds of renderer container (for positioning/sizing/clipping later)\n // Subclasses are responsible of firing the 'update' event.\n var p = this.options.padding,\n map = this._map,\n size = this._map.getSize(),\n padMin = size.multiplyBy(-p),\n padMax = size.multiplyBy(1 + p),\n //// TODO: Somehow refactor this out into map.something() - the code is\n //// pretty much the same as in GridLayer.\n clip = new L.Bounds([\n map.containerPointToLayerPoint([padMin.x, padMin.y]).floor(),\n map.containerPointToLayerPoint([padMin.x, padMax.y]).floor(),\n map.containerPointToLayerPoint([padMax.x, padMin.y]).floor(),\n map.containerPointToLayerPoint([padMax.x, padMax.y]).floor()\n ]);\n //min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();\n\n this._bounds = clip;\n // this._topLeft = clip.min;\n this._topLeft = this._map.layerPointToLatLng(clip.min);\n\n this._center = this._map.getCenter();\n this._zoom = this._map.getZoom();\n },\n\n});\n","/**\n * L.SVG\n */\nconst svgProto = L.extend({}, L.SVG.prototype);\n\nL.SVG.include({\n\n _update: function() {\n svgProto._update.call(this);\n if (this._map._rotate) {\n this.fire('update');\n }\n },\n\n});\n","/**\n * L.Map\n */\nconst mapProto = L.extend({}, L.Map.prototype);\n\nL.Map.mergeOptions({ rotate: false, bearing: 0, });\n\nL.Map.include({\n\n initialize: function(id, options) { // (HTMLElement or String, Object)\n if (options.rotate) {\n this._rotate = true;\n this._bearing = 0;\n }\n mapProto.initialize.call(this, id, options);\n if(this.options.rotate){\n this.setBearing(this.options.bearing);\n }\n },\n\n // createPane: function(name, container) {\n // if (!this._rotate || name == 'mapPane') {\n // return mapProto.createPane.call(this, name, container);\n // }\n // // init \"rotatePane\"\n // if (!this._rotatePane) {\n // // this._pivot = this.getSize().divideBy(2);\n // this._rotatePane = mapProto.createPane.call(this, 'rotatePane', this._mapPane);\n // L.DomUtil.setPosition(this._rotatePane, new L.Point(0, 0), this._bearing, this._pivot);\n // }\n // return mapProto.createPane.call(this, name, container || this._rotatePane);\n // },\n\n containerPointToLayerPoint: function(point) { // (Point)\n if (!this._rotate) {\n return mapProto.containerPointToLayerPoint.call(this, point);\n }\n return L.point(point)\n .subtract(this._getMapPanePos())\n .rotateFrom(-this._bearing, this._getRotatePanePos())\n .subtract(this._getRotatePanePos());\n },\n\n getBounds: function() {\n if (!this._rotate) {\n return mapProto.getBounds.call(this);\n }\n var size = this.getSize();\n var topleft = this.layerPointToLatLng(this.containerPointToLayerPoint([0, 0])),\n topright = this.layerPointToLatLng(this.containerPointToLayerPoint([size.x, 0])),\n bottomright = this.layerPointToLatLng(this.containerPointToLayerPoint([size.x, size.y])),\n bottomleft = this.layerPointToLatLng(this.containerPointToLayerPoint([0, size.y]));\n\n // Use LatLngBounds' build-in constructor that automatically extends the bounds to fit the passed points\n return new L.LatLngBounds([topleft, topright, bottomright, bottomleft]);\n },\n\n layerPointToContainerPoint: function(point) { // (Point)\n if (!this._rotate) {\n return mapProto.layerPointToContainerPoint.call(this, point);\n }\n return L.point(point)\n .add(this._getRotatePanePos())\n .rotateFrom(this._bearing, this._getRotatePanePos())\n .add(this._getMapPanePos());\n },\n\n // Rotation methods\n // setBearing will work with just the 'theta' parameter.\n setBearing: function(theta) {\n if (!L.Browser.any3d || !this._rotate) { return; }\n\n var rotatePanePos = this._getRotatePanePos();\n var halfSize = this.getSize().divideBy(2);\n this._pivot = this._getMapPanePos().clone().multiplyBy(-1).add(halfSize);\n\n rotatePanePos = rotatePanePos.rotateFrom(-this._bearing, this._pivot);\n\n this._bearing = theta * L.DomUtil.DEG_TO_RAD; // TODO: mod 360\n this._rotatePanePos = rotatePanePos.rotateFrom(this._bearing, this._pivot);\n\n L.DomUtil.setPosition(this._rotatePane, rotatePanePos, this._bearing, this._pivot);\n\n this.fire('rotate');\n },\n\n getBearing: function() {\n return this._bearing * L.DomUtil.RAD_TO_DEG;\n },\n\n _initPanes: function() {\n var panes = this._panes = {};\n this._paneRenderers = {};\n\n // @section\n //\n // Panes are DOM elements used to control the ordering of layers on the map. You\n // can access panes with [`map.getPane`](#map-getpane) or\n // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the\n // [`map.createPane`](#map-createpane) method.\n //\n // Every map has the following default panes that differ only in zIndex.\n //\n // @pane mapPane: HTMLElement = 'auto'\n // Pane that contains all other map panes\n\n this._mapPane = this.createPane('mapPane', this._container);\n L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));\n\n if (this._rotate) {\n this._rotatePane = this.createPane('rotatePane', this._mapPane);\n this._norotatePane = this.createPane('norotatePane', this._mapPane);\n\n // @pane tilePane: HTMLElement = 2\n // Pane for tile layers\n this.createPane('tilePane', this._rotatePane);\n // @pane overlayPane: HTMLElement = 4\n // Pane for overlays like polylines and polygons\n this.createPane('overlayPane', this._rotatePane);\n\n // @pane shadowPane: HTMLElement = 5\n // Pane for overlay shadows (e.g. marker shadows)\n this.createPane('shadowPane', this._norotatePane);\n // @pane markerPane: HTMLElement = 6\n // Pane for marker icons\n this.createPane('markerPane', this._norotatePane);\n // @pane tooltipPane: HTMLElement = 650\n // Pane for tooltips.\n this.createPane('tooltipPane', this._norotatePane);\n // @pane popupPane: HTMLElement = 700\n // Pane for popups.\n this.createPane('popupPane', this._norotatePane);\n } else {\n // @pane tilePane: HTMLElement = 2\n // Pane for tile layers\n this.createPane('tilePane');\n // @pane overlayPane: HTMLElement = 4\n // Pane for overlays like polylines and polygons\n this.createPane('overlayPane');\n // @pane shadowPane: HTMLElement = 5\n // Pane for overlay shadows (e.g. marker shadows)\n this.createPane('shadowPane');\n // @pane markerPane: HTMLElement = 6\n // Pane for marker icons\n this.createPane('markerPane');\n // @pane tooltipPane: HTMLElement = 650\n // Pane for tooltips.\n this.createPane('tooltipPane');\n // @pane popupPane: HTMLElement = 700\n // Pane for popups.\n this.createPane('popupPane');\n }\n\n if (!this.options.markerZoomAnimation) {\n L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');\n L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');\n }\n },\n\n // @method rotatedPointToMapPanePoint(point: Point): Point\n // Converts a coordinate from the rotated pane reference system\n // to the reference system of the not rotated map pane.\n rotatedPointToMapPanePoint: function(point) {\n return L.point(point).rotate(this._bearing)._add(this._getRotatePanePos());\n },\n\n // @method mapPanePointToRotatedPoint(point: Point): Point\n // Converts a coordinate from the not rotated map pane reference system\n // to the reference system of the rotated pane.\n mapPanePointToRotatedPoint: function(point) {\n return L.point(point)._subtract(this._getRotatePanePos()).rotate(-this._bearing);\n },\n\n // offset of the specified place to the current center in pixels\n _getCenterOffset: function(latlng) {\n var centerOffset = mapProto._getCenterOffset.call(this, latlng);\n if (this._rotate) {\n centerOffset = centerOffset.rotate(this._bearing);\n }\n return centerOffset;\n },\n\n _getRotatePanePos: function() {\n return this._rotatePanePos || new L.Point(0, 0);\n },\n\n _getNewPixelOrigin: function(center, zoom) {\n var viewHalf = this.getSize()._divideBy(2);\n if (!this._rotate) {\n mapProto._getNewPixelOrigin.call(this, center, zoom);\n }\n return this.project(center, zoom)\n .rotate(this._bearing)\n ._subtract(viewHalf)\n ._add(this._getMapPanePos())\n ._add(this._getRotatePanePos())\n .rotate(-this._bearing)\n ._round();\n },\n\n _handleGeolocationResponse: function(pos) {\n var lat = pos.coords.latitude,\n lng = pos.coords.longitude,\n // TODO: use mapProto._handleGeolocationResponse\n hdg = pos.coords.heading,\n latlng = new L.LatLng(lat, lng),\n bounds = latlng.toBounds(pos.coords.accuracy),\n options = this._locateOptions;\n\n if (options.setView) {\n var zoom = this.getBoundsZoom(bounds);\n this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);\n }\n\n var data = {\n latlng: latlng,\n bounds: bounds,\n timestamp: pos.timestamp,\n // TODO: use mapProto._handleGeolocationResponse\n heading: hdg\n };\n\n for (var i in pos.coords) {\n if (typeof pos.coords[i] === 'number') {\n data[i] = pos.coords[i];\n }\n }\n\n // @event locationfound: LocationEvent\n // Fired when geolocation (using the [`locate`](#map-locate) method)\n // went successfully.\n this.fire('locationfound', data);\n },\n\n});\n","/*\n * L.Map.CompassBearing will rotate the map according to a smartphone's compass.\n */\n\nL.Map.CompassBearing = L.Handler.extend({\n\n initialize: function(map) {\n if (!window.DeviceOrientationEvent) {\n this._capable = false;\n return;\n }\n this._capable = true;\n this._map = map;\n\n this._throttled = L.Util.throttle(this._onDeviceOrientation, 1000, this);\n },\n\n addHooks: function() {\n if (this._capable && this._map._rotate) {\n L.DomEvent.on(window, 'deviceorientation', this._throttled, this);\n }\n },\n\n removeHooks: function() {\n if (this._capable && this._map._rotate) {\n L.DomEvent.off(window, 'deviceorientation', this._throttled, this);\n }\n },\n\n _onDeviceOrientation: function(event) {\n if (event.alpha !== null) {\n this._map.setBearing(event.alpha - window.orientation);\n }\n },\n\n});\n\n// @section Handlers\n// @property compassBearing: Handler\n// Compass bearing handler.\nL.Map.addInitHook('addHandler', 'compassBearing', L.Map.CompassBearing);\n","/*\n * L.Handler.ContainerMutation triggers `invalidateResize` when the map's DOM container mutates.\n */\n\n// @namespace Map\n// @section Interaction Options\nL.Map.mergeOptions({\n\n // @option trackContainerMutation: Boolean = false\n // Whether the map uses [mutation observers](https://developer.mozilla.org/docs/Web/API/MutationObserver)\n // to detect changes in its container and trigger `invalidateSize`. Disabled\n // by default due to support not being available in all web browsers.\n trackContainerMutation: false\n\n});\n\nL.Map.ContainerMutation = L.Handler.extend({\n\n addHooks: function() {\n if (!L.Browser.mutation) {\n return;\n }\n\n if (!this._observer) {\n this._observer = new MutationObserver(L.Util.bind(this._onMutation, this));\n }\n\n this._observer.observe(this._map.getContainer(), {\n childList: false,\n attributes: true,\n characterData: false,\n subtree: false,\n attributeFilter: ['style']\n });\n },\n\n removeHooks: function() {\n if (!L.Browser.mutation) {\n return;\n }\n this._observer.disconnect();\n },\n\n _onMutation: function() {\n this._map.invalidateSize();\n },\n\n});\n\n// @section Handlers\n// @property containerMutation: Handler\n// Container mutation handler (disabled unless [`trackContainerMutation`](#map-trackcontainermutation) is set).\nL.Map.addInitHook('addHandler', 'trackContainerMutation', L.Map.ContainerMutation);\n","/*\n * L.Handler.TouchGestures is both TouchZoom plus TouchRotate.\n */\n\n// @namespace Map\n// @section Interaction Options\nL.Map.mergeOptions({\n\n // @option bounceAtZoomLimits: Boolean = true\n // Set it to false if you don't want the map to zoom beyond min/max zoom\n // and then bounce back when pinch-zooming.\n bounceAtZoomLimits: true,\n\n});\n\nL.Map.TouchGestures = L.Handler.extend({\n\n initialize: function(map) {\n this._map = map;\n this.rotate = !!this._map.options.touchRotate;\n this.zoom = !!this._map.options.touchZoom;\n },\n\n addHooks: function() {\n L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);\n },\n\n removeHooks: function() {\n L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);\n },\n\n _onTouchStart: function(e) {\n var map = this._map;\n\n if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming || this._rotating) { return; }\n\n var p1 = map.mouseEventToContainerPoint(e.touches[0]),\n p2 = map.mouseEventToContainerPoint(e.touches[1]),\n vector = p1.subtract(p2);\n\n this._centerPoint = map.getSize()._divideBy(2);\n this._startLatLng = map.containerPointToLatLng(this._centerPoint);\n\n if (this.zoom) {\n if (map.options.touchZoom !== 'center') {\n this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));\n }\n this._startDist = p1.distanceTo(p2);\n this._startZoom = map.getZoom();\n this._zooming = true;\n } else {\n this._zooming = false;\n }\n\n if (this.rotate) {\n this._startTheta = Math.atan(vector.x / vector.y);\n this._startBearing = map.getBearing();\n if (vector.y < 0) { this._startBearing += 180; }\n this._rotating = true;\n } else {\n this._rotating = false;\n }\n\n this._moved = false;\n\n map.stop();\n\n L.DomEvent\n .on(document, 'touchmove', this._onTouchMove, this)\n .on(document, 'touchend', this._onTouchEnd, this);\n\n L.DomEvent.preventDefault(e);\n },\n\n _onTouchMove: function(e) {\n if (!e.touches || e.touches.length !== 2 || !(this._zooming || this._rotating)) { return; }\n\n var map = this._map,\n p1 = map.mouseEventToContainerPoint(e.touches[0]),\n p2 = map.mouseEventToContainerPoint(e.touches[1]),\n vector = p1.subtract(p2),\n scale = p1.distanceTo(p2) / this._startDist,\n delta;\n\n if (this._rotating) {\n var theta = Math.atan(vector.x / vector.y);\n var bearingDelta = (theta - this._startTheta) * L.DomUtil.RAD_TO_DEG;\n if (vector.y < 0) { bearingDelta += 180; }\n if (bearingDelta) {\n /// TODO: The pivot should be the last touch point, but zoomAnimation manages to\n /// overwrite the rotate pane position. Maybe related to #3529.\n map.setBearing(this._startBearing - bearingDelta);\n }\n }\n\n if (this._zooming) {\n this._zoom = map.getScaleZoom(scale, this._startZoom);\n\n if (!map.options.bounceAtZoomLimits && (\n (this._zoom < map.getMinZoom() && scale < 1) ||\n (this._zoom > map.getMaxZoom() && scale > 1))) {\n this._zoom = map._limitZoom(this._zoom);\n }\n\n if (map.options.touchZoom === 'center') {\n this._center = this._startLatLng;\n if (scale === 1) { return; }\n } else {\n // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng\n delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);\n if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }\n\n var alpha = -map.getBearing() * L.DomUtil.DEG_TO_RAD;\n\n this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(delta.rotate(alpha)));\n }\n\n }\n\n if (!this._moved) {\n map._moveStart(true);\n this._moved = true;\n }\n\n L.Util.cancelAnimFrame(this._animRequest);\n\n var moveFn = L.bind(map._move, map, this._center, this._zoom, { pinch: true, round: false });\n this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);\n\n L.DomEvent.preventDefault(e);\n },\n\n _onTouchEnd: function() {\n if (!this._moved || !this._zooming) {\n this._zooming = false;\n return;\n }\n\n this._zooming = false;\n this._rotating = false;\n L.Util.cancelAnimFrame(this._animRequest);\n\n L.DomEvent\n .off(document, 'touchmove', this._onTouchMove)\n .off(document, 'touchend', this._onTouchEnd);\n\n if (this.zoom) {\n // Pinch updates GridLayers' levels only when snapZoom is off, so snapZoom becomes noUpdate.\n if (this._map.options.zoomAnimation) {\n this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.snapZoom);\n } else {\n this._map._resetView(this._center, this._map._limitZoom(this._zoom));\n }\n }\n },\n\n});\n\n// @section Handlers\n// @property touchGestures: Handler\n// Touch gestures handler.\nL.Map.addInitHook('addHandler', 'touchGestures', L.Map.TouchGestures);\n","/*\n * L.Handler.TouchRotate is used by L.Map to add two-finger rotation gestures.\n */\n\n// @namespace Map\n// @section Interaction Options\nL.Map.mergeOptions({\n\n // @section Touch interaction options\n // @option touchRotate: Boolean|String = *\n // Whether the map can be rotated with a two-finger rotation gesture\n touchRotate: false,\n\n});\n\nL.Map.TouchRotate = L.Handler.extend({\n\n addHooks: function() {\n this._map.touchGestures.enable();\n this._map.touchGestures.rotate = true;\n },\n\n removeHooks: function() {\n this._map.touchGestures.rotate = false;\n },\n\n});\n\n// @section Handlers\n// @property touchZoom: Handler\n// Touch rotate handler.\nL.Map.addInitHook('addHandler', 'touchRotate', L.Map.TouchRotate);\n","/*\n * L.Handler.ShiftKeyRotate is used by L.Map to add shift-wheel rotation.\n */\n\n// @namespace Map\n// @section Interaction Options\nL.Map.mergeOptions({\n\n // @section ShiftKey interaction options\n // @option shiftKeyRotate: Boolean|String = *\n // Whether the map can be rotated with a shit-wheel rotation\n shiftKeyRotate: true,\n\n});\n\nL.Map.ShiftKeyRotate = L.Handler.extend({\n\n addHooks: function() {\n L.DomEvent.on(this._map._container, \"wheel\", this._handleShiftScroll, this);\n // this._map.shiftKeyRotate.enable();\n this._map.shiftKeyRotate.rotate = true;\n },\n\n removeHooks: function() {\n L.DomEvent.off(this._map._container, \"wheel\", this._handleShiftScroll, this);\n this._map.shiftKeyRotate.rotate = false;\n },\n\n _handleShiftScroll: function(e) {\n if (e.shiftKey) {\n e.preventDefault();\n this._map.scrollWheelZoom.disable();\n this._map.setBearing((this._map._bearing * L.DomUtil.RAD_TO_DEG) + e.deltaY);\n } else {\n this._map.scrollWheelZoom.enable();\n }\n },\n\n});\n\n// @section Handlers\n// @property touchZoom: Handler\n// Touch rotate handler.\nL.Map.addInitHook('addHandler', 'shiftKeyRotate', L.Map.ShiftKeyRotate);\n\n// decrease \"scrollWheelZoom\" handler priority over \"shiftKeyRotate\" handler\nL.Map.addInitHook(function() {\n if (this.scrollWheelZoom.enabled() && this.shiftKeyRotate.enabled()) {\n this.scrollWheelZoom.disable();\n this.scrollWheelZoom.enable();\n }\n});\n","/*\n * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.\n */\n\n// @namespace Map\n// @section Interaction Options\nL.Map.mergeOptions({\n\n // @section Touch interaction options\n // @option touchZoom: Boolean|String = *\n // Whether the map can be zoomed by touch-dragging with two fingers. If\n // passed `'center'`, it will zoom to the center of the view regardless of\n // where the touch events (fingers) were. Enabled for touch-capable web\n // browsers except for old Androids.\n touchZoom: L.Browser.touch && !L.Browser.android23,\n\n bounceAtZoomLimits: false,\n});\n\nL.Map.TouchZoom = L.Handler.extend({\n\n addHooks: function() {\n L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');\n this._map.touchGestures.enable();\n this._map.touchGestures.zoom = true;\n },\n\n removeHooks: function() {\n L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');\n this._map.touchGestures.zoom = false;\n },\n\n});\n\n// @section Handlers\n// @property touchZoom: Handler\n// Touch zoom handler.\nL.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);\n","/**\n * L.Control.Rotate\n */\n\n// A tri-state control for map rotation. States are:\n// Locked (default)\n// Unlocked (user can pinch-rotate)\n// Follow (rotation follows device orientation, if available)\nL.Control.Rotate = L.Control.extend({\n\n options: {\n position: 'topleft',\n closeOnZeroBearing: true\n },\n\n onAdd: function(map) {\n this._onDeviceOrientation = L.Util.throttle(this._unthrottledOnDeviceOrientation, 100, this);\n\n var container = this._container = L.DomUtil.create('div', 'leaflet-control-rotate leaflet-bar');\n\n // this.button = L.Control.Zoom.prototype._createButton.call(this, 'R', 'leaflet-control-rotate', 'leaflet-control-rotate', container, this._toggleLock);\n\n var arrow = this._arrow = L.DomUtil.create('span', 'leaflet-control-rotate-arrow');\n\n arrow.style.backgroundImage = `url(\"data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E\")`;\n arrow.style.cursor = 'grab';\n arrow.style.display = 'block';\n arrow.style.width = '100%';\n arrow.style.height = '100%';\n arrow.style.backgroundRepeat = 'no-repeat';\n arrow.style.backgroundPosition = '50%';\n\n // Copy-pasted from L.Control.Zoom\n var link = this._link = L.DomUtil.create('a', 'leaflet-control-rotate-toggle', container);\n link.appendChild(arrow);\n link.href = '#';\n link.title = 'Rotate map';\n\n L.DomEvent\n .on(link, 'dblclick', L.DomEvent.stopPropagation)\n .on(link, 'mousedown', this._handleMouseDown, this)\n .on(link, 'click', L.DomEvent.stop)\n .on(link, 'click', this._cycleState, this)\n .on(link, 'click', this._refocusOnMap, this);\n\n if (!L.Browser.any3d) {\n L.DomUtil.addClass(link, 'leaflet-disabled');\n }\n\n this._restyle();\n\n map.on('rotate', this._restyle.bind(this));\n\n // State flag\n this._follow = false;\n this._canFollow = false;\n\n if (this.options.closeOnZeroBearing && map.getBearing() === 0) {\n container.style.display = 'none';\n }\n\n return container;\n },\n\n _handleMouseDown: function(e) {\n L.DomEvent.stopPropagation(e);\n this.dragging = true;\n this.dragstartX = e.pageX;\n this.dragstartY = e.pageY;\n L.DomEvent\n .on(document, 'mousemove', this._handleMouseDrag, this)\n .on(document, 'mouseup', this._handleMouseUp, this);\n },\n\n _handleMouseUp: function(e) {\n L.DomEvent.stopPropagation(e);\n this.dragging = false;\n\n L.DomEvent\n .off(document, 'mousemove', this._handleMouseDrag, this)\n .off(document, 'mouseup', this._handleMouseUp, this);\n },\n\n _handleMouseDrag: function(e) {\n if (!this.dragging) { return; }\n var deltaX = e.clientX - this.dragstartX;\n this._map.setBearing(deltaX);\n },\n\n _cycleState: function(ev) {\n var map = this._map;\n\n if (!map) { return; }\n\n if (!map.touchRotate.enabled() && !map.compassBearing.enabled()) {\n // Go from disabled to touch\n map.touchRotate.enable();\n\n // console.log('state is now: touch rotate');\n } else {\n\n if (!map.compassBearing.enabled()) {\n // Go from touch to compass\n map.touchRotate.disable();\n map.compassBearing.enable();\n\n // console.log('state is now: compass');\n\n // It is possible that compass is not supported. If so,\n // the hangler will automatically go from compass to disabled.\n } else {\n // Go from compass to disabled\n map.compassBearing.disable();\n\n // console.log('state is now: locked');\n\n map.setBearing(0);\n if (this.options.closeOnZeroBearing) {\n map.touchRotate.enable();\n }\n }\n }\n this._restyle();\n },\n\n _restyle: function() {\n if (this._map.options.rotate) {\n var map = this._map;\n var bearing = map.getBearing();\n if (this.options.closeOnZeroBearing && bearing) {\n this._container.style.display = 'block';\n }\n\n var cssTransform = 'rotate(' + bearing + 'deg)';\n this._arrow.style.transform = cssTransform;\n\n if (map.compassBearing.enabled()) {\n this._link.style.backgroundColor = 'orange';\n } else if (map.touchRotate.enabled()) {\n this._link.style.backgroundColor = null;\n } else {\n this._link.style.backgroundColor = 'grey';\n if (this.options.closeOnZeroBearing && map.getBearing() === 0) {\n this._container.style.display = 'none';\n }\n }\n } else {\n L.DomUtil.addClass(this._link, 'leaflet-disabled');\n }\n },\n\n});\n\nL.control.rotate = function(options) {\n return new L.Control.Rotate(options);\n};\n\nL.Map.mergeOptions({\n rotateControl: true,\n});\n\nL.Map.addInitHook(function() {\n if (this.options.rotateControl) {\n var options = typeof this.options.rotateControl === 'object' ? this.options.rotateControl : {};\n this.rotateControl = L.control.rotate(options);\n this.addControl(this.rotateControl);\n }\n});\n","/*!\nCopyright (c) 2016 Dominik Moritz\n\nThis file is part of the leaflet locate control. It is licensed under the MIT license.\nYou can find the project at: https://github.com/domoritz/leaflet-locatecontrol\n*/\n(function (factory, window) {\n // see https://github.com/Leaflet/Leaflet/blob/master/PLUGIN-GUIDE.md#module-loaders\n // for details on how to structure a leaflet plugin.\n\n // define an AMD module that relies on 'leaflet'\n if (typeof define === 'function' && define.amd) {\n define(['leaflet'], factory);\n\n // define a Common JS module that relies on 'leaflet'\n } else if (typeof exports === 'object') {\n if (typeof window !== 'undefined' && window.L) {\n module.exports = factory(L);\n } else {\n module.exports = factory(require('leaflet'));\n }\n }\n\n // attach your plugin to the global 'L' variable\n if (typeof window !== 'undefined' && window.L){\n window.L.Control.Locate = factory(L);\n }\n} (function (L) {\n var LDomUtilApplyClassesMethod = function(method, element, classNames) {\n classNames = classNames.split(' ');\n classNames.forEach(function(className) {\n L.DomUtil[method].call(this, element, className);\n });\n };\n\n var addClasses = function(el, names) { LDomUtilApplyClassesMethod('addClass', el, names); };\n var removeClasses = function(el, names) { LDomUtilApplyClassesMethod('removeClass', el, names); };\n\n /**\n * Compatible with L.Circle but a true marker instead of a path\n */\n var LocationMarker = L.Marker.extend({\n initialize: function (latlng, options) {\n L.Util.setOptions(this, options);\n this._latlng = latlng;\n this.createIcon();\n },\n\n /**\n * Create a styled circle location marker\n */\n createIcon: function() {\n var opt = this.options;\n\n var style = '';\n\n if (opt.color !== undefined) {\n style += 'stroke:'+opt.color+';';\n }\n if (opt.weight !== undefined) {\n style += 'stroke-width:'+opt.weight+';';\n }\n if (opt.fillColor !== undefined) {\n style += 'fill:'+opt.fillColor+';';\n }\n if (opt.fillOpacity !== undefined) {\n style += 'fill-opacity:'+opt.fillOpacity+';';\n }\n if (opt.opacity !== undefined) {\n style += 'opacity:'+opt.opacity+';';\n }\n\n var icon = this._getIconSVG(opt, style);\n\n this._locationIcon = L.divIcon({\n className: icon.className,\n html: icon.svg,\n iconSize: [icon.w,icon.h],\n });\n\n this.setIcon(this._locationIcon);\n },\n\n /**\n * Return the raw svg for the shape\n *\n * Split so can be easily overridden\n */\n _getIconSVG: function(options, style) {\n var r = options.radius;\n var w = options.weight;\n var s = r + w;\n var s2 = s * 2;\n var svg = '' +\n '' +\n '';\n return {\n className: 'leaflet-control-locate-location',\n svg: svg,\n w: s2,\n h: s2\n };\n },\n\n setStyle: function(style) {\n L.Util.setOptions(this, style);\n this.createIcon();\n }\n });\n\n var CompassMarker = LocationMarker.extend({\n initialize: function (latlng, heading, options) {\n L.Util.setOptions(this, options);\n this._latlng = latlng;\n this._heading = heading;\n this.createIcon();\n },\n\n setHeading: function(heading) {\n this._heading = heading;\n },\n\n /**\n * Create a styled arrow compass marker\n */\n _getIconSVG: function(options, style) {\n var r = options.radius;\n var w = (options.width + options.weight);\n var h = (r+options.depth + options.weight)*2;\n var path = 'M0,0 l'+(options.width/2)+','+options.depth+' l-'+(w)+',0 z';\n var svgstyle = 'transform: rotate('+this._heading+'deg)';\n var svg = ''+\n ''+\n '';\n return {\n className: 'leaflet-control-locate-heading',\n svg: svg,\n w: w,\n h: h\n };\n },\n });\n\n\n var LocateControl = L.Control.extend({\n options: {\n /** Position of the control */\n position: 'topleft',\n /** The layer that the user's location should be drawn on. By default creates a new layer. */\n layer: undefined,\n /**\n * Automatically sets the map view (zoom and pan) to the user's location as it updates.\n * While the map is following the user's location, the control is in the `following` state,\n * which changes the style of the control and the circle marker.\n *\n * Possible values:\n * - false: never updates the map view when location changes.\n * - 'once': set the view when the location is first determined\n * - 'always': always updates the map view when location changes.\n * The map view follows the user's location.\n * - 'untilPan': like 'always', except stops updating the\n * view if the user has manually panned the map.\n * The map view follows the user's location until she pans.\n * - 'untilPanOrZoom': (default) like 'always', except stops updating the\n * view if the user has manually panned the map.\n * The map view follows the user's location until she pans.\n */\n setView: 'untilPanOrZoom',\n /** Keep the current map zoom level when setting the view and only pan. */\n keepCurrentZoomLevel: false,\n\t /** After activating the plugin by clicking on the icon, zoom to the selected zoom level, even when keepCurrentZoomLevel is true. Set to 'false' to disable this feature. */\n\t initialZoomLevel: false,\n /**\n * This callback can be used to override the viewport tracking\n * This function should return a LatLngBounds object.\n *\n * For example to extend the viewport to ensure that a particular LatLng is visible:\n *\n * getLocationBounds: function(locationEvent) {\n * return locationEvent.bounds.extend([-33.873085, 151.219273]);\n * },\n */\n getLocationBounds: function (locationEvent) {\n return locationEvent.bounds;\n },\n /** Smooth pan and zoom to the location of the marker. Only works in Leaflet 1.0+. */\n flyTo: false,\n /**\n * The user location can be inside and outside the current view when the user clicks on the\n * control that is already active. Both cases can be configures separately.\n * Possible values are:\n * - 'setView': zoom and pan to the current location\n * - 'stop': stop locating and remove the location marker\n */\n clickBehavior: {\n /** What should happen if the user clicks on the control while the location is within the current view. */\n inView: 'stop',\n /** What should happen if the user clicks on the control while the location is outside the current view. */\n outOfView: 'setView',\n /**\n * What should happen if the user clicks on the control while the location is within the current view\n * and we could be following but are not. Defaults to a special value which inherits from 'inView';\n */\n inViewNotFollowing: 'inView',\n },\n /**\n * If set, save the map bounds just before centering to the user's\n * location. When control is disabled, set the view back to the\n * bounds that were saved.\n */\n returnToPrevBounds: false,\n /**\n * Keep a cache of the location after the user deactivates the control. If set to false, the user has to wait\n * until the locate API returns a new location before they see where they are again.\n */\n cacheLocation: true,\n /** If set, a circle that shows the location accuracy is drawn. */\n drawCircle: true,\n /** If set, the marker at the users' location is drawn. */\n drawMarker: true,\n /** If set and supported then show the compass heading */\n showCompass: true,\n /** The class to be used to create the marker. For example L.CircleMarker or L.Marker */\n markerClass: LocationMarker,\n /** The class us be used to create the compass bearing arrow */\n compassClass: CompassMarker,\n /** Accuracy circle style properties. NOTE these styles should match the css animations styles */\n circleStyle: {\n className: 'leaflet-control-locate-circle',\n color: '#136AEC',\n fillColor: '#136AEC',\n fillOpacity: 0.15,\n weight: 0\n },\n /** Inner marker style properties. Only works if your marker class supports `setStyle`. */\n markerStyle: {\n className: 'leaflet-control-locate-marker',\n color: '#fff',\n fillColor: '#2A93EE',\n fillOpacity: 1,\n weight: 3,\n opacity: 1,\n radius: 9\n },\n /** Compass */\n compassStyle: {\n fillColor: '#2A93EE',\n fillOpacity: 1,\n weight: 0,\n color: '#fff',\n opacity: 1,\n radius: 9, // How far is the arrow is from the center of of the marker\n width: 9, // Width of the arrow\n depth: 6 // Length of the arrow\n },\n /**\n * Changes to accuracy circle and inner marker while following.\n * It is only necessary to provide the properties that should change.\n */\n followCircleStyle: {},\n followMarkerStyle: {\n // color: '#FFA500',\n // fillColor: '#FFB000'\n },\n followCompassStyle: {},\n /** The CSS class for the icon. For example fa-location-arrow or fa-map-marker */\n icon: 'fa fa-map-marker',\n iconLoading: 'fa fa-spinner fa-spin',\n /** The element to be created for icons. For example span or i */\n iconElementTag: 'span',\n /** The element to be created for the text. For example small or span */\n textElementTag: 'small',\n /** Padding around the accuracy circle. */\n circlePadding: [0, 0],\n /** Use metric units. */\n metric: true,\n /**\n * This callback can be used in case you would like to override button creation behavior.\n * This is useful for DOM manipulation frameworks such as angular etc.\n * This function should return an object with HtmlElement for the button (link property) and the icon (icon property).\n */\n createButtonCallback: function (container, options) {\n var link = L.DomUtil.create('a', 'leaflet-bar-part leaflet-bar-part-single', container);\n link.title = options.strings.title;\n link.role = 'button';\n link.href = '#';\n var icon = L.DomUtil.create(options.iconElementTag, options.icon, link);\n\n if (options.strings.text !== undefined) {\n var text = L.DomUtil.create(options.textElementTag, 'leaflet-locate-text', link);\n text.textContent = options.strings.text;\n\t\t link.classList.add('leaflet-locate-text-active');\n link.parentNode.style.display = \"flex\";\n if (options.icon.length > 0) {\n icon.classList.add('leaflet-locate-icon');\n }\n }\n \n return { link: link, icon: icon };\n },\n /** This event is called in case of any location error that is not a time out error. */\n onLocationError: function(err, control) {\n alert(err.message);\n },\n /**\n * This event is called when the user's location is outside the bounds set on the map.\n * The event is called repeatedly when the location changes.\n */\n onLocationOutsideMapBounds: function(control) {\n control.stop();\n alert(control.options.strings.outsideMapBoundsMsg);\n },\n /** Display a pop-up when the user click on the inner marker. */\n showPopup: true,\n strings: {\n title: \"Show me where I am\",\n metersUnit: \"meters\",\n feetUnit: \"feet\",\n popup: \"You are within {distance} {unit} from this point\",\n outsideMapBoundsMsg: \"You seem located outside the boundaries of the map\"\n },\n /** The default options passed to leaflets locate method. */\n locateOptions: {\n maxZoom: Infinity,\n watch: true, // if you overwrite this, visualization cannot be updated\n setView: false // have to set this to false because we have to\n // do setView manually\n }\n },\n\n initialize: function (options) {\n // set default options if nothing is set (merge one step deep)\n for (var i in options) {\n if (typeof this.options[i] === 'object') {\n L.extend(this.options[i], options[i]);\n } else {\n this.options[i] = options[i];\n }\n }\n\n // extend the follow marker style and circle from the normal style\n this.options.followMarkerStyle = L.extend({}, this.options.markerStyle, this.options.followMarkerStyle);\n this.options.followCircleStyle = L.extend({}, this.options.circleStyle, this.options.followCircleStyle);\n this.options.followCompassStyle = L.extend({}, this.options.compassStyle, this.options.followCompassStyle);\n },\n\n /**\n * Add control to map. Returns the container for the control.\n */\n onAdd: function (map) {\n var container = L.DomUtil.create('div',\n 'leaflet-control-locate leaflet-bar leaflet-control');\n this._container = container;\n this._map = map;\n this._layer = this.options.layer || new L.LayerGroup();\n this._layer.addTo(map);\n this._event = undefined;\n this._compassHeading = null;\n this._prevBounds = null;\n\n var linkAndIcon = this.options.createButtonCallback(container, this.options);\n this._link = linkAndIcon.link;\n this._icon = linkAndIcon.icon;\n\n L.DomEvent.on(\n this._link,\n \"click\",\n function (ev) {\n L.DomEvent.stopPropagation(ev);\n L.DomEvent.preventDefault(ev);\n this._onClick();\n },\n this\n ).on(this._link, \"dblclick\", L.DomEvent.stopPropagation);\n\n this._resetVariables();\n\n this._map.on('unload', this._unload, this);\n\n return container;\n },\n\n /**\n * This method is called when the user clicks on the control.\n */\n _onClick: function() {\n this._justClicked = true;\n var wasFollowing = this._isFollowing();\n this._userPanned = false;\n this._userZoomed = false;\n\n if (this._active && !this._event) {\n // click while requesting\n this.stop();\n } else if (this._active) {\n var behaviors = this.options.clickBehavior;\n var behavior = behaviors.outOfView;\n if (this._map.getBounds().contains(this._event.latlng)) {\n behavior = wasFollowing ? behaviors.inView : behaviors.inViewNotFollowing;\n }\n\n // Allow inheriting from another behavior\n if (behaviors[behavior]) {\n behavior = behaviors[behavior];\n }\n\n switch (behavior) {\n case 'setView':\n this.setView();\n break;\n case 'stop':\n this.stop();\n if (this.options.returnToPrevBounds) {\n var f = this.options.flyTo ? this._map.flyToBounds : this._map.fitBounds;\n f.bind(this._map)(this._prevBounds);\n }\n break;\n }\n } else {\n if (this.options.returnToPrevBounds) {\n this._prevBounds = this._map.getBounds();\n }\n this.start();\n }\n\n this._updateContainerStyle();\n },\n\n /**\n * Starts the plugin:\n * - activates the engine\n * - draws the marker (if coordinates available)\n */\n start: function() {\n this._activate();\n\n if (this._event) {\n this._drawMarker(this._map);\n\n // if we already have a location but the user clicked on the control\n if (this.options.setView) {\n this.setView();\n }\n }\n this._updateContainerStyle();\n },\n\n /**\n * Stops the plugin:\n * - deactivates the engine\n * - reinitializes the button\n * - removes the marker\n */\n stop: function() {\n this._deactivate();\n\n this._cleanClasses();\n this._resetVariables();\n\n this._removeMarker();\n },\n\n /**\n * Keep the control active but stop following the location\n */\n stopFollowing: function() {\n this._userPanned = true;\n this._updateContainerStyle();\n this._drawMarker();\n },\n\n /**\n * This method launches the location engine.\n * It is called before the marker is updated,\n * event if it does not mean that the event will be ready.\n *\n * Override it if you want to add more functionalities.\n * It should set the this._active to true and do nothing if\n * this._active is true.\n */\n _activate: function() {\n if (!this._active) {\n this._map.locate(this.options.locateOptions);\n this._map.fire('locateactivate', this);\n this._active = true;\n\n // bind event listeners\n this._map.on('locationfound', this._onLocationFound, this);\n this._map.on('locationerror', this._onLocationError, this);\n this._map.on('dragstart', this._onDrag, this);\n this._map.on('zoomstart', this._onZoom, this);\n this._map.on('zoomend', this._onZoomEnd, this);\n if (this.options.showCompass) {\n var oriAbs = 'ondeviceorientationabsolute' in window;\n if (oriAbs || ('ondeviceorientation' in window)) {\n var _this = this;\n var deviceorientation = function () {\n L.DomEvent.on(window, oriAbs ? 'deviceorientationabsolute' : 'deviceorientation', _this._onDeviceOrientation, _this);\n };\n if (DeviceOrientationEvent && typeof DeviceOrientationEvent.requestPermission === 'function') {\n DeviceOrientationEvent.requestPermission().then(function (permissionState) {\n if (permissionState === 'granted') {\n deviceorientation();\n }\n });\n } else {\n deviceorientation();\n }\n }\n }\n }\n },\n\n /**\n * Called to stop the location engine.\n *\n * Override it to shutdown any functionalities you added on start.\n */\n _deactivate: function() {\n this._map.stopLocate();\n this._map.fire('locatedeactivate', this);\n this._active = false;\n\n if (!this.options.cacheLocation) {\n this._event = undefined;\n }\n\n // unbind event listeners\n this._map.off('locationfound', this._onLocationFound, this);\n this._map.off('locationerror', this._onLocationError, this);\n this._map.off('dragstart', this._onDrag, this);\n this._map.off('zoomstart', this._onZoom, this);\n this._map.off('zoomend', this._onZoomEnd, this);\n if (this.options.showCompass) {\n this._compassHeading = null;\n if ('ondeviceorientationabsolute' in window) {\n L.DomEvent.off(window, 'deviceorientationabsolute', this._onDeviceOrientation, this);\n } else if ('ondeviceorientation' in window) {\n L.DomEvent.off(window, 'deviceorientation', this._onDeviceOrientation, this);\n }\n }\n },\n\n /**\n * Zoom (unless we should keep the zoom level) and an to the current view.\n */\n setView: function() {\n this._drawMarker();\n if (this._isOutsideMapBounds()) {\n this._event = undefined; // clear the current location so we can get back into the bounds\n this.options.onLocationOutsideMapBounds(this);\n } else {\n\t\tif (this._justClicked && this.options.initialZoomLevel !== false) {\n var f = this.options.flyTo ? this._map.flyTo : this._map.setView;\n f.bind(this._map)([this._event.latitude, this._event.longitude], this.options.initialZoomLevel);\n\t\t} else\n if (this.options.keepCurrentZoomLevel) {\n var f = this.options.flyTo ? this._map.flyTo : this._map.panTo;\n f.bind(this._map)([this._event.latitude, this._event.longitude]);\n } else {\n var f = this.options.flyTo ? this._map.flyToBounds : this._map.fitBounds;\n // Ignore zoom events while setting the viewport as these would stop following\n this._ignoreEvent = true;\n f.bind(this._map)(this.options.getLocationBounds(this._event), {\n padding: this.options.circlePadding,\n maxZoom: this.options.locateOptions.maxZoom\n });\n L.Util.requestAnimFrame(function(){\n // Wait until after the next animFrame because the flyTo can be async\n this._ignoreEvent = false;\n }, this);\n\n }\n }\n },\n\n /**\n *\n */\n _drawCompass: function() {\n if (!this._event) {\n return;\n }\n\n var latlng = this._event.latlng;\n\n if (this.options.showCompass && latlng && this._compassHeading !== null) {\n var cStyle = this._isFollowing() ? this.options.followCompassStyle : this.options.compassStyle;\n if (!this._compass) {\n this._compass = new this.options.compassClass(latlng, this._compassHeading, cStyle).addTo(this._layer);\n } else {\n this._compass.setLatLng(latlng);\n this._compass.setHeading(this._compassHeading);\n // If the compassClass can be updated with setStyle, update it.\n if (this._compass.setStyle) {\n this._compass.setStyle(cStyle);\n }\n }\n // \n }\n if (this._compass && (!this.options.showCompass || this._compassHeading === null)) {\n this._compass.removeFrom(this._layer);\n this._compass = null;\n }\n },\n\n /**\n * Draw the marker and accuracy circle on the map.\n *\n * Uses the event retrieved from onLocationFound from the map.\n */\n _drawMarker: function() {\n if (this._event.accuracy === undefined) {\n this._event.accuracy = 0;\n }\n\n var radius = this._event.accuracy;\n var latlng = this._event.latlng;\n\n // circle with the radius of the location's accuracy\n if (this.options.drawCircle) {\n var style = this._isFollowing() ? this.options.followCircleStyle : this.options.circleStyle;\n\n if (!this._circle) {\n this._circle = L.circle(latlng, radius, style).addTo(this._layer);\n } else {\n this._circle.setLatLng(latlng).setRadius(radius).setStyle(style);\n }\n }\n\n var distance, unit;\n if (this.options.metric) {\n distance = radius.toFixed(0);\n unit = this.options.strings.metersUnit;\n } else {\n distance = (radius * 3.2808399).toFixed(0);\n unit = this.options.strings.feetUnit;\n }\n\n // small inner marker\n if (this.options.drawMarker) {\n var mStyle = this._isFollowing() ? this.options.followMarkerStyle : this.options.markerStyle;\n if (!this._marker) {\n this._marker = new this.options.markerClass(latlng, mStyle).addTo(this._layer);\n } else {\n this._marker.setLatLng(latlng);\n // If the markerClass can be updated with setStyle, update it.\n if (this._marker.setStyle) {\n this._marker.setStyle(mStyle);\n }\n }\n }\n\n this._drawCompass();\n\n var t = this.options.strings.popup;\n function getPopupText() {\n if (typeof t === 'string') {\n return L.Util.template(t, {distance: distance, unit: unit});\n } else if (typeof t === 'function') {\n return t({distance: distance, unit: unit});\n } else {\n return t;\n }\n }\n if (this.options.showPopup && t && this._marker) {\n this._marker\n .bindPopup(getPopupText())\n ._popup.setLatLng(latlng);\n }\n if (this.options.showPopup && t && this._compass) {\n this._compass\n .bindPopup(getPopupText())\n ._popup.setLatLng(latlng);\n }\n },\n\n /**\n * Remove the marker from map.\n */\n _removeMarker: function() {\n this._layer.clearLayers();\n this._marker = undefined;\n this._circle = undefined;\n },\n\n /**\n * Unload the plugin and all event listeners.\n * Kind of the opposite of onAdd.\n */\n _unload: function() {\n this.stop();\n this._map.off('unload', this._unload, this);\n },\n\n /**\n * Sets the compass heading\n */\n _setCompassHeading: function(angle) {\n if (!isNaN(parseFloat(angle)) && isFinite(angle)) {\n angle = Math.round(angle);\n\n this._compassHeading = angle;\n L.Util.requestAnimFrame(this._drawCompass, this);\n } else {\n this._compassHeading = null;\n }\n },\n\n /**\n * If the compass fails calibration just fail safely and remove the compass\n */\n _onCompassNeedsCalibration: function() {\n this._setCompassHeading();\n },\n\n /**\n * Process and normalise compass events\n */\n _onDeviceOrientation: function(e) {\n if (!this._active) {\n return;\n }\n\n if (e.webkitCompassHeading) {\n // iOS\n this._setCompassHeading(e.webkitCompassHeading);\n } else if (e.absolute && e.alpha) {\n // Android\n this._setCompassHeading(360 - e.alpha)\n }\n },\n\n /**\n * Calls deactivate and dispatches an error.\n */\n _onLocationError: function(err) {\n // ignore time out error if the location is watched\n if (err.code == 3 && this.options.locateOptions.watch) {\n return;\n }\n\n this.stop();\n this.options.onLocationError(err, this);\n },\n\n /**\n * Stores the received event and updates the marker.\n */\n _onLocationFound: function(e) {\n // no need to do anything if the location has not changed\n if (this._event &&\n (this._event.latlng.lat === e.latlng.lat &&\n this._event.latlng.lng === e.latlng.lng &&\n this._event.accuracy === e.accuracy)) {\n return;\n }\n\n if (!this._active) {\n // we may have a stray event\n return;\n }\n\n this._event = e;\n\n this._drawMarker();\n this._updateContainerStyle();\n\n switch (this.options.setView) {\n case 'once':\n if (this._justClicked) {\n this.setView();\n }\n break;\n case 'untilPan':\n if (!this._userPanned) {\n this.setView();\n }\n break;\n case 'untilPanOrZoom':\n if (!this._userPanned && !this._userZoomed) {\n this.setView();\n }\n break;\n case 'always':\n this.setView();\n break;\n case false:\n // don't set the view\n break;\n }\n\n this._justClicked = false;\n },\n\n /**\n * When the user drags. Need a separate event so we can bind and unbind event listeners.\n */\n _onDrag: function() {\n // only react to drags once we have a location\n if (this._event && !this._ignoreEvent) {\n this._userPanned = true;\n this._updateContainerStyle();\n this._drawMarker();\n }\n },\n\n /**\n * When the user zooms. Need a separate event so we can bind and unbind event listeners.\n */\n _onZoom: function() {\n // only react to drags once we have a location\n if (this._event && !this._ignoreEvent) {\n this._userZoomed = true;\n this._updateContainerStyle();\n this._drawMarker();\n }\n },\n\n /**\n * After a zoom ends update the compass and handle sideways zooms\n */\n _onZoomEnd: function() {\n if (this._event) {\n this._drawCompass();\n }\n\n if (this._event && !this._ignoreEvent) {\n // If we have zoomed in and out and ended up sideways treat it as a pan\n if (this._marker && !this._map.getBounds().pad(-.3).contains(this._marker.getLatLng())) {\n this._userPanned = true;\n this._updateContainerStyle();\n this._drawMarker();\n }\n }\n },\n\n /**\n * Compute whether the map is following the user location with pan and zoom.\n */\n _isFollowing: function() {\n if (!this._active) {\n return false;\n }\n\n if (this.options.setView === 'always') {\n return true;\n } else if (this.options.setView === 'untilPan') {\n return !this._userPanned;\n } else if (this.options.setView === 'untilPanOrZoom') {\n return !this._userPanned && !this._userZoomed;\n }\n },\n\n /**\n * Check if location is in map bounds\n */\n _isOutsideMapBounds: function() {\n if (this._event === undefined) {\n return false;\n }\n return this._map.options.maxBounds &&\n !this._map.options.maxBounds.contains(this._event.latlng);\n },\n\n /**\n * Toggles button class between following and active.\n */\n _updateContainerStyle: function() {\n if (!this._container) {\n return;\n }\n\n if (this._active && !this._event) {\n // active but don't have a location yet\n this._setClasses('requesting');\n } else if (this._isFollowing()) {\n this._setClasses('following');\n } else if (this._active) {\n this._setClasses('active');\n } else {\n this._cleanClasses();\n }\n },\n\n /**\n * Sets the CSS classes for the state.\n */\n _setClasses: function(state) {\n if (state == 'requesting') {\n removeClasses(this._container, \"active following\");\n addClasses(this._container, \"requesting\");\n\n removeClasses(this._icon, this.options.icon);\n addClasses(this._icon, this.options.iconLoading);\n } else if (state == 'active') {\n removeClasses(this._container, \"requesting following\");\n addClasses(this._container, \"active\");\n\n removeClasses(this._icon, this.options.iconLoading);\n addClasses(this._icon, this.options.icon);\n } else if (state == 'following') {\n removeClasses(this._container, \"requesting\");\n addClasses(this._container, \"active following\");\n\n removeClasses(this._icon, this.options.iconLoading);\n addClasses(this._icon, this.options.icon);\n }\n },\n\n /**\n * Removes all classes from button.\n */\n _cleanClasses: function() {\n L.DomUtil.removeClass(this._container, \"requesting\");\n L.DomUtil.removeClass(this._container, \"active\");\n L.DomUtil.removeClass(this._container, \"following\");\n\n removeClasses(this._icon, this.options.iconLoading);\n addClasses(this._icon, this.options.icon);\n },\n\n /**\n * Reinitializes state variables.\n */\n _resetVariables: function() {\n // whether locate is active or not\n this._active = false;\n\n // true if the control was clicked for the first time\n // we need this so we can pan and zoom once we have the location\n this._justClicked = false;\n\n // true if the user has panned the map after clicking the control\n this._userPanned = false;\n\n // true if the user has zoomed the map after clicking the control\n this._userZoomed = false;\n }\n });\n\n L.control.locate = function (options) {\n return new L.Control.Locate(options);\n };\n\n return LocateControl;\n}, window));\n","(function () {\n\nL.Control.FullScreen = L.Control.extend({\n\toptions: {\n\t\tposition: 'topleft',\n\t\ttitle: 'Full Screen',\n\t\ttitleCancel: 'Exit Full Screen',\n\t\tforceSeparateButton: false,\n\t\tforcePseudoFullscreen: false,\n\t\tfullscreenElement: false\n\t},\n\t\n\tonAdd: function (map) {\n\t\tvar className = 'leaflet-control-zoom-fullscreen', container, content = '';\n\t\t\n\t\tif (map.zoomControl && !this.options.forceSeparateButton) {\n\t\t\tcontainer = map.zoomControl._container;\n\t\t} else {\n\t\t\tcontainer = L.DomUtil.create('div', 'leaflet-bar');\n\t\t}\n\t\t\n\t\tif (this.options.content) {\n\t\t\tcontent = this.options.content;\n\t\t} else {\n\t\t\tclassName += ' fullscreen-icon';\n\t\t}\n\n\t\tthis._createButton(this.options.title, className, content, container, this.toggleFullScreen, this);\n\t\tthis._map.fullscreenControl = this;\n\n\t\tthis._map.on('enterFullscreen exitFullscreen', this._toggleTitle, this);\n\n\t\treturn container;\n\t},\n\t\n\tonRemove: function (map) {\n\t\tL.DomEvent\n\t\t\t.off(this.link, 'click', L.DomEvent.stopPropagation)\n\t\t\t.off(this.link, 'click', L.DomEvent.preventDefault)\n\t\t\t.off(this.link, 'click', this.toggleFullScreen, this);\n\t\t\n\t\tL.DomEvent\n\t\t\t.off(this._container, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)\n\t\t\t.off(this._container, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)\n\t\t\t.off(this._container, fullScreenApi.fullScreenEventName, this._handleFullscreenChange, this);\n\t\t\n\t\tL.DomEvent\n\t\t\t.off(document, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)\n\t\t\t.off(document, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)\n\t\t\t.off(document, fullScreenApi.fullScreenEventName, this._handleFullscreenChange, this);\n\t},\n\t\n\t_createButton: function (title, className, content, container, fn, context) {\n\t\tthis.link = L.DomUtil.create('a', className, container);\n\t\tthis.link.href = '#';\n\t\tthis.link.title = title;\n\t\tthis.link.innerHTML = content;\n\n\t\tthis.link.setAttribute('role', 'button');\n\t\tthis.link.setAttribute('aria-label', title);\n\n\t\tL.DomEvent\n\t\t\t.on(this.link, 'click', L.DomEvent.stopPropagation)\n\t\t\t.on(this.link, 'click', L.DomEvent.preventDefault)\n\t\t\t.on(this.link, 'click', fn, context);\n\t\t\n\t\tL.DomEvent\n\t\t\t.on(container, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)\n\t\t\t.on(container, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)\n\t\t\t.on(container, fullScreenApi.fullScreenEventName, this._handleFullscreenChange, context);\n\t\t\n\t\tL.DomEvent\n\t\t\t.on(document, fullScreenApi.fullScreenEventName, L.DomEvent.stopPropagation)\n\t\t\t.on(document, fullScreenApi.fullScreenEventName, L.DomEvent.preventDefault)\n\t\t\t.on(document, fullScreenApi.fullScreenEventName, this._handleFullscreenChange, context);\n\n\t\treturn this.link;\n\t},\n\t\n\ttoggleFullScreen: function () {\n\t\tvar map = this._map;\n\t\tmap._exitFired = false;\n\t\tif (map._isFullscreen) {\n\t\t\tif (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) {\n\t\t\t\tfullScreenApi.cancelFullScreen();\n\t\t\t} else {\n\t\t\t\tL.DomUtil.removeClass(this.options.fullscreenElement ? this.options.fullscreenElement : map._container, 'leaflet-pseudo-fullscreen');\n\t\t\t}\n\t\t\tmap.fire('exitFullscreen');\n\t\t\tmap._exitFired = true;\n\t\t\tmap._isFullscreen = false;\n\t\t}\n\t\telse {\n\t\t\tif (fullScreenApi.supportsFullScreen && !this.options.forcePseudoFullscreen) {\n\t\t\t\tfullScreenApi.requestFullScreen(this.options.fullscreenElement ? this.options.fullscreenElement : map._container);\n\t\t\t} else {\n\t\t\t\tL.DomUtil.addClass(this.options.fullscreenElement ? this.options.fullscreenElement : map._container, 'leaflet-pseudo-fullscreen');\n\t\t\t}\n\t\t\tmap.fire('enterFullscreen');\n\t\t\tmap._isFullscreen = true;\n\t\t}\n\t},\n\t\n\t_toggleTitle: function () {\n\t\tthis.link.title = this._map._isFullscreen ? this.options.title : this.options.titleCancel;\n\t},\n\t\n\t_handleFullscreenChange: function () {\n\t\tvar map = this._map;\n\t\tmap.invalidateSize();\n\t\tif (!fullScreenApi.isFullScreen() && !map._exitFired) {\n\t\t\tmap.fire('exitFullscreen');\n\t\t\tmap._exitFired = true;\n\t\t\tmap._isFullscreen = false;\n\t\t}\n\t}\n});\n\nL.Map.include({\n\ttoggleFullscreen: function () {\n\t\tthis.fullscreenControl.toggleFullScreen();\n\t}\n});\n\nL.Map.addInitHook(function () {\n\tif (this.options.fullscreenControl) {\n\t\tthis.addControl(L.control.fullscreen(this.options.fullscreenControlOptions));\n\t}\n});\n\nL.control.fullscreen = function (options) {\n\treturn new L.Control.FullScreen(options);\n};\n\n/* \nNative FullScreen JavaScript API\n-------------\nAssumes Mozilla naming conventions instead of W3C for now\n\nsource : http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/\n\n*/\n\n\tvar \n\t\tfullScreenApi = { \n\t\t\tsupportsFullScreen: false,\n\t\t\tisFullScreen: function () { return false; }, \n\t\t\trequestFullScreen: function () {}, \n\t\t\tcancelFullScreen: function () {},\n\t\t\tfullScreenEventName: '',\n\t\t\tprefix: ''\n\t\t},\n\t\tbrowserPrefixes = 'webkit moz o ms khtml'.split(' ');\n\t\n\t// check for native support\n\tif (typeof document.exitFullscreen !== 'undefined') {\n\t\tfullScreenApi.supportsFullScreen = true;\n\t} else {\n\t\t// check for fullscreen support by vendor prefix\n\t\tfor (var i = 0, il = browserPrefixes.length; i < il; i++) {\n\t\t\tfullScreenApi.prefix = browserPrefixes[i];\n\t\t\tif (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] !== 'undefined') {\n\t\t\t\tfullScreenApi.supportsFullScreen = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (typeof document['msExitFullscreen'] !== 'undefined') {\n\t\t\tfullScreenApi.prefix = 'ms';\n\t\t\tfullScreenApi.supportsFullScreen = true;\n\t\t}\n\t}\n\t\n\t// update methods to do something useful\n\tif (fullScreenApi.supportsFullScreen) {\n\t\tif (fullScreenApi.prefix === 'ms') {\n\t\t\tfullScreenApi.fullScreenEventName = 'MSFullscreenChange';\n\t\t} else {\n\t\t\tfullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';\n\t\t}\n\t\tfullScreenApi.isFullScreen = function () {\n\t\t\tswitch (this.prefix) {\n\t\t\t\tcase '':\n\t\t\t\t\treturn document.fullscreen;\n\t\t\t\tcase 'webkit':\n\t\t\t\t\treturn document.webkitIsFullScreen;\n\t\t\t\tcase 'ms':\n\t\t\t\t\treturn document.msFullscreenElement;\n\t\t\t\tdefault:\n\t\t\t\t\treturn document[this.prefix + 'FullScreen'];\n\t\t\t}\n\t\t};\n\t\tfullScreenApi.requestFullScreen = function (el) {\n\t\t\tswitch (this.prefix) {\n\t\t\t\tcase '':\n\t\t\t\t\treturn el.requestFullscreen();\n\t\t\t\tcase 'ms':\n\t\t\t\t\treturn el.msRequestFullscreen();\n\t\t\t\tdefault:\n\t\t\t\t\treturn el[this.prefix + 'RequestFullScreen']();\n\t\t\t}\n\t\t};\n\t\tfullScreenApi.cancelFullScreen = function () {\n\t\t\tswitch (this.prefix) {\n\t\t\t\tcase '':\n\t\t\t\t\treturn document.exitFullscreen();\n\t\t\t\tcase 'ms':\n\t\t\t\t\treturn document.msExitFullscreen();\n\t\t\t\tdefault:\n\t\t\t\t\treturn document[this.prefix + 'CancelFullScreen']();\n\t\t\t}\n\t\t};\n\t}\n\n\t// jQuery plugin\n\tif (typeof jQuery !== 'undefined') {\n\t\tjQuery.fn.requestFullScreen = function () {\n\t\t\treturn this.each(function () {\n\t\t\t\tvar el = jQuery(this);\n\t\t\t\tif (fullScreenApi.supportsFullScreen) {\n\t\t\t\t\tfullScreenApi.requestFullScreen(el);\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\t}\n\n\t// export api\n\twindow.fullScreenApi = fullScreenApi;\n})();\n","/**\r\n * leaflet-pegman\r\n *\r\n * @author Raruto\r\n * @license GPL-3.0+\r\n * @link https://github.com/Raruto/leaflet-pegman\r\n * @desc Leaflet plugin that allows an easy integration with the Google StreetView Service API\r\n */\r\nL.Control.Pegman = L.Control.extend({\r\n\tincludes: L.Evented ? L.Evented.prototype : L.Mixin.Events,\r\n\toptions: {\r\n\t\tposition: 'bottomright',\r\n\t\ttheme: \"leaflet-pegman-v3-default\", // or \"leaflet-pegman-v3-small\"\r\n\t\tdebug: false,\r\n\t\tapiKey: '',\r\n\t\tlibraries: '',\r\n\t\tmutant: {\r\n\t\t\tattribution: 'Map data: © Google',\r\n\t\t\tpane: \"overlayPane\",\r\n\t\t\ttype: null, // Non-image map type (used to force a transparent background)\r\n\t\t},\r\n\t\tpano: {\r\n\t\t\tenableCloseButton: true,\r\n\t\t\tfullscreenControl: false,\r\n\t\t\timageDateControl: true\r\n\t\t},\r\n\t\tmarker: {\r\n\t\t\tdraggable: true,\r\n\t\t\ticon: L.icon({\r\n\t\t\t\tclassName: \"pegman-marker\",\r\n\t\t\t\ticonSize: [52, 52],\r\n\t\t\t\ticonAnchor: [24, 33],\r\n\t\t\t\ticonUrl: 'data:image/png;base64,' + \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAFElEQVR4XgXAAQ0AAABAMP1L30IDCPwC/o5WcS4AAAAASUVORK5CYII=\",\r\n\t\t\t}),\r\n\t\t}\r\n\t},\r\n\r\n\t__interactURL: 'https://unpkg.com/interactjs@1.2.9/dist/interact.min.js',\r\n\t__gmapsURL: 'https://maps.googleapis.com/maps/api/js?v=3',\r\n\t__mutantURL: 'https://unpkg.com/leaflet.gridlayer.googlemutant@0.10.0/Leaflet.GoogleMutant.js',\r\n\r\n\tinitialize: function(options) {\r\n\r\n\t\tif (typeof options.logging !== \"undefined\") options.debug = options.logging;\r\n\r\n\t\tL.Util.setOptions(this, options);\r\n\r\n\t\t// Grab Left/Right/Up/Down Direction of Mouse for Pegman Image\r\n\t\tthis._mousePos = {\r\n\t\t\tdirection: {},\r\n\t\t\told: {},\r\n\t\t};\r\n\r\n\t\tthis._pegmanMarkerCoords = null;\r\n\t\tthis._streetViewCoords = null;\r\n\t\tthis._streetViewLayerEnabled = false;\r\n\r\n\t\tthis._dropzoneMapOpts = {\r\n\t\t\taccept: '.draggable', // Only Accept Elements Matching this CSS Selector\r\n\t\t\toverlap: 0.75, // Require a 75% Element Overlap for a Drop to be Possible\r\n\t\t\tondropactivate: L.bind(this.onDropZoneActivated, this),\r\n\t\t\tondragenter: L.bind(this.onDropZoneDragEntered, this),\r\n\t\t\tondragleave: L.bind(this.onDropZoneDragLeaved, this),\r\n\t\t\tondrop: L.bind(this.onDropZoneDropped, this),\r\n\t\t\tondropdeactivate: L.bind(this.onDropZoneDeactivated, this),\r\n\t\t};\r\n\t\tthis._draggableMarkerOpts = {\r\n\t\t\tinertia: false,\r\n\t\t\tonmove: L.bind(this.onDraggableMove, this),\r\n\t\t\tonend: L.bind(this.onDraggableEnd, this),\r\n\t\t};\r\n\r\n\t\tthis._lazyLoaderAdded = false;\r\n\t},\r\n\r\n\tonAdd: function(map) {\r\n\t\tthis._map = map;\r\n\r\n\t\tthis._container = L.DomUtil.create('div', 'leaflet-pegman pegman-control leaflet-bar');\r\n\t\tthis._pegman = L.DomUtil.create('div', 'pegman draggable drag-drop', this._container);\r\n\t\tthis._pegmanButton = L.DomUtil.create('div', 'pegman-button', this._container);\r\n\t\tthis._pegmanMarker = L.marker([0, 0], this.options.marker);\r\n\t\tthis._panoDiv = this.options.panoDiv ? document.querySelector(this.options.panoDiv) : L.DomUtil.create('div', '', this._map._container);\r\n\r\n\t\tL.DomUtil.addClass(this._panoDiv, 'pano-canvas');\r\n\t\tL.DomUtil.addClass(this._map._container, this.options.theme);\r\n\r\n\t\tL.DomEvent.disableClickPropagation(this._panoDiv);\r\n\t\t// L.DomEvent.on(this._container, 'click mousedown touchstart dblclick', this._disableClickPropagation, this);\r\n\t\tL.DomEvent.on(this._container, 'click mousedown dblclick', this._disableClickPropagation, this);\r\n\r\n\t\tthis._container.addEventListener('touchstart', this._loadScripts.bind(this, !L.Browser.touch), { once: true });\r\n\t\tthis._container.addEventListener('mousedown', this._loadScripts.bind(this, true), { once: true });\r\n\t\tthis._container.addEventListener('mouseover', this._loadScripts.bind(this, false), { once: true });\r\n\r\n\t\tthis._loadInteractHandlers();\r\n\t\tthis._loadGoogleHandlers();\r\n\r\n\t\tL.DomEvent.on(document, 'mousemove', this.mouseMoveTracking, this);\r\n\t\tL.DomEvent.on(document, 'keyup', this.keyUpTracking, this);\r\n\r\n\t\tthis._pegmanMarker.on(\"dragend\", this.onPegmanMarkerDragged, this);\r\n\t\tthis._map.on(\"click\", this.onMapClick, this);\r\n\t\tthis._map.on(\"layeradd\", this.onMapLayerAdd, this);\r\n\r\n\t\treturn this._container;\r\n\t},\r\n\r\n\tonRemove: function(map) {\r\n\t\tif (this._googleStreetViewLayer) this._googleStreetViewLayer.remove();\r\n\t\tif (this._pegmanMarker) this._pegmanMarker.remove();\r\n\r\n\t\tL.DomUtil.remove(this._panoDiv);\r\n\r\n\t\tL.DomEvent.off(document, 'mousemove', this.mouseMoveTracking, this);\r\n\t\tL.DomEvent.off(document, 'keyup', this.keyUpTracking, this);\r\n\r\n\t\tmap.off(\"mousemove\", this._setMouseCursor, this);\r\n\t},\r\n\r\n\t_log: function(args) {\r\n\t\tif (this.options.debug) {\r\n\t\t\tconsole.log(args);\r\n\t\t}\r\n\t},\r\n\r\n\t_addClasses: function(el, classNames) {\r\n\t\tclassNames = classNames.split(\" \");\r\n\t\tfor (var s in classNames) {\r\n\t\t\tL.DomUtil.addClass(el, classNames[s]);\r\n\t\t}\r\n\t},\r\n\r\n\t_removeClasses: function(el, classNames) {\r\n\t\tclassNames = classNames.split(\" \");\r\n\t\tfor (var s in classNames) {\r\n\t\t\tL.DomUtil.removeClass(el, classNames[s]);\r\n\t\t}\r\n\t},\r\n\r\n\t_removeAttributes: function(el, attrNames) {\r\n\t\tfor (var a in attrNames) {\r\n\t\t\tel.removeAttribute(attrNames[a]);\r\n\t\t}\r\n\t},\r\n\r\n\t_insertAfter: function(targetNode, newNode) {\r\n\t\ttargetNode.parentNode.insertBefore(newNode, targetNode.nextSibling);\r\n\t},\r\n\r\n\t_translateElement: function(el, dx, dy) {\r\n\t\tif (dx === false && dy === false) {\r\n\t\t\tthis._removeAttributes(this._pegman, [\"style\", \"data-x\", \"data-y\"]);\r\n\t\t}\r\n\t\t// Element's position is preserved within the data-x/data-y attributes\r\n\t\tvar x = (parseFloat(el.getAttribute('data-x')) || 0) + dx;\r\n\t\tvar y = (parseFloat(el.getAttribute('data-y')) || 0) + dy;\r\n\r\n\t\t// Translate element\r\n\t\tel.style.webkitTransform = el.style.transform = 'translate(' + x + 'px, ' + y + 'px)';\r\n\r\n\t\t// Update position attributes\r\n\t\tel.setAttribute('data-x', x);\r\n\t\tel.setAttribute('data-y', y);\r\n\t},\r\n\r\n\t_updateClasses: function(action) {\r\n\t\tswitch (action) {\r\n\t\t\tcase \"pegman-dragging\":\r\n\t\t\t\tthis._removeClasses(this._pegman, \"dropped\");\r\n\t\t\t\tthis._addClasses(this._container, \"dragging\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"pegman-dragged\":\r\n\t\t\t\tthis._removeClasses(this._pegman, \"can-drop dragged left right active dropped\");\r\n\t\t\t\tthis._removeAttributes(this._pegman, [\"style\", \"data-x\", \"data-y\"]);\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"dropzone-actived\":\r\n\t\t\t\tthis._addClasses(this._map._container, \"drop-active\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"dropzone-drag-entered\":\r\n\t\t\t\tthis._addClasses(this._pegman, \"active can-drop\");\r\n\t\t\t\tthis._addClasses(this._map._container, \"drop-target\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"dropzone-drag-leaved\":\r\n\t\t\t\tthis._removeClasses(this._map._container, \"drop-target\");\r\n\t\t\t\tthis._removeClasses(this._pegman, \"can-drop\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"dropzone-drop\":\r\n\t\t\t\tthis._removeClasses(this._container, \"dragging\");\r\n\t\t\t\tthis._removeClasses(this._pegman, \"active left right\");\r\n\t\t\t\tthis._addClasses(this._pegman, \"dropped\");\r\n\t\t\t\tthis._removeClasses(this._pegman, \"can-drop dragged left right active dropped\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"dropzone-deactivated\":\r\n\t\t\t\tthis._removeClasses(this._pegman, \"active left right\");\r\n\t\t\t\tthis._removeClasses(this._map._container, \"drop-active drop-target\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"mousemove-top\":\r\n\t\t\t\tthis._addClasses(this._pegman, \"top\");\r\n\t\t\t\tthis._removeClasses(this._pegman, \"bottom right left\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"mousemove-bottom\":\r\n\t\t\t\tthis._addClasses(this._pegman, \"bottom\");\r\n\t\t\t\tthis._removeClasses(this._pegman, \"top right left\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"mousemove-left\":\r\n\t\t\t\tthis._addClasses(this._pegman, \"left\");\r\n\t\t\t\tthis._removeClasses(this._pegman, \"right top bottom\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"mousemove-right\":\r\n\t\t\t\tthis._addClasses(this._pegman, \"right\");\r\n\t\t\t\tthis._removeClasses(this._pegman, \"left top bottom\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"pegman-added\":\r\n\t\t\t\tthis._addClasses(this._container, \"active\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"pegman-removed\":\r\n\t\t\t\tthis._removeClasses(this._container, \"active\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"streetview-shown\":\r\n\t\t\t\tthis._addClasses(this._container, \"streetview-layer-active\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"streetview-hidden\":\r\n\t\t\t\tthis._removeClasses(this._container, \"streetview-layer-active\");\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tthrow \"Unhandled event:\" + action;\r\n\t\t}\r\n\t\tthis.fire(\"svpc_\" + action);\r\n\t},\r\n\r\n\tonDraggableMove: function(e) {\r\n\t\tthis.mouseMoveTracking(e);\r\n\t\tthis.pegmanRemove();\r\n\t\tthis._updateClasses(\"pegman-dragging\");\r\n\t\tthis._translateElement(this._pegman, e.dx, e.dy);\r\n\t},\r\n\r\n\tonDraggableEnd: function(e) {\r\n\t\tthis._pegmanMarkerCoords = this._map.mouseEventToLatLng(e);\r\n\t\tthis.pegmanAdd();\r\n\t\tthis.findStreetViewData(this._pegmanMarkerCoords.lat, this._pegmanMarkerCoords.lng);\r\n\t\tthis._updateClasses(\"pegman-dragged\");\r\n\t},\r\n\r\n\tonDropZoneActivated: function(e) {\r\n\t\tthis._updateClasses(\"dropzone-actived\");\r\n\t},\r\n\r\n\tonDropZoneDragEntered: function(e) {\r\n\t\tthis.showStreetViewLayer();\r\n\t\tthis._updateClasses(\"dropzone-drag-entered\");\r\n\t},\r\n\r\n\tonDropZoneDragLeaved: function(e) {\r\n\t\tthis._updateClasses(\"dropzone-drag-leaved\");\r\n\t},\r\n\r\n\tonDropZoneDropped: function(e) {\r\n\t\tthis._updateClasses(\"dropzone-drop\");\r\n\t\tthis._translateElement(this._pegman, false, false);\r\n\t},\r\n\r\n\tonDropZoneDeactivated: function(e) {\r\n\t\tthis._updateClasses(\"dropzone-deactivated\");\r\n\t},\r\n\r\n\tonPegmanMarkerDragged: function(e) {\r\n\t\tthis._pegmanMarkerCoords = this._pegmanMarker.getLatLng();\r\n\t\tthis.findStreetViewData(this._pegmanMarkerCoords.lat, this._pegmanMarkerCoords.lng);\r\n\t},\r\n\r\n\tonMapClick: function(e) {\r\n\t\tif (this._streetViewLayerEnabled) {\r\n\t\t\tthis.findStreetViewData(e.latlng.lat, e.latlng.lng);\r\n\t\t}\r\n\t},\r\n\r\n\tonMapLayerAdd: function(e) {\r\n\t\tif (this._googleStreetViewLayer)\r\n\t\t\tthis._googleStreetViewLayer.bringToFront();\r\n\t},\r\n\r\n\tonStreetViewPanoramaClose: function() {\r\n\t\tthis.clear();\r\n\t},\r\n\r\n\tonPanoramaPositionChanged: function() {\r\n\t\tvar pos = this._panorama.getPosition();\r\n\t\tpos = L.latLng(pos.lat(), pos.lng());\r\n\t\tif (this._map && !this._map.getBounds().pad(-0.05).contains(pos)) {\r\n\t\t\tthis._map.panTo(pos);\r\n\t\t}\r\n\t\tthis._pegmanMarker.setLatLng(pos);\r\n\t},\r\n\r\n\tonPanoramaPovChanged: function() {\r\n\t\tvar pov = this._panorama.getPov();\r\n\t\tthis._pegmanMarker.getElement().style.backgroundPosition = \"0 \" + -Math.abs((Math.round(pov.heading / (360 / 16)) % 16) * Math.round(835 / 16)) + 'px'; // sprite_height = 835px; num_rows = 16; pegman_angle = [0, 360] deg\r\n\t},\r\n\r\n\tclear: function() {\r\n\t\tthis.pegmanRemove();\r\n\t\tthis.hideStreetViewLayer();\r\n\t\tthis.closeStreetViewPanorama();\r\n\t},\r\n\r\n\ttoggleStreetViewLayer: function(e) {\r\n\t\tif (this._streetViewLayerEnabled) this.clear();\r\n\t\telse this.showStreetViewLayer();\r\n\t\tthis._log(\"streetview-layer-toggled\");\r\n\t},\r\n\r\n\tpegmanAdd: function() {\r\n\t\tthis._pegmanMarker.addTo(this._map);\r\n\t\tthis._pegmanMarker.setLatLng(this._pegmanMarkerCoords);\r\n\t\tthis.findStreetViewData(this._pegmanMarkerCoords.lat, this._pegmanMarkerCoords.lng);\r\n\t\tthis._updateClasses(\"pegman-added\");\r\n\t},\r\n\r\n\tpegmanRemove: function() {\r\n\t\tthis._pegmanMarker.removeFrom(this._map);\r\n\t\tthis._updateClasses(\"pegman-removed\");\r\n\t},\r\n\r\n\tcloseStreetViewPanorama: function() {\r\n\t\tthis._panoDiv.style.display = \"none\";\r\n\t},\r\n\r\n\topenStreetViewPanorama: function() {\r\n\t\tthis._panoDiv.style.display = \"block\";\r\n\t},\r\n\r\n\thideStreetViewLayer: function() {\r\n\t\tif (this._googleStreetViewLayer) {\r\n\t\t\tthis._googleStreetViewLayer.removeFrom(this._map);\r\n\t\t\tthis._streetViewLayerEnabled = false;\r\n\t\t\tthis._updateClasses(\"streetview-hidden\");\r\n\t\t}\r\n\t},\r\n\r\n\tshowStreetViewLayer: function() {\r\n\t\tif (this._googleStreetViewLayer) {\r\n\t\t\tthis._googleStreetViewLayer.addTo(this._map);\r\n\t\t\tthis._streetViewLayerEnabled = true;\r\n\t\t\tthis._updateClasses(\"streetview-shown\");\r\n\t\t}\r\n\t},\r\n\r\n\tfindStreetViewData: function(lat, lng) {\r\n\t\tif (typeof google === 'undefined') {\r\n\t\t\tthis._loadScripts(true)\r\n\t\t\treturn this.once('svpc_streetview-shown', L.bind(this.findStreetViewData, this, lat, lng));\r\n\t\t}\r\n\r\n\t\tif (!this._pegmanMarker._map && this._map) {\r\n\t\t\tthis._pegmanMarkerCoords = L.latLng(lat, lng);\r\n\t\t\treturn this.pegmanAdd();\r\n\t\t}\r\n\r\n\t\t// var searchRadiusPx = 24,\r\n\t\t// \tlatlng = L.latLng(lat, lng),\r\n\t\t// \tp = this._map.project(latlng).add([searchRadiusPx, 0]),\r\n\t\t// \tsearchRadius = latlng.distanceTo(this._map.unproject(p));\r\n\r\n\t\tthis._streetViewCoords = new google.maps.LatLng(lat, lng);\r\n\r\n\t\tvar zoom = this._map.getZoom();\r\n\t\tvar searchRadius = 100;\r\n\r\n\t\tif (zoom < 6) searchRadius = 5000;\r\n\t\telse if (zoom < 10) searchRadius = 500;\r\n\t\telse if (zoom < 15) searchRadius = 250;\r\n\t\telse if (zoom >= 17) searchRadius = 50;\r\n\t\telse searchRadius = 100;\r\n\r\n\t\tthis._streetViewService.getPanoramaByLocation(this._streetViewCoords, searchRadius, L.bind(this.processStreetViewServiceData, this));\r\n\t},\r\n\r\n\tprocessStreetViewServiceData: function(data, status) {\r\n\t\tif (status == google.maps.StreetViewStatus.OK) {\r\n\t\t\tthis.openStreetViewPanorama();\r\n\t\t\tthis._panorama.setPano(data.location.pano);\r\n\t\t\tthis._panorama.setPov({\r\n\t\t\t\theading: google.maps.geometry.spherical.computeHeading(data.location.latLng, this._streetViewCoords),\r\n\t\t\t\tpitch: 0,\r\n\t\t\t\tzoom: 0\r\n\t\t\t});\r\n\t\t\tthis._panorama.setVisible(true);\r\n\t\t} else {\r\n\t\t\tconsole.warn(\"Street View data not found for this location.\");\r\n\t\t\t// this.clear(); // TODO: add a visual feedback when no SV data available\r\n\t\t}\r\n\t},\r\n\r\n\t/**\r\n\t * mouseMoveTracking\r\n\t * @desc internal function used to style pegman while dragging\r\n\t */\r\n\tmouseMoveTracking: function(e) {\r\n\t\tvar mousePos = this._mousePos;\r\n\r\n\t\t// Top <--> Bottom\r\n\t\tif (e.pageY < mousePos.old.y) {\r\n\t\t\tmousePos.direction.y = 'top';\r\n\t\t\tthis._updateClasses(\"mousemove-top\");\r\n\t\t} else if (e.pageY > mousePos.old.y) {\r\n\t\t\tmousePos.direction.y = 'bottom';\r\n\t\t\tthis._updateClasses(\"mousemove-bottom\");\r\n\t\t}\r\n\t\t// Left <--> Right\r\n\t\tif (e.pageX < mousePos.old.x) {\r\n\t\t\tmousePos.direction.x = 'left';\r\n\t\t\tthis._updateClasses(\"mousemove-left\");\r\n\t\t} else if (e.pageX > mousePos.old.x) {\r\n\t\t\tmousePos.direction.x = 'right';\r\n\t\t\tthis._updateClasses(\"mousemove-right\");\r\n\t\t}\r\n\r\n\t\tmousePos.old.x = e.pageX;\r\n\t\tmousePos.old.y = e.pageY;\r\n\t},\r\n\r\n\t/**\r\n\t * keyUpTracking\r\n\t * @desc internal function used to track keyup events\r\n\t */\r\n\tkeyUpTracking: function(e) {\r\n\t\tif (e.keyCode == 27) {\r\n\t\t\tthis._log('escape pressed');\r\n\t\t\tthis.clear();\r\n\t\t}\r\n\t},\r\n\r\n\t_disableClickPropagation: function(e) {\r\n\t\tL.DomEvent.stopPropagation(e);\r\n\t\tL.DomEvent.preventDefault(e);\r\n\t},\r\n\r\n\t_loadGoogleHandlers: function(toggleStreetView) {\r\n\t\tif (typeof google !== 'object' || typeof google.maps !== 'object' || typeof L.GridLayer.GoogleMutant !== 'function') return;\r\n\t\tthis._initGoogleMaps(toggleStreetView);\r\n\t\tthis._initMouseTracker();\r\n\t},\r\n\r\n\t_initGoogleMaps: function(toggleStreetView) {\r\n\t\tthis._googleStreetViewLayer = L.gridLayer.googleMutant(this.options.mutant);\r\n\t\tthis._googleStreetViewLayer.addGoogleLayer('StreetViewCoverageLayer');\r\n\r\n\t\tthis._panorama = new google.maps.StreetViewPanorama(this._panoDiv, this.options.pano);\r\n\t\tthis._streetViewService = new google.maps.StreetViewService();\r\n\r\n\t\tthis._panorama.addListener('closeclick', L.bind(this.onStreetViewPanoramaClose, this));\r\n\t\tthis._panorama.addListener('position_changed', L.bind(this.onPanoramaPositionChanged, this));\r\n\t\tthis._panorama.addListener('pov_changed', L.bind(this.onPanoramaPovChanged, this));\r\n\r\n\t\tif (toggleStreetView) {\r\n\t\t\tthis.showStreetViewLayer();\r\n\t\t}\r\n\t},\r\n\r\n\t_initMouseTracker: function() {\r\n\t\tif (!this._googleStreetViewLayer) return;\r\n\r\n\t\tvar tileSize = this._googleStreetViewLayer.getTileSize();\r\n\r\n\t\tthis.tileWidth = tileSize.x;\r\n\t\tthis.tileHeight = tileSize.y;\r\n\r\n\t\tthis.defaultDraggableCursor = this._map._container.style.cursor;\r\n\r\n\t\tthis._map.on(\"mousemove\", this._setMouseCursor, this);\r\n\t},\r\n\r\n\t_setMouseCursor: function(e) {\r\n\t\tvar coords = this._getTileCoords(e.latlng.lat, e.latlng.lng, this._map.getZoom());\r\n\t\tvar img = this._getTileImage(coords);\r\n\t\tvar pixel = this._getTilePixelPoint(img, e.originalEvent);\r\n\t\tvar hasTileData = this._hasTileData(img, pixel);\r\n\t\tthis._map._container.style.cursor = hasTileData ? 'pointer' : this.defaultDraggableCursor;\r\n\t},\r\n\r\n\t_getTileCoords: function(lat, lon, zoom) {\r\n\t\tvar xtile = parseInt(Math.floor((lon + 180) / 360 * (1 << zoom)));\r\n\t\tvar ytile = parseInt(Math.floor((1 - Math.log(Math.tan(this._toRad(lat)) + 1 / Math.cos(this._toRad(lat))) / Math.PI) / 2 * (1 << zoom)));\r\n\t\treturn {\r\n\t\t\tx: xtile,\r\n\t\t\ty: ytile,\r\n\t\t\tz: zoom,\r\n\t\t};\r\n\t},\r\n\r\n\t_getTileImage: function(coords) {\r\n\t\tif (!this._googleStreetViewLayer || !this._googleStreetViewLayer._tiles) return;\r\n\t\tvar key = this._googleStreetViewLayer._tileCoordsToKey(coords);\r\n\t\tvar tile = this._googleStreetViewLayer._tiles[key];\r\n\t\tif (!tile) return;\r\n\t\tvar img = tile.el.querySelector('img');\r\n\t\tif (!img) return;\r\n\t\tthis._downloadTile(img.src, this._tileLoaded); // crossOrigin = \"Anonymous\"\r\n\t\treturn img;\r\n\t},\r\n\r\n\t_getTilePixelPoint: function(img, e) {\r\n\t\tif (!img) return;\r\n\t\tvar imgRect = img.getBoundingClientRect();\r\n\t\tvar imgPos = {\r\n\t\t\tpageY: (imgRect.top + window.scrollY).toFixed(0),\r\n\t\t\tpageX: (imgRect.left + window.scrollX).toFixed(0)\r\n\t\t};\r\n\t\tvar mousePos = {\r\n\t\t\tx: e.pageX - imgPos.pageX,\r\n\t\t\ty: e.pageY - imgPos.pageY\r\n\t\t};\r\n\t\treturn mousePos;\r\n\t},\r\n\r\n\t_hasTileData: function(img, pixelPoint) {\r\n\t\tif (!this.tileContext || !pixelPoint) return;\r\n\t\tvar pixelData = this.tileContext.getImageData(pixelPoint.x, pixelPoint.y, 1, 1).data;\r\n\t\tvar alpha = pixelData[3];\r\n\t\tvar hasTileData = (alpha != 0);\r\n\t\treturn hasTileData;\r\n\t},\r\n\r\n\t_toRad: function(number) {\r\n\t\treturn number * Math.PI / 180;\r\n\t},\r\n\r\n\t_downloadTile: function(imageSrc, callback) {\r\n\t\tif (!imageSrc) return;\r\n\t\tvar img = new Image();\r\n\t\timg.crossOrigin = \"Anonymous\";\r\n\t\timg.addEventListener(\"load\", callback.bind(this, img), false);\r\n\t\timg.src = imageSrc;\r\n\t},\r\n\r\n\t_tileLoaded: function(img) {\r\n\t\tthis.tileCanvas = document.createElement(\"canvas\");\r\n\t\tthis.tileContext = this.tileCanvas.getContext(\"2d\");\r\n\r\n\t\tthis.tileCanvas.width = this.tileWidth;\r\n\t\tthis.tileCanvas.height = this.tileHeight;\r\n\r\n\t\tthis.tileContext.drawImage(img, 0, 0);\r\n\t},\r\n\r\n\t_loadInteractHandlers: function() {\r\n\t\t// TODO: trying to replace \"interact.js\" with default \"L.Draggable\" object\r\n\t\t// var draggable = new L.Draggable(this._container);\r\n\t\t// draggable.enable();\r\n\t\t// draggable.on('drag', function(e) { console.log(e); });\r\n\t\tif (typeof interact !== 'function') return;\r\n\r\n\t\t// Enable Draggable Element to be Dropped into Map Container\r\n\t\tthis._draggable = interact(this._pegman).draggable(this._draggableMarkerOpts);\r\n\t\tthis._dropzone = interact(this._map._container).dropzone(this._dropzoneMapOpts);\r\n\r\n\t\tthis._draggable.styleCursor(false);\r\n\r\n\t\t// Toggle on/off SV Layer on Pegman's Container single clicks\r\n\t\tinteract(this._container).on(\"tap\", L.bind(this.toggleStreetViewLayer, this));\r\n\r\n\t\t// Prevent map drags (Desktop / Mobile) while dragging pegman control\r\n\t\tL.DomEvent.on(this._container, \"touchstart\", function(e) { this._map.dragging.disable(); }, this);\r\n\t\tL.DomEvent.on(this._container, \"touchend\", function(e) { this._map.dragging.enable(); }, this);\r\n\t},\r\n\r\n\t_loadScripts: function(toggleStreetView) {\r\n\t\tif (this._lazyLoaderAdded) return;\r\n\t\tthis._lazyLoaderAdded = true;\r\n\r\n\t\tthis._loadJS(this.__interactURL, this._loadInteractHandlers.bind(this), typeof interact !== 'function');\r\n\t\tthis._loadJS(this.__gmapsURL + '&key=' + this.options.apiKey + '&libraries=' + this.options.libraries + '&callback=?', this._loadGoogleHandlers.bind(this, toggleStreetView), typeof google !== 'object' || typeof google.maps !== 'object');\r\n\t\tthis._loadJS(this.__mutantURL, this._loadGoogleHandlers.bind(this, toggleStreetView), typeof L.GridLayer.GoogleMutant !== 'function');\r\n\r\n\t},\r\n\r\n\t_loadJS: function(url, callback, condition) {\r\n\t\tif (!condition) {\r\n\t\t\tcallback();\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (url.indexOf('callback=?') !== -1) {\r\n\t\t\tthis._jsonp(url, callback);\r\n\t\t} else {\r\n\t\t\tvar script = document.createElement('script');\r\n\t\t\tscript.src = url;\r\n\t\t\tvar loaded = function() {\r\n\t\t\t\tscript.onload = script.onreadystatechange = null;\r\n\t\t\t\tthis._log(url + \" loaded\");\r\n\t\t\t\tcallback();\r\n\t\t\t}.bind(this);\r\n\t\t\tscript.onload = script.onreadystatechange = loaded;\r\n\r\n\t\t\tvar head = document.head || document.getElementsByTagName('head')[0] || document.documentElement;\r\n\t\t\thead.insertBefore(script, head.firstChild);\r\n\t\t}\r\n\t},\r\n\r\n\t_jsonp: function(url, callback, params) {\r\n\t\tvar query = url.indexOf('?') === -1 ? '?' : '&';\r\n\t\tparams = params || {};\r\n\t\tfor (var key in params) {\r\n\t\t\tif (params.hasOwnProperty(key)) {\r\n\t\t\t\tquery += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&';\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvar timestamp = new Date().getUTCMilliseconds();\r\n\t\tvar jsonp = \"json_call_\" + timestamp; // uniqueId('json_call');\r\n\t\twindow[jsonp] = function(data) {\r\n\t\t\tcallback(data);\r\n\t\t\twindow[jsonp] = undefined;\r\n\t\t};\r\n\r\n\t\tvar script = document.createElement('script');\r\n\t\tif (url.indexOf('callback=?') !== -1) {\r\n\t\t\tscript.src = url.replace('callback=?', 'callback=' + jsonp) + query.slice(0, -1);\r\n\t\t} else {\r\n\t\t\tscript.src = url + query + 'callback=' + jsonp;\r\n\t\t}\r\n\t\tvar loaded = function() {\r\n\t\t\tif (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {\r\n\t\t\t\tscript.onload = script.onreadystatechange = null;\r\n\t\t\t\tif (script && script.parentNode) {\r\n\t\t\t\t\tscript.parentNode.removeChild(script);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\t\tscript.async = true;\r\n\t\tscript.onload = script.onreadystatechange = loaded;\r\n\t\tvar head = document.head || document.getElementsByTagName('head')[0] || document.documentElement;\r\n\t\t// Use insertBefore instead of appendChild to circumvent an IE6 bug.\r\n\t\t// This arises when a base node is used.\r\n\t\thead.insertBefore(script, head.firstChild);\r\n\t},\r\n\r\n});\r\n\r\nL.control.pegman = function(options) {\r\n\treturn new L.Control.Pegman(options);\r\n};\r\n","export default {\n\t//Arabic\n\tar: {\n\t\ttouch: \"\\u0627\\u0633\\u062a\\u062e\\u062f\\u0645 \\u0625\\u0635\\u0628\\u0639\\u064a\\u0646 \\u0644\\u062a\\u062d\\u0631\\u064a\\u0643 \\u0627\\u0644\\u062e\\u0631\\u064a\\u0637\\u0629\",\n\t\tscroll: \"\\u200f\\u0627\\u0633\\u062a\\u062e\\u062f\\u0645 ctrl + scroll \\u0644\\u062a\\u0635\\u063a\\u064a\\u0631/\\u062a\\u0643\\u0628\\u064a\\u0631 \\u0627\\u0644\\u062e\\u0631\\u064a\\u0637\\u0629\",\n\t\tscrollMac: \"\\u064a\\u0645\\u0643\\u0646\\u0643 \\u0627\\u0633\\u062a\\u062e\\u062f\\u0627\\u0645 \\u2318 + \\u0627\\u0644\\u062a\\u0645\\u0631\\u064a\\u0631 \\u0644\\u062a\\u0643\\u0628\\u064a\\u0631/\\u062a\\u0635\\u063a\\u064a\\u0631 \\u0627\\u0644\\u062e\\u0631\\u064a\\u0637\\u0629\"\n\t},\n\t//Bulgarian\n\tbg: {\n\t\ttouch: \"\\u0418\\u0437\\u043f\\u043e\\u043b\\u0437\\u0432\\u0430\\u0439\\u0442\\u0435 \\u0434\\u0432\\u0430 \\u043f\\u0440\\u044a\\u0441\\u0442\\u0430, \\u0437\\u0430 \\u0434\\u0430 \\u043f\\u0440\\u0435\\u043c\\u0435\\u0441\\u0442\\u0438\\u0442\\u0435 \\u043a\\u0430\\u0440\\u0442\\u0430\\u0442\\u0430\",\n\t\tscroll: \"\\u0417\\u0430\\u0434\\u0440\\u044a\\u0436\\u0442\\u0435 \\u0431\\u0443\\u0442\\u043e\\u043d\\u0430 Ctrl \\u043d\\u0430\\u0442\\u0438\\u0441\\u043d\\u0430\\u0442, \\u0434\\u043e\\u043a\\u0430\\u0442\\u043e \\u043f\\u0440\\u0435\\u0432\\u044a\\u0440\\u0442\\u0430\\u0442\\u0435, \\u0437\\u0430 \\u0434\\u0430 \\u043f\\u0440\\u043e\\u043c\\u0435\\u043d\\u0438\\u0442\\u0435 \\u043c\\u0430\\u0449\\u0430\\u0431\\u0430 \\u043d\\u0430 \\u043a\\u0430\\u0440\\u0442\\u0430\\u0442\\u0430\",\n\t\tscrollMac: \"\\u0417\\u0430\\u0434\\u0440\\u044a\\u0436\\u0442\\u0435 \\u0431\\u0443\\u0442\\u043e\\u043d\\u0430 \\u2318 \\u043d\\u0430\\u0442\\u0438\\u0441\\u043d\\u0430\\u0442, \\u0434\\u043e\\u043a\\u0430\\u0442\\u043e \\u043f\\u0440\\u0435\\u0432\\u044a\\u0440\\u0442\\u0430\\u0442\\u0435, \\u0437\\u0430 \\u0434\\u0430 \\u043f\\u0440\\u043e\\u043c\\u0435\\u043d\\u0438\\u0442\\u0435 \\u043c\\u0430\\u0449\\u0430\\u0431\\u0430 \\u043d\\u0430 \\u043a\\u0430\\u0440\\u0442\\u0430\\u0442\\u0430\"\n\t},\n\t//Bengali\n\tbn: {\n\t\ttouch: \"\\u09ae\\u09be\\u09a8\\u099a\\u09bf\\u09a4\\u09cd\\u09b0\\u099f\\u09bf\\u0995\\u09c7 \\u09b8\\u09b0\\u09be\\u09a4\\u09c7 \\u09a6\\u09c1\\u099f\\u09bf \\u0986\\u0999\\u09cd\\u0997\\u09c1\\u09b2 \\u09ac\\u09cd\\u09af\\u09ac\\u09b9\\u09be\\u09b0 \\u0995\\u09b0\\u09c1\\u09a8\",\n\t\tscroll: \"\\u09ae\\u09cd\\u09af\\u09be\\u09aa \\u099c\\u09c1\\u09ae \\u0995\\u09b0\\u09a4\\u09c7 ctrl + scroll \\u09ac\\u09cd\\u09af\\u09ac\\u09b9\\u09be\\u09b0 \\u0995\\u09b0\\u09c1\\u09a8\",\n\t\tscrollMac: \"\\u09ae\\u09cd\\u09af\\u09be\\u09aa\\u09c7 \\u099c\\u09c1\\u09ae \\u0995\\u09b0\\u09a4\\u09c7 \\u2318 \\u09ac\\u09cb\\u09a4\\u09be\\u09ae \\u099f\\u09bf\\u09aa\\u09c7 \\u09b8\\u09cd\\u0995\\u09cd\\u09b0\\u09b2 \\u0995\\u09b0\\u09c1\\u09a8\"\n\t},\n\t//Catalan\n\tca: {\n\t\ttouch: \"Fes servir dos dits per moure el mapa\",\n\t\tscroll: \"Prem la tecla Control mentre et desplaces per apropar i allunyar el mapa\",\n\t\tscrollMac: \"Prem la tecla \\u2318 mentre et desplaces per apropar i allunyar el mapa\"\n\t},\n\t//Czech\n\tcs: {\n\t\ttouch: \"K\\u00a0posunut\\u00ed mapy pou\\u017eijte dva prsty\",\n\t\tscroll: \"Velikost zobrazen\\u00ed mapy zm\\u011b\\u0148te podr\\u017een\\u00edm kl\\u00e1vesy Ctrl a\\u00a0posouv\\u00e1n\\u00edm kole\\u010dka my\\u0161i\",\n\t\tscrollMac: \"Velikost zobrazen\\u00ed mapy zm\\u011bn\\u00edte podr\\u017een\\u00edm kl\\u00e1vesy \\u2318 a\\u00a0posunut\\u00edm kole\\u010dka my\\u0161i / touchpadu\"\n\t},\n\t//Danish\n\tda: {\n\t\ttouch: \"Brug to fingre til at flytte kortet\",\n\t\tscroll: \"Brug ctrl + rullefunktionen til at zoome ind og ud p\\u00e5 kortet\",\n\t\tscrollMac: \"Brug \\u2318 + rullefunktionen til at zoome ind og ud p\\u00e5 kortet\"\n\t},\n\t//German\n\tde: {\n\t\ttouch: \"Verschieben der Karte mit zwei Fingern\",\n\t\tscroll: \"Verwende Strg + Scroll zum Zoomen der Karte\",\n\t\tscrollMac: \"Verwende \\u2318 + Scroll zum Zoomen der Karte\"\n\t},\n\t//Greek\n\tel: {\n\t\ttouch: \"\\u03a7\\u03c1\\u03b7\\u03c3\\u03b9\\u03bc\\u03bf\\u03c0\\u03bf\\u03b9\\u03ae\\u03c3\\u03c4\\u03b5 \\u03b4\\u03cd\\u03bf \\u03b4\\u03ac\\u03c7\\u03c4\\u03c5\\u03bb\\u03b1 \\u03b3\\u03b9\\u03b1 \\u03bc\\u03b5\\u03c4\\u03b1\\u03ba\\u03af\\u03bd\\u03b7\\u03c3\\u03b7 \\u03c3\\u03c4\\u03bf\\u03bd \\u03c7\\u03ac\\u03c1\\u03c4\\u03b7\",\n\t\tscroll: \"\\u03a7\\u03c1\\u03b7\\u03c3\\u03b9\\u03bc\\u03bf\\u03c0\\u03bf\\u03b9\\u03ae\\u03c3\\u03c4\\u03b5 \\u03c4\\u03bf \\u03c0\\u03bb\\u03ae\\u03ba\\u03c4\\u03c1\\u03bf Ctrl \\u03ba\\u03b1\\u03b9 \\u03ba\\u03cd\\u03bb\\u03b9\\u03c3\\u03b7, \\u03b3\\u03b9\\u03b1 \\u03bd\\u03b1 \\u03bc\\u03b5\\u03b3\\u03b5\\u03b8\\u03cd\\u03bd\\u03b5\\u03c4\\u03b5 \\u03c4\\u03bf\\u03bd \\u03c7\\u03ac\\u03c1\\u03c4\\u03b7\",\n\t\tscrollMac: \"\\u03a7\\u03c1\\u03b7\\u03c3\\u03b9\\u03bc\\u03bf\\u03c0\\u03bf\\u03b9\\u03ae\\u03c3\\u03c4\\u03b5 \\u03c4\\u03bf \\u03c0\\u03bb\\u03ae\\u03ba\\u03c4\\u03c1\\u03bf \\u2318 + \\u03ba\\u03cd\\u03bb\\u03b9\\u03c3\\u03b7 \\u03b3\\u03b9\\u03b1 \\u03b5\\u03c3\\u03c4\\u03af\\u03b1\\u03c3\\u03b7 \\u03c3\\u03c4\\u03bf\\u03bd \\u03c7\\u03ac\\u03c1\\u03c4\\u03b7\"\n\t},\n\t//English\n\ten: {\n\t\ttouch: \"Use two fingers to move the map\",\n\t\tscroll: \"Use ctrl + scroll to zoom the map\",\n\t\tscrollMac: \"Use \\u2318 + scroll to zoom the map\"\n\t},\n\t//English (Australian)\n\t\"en-AU\": {\n\t\ttouch: \"Use two fingers to move the map\",\n\t\tscroll: \"Use ctrl + scroll to zoom the map\",\n\t\tscrollMac: \"Use \\u2318 + scroll to zoom the map\"\n\t},\n\t//English (Great Britain)\n\t\"en-GB\": {\n\t\ttouch: \"Use two fingers to move the map\",\n\t\tscroll: \"Use ctrl + scroll to zoom the map\",\n\t\tscrollMac: \"Use \\u2318 + scroll to zoom the map\"\n\t},\n\t//Spanish\n\tes: {\n\t\ttouch: \"Para mover el mapa, utiliza dos dedos\",\n\t\tscroll: \"Mant\\u00e9n pulsada la tecla Ctrl mientras te desplazas para acercar o alejar el mapa\",\n\t\tscrollMac: \"Mant\\u00e9n pulsada la tecla \\u2318 mientras te desplazas para acercar o alejar el mapa\"\n\t},\n\t//Basque\n\teu: {\n\t\ttouch: \"Erabili bi hatz mapa mugitzeko\",\n\t\tscroll: \"Mapan zooma aplikatzeko, sakatu Ktrl eta egin gora edo behera\",\n\t\tscrollMac: \"Eduki sakatuta \\u2318 eta egin gora eta behera mapa handitu eta txikitzeko\"\n\t},\n\t//Farsi\n\tfa: {\n\t\ttouch: \"\\u0628\\u0631\\u0627\\u06cc \\u062d\\u0631\\u06a9\\u062a \\u062f\\u0627\\u062f\\u0646 \\u0646\\u0642\\u0634\\u0647 \\u0627\\u0632 \\u062f\\u0648 \\u0627\\u0646\\u06af\\u0634\\u062a \\u0627\\u0633\\u062a\\u0641\\u0627\\u062f\\u0647 \\u06a9\\u0646\\u06cc\\u062f.\",\n\t\tscroll: \"\\u200f\\u0628\\u0631\\u0627\\u06cc \\u0628\\u0632\\u0631\\u06af\\u200c\\u0646\\u0645\\u0627\\u06cc\\u06cc \\u0646\\u0642\\u0634\\u0647 \\u0627\\u0632 ctrl + scroll \\u0627\\u0633\\u062a\\u0641\\u0627\\u062f\\u0647 \\u06a9\\u0646\\u06cc\\u062f\",\n\t\tscrollMac: \"\\u0628\\u0631\\u0627\\u06cc \\u0628\\u0632\\u0631\\u06af\\u200c\\u0646\\u0645\\u0627\\u06cc\\u06cc \\u0646\\u0642\\u0634\\u0647\\u060c \\u0627\\u0632 \\u2318 + \\u067e\\u06cc\\u0645\\u0627\\u06cc\\u0634 \\u0627\\u0633\\u062a\\u0641\\u0627\\u062f\\u0647 \\u06a9\\u0646\\u06cc\\u062f.\"\n\t},\n\t//Finnish\n\tfi: {\n\t\ttouch: \"Siirr\\u00e4 karttaa kahdella sormella.\",\n\t\tscroll: \"Zoomaa karttaa painamalla Ctrl-painiketta ja vieritt\\u00e4m\\u00e4ll\\u00e4.\",\n\t\tscrollMac: \"Zoomaa karttaa pit\\u00e4m\\u00e4ll\\u00e4 painike \\u2318 painettuna ja vieritt\\u00e4m\\u00e4ll\\u00e4.\"\n\t},\n\t//Filipino\n\tfil: {\n\t\ttouch: \"Gumamit ng dalawang daliri upang iusog ang mapa\",\n\t\tscroll: \"Gamitin ang ctrl + scroll upang i-zoom ang mapa\",\n\t\tscrollMac: \"Gamitin ang \\u2318 + scroll upang i-zoom ang mapa\"\n\t},\n\t//French\n\tfr: {\n\t\ttouch: \"Utilisez deux\\u00a0doigts pour d\\u00e9placer la carte\",\n\t\tscroll: \"Vous pouvez zoomer sur la carte \\u00e0 l'aide de CTRL + Molette de d\\u00e9filement\",\n\t\tscrollMac: \"Vous pouvez zoomer sur la carte \\u00e0 l'aide de \\u2318 + Molette de d\\u00e9filement\"\n\t},\n\t//Galician\n\tgl: {\n\t\ttouch: \"Utiliza dous dedos para mover o mapa\",\n\t\tscroll: \"Preme Ctrl mentres te desprazas para ampliar o mapa\",\n\t\tscrollMac: \"Preme \\u2318 e despr\\u00e1zate para ampliar o mapa\"\n\t},\n\t//Gujarati\n\tgu: {\n\t\ttouch: \"\\u0aa8\\u0a95\\u0ab6\\u0acb \\u0a96\\u0ab8\\u0ac7\\u0aa1\\u0ab5\\u0abe \\u0aac\\u0ac7 \\u0a86\\u0a82\\u0a97\\u0ab3\\u0ac0\\u0a93\\u0aa8\\u0acb \\u0a89\\u0aaa\\u0aaf\\u0acb\\u0a97 \\u0a95\\u0ab0\\u0acb\",\n\t\tscroll: \"\\u0aa8\\u0a95\\u0ab6\\u0abe\\u0aa8\\u0ac7 \\u0a9d\\u0ac2\\u0aae \\u0a95\\u0ab0\\u0ab5\\u0abe \\u0aae\\u0abe\\u0a9f\\u0ac7 ctrl + \\u0ab8\\u0acd\\u0a95\\u0acd\\u0ab0\\u0acb\\u0ab2\\u0aa8\\u0acb \\u0a89\\u0aaa\\u0aaf\\u0acb\\u0a97 \\u0a95\\u0ab0\\u0acb\",\n\t\tscrollMac: \"\\u0aa8\\u0a95\\u0ab6\\u0abe\\u0aa8\\u0ac7 \\u0a9d\\u0ac2\\u0aae \\u0a95\\u0ab0\\u0ab5\\u0abe \\u2318 + \\u0ab8\\u0acd\\u0a95\\u0acd\\u0ab0\\u0acb\\u0ab2\\u0aa8\\u0acb \\u0a89\\u0aaa\\u0aaf\\u0acb\\u0a97 \\u0a95\\u0ab0\\u0acb\"\n\t},\n\t//Hindi\n\thi: {\n\t\ttouch: \"\\u092e\\u0948\\u092a \\u090f\\u0915 \\u091c\\u0917\\u0939 \\u0938\\u0947 \\u0926\\u0942\\u0938\\u0930\\u0940 \\u091c\\u0917\\u0939 \\u0932\\u0947 \\u091c\\u093e\\u0928\\u0947 \\u0915\\u0947 \\u0932\\u093f\\u090f \\u0926\\u094b \\u0909\\u0902\\u0917\\u0932\\u093f\\u092f\\u094b\\u0902 \\u0915\\u093e \\u0907\\u0938\\u094d\\u0924\\u0947\\u092e\\u093e\\u0932 \\u0915\\u0930\\u0947\\u0902\",\n\t\tscroll: \"\\u092e\\u0948\\u092a \\u0915\\u094b \\u091c\\u093c\\u0942\\u092e \\u0915\\u0930\\u0928\\u0947 \\u0915\\u0947 \\u0932\\u093f\\u090f ctrl + \\u0938\\u094d\\u0915\\u094d\\u0930\\u094b\\u0932 \\u0915\\u093e \\u0909\\u092a\\u092f\\u094b\\u0917 \\u0915\\u0930\\u0947\\u0902\",\n\t\tscrollMac: \"\\u092e\\u0948\\u092a \\u0915\\u094b \\u091c\\u093c\\u0942\\u092e \\u0915\\u0930\\u0928\\u0947 \\u0915\\u0947 \\u0932\\u093f\\u090f \\u2318 + \\u0938\\u094d\\u0915\\u094d\\u0930\\u094b\\u0932 \\u0915\\u093e \\u0909\\u092a\\u092f\\u094b\\u0917 \\u0915\\u0930\\u0947\\u0902\"\n\t},\n\t//Croatian\n\thr: {\n\t\ttouch: \"Pomi\\u010dite kartu pomo\\u0107u dva prsta\",\n\t\tscroll: \"Upotrijebite Ctrl i kliza\\u010d mi\\u0161a da biste zumirali kartu\",\n\t\tscrollMac: \"Upotrijebite gumb \\u2318 dok se pomi\\u010dete za zumiranje karte\"\n\t},\n\t//Hungarian\n\thu: {\n\t\ttouch: \"K\\u00e9t ujjal mozgassa a t\\u00e9rk\\u00e9pet\",\n\t\tscroll: \"A t\\u00e9rk\\u00e9p a ctrl + g\\u00f6rget\\u00e9s haszn\\u00e1lat\\u00e1val nagy\\u00edthat\\u00f3\",\n\t\tscrollMac: \"A t\\u00e9rk\\u00e9p a \\u2318 + g\\u00f6rget\\u00e9s haszn\\u00e1lat\\u00e1val nagy\\u00edthat\\u00f3\"\n\t},\n\t//Indonesian\n\tid: {\n\t\ttouch: \"Gunakan dua jari untuk menggerakkan peta\",\n\t\tscroll: \"Gunakan ctrl + scroll untuk memperbesar atau memperkecil peta\",\n\t\tscrollMac: \"Gunakan \\u2318 + scroll untuk memperbesar atau memperkecil peta\"\n\t},\n\t//Italian\n\tit: {\n\t\ttouch: \"Utilizza due dita per spostare la mappa\",\n\t\tscroll: \"Utilizza CTRL + scorrimento per eseguire lo zoom della mappa\",\n\t\tscrollMac: \"Utilizza \\u2318 + scorrimento per eseguire lo zoom della mappa\"\n\t},\n\t//Hebrew\n\tiw: {\n\t\ttouch: \"\\u05d4\\u05d6\\u05d6 \\u05d0\\u05ea \\u05d4\\u05de\\u05e4\\u05d4 \\u05d1\\u05d0\\u05de\\u05e6\\u05e2\\u05d5\\u05ea \\u05e9\\u05ea\\u05d9 \\u05d0\\u05e6\\u05d1\\u05e2\\u05d5\\u05ea\",\n\t\tscroll: \"\\u200f\\u05d0\\u05e4\\u05e9\\u05e8 \\u05dc\\u05e9\\u05e0\\u05d5\\u05ea \\u05d0\\u05ea \\u05de\\u05e8\\u05d7\\u05e7 \\u05d4\\u05ea\\u05e6\\u05d5\\u05d2\\u05d4 \\u05d1\\u05de\\u05e4\\u05d4 \\u05d1\\u05d0\\u05de\\u05e6\\u05e2\\u05d5\\u05ea \\u05de\\u05e7\\u05e9 ctrl \\u05d5\\u05d2\\u05dc\\u05d9\\u05dc\\u05d4\",\n\t\tscrollMac: \"\\u05d0\\u05e4\\u05e9\\u05e8 \\u05dc\\u05e9\\u05e0\\u05d5\\u05ea \\u05d0\\u05ea \\u05de\\u05e8\\u05d7\\u05e7 \\u05d4\\u05ea\\u05e6\\u05d5\\u05d2\\u05d4 \\u05d1\\u05de\\u05e4\\u05d4 \\u05d1\\u05d0\\u05de\\u05e6\\u05e2\\u05d5\\u05ea \\u05de\\u05e7\\u05e9 \\u2318 \\u05d5\\u05d2\\u05dc\\u05d9\\u05dc\\u05d4\"\n\t},\n\t//Japanese\n\tja: {\n\t\ttouch: \"\\u5730\\u56f3\\u3092\\u79fb\\u52d5\\u3055\\u305b\\u308b\\u306b\\u306f\\u6307 2 \\u672c\\u3067\\u64cd\\u4f5c\\u3057\\u307e\\u3059\",\n\t\tscroll: \"\\u5730\\u56f3\\u3092\\u30ba\\u30fc\\u30e0\\u3059\\u308b\\u306b\\u306f\\u3001Ctrl \\u30ad\\u30fc\\u3092\\u62bc\\u3057\\u306a\\u304c\\u3089\\u30b9\\u30af\\u30ed\\u30fc\\u30eb\\u3057\\u3066\\u304f\\u3060\\u3055\\u3044\",\n\t\tscrollMac: \"\\u5730\\u56f3\\u3092\\u30ba\\u30fc\\u30e0\\u3059\\u308b\\u306b\\u306f\\u3001\\u2318 \\u30ad\\u30fc\\u3092\\u62bc\\u3057\\u306a\\u304c\\u3089\\u30b9\\u30af\\u30ed\\u30fc\\u30eb\\u3057\\u3066\\u304f\\u3060\\u3055\\u3044\"\n\t},\n\t//Kannada\n\tkn: {\n\t\ttouch: \"Use two fingers to move the map\",\n\t\tscroll: \"Use Ctrl + scroll to zoom the map\",\n\t\tscrollMac: \"Use ⌘ + scroll to zoom the map\"\n\t},\n\t//Korean\n\tko: {\n\t\ttouch: \"\\uc9c0\\ub3c4\\ub97c \\uc6c0\\uc9c1\\uc774\\ub824\\uba74 \\ub450 \\uc190\\uac00\\ub77d\\uc744 \\uc0ac\\uc6a9\\ud558\\uc138\\uc694.\",\n\t\tscroll: \"\\uc9c0\\ub3c4\\ub97c \\ud655\\ub300/\\ucd95\\uc18c\\ud558\\ub824\\uba74 Ctrl\\uc744 \\ub204\\ub978 \\ucc44 \\uc2a4\\ud06c\\ub864\\ud558\\uc138\\uc694.\",\n\t\tscrollMac: \"\\uc9c0\\ub3c4\\ub97c \\ud655\\ub300\\ud558\\ub824\\uba74 \\u2318 + \\uc2a4\\ud06c\\ub864 \\uc0ac\\uc6a9\"\n\t},\n\t//Lithuanian\n\tlt: {\n\t\ttouch: \"Perkelkite \\u017eem\\u0117lap\\u012f dviem pir\\u0161tais\",\n\t\tscroll: \"Slinkite nuspaud\\u0119 klavi\\u0161\\u0105 \\u201eCtrl\\u201c, kad pakeistum\\u0117te \\u017eem\\u0117lapio mastel\\u012f\",\n\t\tscrollMac: \"Paspauskite klavi\\u0161\\u0105 \\u2318 ir slinkite, kad priartintum\\u0117te \\u017eem\\u0117lap\\u012f\"\n\t},\n\t//Latvian\n\tlv: {\n\t\ttouch: \"Lai p\\u0101rvietotu karti, b\\u012bdiet to ar diviem pirkstiem\",\n\t\tscroll: \"Kartes t\\u0101lummai\\u0146ai izmantojiet ctrl + ritin\\u0101\\u0161anu\",\n\t\tscrollMac: \"Lai veiktu kartes t\\u0101lummai\\u0146u, izmantojiet \\u2318 + ritin\\u0101\\u0161anu\"\n\t},\n\t//Malayalam\n\tml: {\n\t\ttouch: \"\\u0d2e\\u0d3e\\u0d2a\\u0d4d\\u0d2a\\u0d4d \\u0d28\\u0d40\\u0d15\\u0d4d\\u0d15\\u0d3e\\u0d7b \\u0d30\\u0d23\\u0d4d\\u0d1f\\u0d4d \\u0d35\\u0d3f\\u0d30\\u0d32\\u0d41\\u0d15\\u0d7e \\u0d09\\u0d2a\\u0d2f\\u0d4b\\u0d17\\u0d3f\\u0d15\\u0d4d\\u0d15\\u0d41\\u0d15\",\n\t\tscroll: \"\\u0d15\\u0d7a\\u0d1f\\u0d4d\\u0d30\\u0d4b\\u0d7e + \\u0d38\\u0d4d\\u200c\\u0d15\\u0d4d\\u0d30\\u0d4b\\u0d7e \\u0d09\\u0d2a\\u0d2f\\u0d4b\\u0d17\\u0d3f\\u0d1a\\u0d4d\\u0d1a\\u0d4d \\u200c\\u0d2e\\u0d3e\\u0d2a\\u0d4d\\u0d2a\\u0d4d \\u200c\\u0d38\\u0d42\\u0d02 \\u0d1a\\u0d46\\u0d2f\\u0d4d\\u0d2f\\u0d41\\u0d15\",\n\t\tscrollMac: \"\\u2318 + \\u0d38\\u0d4d\\u200c\\u0d15\\u0d4d\\u0d30\\u0d4b\\u0d7e \\u0d09\\u0d2a\\u0d2f\\u0d4b\\u0d17\\u0d3f\\u0d1a\\u0d4d\\u0d1a\\u0d4d \\u200c\\u0d2e\\u0d3e\\u0d2a\\u0d4d\\u0d2a\\u0d4d \\u200c\\u0d38\\u0d42\\u0d02 \\u0d1a\\u0d46\\u0d2f\\u0d4d\\u0d2f\\u0d41\\u0d15\"\n\t},\n\t//Marathi\n\tmr: {\n\t\ttouch: \"\\u0928\\u0915\\u093e\\u0936\\u093e \\u0939\\u0932\\u0935\\u093f\\u0923\\u094d\\u092f\\u093e\\u0938\\u093e\\u0920\\u0940 \\u0926\\u094b\\u0928 \\u092c\\u094b\\u091f\\u0947 \\u0935\\u093e\\u092a\\u0930\\u093e\",\n\t\tscroll: \"\\u0928\\u0915\\u093e\\u0936\\u093e \\u091d\\u0942\\u092e \\u0915\\u0930\\u0923\\u094d\\u092f\\u093e\\u0938\\u093e\\u0920\\u0940 ctrl + scroll \\u0935\\u093e\\u092a\\u0930\\u093e\",\n\t\tscrollMac: \"\\u0928\\u0915\\u093e\\u0936\\u093e\\u0935\\u0930 \\u091d\\u0942\\u092e \\u0915\\u0930\\u0923\\u094d\\u092f\\u093e\\u0938\\u093e\\u0920\\u0940 \\u2318 + \\u0938\\u094d\\u0915\\u094d\\u0930\\u094b\\u0932 \\u0935\\u093e\\u092a\\u0930\\u093e\"\n\t},\n\t//Dutch\n\tnl: {\n\t\ttouch: \"Gebruik twee vingers om de kaart te verplaatsen\",\n\t\tscroll: \"Gebruik Ctrl + scrollen om in- en uit te zoomen op de kaart\",\n\t\tscrollMac: \"Gebruik \\u2318 + scrollen om in en uit te zoomen op de kaart\"\n\t},\n\t//Norwegian\n\tno: {\n\t\ttouch: \"Bruk to fingre for \\u00e5 flytte kartet\",\n\t\tscroll: \"Hold ctrl-tasten inne og rull for \\u00e5 zoome p\\u00e5 kartet\",\n\t\tscrollMac: \"Hold inne \\u2318-tasten og rull for \\u00e5 zoome p\\u00e5 kartet\"\n\t},\n\t//Polish\n\tpl: {\n\t\ttouch: \"Przesu\\u0144 map\\u0119 dwoma palcami\",\n\t\tscroll: \"Naci\\u015bnij CTRL i przewi\\u0144, by przybli\\u017cy\\u0107 map\\u0119\",\n\t\tscrollMac: \"Naci\\u015bnij\\u00a0\\u2318 i przewi\\u0144, by przybli\\u017cy\\u0107 map\\u0119\"\n\t},\n\t//Portuguese\n\tpt: {\n\t\ttouch: \"Use dois dedos para mover o mapa\",\n\t\tscroll: \"Pressione Ctrl e role a tela simultaneamente para aplicar zoom no mapa\",\n\t\tscrollMac: \"Use \\u2318 e role a tela simultaneamente para aplicar zoom no mapa\"\n\t},\n\t//Portuguese (Brazil)\n\t\"pt-BR\": {\n\t\ttouch: \"Use dois dedos para mover o mapa\",\n\t\tscroll: \"Pressione Ctrl e role a tela simultaneamente para aplicar zoom no mapa\",\n\t\tscrollMac: \"Use \\u2318 e role a tela simultaneamente para aplicar zoom no mapa\"\n\t},\n\t//Portuguese (Portugal\n\t\"pt-PT\": {\n\t\ttouch: \"Utilize dois dedos para mover o mapa\",\n\t\tscroll: \"Utilizar ctrl + deslocar para aumentar/diminuir zoom do mapa\",\n\t\tscrollMac: \"Utilize \\u2318 + deslocar para aumentar/diminuir o zoom do mapa\"\n\t},\n\t//Romanian\n\tro: {\n\t\ttouch: \"Folosi\\u021bi dou\\u0103 degete pentru a deplasa harta\",\n\t\tscroll: \"Ap\\u0103sa\\u021bi tasta ctrl \\u0219i derula\\u021bi simultan pentru a m\\u0103ri harta\",\n\t\tscrollMac: \"Folosi\\u021bi \\u2318 \\u0219i derula\\u021bi pentru a m\\u0103ri/mic\\u0219ora harta\"\n\t},\n\t//Russian\n\tru: {\n\t\ttouch: \"\\u0427\\u0442\\u043e\\u0431\\u044b \\u043f\\u0435\\u0440\\u0435\\u043c\\u0435\\u0441\\u0442\\u0438\\u0442\\u044c \\u043a\\u0430\\u0440\\u0442\\u0443, \\u043f\\u0440\\u043e\\u0432\\u0435\\u0434\\u0438\\u0442\\u0435 \\u043f\\u043e \\u043d\\u0435\\u0439 \\u0434\\u0432\\u0443\\u043c\\u044f \\u043f\\u0430\\u043b\\u044c\\u0446\\u0430\\u043c\\u0438\",\n\t\tscroll: \"\\u0427\\u0442\\u043e\\u0431\\u044b \\u0438\\u0437\\u043c\\u0435\\u043d\\u0438\\u0442\\u044c \\u043c\\u0430\\u0441\\u0448\\u0442\\u0430\\u0431, \\u043f\\u0440\\u043e\\u043a\\u0440\\u0443\\u0447\\u0438\\u0432\\u0430\\u0439\\u0442\\u0435 \\u043a\\u0430\\u0440\\u0442\\u0443, \\u0443\\u0434\\u0435\\u0440\\u0436\\u0438\\u0432\\u0430\\u044f \\u043a\\u043b\\u0430\\u0432\\u0438\\u0448\\u0443 Ctrl.\",\n\t\tscrollMac: \"\\u0427\\u0442\\u043e\\u0431\\u044b \\u0438\\u0437\\u043c\\u0435\\u043d\\u0438\\u0442\\u044c \\u043c\\u0430\\u0441\\u0448\\u0442\\u0430\\u0431, \\u043d\\u0430\\u0436\\u043c\\u0438\\u0442\\u0435 \\u2318\\u00a0+ \\u043f\\u0440\\u043e\\u043a\\u0440\\u0443\\u0442\\u043a\\u0430\"\n\t},\n\t//Slovak\n\tsk: {\n\t\ttouch: \"Mapu m\\u00f4\\u017eete posun\\u00fa\\u0165 dvoma prstami\",\n\t\tscroll: \"Ak chcete pribl\\u00ed\\u017ei\\u0165 mapu, stla\\u010dte kl\\u00e1ves ctrl a\\u00a0pos\\u00favajte\",\n\t\tscrollMac: \"Ak chcete pribl\\u00ed\\u017ei\\u0165 mapu, stla\\u010dte kl\\u00e1ves \\u2318 a\\u00a0pos\\u00favajte kolieskom my\\u0161i\"\n\t},\n\t//Slovenian\n\tsl: {\n\t\ttouch: \"Premaknite zemljevid z dvema prstoma\",\n\t\tscroll: \"Zemljevid pove\\u010date tako, da dr\\u017eite tipko Ctrl in vrtite kolesce na mi\\u0161ki\",\n\t\tscrollMac: \"Uporabite \\u2318 + funkcijo pomika, da pove\\u010date ali pomanj\\u0161ate zemljevid\"\n\t},\n\t//Serbian\n\tsr: {\n\t\ttouch: \"\\u041c\\u0430\\u043f\\u0443 \\u043f\\u043e\\u043c\\u0435\\u0440\\u0430\\u0458\\u0442\\u0435 \\u043f\\u043e\\u043c\\u043e\\u045b\\u0443 \\u0434\\u0432\\u0430 \\u043f\\u0440\\u0441\\u0442\\u0430\",\n\t\tscroll: \"\\u041f\\u0440\\u0438\\u0442\\u0438\\u0441\\u043d\\u0438\\u0442\\u0435 ctrl \\u0442\\u0430\\u0441\\u0442\\u0435\\u0440 \\u0434\\u043e\\u043a \\u043f\\u043e\\u043c\\u0435\\u0440\\u0430\\u0442\\u0435 \\u0434\\u0430 \\u0431\\u0438\\u0441\\u0442\\u0435 \\u0437\\u0443\\u043c\\u0438\\u0440\\u0430\\u043b\\u0438 \\u043c\\u0430\\u043f\\u0443\",\n\t\tscrollMac: \"\\u041f\\u0440\\u0438\\u0442\\u0438\\u0441\\u043d\\u0438\\u0442\\u0435 \\u0442\\u0430\\u0441\\u0442\\u0435\\u0440 \\u2318 \\u0434\\u043e\\u043a \\u043f\\u043e\\u043c\\u0435\\u0440\\u0430\\u0442\\u0435 \\u0434\\u0430 \\u0431\\u0438\\u0441\\u0442\\u0435 \\u0437\\u0443\\u043c\\u0438\\u0440\\u0430\\u043b\\u0438 \\u043c\\u0430\\u043f\\u0443\"\n\t},\n\t//Swedish\n\tsv: {\n\t\ttouch: \"Anv\\u00e4nd tv\\u00e5 fingrar f\\u00f6r att flytta kartan\",\n\t\tscroll: \"Anv\\u00e4nd ctrl + rulla f\\u00f6r att zooma kartan\",\n\t\tscrollMac: \"Anv\\u00e4nd \\u2318 + rulla f\\u00f6r att zooma p\\u00e5 kartan\"\n\t},\n\t//Tamil\n\tta: {\n\t\ttouch: \"\\u0bae\\u0bc7\\u0baa\\u0bcd\\u0baa\\u0bc8 \\u0ba8\\u0b95\\u0bb0\\u0bcd\\u0ba4\\u0bcd\\u0ba4 \\u0b87\\u0bb0\\u0ba3\\u0bcd\\u0b9f\\u0bc1 \\u0bb5\\u0bbf\\u0bb0\\u0bb2\\u0bcd\\u0b95\\u0bb3\\u0bc8\\u0baa\\u0bcd \\u0baa\\u0baf\\u0ba9\\u0bcd\\u0baa\\u0b9f\\u0bc1\\u0ba4\\u0bcd\\u0ba4\\u0bb5\\u0bc1\\u0bae\\u0bcd\",\n\t\tscroll: \"\\u0bae\\u0bc7\\u0baa\\u0bcd\\u0baa\\u0bc8 \\u0baa\\u0bc6\\u0bb0\\u0bbf\\u0ba4\\u0bbe\\u0b95\\u0bcd\\u0b95\\u0bbf/\\u0b9a\\u0bbf\\u0bb1\\u0bbf\\u0ba4\\u0bbe\\u0b95\\u0bcd\\u0b95\\u0bbf\\u0baa\\u0bcd \\u0baa\\u0bbe\\u0bb0\\u0bcd\\u0b95\\u0bcd\\u0b95, ctrl \\u0baa\\u0b9f\\u0bcd\\u0b9f\\u0ba9\\u0bc8\\u0baa\\u0bcd \\u0baa\\u0bbf\\u0b9f\\u0bbf\\u0ba4\\u0bcd\\u0ba4\\u0baa\\u0b9f\\u0bbf, \\u0bae\\u0bc7\\u0bb2\\u0bc7/\\u0b95\\u0bc0\\u0bb4\\u0bc7 \\u0bb8\\u0bcd\\u0b95\\u0bcd\\u0bb0\\u0bbe\\u0bb2\\u0bcd \\u0b9a\\u0bc6\\u0baf\\u0bcd\\u0baf\\u0bb5\\u0bc1\\u0bae\\u0bcd\",\n\t\tscrollMac: \"\\u0bae\\u0bc7\\u0baa\\u0bcd\\u0baa\\u0bc8 \\u0baa\\u0bc6\\u0bb0\\u0bbf\\u0ba4\\u0bbe\\u0b95\\u0bcd\\u0b95\\u0bbf/\\u0b9a\\u0bbf\\u0bb1\\u0bbf\\u0ba4\\u0bbe\\u0b95\\u0bcd\\u0b95\\u0bbf\\u0baa\\u0bcd \\u0baa\\u0bbe\\u0bb0\\u0bcd\\u0b95\\u0bcd\\u0b95, \\u2318 \\u0baa\\u0b9f\\u0bcd\\u0b9f\\u0ba9\\u0bc8\\u0baa\\u0bcd \\u0baa\\u0bbf\\u0b9f\\u0bbf\\u0ba4\\u0bcd\\u0ba4\\u0baa\\u0b9f\\u0bbf, \\u0bae\\u0bc7\\u0bb2\\u0bc7/\\u0b95\\u0bc0\\u0bb4\\u0bc7 \\u0bb8\\u0bcd\\u0b95\\u0bcd\\u0bb0\\u0bbe\\u0bb2\\u0bcd \\u0b9a\\u0bc6\\u0baf\\u0bcd\\u0baf\\u0bb5\\u0bc1\\u0bae\\u0bcd\"\n\t},\n\t//Telugu\n\tte: {\n\t\ttouch: \"\\u0c2e\\u0c4d\\u0c2f\\u0c3e\\u0c2a\\u0c4d\\u200c\\u0c28\\u0c3f \\u0c24\\u0c30\\u0c32\\u0c3f\\u0c02\\u0c1a\\u0c21\\u0c02 \\u0c15\\u0c4b\\u0c38\\u0c02 \\u0c30\\u0c46\\u0c02\\u0c21\\u0c41 \\u0c35\\u0c47\\u0c33\\u0c4d\\u0c32\\u0c28\\u0c41 \\u0c09\\u0c2a\\u0c2f\\u0c4b\\u0c17\\u0c3f\\u0c02\\u0c1a\\u0c02\\u0c21\\u0c3f\",\n\t\tscroll: \"\\u0c2e\\u0c4d\\u0c2f\\u0c3e\\u0c2a\\u0c4d\\u200c\\u0c28\\u0c3f \\u0c1c\\u0c42\\u0c2e\\u0c4d \\u0c1a\\u0c47\\u0c2f\\u0c21\\u0c3e\\u0c28\\u0c3f\\u0c15\\u0c3f ctrl \\u0c2c\\u0c1f\\u0c28\\u0c4d\\u200c\\u0c28\\u0c41 \\u0c28\\u0c4a\\u0c15\\u0c4d\\u0c15\\u0c3f \\u0c09\\u0c02\\u0c1a\\u0c3f, \\u0c38\\u0c4d\\u0c15\\u0c4d\\u0c30\\u0c4b\\u0c32\\u0c4d \\u0c1a\\u0c47\\u0c2f\\u0c02\\u0c21\\u0c3f\",\n\t\tscrollMac: \"\\u0c2e\\u0c4d\\u0c2f\\u0c3e\\u0c2a\\u0c4d \\u0c1c\\u0c42\\u0c2e\\u0c4d \\u0c1a\\u0c47\\u0c2f\\u0c3e\\u0c32\\u0c02\\u0c1f\\u0c47 \\u2318 + \\u0c38\\u0c4d\\u0c15\\u0c4d\\u0c30\\u0c4b\\u0c32\\u0c4d \\u0c09\\u0c2a\\u0c2f\\u0c4b\\u0c17\\u0c3f\\u0c02\\u0c1a\\u0c02\\u0c21\\u0c3f\"\n\t},\n\t//Thai\n\tth: {\n\t\ttouch: \"\\u0e43\\u0e0a\\u0e49 2 \\u0e19\\u0e34\\u0e49\\u0e27\\u0e40\\u0e1e\\u0e37\\u0e48\\u0e2d\\u0e40\\u0e25\\u0e37\\u0e48\\u0e2d\\u0e19\\u0e41\\u0e1c\\u0e19\\u0e17\\u0e35\\u0e48\",\n\t\tscroll: \"\\u0e01\\u0e14 Ctrl \\u0e04\\u0e49\\u0e32\\u0e07\\u0e44\\u0e27\\u0e49 \\u0e41\\u0e25\\u0e49\\u0e27\\u0e40\\u0e25\\u0e37\\u0e48\\u0e2d\\u0e19\\u0e2b\\u0e19\\u0e49\\u0e32\\u0e08\\u0e2d\\u0e40\\u0e1e\\u0e37\\u0e48\\u0e2d\\u0e0b\\u0e39\\u0e21\\u0e41\\u0e1c\\u0e19\\u0e17\\u0e35\\u0e48\",\n\t\tscrollMac: \"\\u0e01\\u0e14 \\u2318 \\u0e41\\u0e25\\u0e49\\u0e27\\u0e40\\u0e25\\u0e37\\u0e48\\u0e2d\\u0e19\\u0e2b\\u0e19\\u0e49\\u0e32\\u0e08\\u0e2d\\u0e40\\u0e1e\\u0e37\\u0e48\\u0e2d\\u0e0b\\u0e39\\u0e21\\u0e41\\u0e1c\\u0e19\\u0e17\\u0e35\\u0e48\"\n\t},\n\t//Tagalog\n\ttl: {\n\t\ttouch: \"Gumamit ng dalawang daliri upang iusog ang mapa\",\n\t\tscroll: \"Gamitin ang ctrl + scroll upang i-zoom ang mapa\",\n\t\tscrollMac: \"Gamitin ang \\u2318 + scroll upang i-zoom ang mapa\"\n\t},\n\t//Turkish\n\ttr: {\n\t\ttouch: \"Haritada gezinmek i\\u00e7in iki parma\\u011f\\u0131n\\u0131z\\u0131 kullan\\u0131n\",\n\t\tscroll: \"Haritay\\u0131 yak\\u0131nla\\u015ft\\u0131rmak i\\u00e7in ctrl + kayd\\u0131rma kombinasyonunu kullan\\u0131n\",\n\t\tscrollMac: \"Haritay\\u0131 yak\\u0131nla\\u015ft\\u0131rmak i\\u00e7in \\u2318 tu\\u015funa bas\\u0131p ekran\\u0131 kayd\\u0131r\\u0131n\"\n\t},\n\t//Ukrainian\n\tuk: {\n\t\ttouch: \"\\u041f\\u0435\\u0440\\u0435\\u043c\\u0456\\u0449\\u0443\\u0439\\u0442\\u0435 \\u043a\\u0430\\u0440\\u0442\\u0443 \\u0434\\u0432\\u043e\\u043c\\u0430 \\u043f\\u0430\\u043b\\u044c\\u0446\\u044f\\u043c\\u0438\",\n\t\tscroll: \"\\u0429\\u043e\\u0431 \\u0437\\u043c\\u0456\\u043d\\u044e\\u0432\\u0430\\u0442\\u0438 \\u043c\\u0430\\u0441\\u0448\\u0442\\u0430\\u0431 \\u043a\\u0430\\u0440\\u0442\\u0438, \\u043f\\u0440\\u043e\\u043a\\u0440\\u0443\\u0447\\u0443\\u0439\\u0442\\u0435 \\u043a\\u043e\\u043b\\u0456\\u0449\\u0430\\u0442\\u043a\\u043e \\u043c\\u0438\\u0448\\u0456, \\u0443\\u0442\\u0440\\u0438\\u043c\\u0443\\u044e\\u0447\\u0438 \\u043a\\u043b\\u0430\\u0432\\u0456\\u0448\\u0443 Ctrl\",\n\t\tscrollMac: \"\\u0429\\u043e\\u0431 \\u0437\\u043c\\u0456\\u043d\\u0438\\u0442\\u0438 \\u043c\\u0430\\u0441\\u0448\\u0442\\u0430\\u0431 \\u043a\\u0430\\u0440\\u0442\\u0438, \\u0432\\u0438\\u043a\\u043e\\u0440\\u0438\\u0441\\u0442\\u043e\\u0432\\u0443\\u0439\\u0442\\u0435 \\u2318 + \\u043f\\u0440\\u043e\\u043a\\u0440\\u0443\\u0447\\u0443\\u0432\\u0430\\u043d\\u043d\\u044f\"\n\t},\n\t//Vietnamese\n\tvi: {\n\t\ttouch: \"S\\u1eed d\\u1ee5ng hai ng\\u00f3n tay \\u0111\\u1ec3 di chuy\\u1ec3n b\\u1ea3n \\u0111\\u1ed3\",\n\t\tscroll: \"S\\u1eed d\\u1ee5ng ctrl + cu\\u1ed9n \\u0111\\u1ec3 thu ph\\u00f3ng b\\u1ea3n \\u0111\\u1ed3\",\n\t\tscrollMac: \"S\\u1eed d\\u1ee5ng \\u2318 + cu\\u1ed9n \\u0111\\u1ec3 thu ph\\u00f3ng b\\u1ea3n \\u0111\\u1ed3\"\n\t},\n\t//Chinese (Simplified)\n\t\"zh-CN\": {\n\t\ttouch: \"\\u4f7f\\u7528\\u53cc\\u6307\\u79fb\\u52a8\\u5730\\u56fe\",\n\t\tscroll: \"\\u6309\\u4f4f Ctrl \\u5e76\\u6eda\\u52a8\\u9f20\\u6807\\u6eda\\u8f6e\\u624d\\u53ef\\u7f29\\u653e\\u5730\\u56fe\",\n\t\tscrollMac: \"\\u6309\\u4f4f \\u2318 \\u5e76\\u6eda\\u52a8\\u9f20\\u6807\\u6eda\\u8f6e\\u624d\\u53ef\\u7f29\\u653e\\u5730\\u56fe\"\n\t},\n\t//Chinese (Traditional)\n\t\"zh-TW\": {\n\t\ttouch: \"\\u540c\\u6642\\u4ee5\\u5169\\u6307\\u79fb\\u52d5\\u5730\\u5716\",\n\t\tscroll: \"\\u6309\\u4f4f ctrl \\u9375\\u52a0\\u4e0a\\u6372\\u52d5\\u6ed1\\u9f20\\u53ef\\u4ee5\\u7e2e\\u653e\\u5730\\u5716\",\n\t\tscrollMac: \"\\u6309 \\u2318 \\u52a0\\u4e0a\\u6efe\\u52d5\\u6372\\u8ef8\\u53ef\\u4ee5\\u7e2e\\u653e\\u5730\\u5716\"\n\t}\n};\n","import LanguageContent from \"./locales\";\n\nwindow.LanguageContent = LanguageContent;\n\nvar draggingMap = false;\nvar gestureHandlingOptions = {\n\ttext: {},\n\tduration: 1700\n};\n\nexport var GestureHandling = L.Handler.extend({\n\n\t_isScrolling: false,\n\t_isTouching: false,\n\t_isFading: false,\n\n\taddHooks: function() {\n\t\tthis._handleTouch = L.bind(this._handleTouch, this);\n\n\t\tthis._setGestureHandlingOptions();\n\t\tthis._disableInteractions();\n\n\t\t//Uses native event listeners instead of L.DomEvent due to issues with Android touch events turning into pointer events\n\t\tthis._map._container.addEventListener(\"touchstart\", this._handleTouch);\n\t\tthis._map._container.addEventListener(\"touchmove\", this._handleTouch);\n\t\tthis._map._container.addEventListener(\"touchend\", this._handleTouch);\n\t\tthis._map._container.addEventListener(\"touchcancel\", this._handleTouch);\n\t\tthis._map._container.addEventListener(\"click\", this._handleTouch);\n\n\t\tL.DomEvent.on(this._map._container, \"wheel\", this._handleScroll, this);\n\t\tL.DomEvent.on(this._map._container, \"mouseenter\", this._handleMouseOver, this);\n\t\tL.DomEvent.on(this._map._container, \"mouseleave\", this._handleMouseOut, this);\n\n\t\t// Listen to these events so will not disable dragging if the user moves the mouse out the boundary of the map container whilst actively dragging the map.\n\t\tL.DomEvent.on(this._map, \"movestart\", this._handleDragging, this);\n\t\tL.DomEvent.on(this._map, \"move\", this._handleDragging, this);\n\t\tL.DomEvent.on(this._map, \"moveend\", this._handleDragging, this);\n\n\t\t// Prevent page scroll on \"leaflet-popup-content\"\n\t\tthis._map.on(\"popupopen\", this._handleScrollOnPopup, this);\n\t\tthis._map.on(\"popupclose\", this._handleScrollOnPopup, this);\n\n\t\t// Reset any previously added fullscreen events\n\t\tL.DomEvent.off(this._map, \"enterFullscreen\", this._onEnterFullscreen, this);\n\t\tL.DomEvent.off(this._map, \"exitFullscreen\", this._onExitFullscreen, this);\n\t\tL.DomEvent.on(this._map, \"enterFullscreen\", this._onEnterFullscreen, this);\n\t\tL.DomEvent.on(this._map, \"exitFullscreen\", this._onExitFullscreen, this);\n\n\t\tL.DomUtil.addClass(this._map._container, \"leaflet-gesture-handling\");\n\t},\n\n\tremoveHooks: function() {\n\t\tthis._enableInteractions();\n\n\t\tthis._map._container.removeEventListener(\"touchstart\", this._handleTouch);\n\t\tthis._map._container.removeEventListener(\"touchmove\", this._handleTouch);\n\t\tthis._map._container.removeEventListener(\"touchend\", this._handleTouch);\n\t\tthis._map._container.removeEventListener(\"touchcancel\", this._handleTouch);\n\t\tthis._map._container.removeEventListener(\"click\", this._handleTouch);\n\n\t\tL.DomEvent.off(this._map._container, \"wheel\", this._handleScroll, this);\n\t\tL.DomEvent.off(this._map._container, \"mouseenter\", this._handleMouseOver, this);\n\t\tL.DomEvent.off(this._map._container, \"mouseleave\", this._handleMouseOut, this);\n\n\t\tL.DomEvent.off(this._map, \"movestart\", this._handleDragging, this);\n\t\tL.DomEvent.off(this._map, \"move\", this._handleDragging, this);\n\t\tL.DomEvent.off(this._map, \"moveend\", this._handleDragging, this);\n\n\t\tthis._map.off(\"popupopen\", this._handleScrollOnPopup, this);\n\t\tthis._map.off(\"popupclose\", this._handleScrollOnPopup, this);\n\n\t\tL.DomUtil.removeClass(this._map._container, \"leaflet-gesture-handling\");\n\t},\n\n\t_handleDragging: function(e) {\n\t\tif (e.type == \"movestart\" || e.type == \"move\") {\n\t\t\tdraggingMap = true;\n\t\t} else if (e.type == \"moveend\") {\n\t\t\tdraggingMap = false;\n\t\t}\n\t},\n\n\t_disableInteractions: function() {\n\t\tthis._map.dragging.disable();\n\t\tthis._map.scrollWheelZoom.disable();\n\t\tif (this._map.tap) this._map.tap.disable();\n\t},\n\n\t_enableInteractions: function() {\n\t\tthis._map.dragging.enable();\n\t\tthis._map.scrollWheelZoom.enable();\n\t\tif (this._map.tap) this._map.tap.enable();\n\t},\n\n\t_enableWarning: function(gesture) {\n\t\tclearTimeout(this._isFading);\n\t\tL.DomUtil.addClass(this._map._container, \"leaflet-gesture-handling-\" + gesture);\n\t\tL.DomUtil.addClass(this._map._container, \"leaflet-gesture-handling-warning\");\n\t},\n\n\t_disableWarning: function(gesture, delay) {\n\t\tclearTimeout(this._isFading);\n\t\tthis._isFading = setTimeout(\n\t\t\tL.bind(function(gesture) {\n\t\t\t\tL.DomUtil.removeClass(this._map._container, \"leaflet-gesture-handling-\" + gesture);\n\t\t\t}, this, gesture),\n\t\t\tdelay || this._map.options.gestureHandlingOptions.duration\n\t\t);\n\t\tL.DomUtil.removeClass(this._map._container, \"leaflet-gesture-handling-warning\");\n\t},\n\n\t_isLanguageContent: function(text) {\n\t\treturn text && text.touch && text.scroll && text.scrollMac;\n\t},\n\n\t_isMacUser: function() {\n\t\treturn navigator.platform.toUpperCase().indexOf(\"MAC\") >= 0;\n\t},\n\n\t_parseGestureHandlingOptions: function() {\n\t\tvar options = L.extend(this._map.options.gestureHandlingOptions, gestureHandlingOptions);\n\n\t\t//For backwards compatibility, merge gestureHandlingText into the new options object\n\t\tif (this._map.options.gestureHandlingText) {\n\t\t\toptions.text = this._map.options.gestureHandlingText;\n\t\t}\n\t\treturn options;\n\t},\n\n\t_setGestureHandlingOptions: function() {\n\t\tvar opts = this._parseGestureHandlingOptions();\n\n\t\t//If user has supplied custom language, use that, otherwise auto set it from the language files\n\t\tvar content = this._isLanguageContent(opts.text) ? opts.text : this._getLanguageContent(opts.locale);\n\n\t\tthis._map._container.setAttribute(\"data-gesture-handling-touch-content\", content.touch);\n\t\tthis._map._container.setAttribute(\"data-gesture-handling-scroll-content\", content.scroll);\n\n\t\tthis._touchWarning = content.touch;\n\t\tthis._scrollWarning = content.scroll;\n\t},\n\n\t_getUserLanguage: function() {\n\t\treturn navigator.languages ? navigator.languages[0] : navigator.language || navigator.userLanguage;\n\t},\n\n\t_getLanguageContent: function(lang) {\n\t\t//Determine user language (eg. fr or en-US)\n\t\tlang = lang || this._getUserLanguage() || \"en\";\n\n\t\t//Lookup the appropriate language content\n\t\tvar content = LanguageContent[lang];\n\n\t\t//If no result, try searching by the first part only (eg. en-US, just use en).\n\t\tcontent = (!content && lang.indexOf(\"-\") !== -1) ? LanguageContent[lang.split(\"-\")[0]] : content;\n\n\t\t// If still nothing, default to English.\n\t\tcontent = content || LanguageContent[\"en\"];\n\n\t\t//Check if they're on a mac for displaying appropriate command control (⌘ instead of Ctrl)\n\t\tcontent.scroll = this._isMacUser() ? content.scrollMac : content.scroll;\n\n\t\treturn content;\n\t},\n\n\t_hasClass: function(element, classList) {\n\t\tfor (var i = 0; i < classList.length; i++) {\n\t\t\tif (L.DomUtil.hasClass(element, classList[i])) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t},\n\n\t_handleTouch: function(e) {\n\t\t//Disregard touch events on the minimap if present\n\t\tvar ignore = this._hasClass(e.target, [\"leaflet-control-minimap\", \"leaflet-interactive\", \"leaflet-popup-content\", \"leaflet-popup-content-wrapper\", \"leaflet-popup-close-button\", \"leaflet-control-zoom-in\", \"leaflet-control-zoom-out\"]);\n\n\t\tif (ignore) {\n\t\t\tif (L.DomUtil.hasClass(e.target, \"leaflet-interactive\") && e.type === \"touchmove\" && e.touches.length === 1) {\n\t\t\t\tthis._enableTouchWarning();\n\t\t\t} else {\n\t\t\t\tthis._disableTouchWarning();\n\t\t\t}\n\t\t} else if (e.type !== \"touchmove\" && e.type !== \"touchstart\") {\n\t\t\tthis._disableTouchWarning();\n\t\t} else if (e.touches.length === 1) {\n\t\t\tthis._enableTouchWarning();\n\t\t} else {\n\t\t\tthis._disableTouchWarning();\n\t\t\tthis._enableInteractions();\n\t\t}\n\t},\n\n\t_enableTouchWarning: function() {\n\t\tthis._enableWarning('touch');\n\t\tthis._disableInteractions();\n\t},\n\n\t_disableTouchWarning: function(delay) {\n\t\tclearTimeout(this._isTouching);\n\t\t// Set a timeout to run after touching ends\n\t\tthis._isTouching = setTimeout(\n\t\t\tL.bind(\n\t\t\t\tfunction() {\n\t\t\t\t\tthis._disableWarning('touch');\n\t\t\t\t\t// this._enableInteractions();\n\t\t\t\t}, this),\n\t\t\tdelay || 0\n\t\t);\n\t},\n\n\t_enableScrollWarning: function() {\n\t\tthis._enableWarning('scroll');\n\t\tthis._map.scrollWheelZoom.disable();\n\t},\n\n\t_disableScrollWarning: function(delay) {\n\t\tclearTimeout(this._isScrolling);\n\t\t// Set a timeout to run after scrolling ends\n\t\tthis._isScrolling = setTimeout(\n\t\t\tL.bind(\n\t\t\t\tfunction() {\n\t\t\t\t\tthis._disableWarning('scroll');\n\t\t\t\t\tthis._map.scrollWheelZoom.enable();\n\t\t\t\t}, this),\n\t\t\tdelay || 0\n\t\t);\n\t},\n\n\t_handleScroll: function(e) {\n\t\tif (e.metaKey || e.ctrlKey || (e.shiftKey && this._map._rotate)) {\n\t\t\te.preventDefault();\n\t\t\tthis._disableScrollWarning();\n\t\t} else {\n\t\t\tthis._enableScrollWarning();\n\t\t\tthis._disableScrollWarning(this._map.options.gestureHandlingOptions.duration);\n\t\t}\n\t},\n\n\t_handleScrollOnPopup: function(e) {\n\t\tL.DomEvent[e.type == 'popupopen' ? 'on' : 'off']\n\t\t(e.popup._contentNode, \"wheel\", this._handleScroll, this);\n\t},\n\n\t_handleMouseOver: function(e) {\n\t\tthis._enableInteractions();\n\t},\n\n\t_handleMouseOut: function(e) {\n\t\tif (!draggingMap) this._disableInteractions();\n\t},\n\n\t_onExitFullscreen: function() {\n\t\tif (this._map.options.gestureHandling)\n\t\t\tthis._map.gestureHandling.enable();\n\t},\n\n\t_onEnterFullscreen: function() {\n\t\tif (this._map.options.gestureHandling)\n\t\t\tthis._map.gestureHandling.disable();\n\t},\n\n});\n\nL.Map.mergeOptions({\n\tgestureHandlingOptions: gestureHandlingOptions\n});\n\nL.Map.addInitHook(\"addHandler\", \"gestureHandling\", GestureHandling);\n","L.Control.EditInOSM = L.Control.extend({\r\n\toptions: {\r\n\t\tposition: \"bottomright\",\r\n\t\teditor: false, // eg: \"id\", \"potlatch2\" or \"remote\"\r\n\t},\r\n\t_edit: function() {\r\n\t\tvar center = this._map.getCenter();\r\n\t\tvar z = this._map.getZoom();\r\n\t\tvar editor = this.options.editor ? '&editor=' + this.options.editor : '';\r\n\t\twindow.open('http://www.openstreetmap.org/edit?' + 'zoom=' + z + editor + '&lat=' + center.lat + '&lon=' + center.lng);\r\n\t},\r\n\tonAdd: function(map) {\r\n\t\tvar container = L.DomUtil.create('div', 'leaflet-control-attribution leaflet-edit-osm'),\r\n\t\t\tlink = L.DomUtil.create('a', '', container);\r\n\r\n\t\tlink.href = '#';\r\n\t\tlink.innerHTML = '✎ Edit';\r\n\t\tlink.title = 'Edit in OpenStreetMap';\r\n\r\n\t\tL.DomEvent\r\n\t\t\t.on(link, 'click', L.DomEvent.stopPropagation)\r\n\t\t\t.on(link, 'mousedown', L.DomEvent.stopPropagation)\r\n\t\t\t.on(link, 'dblclick', L.DomEvent.stopPropagation)\r\n\t\t\t.on(link, 'click', L.DomEvent.preventDefault)\r\n\t\t\t.on(link, 'click', L.bind(this._edit, this), this);\r\n\r\n\t\treturn container;\r\n\t}\r\n});\r\n\r\nL.control.editInOSM = function(options) {\r\n\treturn new L.Control.EditInOSM(options);\r\n};\r\n","/**\r\n * leaflet-control-layers-inline\r\n *\r\n * @author Raruto\r\n * @license GPL-3.0+\r\n * @link https://github.com/Raruto/leaflet-control-layers-inline\r\n * @desc Leaflet plugin that allows to display inline layers control\r\n */\r\n(function() {\r\n\r\n var layersProto = L.Control.Layers.prototype;\r\n var initializeLayersProto = layersProto.initialize;\r\n var onAddLayersProto = layersProto.onAdd;\r\n\r\n layersProto.options.inline = false;\r\n\r\n L.Control.Layers.include({\r\n\r\n initialize: function(baseLayers, overlays, options) {\r\n if (options.inline) {\r\n options.collapsed = false;\r\n }\r\n initializeLayersProto.call(this, baseLayers, overlays, options);\r\n },\r\n\r\n onAdd: function(map) {\r\n onAddLayersProto.call(this, map);\r\n if (this.options.inline) {\r\n this.options.collapsed = false;\r\n L.DomUtil.addClass(this._container, \"leaflet-control-layers-inline\");\r\n }\r\n if (this.options.className) {\r\n L.DomUtil.addClass(this._container, this.options.className);\r\n }\r\n return this._container;\r\n },\r\n\r\n });\r\n\r\n})();\r\n","(function(factory,window){if(typeof define===\"function\"&&define.amd){define([\"leaflet\"],factory)}else if(typeof exports===\"object\"){module.exports=factory(require(\"leaflet\"))}if(typeof window!==\"undefined\"&&window.L){window.L.Control.MiniMap=factory(L);window.L.control.minimap=function(layer,options){return new window.L.Control.MiniMap(layer,options)}}})(function(L){var MiniMap=L.Control.extend({includes:L.Evented?L.Evented.prototype:L.Mixin.Events,options:{position:\"bottomright\",toggleDisplay:false,zoomLevelOffset:-5,zoomLevelFixed:false,centerFixed:false,zoomAnimation:false,autoToggleDisplay:false,minimized:false,width:150,height:150,collapsedWidth:19,collapsedHeight:19,aimingRectOptions:{color:\"#ff7800\",weight:1,clickable:false},shadowRectOptions:{color:\"#000000\",weight:1,clickable:false,opacity:0,fillOpacity:0},strings:{hideText:\"Hide MiniMap\",showText:\"Show MiniMap\"},mapOptions:{}},initialize:function(layer,options){L.Util.setOptions(this,options);this.options.aimingRectOptions.clickable=false;this.options.shadowRectOptions.clickable=false;this._layer=layer},onAdd:function(map){this._mainMap=map;this._container=L.DomUtil.create(\"div\",\"leaflet-control-minimap\");this._container.style.width=this.options.width+\"px\";this._container.style.height=this.options.height+\"px\";L.DomEvent.disableClickPropagation(this._container);L.DomEvent.on(this._container,\"mousewheel\",L.DomEvent.stopPropagation);var mapOptions={attributionControl:false,dragging:!this.options.centerFixed,zoomControl:false,zoomAnimation:this.options.zoomAnimation,autoToggleDisplay:this.options.autoToggleDisplay,touchZoom:this.options.centerFixed?\"center\":!this._isZoomLevelFixed(),scrollWheelZoom:this.options.centerFixed?\"center\":!this._isZoomLevelFixed(),doubleClickZoom:this.options.centerFixed?\"center\":!this._isZoomLevelFixed(),boxZoom:!this._isZoomLevelFixed(),crs:map.options.crs};mapOptions=L.Util.extend(this.options.mapOptions,mapOptions);this._miniMap=new L.Map(this._container,mapOptions);this._miniMap.addLayer(this._layer);this._mainMapMoving=false;this._miniMapMoving=false;this._userToggledDisplay=false;this._minimized=false;if(this.options.toggleDisplay){this._addToggleButton()}this._miniMap.whenReady(L.Util.bind(function(){this._aimingRect=L.rectangle(this._mainMap.getBounds(),this.options.aimingRectOptions).addTo(this._miniMap);this._shadowRect=L.rectangle(this._mainMap.getBounds(),this.options.shadowRectOptions).addTo(this._miniMap);this._mainMap.on(\"moveend\",this._onMainMapMoved,this);this._mainMap.on(\"move\",this._onMainMapMoving,this);this._miniMap.on(\"movestart\",this._onMiniMapMoveStarted,this);this._miniMap.on(\"move\",this._onMiniMapMoving,this);this._miniMap.on(\"moveend\",this._onMiniMapMoved,this)},this));return this._container},addTo:function(map){L.Control.prototype.addTo.call(this,map);var center=this.options.centerFixed||this._mainMap.getCenter();this._miniMap.setView(center,this._decideZoom(true));this._setDisplay(this.options.minimized);return this},onRemove:function(map){this._mainMap.off(\"moveend\",this._onMainMapMoved,this);this._mainMap.off(\"move\",this._onMainMapMoving,this);this._miniMap.off(\"moveend\",this._onMiniMapMoved,this);this._miniMap.removeLayer(this._layer)},changeLayer:function(layer){this._miniMap.removeLayer(this._layer);this._layer=layer;this._miniMap.addLayer(this._layer)},_addToggleButton:function(){this._toggleDisplayButton=this.options.toggleDisplay?this._createButton(\"\",this._toggleButtonInitialTitleText(),\"leaflet-control-minimap-toggle-display leaflet-control-minimap-toggle-display-\"+this.options.position,this._container,this._toggleDisplayButtonClicked,this):undefined;this._toggleDisplayButton.style.width=this.options.collapsedWidth+\"px\";this._toggleDisplayButton.style.height=this.options.collapsedHeight+\"px\"},_toggleButtonInitialTitleText:function(){if(this.options.minimized){return this.options.strings.showText}else{return this.options.strings.hideText}},_createButton:function(html,title,className,container,fn,context){var link=L.DomUtil.create(\"a\",className,container);link.innerHTML=html;link.href=\"#\";link.title=title;var stop=L.DomEvent.stopPropagation;L.DomEvent.on(link,\"click\",stop).on(link,\"mousedown\",stop).on(link,\"dblclick\",stop).on(link,\"click\",L.DomEvent.preventDefault).on(link,\"click\",fn,context);return link},_toggleDisplayButtonClicked:function(){this._userToggledDisplay=true;if(!this._minimized){this._minimize()}else{this._restore()}},_setDisplay:function(minimize){if(minimize!==this._minimized){if(!this._minimized){this._minimize()}else{this._restore()}}},_minimize:function(){if(this.options.toggleDisplay){this._container.style.width=this.options.collapsedWidth+\"px\";this._container.style.height=this.options.collapsedHeight+\"px\";this._toggleDisplayButton.className+=\" minimized-\"+this.options.position;this._toggleDisplayButton.title=this.options.strings.showText}else{this._container.style.display=\"none\"}this._minimized=true;this._onToggle()},_restore:function(){if(this.options.toggleDisplay){this._container.style.width=this.options.width+\"px\";this._container.style.height=this.options.height+\"px\";this._toggleDisplayButton.className=this._toggleDisplayButton.className.replace(\"minimized-\"+this.options.position,\"\");this._toggleDisplayButton.title=this.options.strings.hideText}else{this._container.style.display=\"block\"}this._minimized=false;this._onToggle()},_onMainMapMoved:function(e){if(!this._miniMapMoving){var center=this.options.centerFixed||this._mainMap.getCenter();this._mainMapMoving=true;this._miniMap.setView(center,this._decideZoom(true));this._setDisplay(this._decideMinimized())}else{this._miniMapMoving=false}this._aimingRect.setBounds(this._mainMap.getBounds())},_onMainMapMoving:function(e){this._aimingRect.setBounds(this._mainMap.getBounds())},_onMiniMapMoveStarted:function(e){if(!this.options.centerFixed){var lastAimingRect=this._aimingRect.getBounds();var sw=this._miniMap.latLngToContainerPoint(lastAimingRect.getSouthWest());var ne=this._miniMap.latLngToContainerPoint(lastAimingRect.getNorthEast());this._lastAimingRectPosition={sw:sw,ne:ne}}},_onMiniMapMoving:function(e){if(!this.options.centerFixed){if(!this._mainMapMoving&&this._lastAimingRectPosition){this._shadowRect.setBounds(new L.LatLngBounds(this._miniMap.containerPointToLatLng(this._lastAimingRectPosition.sw),this._miniMap.containerPointToLatLng(this._lastAimingRectPosition.ne)));this._shadowRect.setStyle({opacity:1,fillOpacity:.3})}}},_onMiniMapMoved:function(e){if(!this._mainMapMoving){this._miniMapMoving=true;this._mainMap.setView(this._miniMap.getCenter(),this._decideZoom(false));this._shadowRect.setStyle({opacity:0,fillOpacity:0})}else{this._mainMapMoving=false}},_isZoomLevelFixed:function(){var zoomLevelFixed=this.options.zoomLevelFixed;return this._isDefined(zoomLevelFixed)&&this._isInteger(zoomLevelFixed)},_decideZoom:function(fromMaintoMini){if(!this._isZoomLevelFixed()){if(fromMaintoMini){return this._mainMap.getZoom()+this.options.zoomLevelOffset}else{var currentDiff=this._miniMap.getZoom()-this._mainMap.getZoom();var proposedZoom=this._miniMap.getZoom()-this.options.zoomLevelOffset;var toRet;if(currentDiff>this.options.zoomLevelOffset&&this._mainMap.getZoom()this._lastMiniMapZoom){toRet=this._mainMap.getZoom()+1;this._miniMap.setZoom(this._miniMap.getZoom()-1)}else{toRet=this._mainMap.getZoom()}}else{toRet=proposedZoom}this._lastMiniMapZoom=this._miniMap.getZoom();return toRet}}else{if(fromMaintoMini){return this.options.zoomLevelFixed}else{return this._mainMap.getZoom()}}},_decideMinimized:function(){if(this._userToggledDisplay){return this._minimized}if(this.options.autoToggleDisplay){if(this._mainMap.getBounds().contains(this._miniMap.getBounds())){return true}return false}return this._minimized},_isInteger:function(value){return typeof value===\"number\"},_isDefined:function(value){return typeof value!==\"undefined\"},_onToggle:function(){L.Util.requestAnimFrame(function(){L.DomEvent.on(this._container,\"transitionend\",this._fireToggleEvents,this);if(!L.Browser.any3d){L.Util.requestAnimFrame(this._fireToggleEvents,this)}},this)},_fireToggleEvents:function(){L.DomEvent.off(this._container,\"transitionend\",this._fireToggleEvents,this);var data={minimized:this._minimized};this.fire(this._minimized?\"minimize\":\"restore\",data);this.fire(\"toggle\",data)}});L.Map.mergeOptions({miniMapControl:false});L.Map.addInitHook(function(){if(this.options.miniMapControl){this.miniMapControl=(new MiniMap).addTo(this)}});return MiniMap},window);","/*\n * L.Control.Loading is a control that shows a loading indicator when tiles are\n * loading or when map-related AJAX requests are taking place.\n */\n\n(function () {\n\n var console = window.console || {\n error: function () {},\n warn: function () {}\n };\n\n function defineLeafletLoading(L) {\n L.Control.Loading = L.Control.extend({\n options: {\n delayIndicator: null,\n position: 'topleft',\n separate: false,\n zoomControl: null,\n spinjs: false,\n spin: { \n lines: 7, \n length: 3, \n width: 3, \n radius: 5, \n rotate: 13, \n top: \"83%\"\n }\n },\n\n initialize: function(options) {\n L.setOptions(this, options);\n this._dataLoaders = {};\n\n // Try to set the zoom control this control is attached to from the \n // options\n if (this.options.zoomControl !== null) {\n this.zoomControl = this.options.zoomControl;\n }\n },\n\n onAdd: function(map) {\n if (this.options.spinjs && (typeof Spinner !== 'function')) {\n return console.error(\"Leaflet.loading cannot load because you didn't load spin.js (http://fgnass.github.io/spin.js/), even though you set it in options.\");\n }\n this._addLayerListeners(map);\n this._addMapListeners(map);\n\n // Try to set the zoom control this control is attached to from the map\n // the control is being added to\n if (!this.options.separate && !this.zoomControl) {\n if (map.zoomControl) {\n this.zoomControl = map.zoomControl;\n } else if (map.zoomsliderControl) {\n this.zoomControl = map.zoomsliderControl;\n }\n }\n\n // Create the loading indicator\n var classes = 'leaflet-control-loading';\n var container;\n if (this.zoomControl && !this.options.separate) {\n // If there is a zoom control, hook into the bottom of it\n container = this.zoomControl._container;\n // These classes are no longer used as of Leaflet 0.6\n classes += ' leaflet-bar-part-bottom leaflet-bar-part last';\n\n // Loading control will be added to the zoom control. So the visible last element is not the\n // last dom element anymore. So add the part-bottom class.\n L.DomUtil.addClass(this._getLastControlButton(), 'leaflet-bar-part-bottom');\n }\n else {\n // Otherwise, create a container for the indicator\n container = L.DomUtil.create('div', 'leaflet-control-zoom leaflet-control-layer-container leaflet-bar');\n }\n this._indicatorContainer = container;\n this._indicator = L.DomUtil.create('a', classes, container);\n if (this.options.spinjs) {\n this._spinner = new Spinner(this.options.spin).spin();\n this._indicator.appendChild(this._spinner.el);\n }\n return container;\n },\n\n onRemove: function(map) {\n this._removeLayerListeners(map);\n this._removeMapListeners(map);\n },\n\n removeFrom: function (map) {\n if (this.zoomControl && !this.options.separate) {\n // Override Control.removeFrom() to avoid clobbering the entire\n // _container, which is the same as zoomControl's\n this._container.removeChild(this._indicator);\n this._map = null;\n this.onRemove(map);\n return this;\n }\n else {\n // If this control is separate from the zoomControl, call the\n // parent method so we don't leave behind an empty container\n return L.Control.prototype.removeFrom.call(this, map);\n }\n },\n\n addLoader: function(id) {\n this._dataLoaders[id] = true;\n if (this.options.delayIndicator && !this.delayIndicatorTimeout) {\n // If we are delaying showing the indicator and we're not\n // already waiting for that delay, set up a timeout.\n var that = this;\n this.delayIndicatorTimeout = setTimeout(function () {\n that.updateIndicator();\n that.delayIndicatorTimeout = null;\n }, this.options.delayIndicator);\n }\n else {\n // Otherwise show the indicator immediately\n this.updateIndicator();\n }\n },\n\n removeLoader: function(id) {\n delete this._dataLoaders[id];\n this.updateIndicator();\n\n // If removing this loader means we're in no danger of loading,\n // clear the timeout. This prevents old delays from instantly \n // triggering the indicator.\n if (this.options.delayIndicator && this.delayIndicatorTimeout && !this.isLoading()) {\n clearTimeout(this.delayIndicatorTimeout);\n this.delayIndicatorTimeout = null;\n }\n },\n\n updateIndicator: function() {\n if (this.isLoading()) {\n this._showIndicator();\n }\n else {\n this._hideIndicator();\n }\n },\n\n isLoading: function() {\n return this._countLoaders() > 0;\n },\n\n _countLoaders: function() {\n var size = 0, key;\n for (key in this._dataLoaders) {\n if (this._dataLoaders.hasOwnProperty(key)) size++;\n }\n return size;\n },\n\n _showIndicator: function() {\n // Show loading indicator\n L.DomUtil.addClass(this._indicator, 'is-loading');\n L.DomUtil.addClass(this._indicatorContainer, 'is-loading');\n\n // If zoomControl exists, make the zoom-out button not last\n if (!this.options.separate) {\n if (this.zoomControl instanceof L.Control.Zoom) {\n L.DomUtil.removeClass(this._getLastControlButton(), 'leaflet-bar-part-bottom');\n }\n else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) {\n L.DomUtil.removeClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom');\n }\n }\n },\n\n _hideIndicator: function() {\n // Hide loading indicator\n L.DomUtil.removeClass(this._indicator, 'is-loading');\n L.DomUtil.removeClass(this._indicatorContainer, 'is-loading');\n\n // If zoomControl exists, make the zoom-out button last\n if (!this.options.separate) {\n if (this.zoomControl instanceof L.Control.Zoom) {\n L.DomUtil.addClass(this._getLastControlButton(), 'leaflet-bar-part-bottom');\n }\n else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) {\n L.DomUtil.addClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom');\n }\n }\n },\n\n _getLastControlButton: function() {\n var container = this.zoomControl._container,\n index = container.children.length - 1;\n\n // Find the last visible control button that is not our loading\n // indicator\n while (index > 0) {\n var button = container.children[index];\n if (!(this._indicator === button || button.offsetWidth === 0 || button.offsetHeight === 0)) {\n break;\n }\n index--;\n }\n\n return container.children[index];\n },\n\n _handleLoading: function(e) {\n this.addLoader(this.getEventId(e));\n },\n\n _handleBaseLayerChange: function (e) {\n var that = this;\n\n // Check for a target 'layer' that contains multiple layers, such as\n // L.LayerGroup. This will happen if you have an L.LayerGroup in an\n // L.Control.Layers.\n if (e.layer && e.layer.eachLayer && typeof e.layer.eachLayer === 'function') {\n e.layer.eachLayer(function (layer) {\n that._handleBaseLayerChange({ layer: layer });\n });\n }\n else {\n // If we're changing to a canvas layer, don't handle loading\n // as canvas layers will not fire load events.\n if (!(L.TileLayer.Canvas && e.layer instanceof L.TileLayer.Canvas)) {\n that._handleLoading(e);\n }\n }\n },\n\n _handleLoad: function(e) {\n this.removeLoader(this.getEventId(e));\n },\n\n getEventId: function(e) {\n if (e.id) {\n return e.id;\n }\n else if (e.layer) {\n return e.layer._leaflet_id;\n }\n return e.target._leaflet_id;\n },\n\n _layerAdd: function(e) {\n if (!e.layer || !e.layer.on) return\n try {\n e.layer.on({\n loading: this._handleLoading,\n load: this._handleLoad\n }, this);\n }\n catch (exception) {\n console.warn('L.Control.Loading: Tried and failed to add ' +\n ' event handlers to layer', e.layer);\n console.warn('L.Control.Loading: Full details', exception);\n }\n },\n\n _layerRemove: function(e) {\n if (!e.layer || !e.layer.off) return;\n try {\n e.layer.off({\n loading: this._handleLoading,\n load: this._handleLoad\n }, this);\n }\n catch (exception) {\n console.warn('L.Control.Loading: Tried and failed to remove ' +\n 'event handlers from layer', e.layer);\n console.warn('L.Control.Loading: Full details', exception);\n }\n },\n\n _addLayerListeners: function(map) {\n // Add listeners for begin and end of load to any layers already on the \n // map\n map.eachLayer(function(layer) {\n if (!layer.on) return;\n layer.on({\n loading: this._handleLoading,\n load: this._handleLoad\n }, this);\n }, this);\n\n // When a layer is added to the map, add listeners for begin and end\n // of load\n map.on('layeradd', this._layerAdd, this);\n map.on('layerremove', this._layerRemove, this);\n },\n\n _removeLayerListeners: function(map) {\n // Remove listeners for begin and end of load from all layers\n map.eachLayer(function(layer) {\n if (!layer.off) return;\n layer.off({\n loading: this._handleLoading,\n load: this._handleLoad\n }, this);\n }, this);\n\n // Remove layeradd/layerremove listener from map\n map.off('layeradd', this._layerAdd, this);\n map.off('layerremove', this._layerRemove, this);\n },\n\n _addMapListeners: function(map) {\n // Add listeners to the map for (custom) dataloading and dataload\n // events, eg, for AJAX calls that affect the map but will not be\n // reflected in the above layer events.\n map.on({\n baselayerchange: this._handleBaseLayerChange,\n dataloading: this._handleLoading,\n dataload: this._handleLoad,\n layerremove: this._handleLoad\n }, this);\n },\n\n _removeMapListeners: function(map) {\n map.off({\n baselayerchange: this._handleBaseLayerChange,\n dataloading: this._handleLoading,\n dataload: this._handleLoad,\n layerremove: this._handleLoad\n }, this);\n }\n });\n\n L.Map.addInitHook(function () {\n if (this.options.loadingControl) {\n this.loadingControl = new L.Control.Loading();\n this.addControl(this.loadingControl);\n }\n });\n\n L.Control.loading = function(options) {\n return new L.Control.Loading(options);\n };\n }\n\n if (typeof define === 'function' && define.amd) {\n // Try to add leaflet.loading to Leaflet using AMD\n define(['leaflet'], function (L) {\n defineLeafletLoading(L);\n });\n }\n else {\n // Else use the global L\n defineLeafletLoading(L);\n }\n\n})();\n","/* \n * Leaflet Control Search v2.9.9 - 2020-12-01 \n * \n * Copyright 2020 Stefano Cudini \n * stefano.cudini@gmail.com \n * https://opengeo.tech/ \n * \n * Licensed under the MIT license. \n * \n * Demo: \n * https://opengeo.tech/maps/leaflet-search/ \n * \n * Source: \n * git@github.com:stefanocudini/leaflet-search.git \n * \n */\n/*\n\tName\t\t\t\t\tData passed\t\t\t Description\n\n\tManaged Events:\n\t search:locationfound\t{latlng, title, layer} fired after moved and show markerLocation\n\t search:expanded\t\t{}\t\t\t\t\t fired after control was expanded\n\t search:collapsed\t\t{}\t\t\t\t\t fired after control was collapsed\n \t search:cancel\t\t\t{}\t\t\t\t\t fired after cancel button clicked\n\n\tPublic methods:\n\t setLayer()\t\t\t\tL.LayerGroup() set layer search at runtime\n\t showAlert() 'Text message' show alert message\n\t searchText()\t\t\t'Text searched' search text by external code\n*/\n\n//TODO implement can do research on multiple sources layers and remote\t\t\n//TODO history: false,\t\t//show latest searches in tooltip\t\t\n//FIXME option condition problem {autoCollapse: true, markerLocation: true} not show location\n//FIXME option condition problem {autoCollapse: false }\n//\n//TODO here insert function search inputText FIRST in _recordsCache keys and if not find results.. \n// run one of callbacks search(sourceData,jsonpUrl or options.layer) and run this.showTooltip\n//\n//TODO change structure of _recordsCache\n//\tlike this: _recordsCache = {\"text-key1\": {loc:[lat,lng], ..other attributes.. }, {\"text-key2\": {loc:[lat,lng]}...}, ...}\n//\tin this mode every record can have a free structure of attributes, only 'loc' is required\n//TODO important optimization!!! always append data in this._recordsCache\n// now _recordsCache content is emptied and replaced with new data founded\n// always appending data on _recordsCache give the possibility of caching ajax, jsonp and layersearch!\n//\n//TODO here insert function search inputText FIRST in _recordsCache keys and if not find results.. \n// run one of callbacks search(sourceData,jsonpUrl or options.layer) and run this.showTooltip\n//\n//TODO change structure of _recordsCache\n//\tlike this: _recordsCache = {\"text-key1\": {loc:[lat,lng], ..other attributes.. }, {\"text-key2\": {loc:[lat,lng]}...}, ...}\n//\tin this way every record can have a free structure of attributes, only 'loc' is required\n\n(function (factory) {\n if(typeof define === 'function' && define.amd) {\n //AMD\n define(['leaflet'], factory);\n } else if(typeof module !== 'undefined') {\n // Node/CommonJS\n module.exports = factory(require('leaflet'));\n } else {\n // Browser globals\n if(typeof window.L === 'undefined')\n throw 'Leaflet must be loaded first';\n factory(window.L);\n }\n})(function (L) {\n\n\nL.Control.Search = L.Control.extend({\n\t\n\tincludes: L.version[0]==='1' ? L.Evented.prototype : L.Mixin.Events,\n\n\toptions: {\n\t\turl: '',\t\t\t\t\t\t//url for search by ajax request, ex: \"search.php?q={s}\". Can be function to returns string for dynamic parameter setting\n\t\tlayer: null,\t\t\t\t\t//layer where search markers(is a L.LayerGroup)\t\t\t\t\n\t\tsourceData: null,\t\t\t\t//function to fill _recordsCache, passed searching text by first param and callback in second\t\t\t\t\n\t\t//TODO implements uniq option 'sourceData' to recognizes source type: url,array,callback or layer\t\t\t\t\n\t\tjsonpParam: null,\t\t\t\t//jsonp param name for search by jsonp service, ex: \"callback\"\n\t\tpropertyLoc: 'loc',\t\t\t\t//field for remapping location, using array: ['latname','lonname'] for select double fields(ex. ['lat','lon'] ) support dotted format: 'prop.subprop.title'\n\t\tpropertyName: 'title',\t\t\t//property in marker.options(or feature.properties for vector layer) trough filter elements in layer,\n\t\tformatData: null,\t\t\t\t//callback for reformat all data from source to indexed data object\n\t\tfilterData: null,\t\t\t\t//callback for filtering data from text searched, params: textSearch, allRecords\n\t\tmoveToLocation: null,\t\t\t//callback run on location found, params: latlng, title, map\n\t\tbuildTip: null,\t\t\t\t\t//function to return row tip html node(or html string), receive text tooltip in first param\n\t\tcontainer: '',\t\t\t\t\t//container id to insert Search Control\t\t\n\t\tzoom: null,\t\t\t\t\t\t//default zoom level for move to location\n\t\tminLength: 1,\t\t\t\t\t//minimal text length for autocomplete\n\t\tinitial: true,\t\t\t\t\t//search elements only by initial text\n\t\tcasesensitive: false,\t\t\t//search elements in case sensitive text\n\t\tautoType: true,\t\t\t\t\t//complete input with first suggested result and select this filled-in text.\n\t\tdelayType: 400,\t\t\t\t\t//delay while typing for show tooltip\n\t\ttooltipLimit: -1,\t\t\t\t//limit max results to show in tooltip. -1 for no limit, 0 for no results\n\t\ttipAutoSubmit: true,\t\t\t//auto map panTo when click on tooltip\n\t\tfirstTipSubmit: false,\t\t\t//auto select first result con enter click\n\t\tautoResize: true,\t\t\t\t//autoresize on input change\n\t\tcollapsed: true,\t\t\t\t//collapse search control at startup\n\t\tautoCollapse: false,\t\t\t//collapse search control after submit(on button or on tips if enabled tipAutoSubmit)\n\t\tautoCollapseTime: 1200,\t\t\t//delay for autoclosing alert and collapse after blur\n\t\ttextErr: 'Location not found',\t//error message\n\t\ttextCancel: 'Cancel',\t\t //title in cancel button\t\t\n\t\ttextPlaceholder: 'Search...', //placeholder value\t\t\t\n\t\thideMarkerOnCollapse: false, //remove circle and marker on search control collapsed\t\t\n\t\tposition: 'topleft',\t\t\n\t\tmarker: {\t\t\t\t\t\t//custom L.Marker or false for hide\n\t\t\ticon: false,\t\t\t\t//custom L.Icon for maker location or false for hide\n\t\t\tanimate: true,\t\t\t\t//animate a circle over location found\n\t\t\tcircle: {\t\t\t\t\t//draw a circle in location found\n\t\t\t\tradius: 10,\n\t\t\t\tweight: 3,\n\t\t\t\tcolor: '#e03',\n\t\t\t\tstroke: true,\n\t\t\t\tfill: false\n\t\t\t}\n\t\t}\n\t},\n\n\t_getPath: function(obj, prop) {\n\t\tvar parts = prop.split('.'),\n\t\t\tlast = parts.pop(),\n\t\t\tlen = parts.length,\n\t\t\tcur = parts[0],\n\t\t\ti = 1;\n\n\t\tif(len > 0)\n\t\t\twhile((obj = obj[cur]) && i < len)\n\t\t\t\tcur = parts[i++];\n\n\t\tif(obj)\n\t\t\treturn obj[last];\n\t},\n\n\t_isObject: function(obj) {\n\t\treturn Object.prototype.toString.call(obj) === \"[object Object]\";\n\t},\n\n\tinitialize: function(options) {\n\t\tL.Util.setOptions(this, options || {});\n\t\tthis._inputMinSize = this.options.textPlaceholder ? this.options.textPlaceholder.length : 10;\n\t\tthis._layer = this.options.layer || new L.LayerGroup();\n\t\tthis._filterData = this.options.filterData || this._defaultFilterData;\n\t\tthis._formatData = this.options.formatData || this._defaultFormatData;\n\t\tthis._moveToLocation = this.options.moveToLocation || this._defaultMoveToLocation;\n\t\tthis._autoTypeTmp = this.options.autoType;\t//useful for disable autoType temporarily in delete/backspace keydown\n\t\tthis._countertips = 0;\t\t//number of tips items\n\t\tthis._recordsCache = {};\t//key,value table! to store locations! format: key,latlng\n\t\tthis._curReq = null;\n\t},\n\n\tonAdd: function (map) {\n\t\tthis._map = map;\n\t\tthis._container = L.DomUtil.create('div', 'leaflet-control-search');\n\t\tthis._input = this._createInput(this.options.textPlaceholder, 'search-input');\n\t\tthis._tooltip = this._createTooltip('search-tooltip');\n\t\tthis._cancel = this._createCancel(this.options.textCancel, 'search-cancel');\n\t\tthis._button = this._createButton(this.options.textPlaceholder, 'search-button');\n\t\tthis._alert = this._createAlert('search-alert');\n\n\t\tif(this.options.collapsed===false)\n\t\t\tthis.expand(this.options.collapsed);\n\n\t\tif(this.options.marker) {\n\t\t\t\n\t\t\tif(this.options.marker instanceof L.Marker || this.options.marker instanceof L.CircleMarker)\n\t\t\t\tthis._markerSearch = this.options.marker;\n\n\t\t\telse if(this._isObject(this.options.marker))\n\t\t\t\tthis._markerSearch = new L.Control.Search.Marker([0,0], this.options.marker);\n\n\t\t\tthis._markerSearch._isMarkerSearch = true;\n\t\t}\n\n\t\tthis.setLayer( this._layer );\n\n\t\tmap.on({\n\t\t\t// \t\t'layeradd': this._onLayerAddRemove,\n\t\t\t// \t\t'layerremove': this._onLayerAddRemove\n\t\t\t'resize': this._handleAutoresize\n\t\t\t}, this);\n\t\treturn this._container;\n\t},\n\taddTo: function (map) {\n\n\t\tif(this.options.container) {\n\t\t\tthis._container = this.onAdd(map);\n\t\t\tthis._wrapper = L.DomUtil.get(this.options.container);\n\t\t\tthis._wrapper.style.position = 'relative';\n\t\t\tthis._wrapper.appendChild(this._container);\n\t\t}\n\t\telse\n\t\t\tL.Control.prototype.addTo.call(this, map);\n\n\t\treturn this;\n\t},\n\n\tonRemove: function(map) {\n\t\tthis._recordsCache = {};\n\t\t// map.off({\n\t\t// \t\t'layeradd': this._onLayerAddRemove,\n\t\t// \t\t'layerremove': this._onLayerAddRemove\n\t\t// \t}, this);\n\t\tmap.off({\n\t\t\t// \t\t'layeradd': this._onLayerAddRemove,\n\t\t\t// \t\t'layerremove': this._onLayerAddRemove\n\t\t\t'resize': this._handleAutoresize\n\t\t\t}, this);\n\t},\n\n\t// _onLayerAddRemove: function(e) {\n\t// \t//without this, run setLayer also for each Markers!! to optimize!\n\t// \tif(e.layer instanceof L.LayerGroup)\n\t// \t\tif( L.stamp(e.layer) != L.stamp(this._layer) )\n\t// \t\t\tthis.setLayer(e.layer);\n\t// },\n\n\tsetLayer: function(layer) {\t//set search layer at runtime\n\t\t//this.options.layer = layer; //setting this, run only this._recordsFromLayer()\n\t\tthis._layer = layer;\n\t\tthis._layer.addTo(this._map);\n\t\treturn this;\n\t},\n\t\n\tshowAlert: function(text) {\n\t\tvar self = this;\n\t\ttext = text || this.options.textErr;\n\t\tthis._alert.style.display = 'block';\n\t\tthis._alert.innerHTML = text;\n\t\tclearTimeout(this.timerAlert);\n\t\t\n\t\tthis.timerAlert = setTimeout(function() {\n\t\t\tself.hideAlert();\n\t\t},this.options.autoCollapseTime);\n\t\treturn this;\n\t},\n\t\n\thideAlert: function() {\n\t\tthis._alert.style.display = 'none';\n\t\treturn this;\n\t},\n\t\t\n\tcancel: function() {\n\t\tthis._input.value = '';\n\t\tthis._handleKeypress({ keyCode: 8 });//simulate backspace keypress\n\t\tthis._input.size = this._inputMinSize;\n\t\tthis._input.focus();\n\t\tthis._cancel.style.display = 'none';\n\t\tthis._hideTooltip();\n\t\tthis.fire('search:cancel');\n\t\treturn this;\n\t},\n\t\n\texpand: function(toggle) {\n\t\ttoggle = typeof toggle === 'boolean' ? toggle : true;\n\t\tthis._input.style.display = 'block';\n\t\tL.DomUtil.addClass(this._container, 'search-exp');\n\t\tif ( toggle !== false ) {\n\t\t\tthis._input.focus();\n\t\t\tthis._map.on('dragstart click', this.collapse, this);\n\t\t}\n\t\tthis.fire('search:expanded');\n\t\treturn this;\t\n\t},\n\n\tcollapse: function() {\n\t\tthis._hideTooltip();\n\t\tthis.cancel();\n\t\tthis._alert.style.display = 'none';\n\t\tthis._input.blur();\n\t\tif(this.options.collapsed)\n\t\t{\n\t\t\tthis._input.style.display = 'none';\n\t\t\tthis._cancel.style.display = 'none';\t\t\t\n\t\t\tL.DomUtil.removeClass(this._container, 'search-exp');\t\t\n\t\t\tif (this.options.hideMarkerOnCollapse) {\n\t\t\t\tthis._map.removeLayer(this._markerSearch);\n\t\t\t}\n\t\t\tthis._map.off('dragstart click', this.collapse, this);\n\t\t}\n\t\tthis.fire('search:collapsed');\n\t\treturn this;\n\t},\n\t\n\tcollapseDelayed: function() {\t//collapse after delay, used on_input blur\n\t\tvar self = this;\n\t\tif (!this.options.autoCollapse) return this;\n\t\tclearTimeout(this.timerCollapse);\n\t\tthis.timerCollapse = setTimeout(function() {\n\t\t\tself.collapse();\n\t\t}, this.options.autoCollapseTime);\n\t\treturn this;\t\t\n\t},\n\n\tcollapseDelayedStop: function() {\n\t\tclearTimeout(this.timerCollapse);\n\t\treturn this;\t\t\n\t},\n\n\t////start DOM creations\n\t_createAlert: function(className) {\n\t\tvar alert = L.DomUtil.create('div', className, this._container);\n\t\talert.style.display = 'none';\n\n\t\tL.DomEvent\n\t\t\t.on(alert, 'click', L.DomEvent.stop, this)\n\t\t\t.on(alert, 'click', this.hideAlert, this);\n\n\t\treturn alert;\n\t},\n\n\t_createInput: function (text, className) {\n\t\tvar self = this;\n\t\tvar label = L.DomUtil.create('label', className, this._container);\n\t\tvar input = L.DomUtil.create('input', className, this._container);\n\t\tinput.type = 'text';\n\t\tinput.size = this._inputMinSize;\n\t\tinput.value = '';\n\t\tinput.autocomplete = 'off';\n\t\tinput.autocorrect = 'off';\n\t\tinput.autocapitalize = 'off';\n\t\tinput.placeholder = text;\n\t\tinput.style.display = 'none';\n\t\tinput.role = 'search';\n\t\tinput.id = input.role + input.type + input.size;\n\t\t\n\t\tlabel.htmlFor = input.id;\n\t\tlabel.style.display = 'none';\n\t\tlabel.value = text;\n\n\t\tL.DomEvent\n\t\t\t.disableClickPropagation(input)\n\t\t\t.on(input, 'keyup', this._handleKeypress, this)\n\t\t\t.on(input, 'paste', function(e) {\n\t\t\t\tsetTimeout(function(e) {\n\t\t\t\t\tself._handleKeypress(e);\n\t\t\t\t},10,e);\n\t\t\t}, this)\n\t\t\t.on(input, 'blur', this.collapseDelayed, this)\n\t\t\t.on(input, 'focus', this.collapseDelayedStop, this);\n\t\t\n\t\treturn input;\n\t},\n\n\t_createCancel: function (title, className) {\n\t\tvar cancel = L.DomUtil.create('a', className, this._container);\n\t\tcancel.href = '#';\n\t\tcancel.title = title;\n\t\tcancel.style.display = 'none';\n\t\tcancel.innerHTML = \"\";//imageless(see css)\n\n\t\tL.DomEvent\n\t\t\t.on(cancel, 'click', L.DomEvent.stop, this)\n\t\t\t.on(cancel, 'click', this.cancel, this);\n\n\t\treturn cancel;\n\t},\n\t\n\t_createButton: function (title, className) {\n\t\tvar button = L.DomUtil.create('a', className, this._container);\n\t\tbutton.href = '#';\n\t\tbutton.title = title;\n\n\t\tL.DomEvent\n\t\t\t.on(button, 'click', L.DomEvent.stop, this)\n\t\t\t.on(button, 'click', this._handleSubmit, this)\t\t\t\n\t\t\t.on(button, 'focus', this.collapseDelayedStop, this)\n\t\t\t.on(button, 'blur', this.collapseDelayed, this);\n\n\t\treturn button;\n\t},\n\n\t_createTooltip: function(className) {\n\t\tvar self = this;\t\t\n\t\tvar tool = L.DomUtil.create('ul', className, this._container);\n\t\ttool.style.display = 'none';\n\t\tL.DomEvent\n\t\t\t.disableClickPropagation(tool)\n\t\t\t.on(tool, 'blur', this.collapseDelayed, this)\n\t\t\t.on(tool, 'mousewheel', function(e) {\n\t\t\t\tself.collapseDelayedStop();\n\t\t\t\tL.DomEvent.stopPropagation(e);//disable zoom map\n\t\t\t}, this)\n\t\t\t.on(tool, 'mouseover', function(e) {\n\t\t\t\tself.collapseDelayedStop();\n\t\t\t}, this);\n\t\treturn tool;\n\t},\n\n\t_createTip: function(text, val) {//val is object in recordCache, usually is Latlng\n\t\tvar tip;\n\t\t\n\t\tif(this.options.buildTip)\n\t\t{\n\t\t\ttip = this.options.buildTip.call(this, text, val); //custom tip node or html string\n\t\t\tif(typeof tip === 'string')\n\t\t\t{\n\t\t\t\tvar tmpNode = L.DomUtil.create('div');\n\t\t\t\ttmpNode.innerHTML = tip;\n\t\t\t\ttip = tmpNode.firstChild;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttip = L.DomUtil.create('li', '');\n\t\t\ttip.innerHTML = text;\n\t\t}\n\t\t\n\t\tL.DomUtil.addClass(tip, 'search-tip');\n\t\ttip._text = text; //value replaced in this._input and used by _autoType\n\n\t\tif(this.options.tipAutoSubmit)\n\t\t\tL.DomEvent\n\t\t\t\t.disableClickPropagation(tip)\t\t\n\t\t\t\t.on(tip, 'click', L.DomEvent.stop, this)\n\t\t\t\t.on(tip, 'click', function(e) {\n\t\t\t\t\tthis._input.value = text;\n\t\t\t\t\tthis._handleAutoresize();\n\t\t\t\t\tthis._input.focus();\n\t\t\t\t\tthis._hideTooltip();\t\n\t\t\t\t\tthis._handleSubmit();\n\t\t\t\t}, this);\n\n\t\treturn tip;\n\t},\n\n\t//////end DOM creations\n\n\t_getUrl: function(text) {\n\t\treturn (typeof this.options.url === 'function') ? this.options.url(text) : this.options.url;\n\t},\n\n\t_defaultFilterData: function(text, records) {\n\t\n\t\tvar I, icase, regSearch, frecords = {};\n\n\t\ttext = text.replace(/[.*+?^${}()|[\\]\\\\]/g, ''); //sanitize remove all special characters\n\t\tif(text==='')\n\t\t\treturn [];\n\n\t\tI = this.options.initial ? '^' : ''; //search only initial text\n\t\ticase = !this.options.casesensitive ? 'i' : undefined;\n\n\t\tregSearch = new RegExp(I + text, icase);\n\n\t\t//TODO use .filter or .map\n\t\tfor(var key in records) {\n\t\t\tif( regSearch.test(key) )\n\t\t\t\tfrecords[key]= records[key];\n\t\t}\n\t\t\n\t\treturn frecords;\n\t},\n\n\tshowTooltip: function(records) {\n\t\t\n\n\t\tthis._countertips = 0;\n\t\tthis._tooltip.innerHTML = '';\n\t\tthis._tooltip.currentSelection = -1; //inizialized for _handleArrowSelect()\n\n\t\tif(this.options.tooltipLimit)\n\t\t{\n\t\t\tfor(var key in records)//fill tooltip\n\t\t\t{\n\t\t\t\tif(this._countertips === this.options.tooltipLimit)\n\t\t\t\t\tbreak;\n\t\t\t\t\n\t\t\t\tthis._countertips++;\n\n\t\t\t\tthis._tooltip.appendChild( this._createTip(key, records[key]) );\n\t\t\t}\n\t\t}\n\t\t\n\t\tif(this._countertips > 0)\n\t\t{\n\t\t\tthis._tooltip.style.display = 'block';\n\t\t\t\n\t\t\tif(this._autoTypeTmp)\n\t\t\t\tthis._autoType();\n\n\t\t\tthis._autoTypeTmp = this.options.autoType;//reset default value\n\t\t}\n\t\telse\n\t\t\tthis._hideTooltip();\n\n\t\tthis._tooltip.scrollTop = 0;\n\n\t\treturn this._countertips;\n\t},\n\n\t_hideTooltip: function() {\n\t\tthis._tooltip.style.display = 'none';\n\t\tthis._tooltip.innerHTML = '';\n\t\treturn 0;\n\t},\n\n\t_defaultFormatData: function(json) {\t//default callback for format data to indexed data\n\t\tvar self = this,\n\t\t\tpropName = this.options.propertyName,\n\t\t\tpropLoc = this.options.propertyLoc,\n\t\t\ti, jsonret = {};\n\n\t\tif( L.Util.isArray(propLoc) )\n\t\t\tfor(i in json)\n\t\t\t\tjsonret[ self._getPath(json[i],propName) ]= L.latLng( json[i][ propLoc[0] ], json[i][ propLoc[1] ] );\n\t\telse\n\t\t\tfor(i in json)\n\t\t\t\tjsonret[ self._getPath(json[i],propName) ]= L.latLng( self._getPath(json[i],propLoc) );\n\t\t//TODO throw new Error(\"propertyName '\"+propName+\"' not found in JSON data\");\n\t\treturn jsonret;\n\t},\n\n\t_recordsFromJsonp: function(text, callAfter) { //extract searched records from remote jsonp service\n\t\tL.Control.Search.callJsonp = callAfter;\n\t\tvar script = L.DomUtil.create('script','leaflet-search-jsonp', document.getElementsByTagName('body')[0] ),\t\t\t\n\t\t\turl = L.Util.template(this._getUrl(text)+'&'+this.options.jsonpParam+'=L.Control.Search.callJsonp', {s: text}); //parsing url\n\t\t\t//rnd = '&_='+Math.floor(Math.random()*10000);\n\t\t\t//TODO add rnd param or randomize callback name! in recordsFromJsonp\n\t\tscript.type = 'text/javascript';\n\t\tscript.src = url;\n\t\treturn { abort: function() { script.parentNode.removeChild(script); } };\n\t},\n\n\t_recordsFromAjax: function(text, callAfter) {\t//Ajax request\n\t\tif (window.XMLHttpRequest === undefined) {\n\t\t\twindow.XMLHttpRequest = function() {\n\t\t\t\ttry { return new ActiveXObject(\"Microsoft.XMLHTTP.6.0\"); }\n\t\t\t\tcatch (e1) {\n\t\t\t\t\ttry { return new ActiveXObject(\"Microsoft.XMLHTTP.3.0\"); }\n\t\t\t\t\tcatch (e2) { throw new Error(\"XMLHttpRequest is not supported\"); }\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tvar IE8or9 = ( L.Browser.ie && !window.atob && document.querySelector ),\n\t\t\trequest = IE8or9 ? new XDomainRequest() : new XMLHttpRequest(),\n\t\t\turl = L.Util.template(this._getUrl(text), {s: text});\n\n\t\t//rnd = '&_='+Math.floor(Math.random()*10000);\n\t\t//TODO add rnd param or randomize callback name! in recordsFromAjax\t\t\t\n\t\t\n\t\trequest.open(\"GET\", url);\n\t\t\n\n\t\trequest.onload = function() {\n\t\t\tcallAfter( JSON.parse(request.responseText) );\n\t\t};\n\t\trequest.onreadystatechange = function() {\n\t\t if(request.readyState === 4 && request.status === 200) {\n\t\t \tthis.onload();\n\t\t }\n\t\t};\n\n\t\trequest.send();\n\t\treturn request; \n\t},\n\n _searchInLayer: function(layer, retRecords, propName) {\n var self = this, loc;\n\n if(layer instanceof L.Control.Search.Marker) return;\n\n if(layer instanceof L.Marker || layer instanceof L.CircleMarker)\n {\n if(self._getPath(layer.options,propName))\n {\n loc = layer.getLatLng();\n loc.layer = layer;\n retRecords[ self._getPath(layer.options,propName) ] = loc;\n }\n else if(self._getPath(layer.feature.properties,propName))\n {\n loc = layer.getLatLng();\n loc.layer = layer;\n retRecords[ self._getPath(layer.feature.properties,propName) ] = loc;\n }\n else {\n //throw new Error(\"propertyName '\"+propName+\"' not found in marker\"); \n \n }\n }\n else if(layer instanceof L.Path || layer instanceof L.Polyline || layer instanceof L.Polygon)\n {\n if(self._getPath(layer.options,propName))\n {\n loc = layer.getBounds().getCenter();\n loc.layer = layer;\n retRecords[ self._getPath(layer.options,propName) ] = loc;\n }\n else if(self._getPath(layer.feature.properties,propName))\n {\n loc = layer.getBounds().getCenter();\n loc.layer = layer;\n retRecords[ self._getPath(layer.feature.properties,propName) ] = loc;\n }\n else {\n //throw new Error(\"propertyName '\"+propName+\"' not found in shape\"); \n \n }\n }\n else if(layer.hasOwnProperty('feature'))//GeoJSON\n {\n if(layer.feature.properties.hasOwnProperty(propName))\n {\n if(layer.getLatLng && typeof layer.getLatLng === 'function') {\n loc = layer.getLatLng();\n loc.layer = layer;\t\t\t\n retRecords[ layer.feature.properties[propName] ] = loc;\n } else if(layer.getBounds && typeof layer.getBounds === 'function') {\n loc = layer.getBounds().getCenter();\n loc.layer = layer;\t\t\t\n retRecords[ layer.feature.properties[propName] ] = loc;\n } else {\n \n }\n }\n else {\n //throw new Error(\"propertyName '\"+propName+\"' not found in feature\");\n \n }\n }\n else if(layer instanceof L.LayerGroup)\n {\n layer.eachLayer(function (layer) {\n self._searchInLayer(layer, retRecords, propName);\n });\n }\n },\n\t\n\t_recordsFromLayer: function() {\t//return table: key,value from layer\n\t\tvar self = this,\n\t\t\tretRecords = {},\n\t\t\tpropName = this.options.propertyName;\n\t\t\n\t\tthis._layer.eachLayer(function (layer) {\n\t\t\tself._searchInLayer(layer, retRecords, propName);\n\t\t});\n\t\t\n\t\treturn retRecords;\n\t},\n\t\n\t_autoType: function() {\n\t\t\n\t\t//TODO implements autype without selection(useful for mobile device)\n\t\t\n\t\tvar start = this._input.value.length,\n\t\t\tfirstRecord = this._tooltip.firstChild ? this._tooltip.firstChild._text : '',\n\t\t\tend = firstRecord.length;\n\n\t\tif (firstRecord.indexOf(this._input.value) === 0) { // If prefix match\n\t\t\tthis._input.value = firstRecord;\n\t\t\tthis._handleAutoresize();\n\n\t\t\tif (this._input.createTextRange) {\n\t\t\t\tvar selRange = this._input.createTextRange();\n\t\t\t\tselRange.collapse(true);\n\t\t\t\tselRange.moveStart('character', start);\n\t\t\t\tselRange.moveEnd('character', end);\n\t\t\t\tselRange.select();\n\t\t\t}\n\t\t\telse if(this._input.setSelectionRange) {\n\t\t\t\tthis._input.setSelectionRange(start, end);\n\t\t\t}\n\t\t\telse if(this._input.selectionStart) {\n\t\t\t\tthis._input.selectionStart = start;\n\t\t\t\tthis._input.selectionEnd = end;\n\t\t\t}\n\t\t}\n\t},\n\n\t_hideAutoType: function() {\t// deselect text:\n\n\t\tvar sel;\n\t\tif ((sel = this._input.selection) && sel.empty) {\n\t\t\tsel.empty();\n\t\t}\n\t\telse if (this._input.createTextRange) {\n\t\t\tsel = this._input.createTextRange();\n\t\t\tsel.collapse(true);\n\t\t\tvar end = this._input.value.length;\n\t\t\tsel.moveStart('character', end);\n\t\t\tsel.moveEnd('character', end);\n\t\t\tsel.select();\n\t\t}\n\t\telse {\n\t\t\tif (this._input.getSelection) {\n\t\t\t\tthis._input.getSelection().removeAllRanges();\n\t\t\t}\n\t\t\tthis._input.selectionStart = this._input.selectionEnd;\n\t\t}\n\t},\n\t\n\t_handleKeypress: function (e) {\t//run _input keyup event\n\t\tvar self = this;\n\n\t\tswitch(e.keyCode)\n\t\t{\n\t\t\tcase 27://Esc\n\t\t\t\tthis.collapse();\n\t\t\tbreak;\n\t\t\tcase 13://Enter\n\t\t\t\tif(this._countertips == 1 || (this.options.firstTipSubmit && this._countertips > 0)) {\n \t\t\tif(this._tooltip.currentSelection == -1) {\n\t\t\t\t\t\tthis._handleArrowSelect(1);\n \t\t\t}\n\t\t\t\t}\n\t\t\t\tthis._handleSubmit();\t//do search\n\t\t\tbreak;\n\t\t\tcase 38://Up\n\t\t\t\tthis._handleArrowSelect(-1);\n\t\t\tbreak;\n\t\t\tcase 40://Down\n\t\t\t\tthis._handleArrowSelect(1);\n\t\t\tbreak;\n\t\t\tcase 8://Backspace\n\t\t\tcase 45://Insert\n\t\t\tcase 46://Delete\n\t\t\t\tthis._autoTypeTmp = false;//disable temporarily autoType\n\t\t\tbreak;\n\t\t\tcase 37://Left\n\t\t\tcase 39://Right\n\t\t\tcase 16://Shift\n\t\t\tcase 17://Ctrl\n\t\t\tcase 35://End\n\t\t\tcase 36://Home\n\t\t\tbreak;\n\t\t\tdefault://All keys\n\t\t\t\tif(this._input.value.length)\n\t\t\t\t\tthis._cancel.style.display = 'block';\n\t\t\t\telse\n\t\t\t\t\tthis._cancel.style.display = 'none';\n\n\t\t\t\tif(this._input.value.length >= this.options.minLength)\n\t\t\t\t{\n\t\t\t\t\tclearTimeout(this.timerKeypress);\t//cancel last search request while type in\t\t\t\t\n\t\t\t\t\tthis.timerKeypress = setTimeout(function() {\t//delay before request, for limit jsonp/ajax request\n\n\t\t\t\t\t\tself._fillRecordsCache();\n\t\t\t\t\t\n\t\t\t\t\t}, this.options.delayType);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tthis._hideTooltip();\n\t\t}\n\n\t\tthis._handleAutoresize();\n\t},\n\n\tsearchText: function(text) {\n\t\tvar code = text.charCodeAt(text.length);\n\n\t\tthis._input.value = text;\n\n\t\tthis._input.style.display = 'block';\n\t\tL.DomUtil.addClass(this._container, 'search-exp');\n\n\t\tthis._autoTypeTmp = false;\n\n\t\tthis._handleKeypress({keyCode: code});\n\t},\n\t\n\t_fillRecordsCache: function() {\n\n\t\tvar self = this,\n\t\t\tinputText = this._input.value, records;\n\n\t\tif(this._curReq && this._curReq.abort)\n\t\t\tthis._curReq.abort();\n\t\t//abort previous requests\n\n\t\tL.DomUtil.addClass(this._container, 'search-load');\t\n\n\t\tif(this._layer)\n\t\t{\n\t\t\t//TODO _recordsFromLayer must return array of objects, formatted from _formatData\n\t\t\tthis._recordsCache = this._recordsFromLayer();\n\t\t\t\n\t\t\trecords = this._filterData( this._input.value, this._recordsCache );\n\n\t\t\tthis.showTooltip( records );\n\n\t\t\tL.DomUtil.removeClass(this._container, 'search-load');\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif(this.options.sourceData)\n\t\t\t\tthis._retrieveData = this.options.sourceData;\n\n\t\t\telse if(this.options.url)\t//jsonp or ajax\n\t\t\t\tthis._retrieveData = this.options.jsonpParam ? this._recordsFromJsonp : this._recordsFromAjax;\n\n\t\t\tthis._curReq = this._retrieveData.call(this, inputText, function(data) {\n\t\t\t\t\n\t\t\t\tself._recordsCache = self._formatData.call(self, data);\n\n\t\t\t\t//TODO refact!\n\t\t\t\tif(self.options.sourceData)\n\t\t\t\t\trecords = self._filterData( self._input.value, self._recordsCache );\n\t\t\t\telse\n\t\t\t\t\trecords = self._recordsCache;\n\n\t\t\t\tself.showTooltip( records );\n \n\t\t\t\tL.DomUtil.removeClass(self._container, 'search-load');\n\t\t\t});\n\t\t}\n\t},\n\t\n\t_handleAutoresize: function() {\n\t var maxWidth;\n\n\t\tif (this._input.style.maxWidth !== this._map._container.offsetWidth) {\n\t\t\tmaxWidth = this._map._container.clientWidth;\n\n\t\t\t// other side margin + padding + width border + width search-button + width search-cancel\n\t\t\tmaxWidth -= 10 + 20 + 1 + 30 + 22; \n\n\t\t\tthis._input.style.maxWidth = maxWidth.toString() + 'px';\n\t\t}\n\n\t\tif (this.options.autoResize && (this._container.offsetWidth + 20 < this._map._container.offsetWidth)) {\n\t\t\tthis._input.size = this._input.value.length < this._inputMinSize ? this._inputMinSize : this._input.value.length;\n\t\t}\n\t},\n\n\t_handleArrowSelect: function(velocity) {\n\t\n\t\tvar searchTips = this._tooltip.hasChildNodes() ? this._tooltip.childNodes : [];\n\t\t\t\n\t\tfor (i=0; i= (searchTips.length - 1))) {// If at end of list.\n\t\t\tL.DomUtil.addClass(searchTips[this._tooltip.currentSelection], 'search-tip-select');\n\t\t}\n\t\telse if ((velocity == -1 ) && (this._tooltip.currentSelection <= 0)) { // Going back up to the search box.\n\t\t\tthis._tooltip.currentSelection = -1;\n\t\t}\n\t\telse if (this._tooltip.style.display != 'none') {\n\t\t\tthis._tooltip.currentSelection += velocity;\n\t\t\t\n\t\t\tL.DomUtil.addClass(searchTips[this._tooltip.currentSelection], 'search-tip-select');\n\t\t\t\n\t\t\tthis._input.value = searchTips[this._tooltip.currentSelection]._text;\n\n\t\t\t// scroll:\n\t\t\tvar tipOffsetTop = searchTips[this._tooltip.currentSelection].offsetTop;\n\t\t\t\n\t\t\tif (tipOffsetTop + searchTips[this._tooltip.currentSelection].clientHeight >= this._tooltip.scrollTop + this._tooltip.clientHeight) {\n\t\t\t\tthis._tooltip.scrollTop = tipOffsetTop - this._tooltip.clientHeight + searchTips[this._tooltip.currentSelection].clientHeight;\n\t\t\t}\n\t\t\telse if (tipOffsetTop <= this._tooltip.scrollTop) {\n\t\t\t\tthis._tooltip.scrollTop = tipOffsetTop;\n\t\t\t}\n\t\t}\n\t},\n\n\t_handleSubmit: function() {\t//button and tooltip click and enter submit\n\n\t\tthis._hideAutoType();\n\t\t\n\t\tthis.hideAlert();\n\t\tthis._hideTooltip();\n\n\t\tif(this._input.style.display == 'none')\t//on first click show _input only\n\t\t\tthis.expand();\n\t\telse\n\t\t{\n\t\t\tif(this._input.value === '')\t//hide _input only\n\t\t\t\tthis.collapse();\n\t\t\telse\n\t\t\t{\n\t\t\t\tvar loc = this._getLocation(this._input.value);\n\t\t\t\t\n\t\t\t\tif(loc===false)\n\t\t\t\t\tthis.showAlert();\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis.showLocation(loc, this._input.value);\n\t\t\t\t\tthis.fire('search:locationfound', {\n\t\t\t\t\t\t\tlatlng: loc,\n\t\t\t\t\t\t\ttext: this._input.value,\n\t\t\t\t\t\t\tlayer: loc.layer ? loc.layer : null\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t_getLocation: function(key) {\t//extract latlng from _recordsCache\n\n\t\tif( this._recordsCache.hasOwnProperty(key) )\n\t\t\treturn this._recordsCache[key];//then after use .loc attribute\n\t\telse\n\t\t\treturn false;\n\t},\n\n\t_defaultMoveToLocation: function(latlng, title, map) {\n\t\tif(this.options.zoom)\n \t\t\tthis._map.setView(latlng, this.options.zoom);\n \t\telse\n\t\t\tthis._map.panTo(latlng);\n\t},\n\n\tshowLocation: function(latlng, title) {\t//set location on map from _recordsCache\n\t\tvar self = this;\n\n\t\tself._map.once('moveend zoomend', function(e) {\n\n\t\t\tif(self._markerSearch) {\n\t\t\t\tself._markerSearch.addTo(self._map).setLatLng(latlng);\n\t\t\t}\n\t\t\t\n\t\t});\n\n\t\tself._moveToLocation(latlng, title, self._map);\n\t\t//FIXME autoCollapse option hide self._markerSearch before visualized!!\n\t\tif(self.options.autoCollapse)\n\t\t\tself.collapse();\n\n\t\treturn self;\n\t}\n});\n\nL.Control.Search.Marker = L.Marker.extend({\n\n\tincludes: L.version[0]==='1' ? L.Evented.prototype : L.Mixin.Events,\n\t\n\toptions: {\n\t\ticon: new L.Icon.Default(),\n\t\tanimate: true,\n\t\tcircle: {\n\t\t\tradius: 10,\n\t\t\tweight: 3,\n\t\t\tcolor: '#e03',\n\t\t\tstroke: true,\n\t\t\tfill: false\n\t\t}\n\t},\n\t\n\tinitialize: function (latlng, options) {\n\t\tL.setOptions(this, options);\n\n\t\tif(options.icon === true)\n\t\t\toptions.icon = new L.Icon.Default();\n\n\t\tL.Marker.prototype.initialize.call(this, latlng, options);\n\t\t\n\t\tif( L.Control.Search.prototype._isObject(this.options.circle) )\n\t\t\tthis._circleLoc = new L.CircleMarker(latlng, this.options.circle);\n\t},\n\n\tonAdd: function (map) {\n\t\tL.Marker.prototype.onAdd.call(this, map);\n\t\tif(this._circleLoc) {\n\t\t\tmap.addLayer(this._circleLoc);\n\t\t\tif(this.options.animate)\n\t\t\t\tthis.animate();\n\t\t}\n\t},\n\n\tonRemove: function (map) {\n\t\tL.Marker.prototype.onRemove.call(this, map);\n\t\tif(this._circleLoc)\n\t\t\tmap.removeLayer(this._circleLoc);\n\t},\n\t\n\tsetLatLng: function (latlng) {\n\t\tL.Marker.prototype.setLatLng.call(this, latlng);\n\t\tif(this._circleLoc)\n\t\t\tthis._circleLoc.setLatLng(latlng);\n\t\treturn this;\n\t},\n\t\n\t_initIcon: function () {\n\t\tif(this.options.icon)\n\t\t\tL.Marker.prototype._initIcon.call(this);\n\t},\n\n\t_removeIcon: function () {\n\t\tif(this.options.icon)\n\t\t\tL.Marker.prototype._removeIcon.call(this);\n\t},\n\n\tanimate: function() {\n\t//TODO refact animate() more smooth! like this: http://goo.gl/DDlRs\n\t\tif(this._circleLoc)\n\t\t{\n\t\t\tvar circle = this._circleLoc,\n\t\t\t\ttInt = 200,\t//time interval\n\t\t\t\tss = 5,\t//frames\n\t\t\t\tmr = parseInt(circle._radius/ss),\n\t\t\t\toldrad = this.options.circle.radius,\n\t\t\t\tnewrad = circle._radius * 2,\n\t\t\t\tacc = 0;\n\n\t\t\tcircle._timerAnimLoc = setInterval(function() {\n\t\t\t\tacc += 0.5;\n\t\t\t\tmr += acc;\t//adding acceleration\n\t\t\t\tnewrad -= mr;\n\t\t\t\t\n\t\t\t\tcircle.setRadius(newrad);\n\n\t\t\t\tif(newrad'+t+\"\"}).then(function(t){return''+t+\"\"}).then(function(t){return\"data:image/svg+xml;charset=utf-8,\"+t})}var h=function(){function t(){var t=\"application/font-woff\",e=\"image/jpeg\";return{woff:t,woff2:t,ttf:\"application/font-truetype\",eot:\"application/vnd.ms-fontobject\",png:\"image/png\",jpg:e,jpeg:e,gif:\"image/gif\",tiff:\"image/tiff\",svg:\"image/svg+xml\"}}function e(t){var e=/\\.([^\\.\\/]*?)$/g.exec(t);return e?e[1]:\"\"}function n(n){var i=e(n).toLowerCase();return t()[i]||\"\"}function i(t){return-1!==t.search(/^(data:)/)}function o(t){return new Promise(function(e){for(var n=window.atob(t.toDataURL().split(\",\")[1]),i=n.length,o=new Uint8Array(i),r=0;r0&&this._toggleClasses(this.options.hideClasses);var n=\"string\"!=typeof t?t.target.className:t;if(\"CurrentSize\"===n)return this._printOpertion(n);this.outerContainer=this._createOuterContainer(this.mapContainer),this.originalState.widthWasAuto&&(this.outerContainer.style.width=this.originalState.mapWidth),this._createImagePlaceholder(n)},_createImagePlaceholder:function(t){var e=this;n.toPng(this.mapContainer,{width:parseInt(this.originalState.mapWidth.replace(\"px\")),height:parseInt(this.originalState.mapHeight.replace(\"px\"))}).then(function(n){e.blankDiv=document.createElement(\"div\");var i=e.blankDiv;e.outerContainer.parentElement.insertBefore(i,e.outerContainer),i.className=\"epHolder\",i.style.backgroundImage='url(\"'+n+'\")',i.style.position=\"absolute\",i.style.zIndex=1011,i.style.display=\"initial\",i.style.width=e.originalState.mapWidth,i.style.height=e.originalState.mapHeight,e._resizeAndPrintMap(t)}).catch(function(t){console.error(\"oops, something went wrong!\",t)})},_resizeAndPrintMap:function(t){this.outerContainer.style.opacity=0;var e=this.options.sizeModes.filter(function(e){return e.className===t});e=e[0],this.mapContainer.style.width=e.width+\"px\",this.mapContainer.style.height=e.height+\"px\",this.mapContainer.style.width>this.mapContainer.style.height?this.orientation=\"portrait\":this.orientation=\"landscape\",this._map.setView(this.originalState.center),this._map.setZoom(this.originalState.zoom),this._map.invalidateSize(),this.options.tileLayer?this._pausePrint(t):this._printOpertion(t)},_pausePrint:function(t){var e=this,n=setInterval(function(){e.options.tileLayer.isLoading()||(clearInterval(n),e._printOpertion(t))},e.options.tileWait)},_printOpertion:function(t){var e=this,o=this.mapContainer.style.width;(this.originalState.widthWasAuto&&\"CurrentSize\"===t||this.originalState.widthWasPercentage&&\"CurrentSize\"===t)&&(o=this.originalState.mapWidth),n.toPng(e.mapContainer,{width:parseInt(o),height:parseInt(e.mapContainer.style.height.replace(\"px\"))}).then(function(t){var n=e._dataURItoBlob(t);e.options.exportOnly?i.saveAs(n,e.options.filename+\".png\"):e._sendToBrowserPrint(t,e.orientation),e._toggleControls(!0),e._toggleClasses(e.options.hideClasses,!0),e.outerContainer&&(e.originalState.widthWasAuto?e.mapContainer.style.width=\"auto\":e.originalState.widthWasPercentage?e.mapContainer.style.width=e.originalState.percentageWidth:e.mapContainer.style.width=e.originalState.mapWidth,e.mapContainer.style.height=e.originalState.mapHeight,e._removeOuterContainer(e.mapContainer,e.outerContainer,e.blankDiv),e._map.invalidateSize(),e._map.setView(e.originalState.center),e._map.setZoom(e.originalState.zoom)),e._map.fire(\"easyPrint-finished\")}).catch(function(t){console.error(\"Print operation failed\",t)})},_sendToBrowserPrint:function(t,e){this._page.resizeTo(600,800);var n=this._createNewWindow(t,e,this);this._page.document.body.innerHTML=\"\",this._page.document.write(n),this._page.document.close()},_createSpinner:function(t,e,n){return\"\"+t+\"\\n
Loading...
'},_createNewWindow:function(t,e,n){return\"\\n \\n