diff --git a/Files/LICENSE b/Files/LICENSE
new file mode 100644
index 0000000..a1214f8
--- /dev/null
+++ b/Files/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Muyang Ye
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Files/OSMTools.js b/Files/OSMTools.js
new file mode 100644
index 0000000..5d4303d
--- /dev/null
+++ b/Files/OSMTools.js
@@ -0,0 +1,1033 @@
+if ( typeof at == "undefined" ) { at=new Object(); }
+if ( typeof at.quelltextlich == "undefined" ) { at.quelltextlich=new Object(); }
+at.quelltextlich.osm={
+
+
+
+ // Version information of this script
+ VERSION_NUMBER: "0.0.36",
+ RELEASE_DATE: "2022-02-16",
+ HOMEPAGE: "http://osm.quelltextlich.at/",
+ LEGAL_INFO: "http://quelltextlich.at/impressum.html",
+
+
+
+ // Adds the required JavaScript tags to the document head
+ addJavaScript: function ( onLoadFunc )
+ {
+ var jsNode = document.createElement('script');
+ jsNode.setAttribute('type', 'text/javascript');
+ jsNode.setAttribute('src', '//osm.quelltextlich.at/openlayers/2.11/OpenLayers.js');
+ if ( onLoadFunc )
+ {
+ jsNode.onreadystatechange= function () {
+ if ( (this.readyState == 'loaded') || (this.readyState == 'complete') )
+ {
+ onLoadFunc();
+ }
+ }
+ jsNode.onload=onLoadFunc;
+ }
+ document.getElementsByTagName('head')[0].appendChild(jsNode);
+ },
+
+
+
+ // Adds the CSS Stylesheet href to the document head
+ addStyleSheet: function ( href )
+ {
+ var cssNode = document.createElement('link');
+ cssNode.setAttribute('rel', 'stylesheet');
+ cssNode.setAttribute('type', 'text/css');
+ cssNode.setAttribute('href', href);
+ document.getElementsByTagName('head')[0].appendChild(cssNode);
+ },
+
+
+
+ // Yields the given text as valid, escaped xml
+ encodeAsXml: function ( text )
+ {
+ return text.replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ },
+
+
+
+ // Gets the width and height of the viewport
+ getViewPortDimension: function ( )
+ {
+ var width;
+ var height;
+
+ try {
+ width = window.innerWidth;
+ height = window.innerHeight;
+ } catch (e) { ; }
+
+ if ( ( typeof width == "undefined" ) || ( width <= 0 ) )
+ {
+ try {
+ width = document.documentElement.clientWidth;
+ height = document.documentElement.clientHeight;
+ } catch (e) { ; }
+ }
+
+ if ( ( typeof width == "undefined" ) || ( width <= 0 ) )
+ {
+ try {
+ width = document.body.clientHeight;
+ height = document.body.clientHeight;
+ } catch (e) { ; }
+ }
+
+ if ( ( typeof width == "undefined" ) || ( width <= 0 ) )
+ {
+ width = 320;
+ height = 256;
+ }
+ return { width: width, height: height };
+ },
+
+
+
+ // creates a div node with id divId.
+ // If a node with the given id existed beforehand, it is replaced by the newly created one.
+ // Otherwise, the newly created div node is inserted before siblingNode
+ // The created div gets width width and height height (each in Pixels).
+ replaceOrAddDiv: function ( divId, siblingNode, width, height )
+ {
+ var widthIsPercent = ( ( typeof width == "string" ) && ( width.substr( width.length-1, 1 ) == "%" ) );
+ var heightIsPercent = ( ( typeof height == "string" ) && ( height.substr( height.length-1, 1 ) == "%" ) );
+
+ if ( ( widthIsPercent ) || ( heightIsPercent ) )
+ {
+ var viewportDim=at.quelltextlich.osm.getViewPortDimension();
+
+ if ( widthIsPercent )
+ {
+ width = viewportDim.width * width.substr( 0, width.length-1 ) / 100;
+ }
+
+ if ( heightIsPercent )
+ {
+ height =viewportDim.height * height.substr( 0, height.length-1 ) / 100;
+ }
+ }
+
+ width = Math.max( width, 170 );
+ height = Math.max( height, 170 );
+
+ var divNode = document.createElement('div');
+ divNode.setAttribute('id', divId );
+ divNode.setAttribute('style', 'width: '+width+'px; height: '+height+'px;');
+
+ var nodeToReplace = document.getElementById( divId );
+ if ( nodeToReplace == null )
+ {
+ siblingNode.parentNode.insertBefore( divNode, siblingNode );
+ } else {
+ nodeToReplace.parentNode.replaceChild( divNode, nodeToReplace );
+ }
+
+ return divNode;
+ },
+
+
+
+ // Removes 2nd, 3rd, declaration of the same CSS from
+ deduplicateHeadCss: function()
+ {
+ const headChildren = document.head.children;
+ var foundCSS = {};
+ for (var idx = 0 ; idx < headChildren.length ; idx++) {
+ const headChild = headChildren[idx];
+ if (headChild.tagName == "LINK" && headChild.type == "text/css") {
+ if (headChild.href in foundCSS) {
+ headChild.remove();
+ idx--;
+ } else {
+ foundCSS[headChild.href] = true;
+ }
+ }
+ }
+ },
+
+
+
+ // Maps real world lat, lon to the coordinate system of the map
+ worldToMap: function ( lat, lon )
+ {
+ var lonLat = new OpenLayers.LonLat( lon, lat );
+ lonLat.transform(
+ new OpenLayers.Projection("EPSG:4326"),
+ new OpenLayers.Projection("EPSG:900913") /* This is map.getProjectionObject() */
+ );
+ return lonLat;
+ },
+
+
+
+ // Adds the maps once the OpenLayers JavaScript file is fully loaded
+ getText: function ( label )
+ {
+ var i18n_text=null;
+ try {
+ i18n_text = at.quelltextlich.osm.vars['i18n'][ at.quelltextlich.osm.vars['i18n']['selectedLanguage'] ][ label ];
+ } catch (e) { ; }
+
+ if ( typeof i18n_text == "undefined" )
+ {
+ try {
+ i18n_text = at.quelltextlich.osm.vars['i18n'][ at.quelltextlich.osm.vars['i18n']['defaultLanguage'] ][ label ];
+ } catch (e) { ; }
+ }
+
+ if ( typeof i18n_text == "undefined" )
+ {
+ i18n_text = label;
+ }
+ return i18n_text;
+ },
+
+
+
+ // Sets up a basic map with controls.
+ // Without setting the center.
+ // Without markers.
+ addRawMap: function ( div, useZoomBar, navigation, plainMouseWheelZoom )
+ {
+
+ var map = new OpenLayers.Map( div, { controls: [] } );
+
+ var osmLayer = new OpenLayers.Layer.OSM();
+ var navigationOptions = {};
+
+ osmLayer.attribution=at.quelltextlich.osm.getText( "osm.Attribution" );
+ osmLayer.url=osmLayer.url.replace(/^https?:/,"")
+ map.addLayer( osmLayer );
+
+ if (plainMouseWheelZoom === false)
+ {
+ navigationOptions = {mouseWheelOptions:{keyMask:OpenLayers.Handler.MOD_CTRL}};
+ }
+
+ map.addControl(new OpenLayers.Control.Navigation(navigationOptions));
+ map.addControl(new OpenLayers.Control.KeyboardDefaults());
+ //map.addControl(new OpenLayers.Control.LayerSwitcher());
+ map.addControl(new OpenLayers.Control.ScaleLine());
+ if ( navigation !== false )
+ {
+ if ( useZoomBar )
+ {
+ map.addControl(new OpenLayers.Control.PanZoomBar());
+ } else {
+ map.addControl(new OpenLayers.Control.PanZoom());
+ }
+ }
+ map.addControl(new OpenLayers.Control.Attribution());
+
+ // This fixup allows Opera to show KML Layers in a viewer that is
+ // included via an