diff --git a/justfile b/justfile
index 9ae82e8..ff784ce 100644
--- a/justfile
+++ b/justfile
@@ -7,7 +7,7 @@ serve:
 uglify:
     uglifyjs ./src/js/main.js --compress --mangle -o ./static/js/main.js && uglifyjs ./src/js/page.js --compress --mangle -o ./static/js/page.js && uglifyjs ./src/js/search.js --compress --mangle -o ./static/js/search.js && uglifyjs ./src/js/lang.js --compress --mangle -o ./static/js/lang.js
 dev:
-    NODE_ENV=development ./themes/blowfish/node_modules/tailwindcss/lib/cli.js -c ./themes/blowfish/tailwind.config.js -i ./themes/blowfish/assets/css/main.css -o ./assets/css/compiled/main.css --jit -w
+    tailwindcss -i static/css/base.css -o static/css/main.css --watch
 api:
     cargo build
 clean:
diff --git a/static/android-chrome-192x192.png b/static/android-chrome-192x192.png
new file mode 100644
index 0000000..3360f18
Binary files /dev/null and b/static/android-chrome-192x192.png differ
diff --git a/static/android-chrome-512x512.png b/static/android-chrome-512x512.png
new file mode 100644
index 0000000..1d633c1
Binary files /dev/null and b/static/android-chrome-512x512.png differ
diff --git a/static/apple-touch-icon.png b/static/apple-touch-icon.png
new file mode 100644
index 0000000..c9277fc
Binary files /dev/null and b/static/apple-touch-icon.png differ
diff --git a/static/css/leaflet.extra-markers.min.css b/static/css/leaflet.extra-markers.min.css
new file mode 100755
index 0000000..2638d59
--- /dev/null
+++ b/static/css/leaflet.extra-markers.min.css
@@ -0,0 +1,8 @@
+/*!
+ * leaflet-extra-markers
+ * Custom Markers for Leaflet JS based on Awesome Markers
+ * Leaflet ExtraMarkers
+ * https://github.com/coryasilva/Leaflet.ExtraMarkers/
+ * @author coryasilva <https://github.com/coryasilva>
+ * @version 1.2.1
+ */.extra-marker{background:url("../img/markers_default.png") no-repeat 0 0;width:35px;height:46px;position:absolute;left:0;top:0;display:block;text-align:center}  .extra-marker-shadow{background:url("../img/markers_shadow.png") no-repeat 0 0;width:36px;height:16px}@media (min--moz-device-pixel-ratio:1.5),(-webkit-min-device-pixel-ratio:1.5),(min-device-pixel-ratio:1.5),(min-resolution:1.5dppx){.extra-marker{background-image:url("../img/markers_default@2x.png");background-size:540px 184px}  .extra-marker-shadow{background-image:url("../img/markers_shadow@2x.png");background-size:35px 16px}}  .extra-marker.extra-marker-svg{background:none}  .extra-marker.extra-marker-svg .svg-inline--fa,.extra-marker.extra-marker-svg i{position:absolute;left:0;width:35px}  .extra-marker .svg-inline--fa,.extra-marker i{color:#fff;margin-top:7px;display:inline-block;font-size:14px}  .extra-marker .svg-inline--fa{margin-top:10px;background:none}  .extra-marker .svg-inline--fa,.extra-marker i.fa,.extra-marker i.fab,.extra-marker i.fas,.extra-marker i.far,.extra-marker i.fal{margin-top:10px}  .extra-marker .svg-inline--fa.fa-2x,.extra-marker i.fa.fa-2x,.extra-marker i.fab.fa-2x,.extra-marker i.fas.fa-2x,.extra-marker i.far.fa-2x,.extra-marker i.fal.fa-2x{font-size:16px;margin-top:9px}  .extra-marker .svg-inline--fa.fa-3x,.extra-marker i.fa.fa-3x,.extra-marker i.fab.fa-3x,.extra-marker i.fas.fa-3x,.extra-marker i.far.fa-3x,.extra-marker i.fal.fa-3x{font-size:18px;margin-top:9px}  .extra-marker .svg-inline--fa.fa-4x,.extra-marker i.fa.fa-4x,.extra-marker i.fab.fa-4x,.extra-marker i.fas.fa-4x,.extra-marker i.far.fa-4x,.extra-marker i.fal.fa-4x{font-size:20px;margin-top:8px}  .extra-marker .svg-inline--fa.fa-5x,.extra-marker i.fa.fa-5x,.extra-marker i.fab.fa-5x,.extra-marker i.fas.fa-5x,.extra-marker i.far.fa-5x,.extra-marker i.fal.fa-5x{font-size:24px;margin-top:6px}  .extra-marker .fa-number:before{content:attr(number)}  .extra-marker i.glyphicon{margin-top:10px}  .extra-marker i.icon{margin-right:0;opacity:1}  .extra-marker-circle-red{background-position:0 0}  .extra-marker-circle-orange-dark{background-position:-36px 0}  .extra-marker-circle-orange{background-position:-72px 0}  .extra-marker-circle-yellow{background-position:-108px 0}  .extra-marker-circle-blue-dark{background-position:-144px 0}  .extra-marker-circle-blue{background-position:-180px 0}  .extra-marker-circle-cyan{background-position:-216px 0}  .extra-marker-circle-purple{background-position:-252px 0}  .extra-marker-circle-violet{background-position:-288px 0}  .extra-marker-circle-pink{background-position:-324px 0}  .extra-marker-circle-green-dark{background-position:-360px 0}  .extra-marker-circle-green{background-position:-396px 0}  .extra-marker-circle-green-light{background-position:-432px 0}  .extra-marker-circle-black{background-position:-468px 0}  .extra-marker-circle-white{background-position:-504px 0}  .extra-marker-square-red{background-position:0 -46px}  .extra-marker-square-orange-dark{background-position:-36px -46px}  .extra-marker-square-orange{background-position:-72px -46px}  .extra-marker-square-yellow{background-position:-108px -46px}  .extra-marker-square-blue-dark{background-position:-144px -46px}  .extra-marker-square-blue{background-position:-180px -46px}  .extra-marker-square-cyan{background-position:-216px -46px}  .extra-marker-square-purple{background-position:-252px -46px}  .extra-marker-square-violet{background-position:-288px -46px}  .extra-marker-square-pink{background-position:-324px -46px}  .extra-marker-square-green-dark{background-position:-360px -46px}  .extra-marker-square-green{background-position:-396px -46px}  .extra-marker-square-green-light{background-position:-432px -46px}  .extra-marker-square-black{background-position:-468px -46px}  .extra-marker-square-white{background-position:-504px -46px}  .extra-marker-star-red{background-position:0 -92px}  .extra-marker-star-orange-dark{background-position:-36px -92px}  .extra-marker-star-orange{background-position:-72px -92px}  .extra-marker-star-yellow{background-position:-108px -92px}  .extra-marker-star-blue-dark{background-position:-144px -92px}  .extra-marker-star-blue{background-position:-180px -92px}  .extra-marker-star-cyan{background-position:-216px -92px}  .extra-marker-star-purple{background-position:-252px -92px}  .extra-marker-star-violet{background-position:-288px -92px}  .extra-marker-star-pink{background-position:-324px -92px}  .extra-marker-star-green-dark{background-position:-360px -92px}  .extra-marker-star-green{background-position:-396px -92px}  .extra-marker-star-green-light{background-position:-432px -92px}  .extra-marker-star-black{background-position:-468px -92px}  .extra-marker-star-white{background-position:-504px -92px}  .extra-marker-penta-red{background-position:0 -138px}  .extra-marker-penta-orange-dark{background-position:-36px -138px}  .extra-marker-penta-orange{background-position:-72px -138px}  .extra-marker-penta-yellow{background-position:-108px -138px}  .extra-marker-penta-blue-dark{background-position:-144px -138px}  .extra-marker-penta-blue{background-position:-180px -138px}  .extra-marker-penta-cyan{background-position:-216px -138px}  .extra-marker-penta-purple{background-position:-252px -138px}  .extra-marker-penta-violet{background-position:-288px -138px}  .extra-marker-penta-pink{background-position:-324px -138px}  .extra-marker-penta-green-dark{background-position:-360px -138px}  .extra-marker-penta-green{background-position:-396px -138px}  .extra-marker-penta-green-light{background-position:-432px -138px}  .extra-marker-penta-black{background-position:-468px -138px}  .extra-marker-penta-white{background-position:-504px -138px}
diff --git a/static/css/main.css b/static/css/main.css
index 53dc04f..c3352a3 100644
--- a/static/css/main.css
+++ b/static/css/main.css
@@ -25,9 +25,7 @@
     --color-gray-700: oklch(37.3% 0.034 259.733);
     --color-gray-800: oklch(27.8% 0.033 256.848);
     --color-gray-900: oklch(21% 0.034 264.665);
-    --color-neutral-50: oklch(98.5% 0 0);
     --color-neutral-100: oklch(97% 0 0);
-    --color-neutral-300: oklch(87% 0 0);
     --color-neutral-500: oklch(55.6% 0 0);
     --color-black: #000;
     --color-white: #fff;
@@ -209,6 +207,9 @@
   .pointer-events-none {
     pointer-events: none;
   }
+  .collapse {
+    visibility: collapse;
+  }
   .invisible {
     visibility: hidden;
   }
@@ -259,9 +260,6 @@
   .bottom-4 {
     bottom: calc(var(--spacing) * 4);
   }
-  .-left-1 {
-    left: calc(var(--spacing) * -1);
-  }
   .-left-1\/2 {
     left: calc(calc(1/2 * 100%) * -1);
   }
@@ -428,9 +426,6 @@
   .h-0 {
     height: calc(var(--spacing) * 0);
   }
-  .h-2 {
-    height: calc(var(--spacing) * 2);
-  }
   .h-2\/3 {
     height: calc(2/3 * 100%);
   }
@@ -473,21 +468,12 @@
   .h-screen {
     height: 100vh;
   }
-  .max-h-8 {
-    max-height: calc(var(--spacing) * 8);
-  }
   .max-h-12 {
     max-height: calc(var(--spacing) * 12);
   }
-  .w-1 {
-    width: calc(var(--spacing) * 1);
-  }
   .w-1\/3 {
     width: calc(1/3 * 100%);
   }
-  .w-2 {
-    width: calc(var(--spacing) * 2);
-  }
   .w-2\/3 {
     width: calc(2/3 * 100%);
   }
@@ -503,9 +489,6 @@
   .w-9 {
     width: calc(var(--spacing) * 9);
   }
-  .w-11 {
-    width: calc(var(--spacing) * 11);
-  }
   .w-11\/12 {
     width: calc(11/12 * 100%);
   }
@@ -551,15 +534,9 @@
   .flex-none {
     flex: none;
   }
-  .flex-shrink {
-    flex-shrink: 1;
-  }
   .flex-shrink-0 {
     flex-shrink: 0;
   }
-  .flex-grow {
-    flex-grow: 1;
-  }
   .grow {
     flex-grow: 1;
   }
@@ -783,9 +760,6 @@
   .bg-indigo-500 {
     background-color: var(--color-indigo-500);
   }
-  .bg-neutral-500 {
-    background-color: var(--color-neutral-500);
-  }
   .bg-transparent {
     background-color: transparent;
   }
@@ -810,9 +784,6 @@
   .p-5 {
     padding: calc(var(--spacing) * 5);
   }
-  .px-1 {
-    padding-inline: calc(var(--spacing) * 1);
-  }
   .px-1\.5 {
     padding-inline: calc(var(--spacing) * 1.5);
   }
@@ -828,9 +799,6 @@
   .px-6 {
     padding-inline: calc(var(--spacing) * 6);
   }
-  .py-0 {
-    padding-block: calc(var(--spacing) * 0);
-  }
   .py-0\.5 {
     padding-block: calc(var(--spacing) * 0.5);
   }
@@ -849,12 +817,6 @@
   .py-6 {
     padding-block: calc(var(--spacing) * 6);
   }
-  .ps-4 {
-    padding-inline-start: calc(var(--spacing) * 4);
-  }
-  .pe-4 {
-    padding-inline-end: calc(var(--spacing) * 4);
-  }
   .pt-1 {
     padding-top: calc(var(--spacing) * 1);
   }
@@ -971,9 +933,6 @@
   .text-indigo-500 {
     color: var(--color-indigo-500);
   }
-  .text-neutral-50 {
-    color: var(--color-neutral-50);
-  }
   .text-white {
     color: var(--color-white);
   }
@@ -983,6 +942,9 @@
   .\!no-underline {
     text-decoration-line: none !important;
   }
+  .line-through {
+    text-decoration-line: line-through;
+  }
   .no-underline {
     text-decoration-line: none;
   }
@@ -994,11 +956,6 @@
       color: var(--color-gray-300);
     }
   }
-  .placeholder-neutral-300 {
-    &::placeholder {
-      color: var(--color-neutral-300);
-    }
-  }
   .opacity-0 {
     opacity: 0%;
   }
@@ -1008,6 +965,10 @@
   .opacity-100 {
     opacity: 100%;
   }
+  .shadow {
+    --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
+    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
+  }
   .shadow-2xl {
     --tw-shadow: 0 25px 50px -12px var(--tw-shadow-color, rgb(0 0 0 / 0.25));
     box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1023,17 +984,10 @@
   .ring-black {
     --tw-ring-color: var(--color-black);
   }
-  .outline {
-    outline-style: var(--tw-outline-style);
-    outline-width: 1px;
-  }
   .blur {
     --tw-blur: blur(8px);
     filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
   }
-  .filter {
-    filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
-  }
   .transition {
     transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
     transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
@@ -1865,11 +1819,6 @@
   inherits: false;
   initial-value: 0 0 #0000;
 }
-@property --tw-outline-style {
-  syntax: "*";
-  inherits: false;
-  initial-value: solid;
-}
 @property --tw-blur {
   syntax: "*";
   inherits: false;
@@ -1965,7 +1914,6 @@
       --tw-ring-offset-width: 0px;
       --tw-ring-offset-color: #fff;
       --tw-ring-offset-shadow: 0 0 #0000;
-      --tw-outline-style: solid;
       --tw-blur: initial;
       --tw-brightness: initial;
       --tw-contrast: initial;
diff --git a/static/favicon-16x16.png b/static/favicon-16x16.png
new file mode 100644
index 0000000..f26915f
Binary files /dev/null and b/static/favicon-16x16.png differ
diff --git a/static/favicon-32x32.png b/static/favicon-32x32.png
new file mode 100644
index 0000000..3e4484b
Binary files /dev/null and b/static/favicon-32x32.png differ
diff --git a/static/favicon.ico b/static/favicon.ico
new file mode 100644
index 0000000..010781f
Binary files /dev/null and b/static/favicon.ico differ
diff --git a/static/img/april.jpg b/static/img/april.jpg
new file mode 100644
index 0000000..155d397
Binary files /dev/null and b/static/img/april.jpg differ
diff --git a/static/img/atwood-hs.jpg b/static/img/atwood-hs.jpg
new file mode 100644
index 0000000..5644cac
Binary files /dev/null and b/static/img/atwood-hs.jpg differ
diff --git a/static/img/atwood-jh.jpg b/static/img/atwood-jh.jpg
new file mode 100644
index 0000000..c8a41e5
Binary files /dev/null and b/static/img/atwood-jh.jpg differ
diff --git a/static/img/brain.jpg b/static/img/brain.jpg
new file mode 100644
index 0000000..11a5db2
Binary files /dev/null and b/static/img/brain.jpg differ
diff --git a/static/img/camp.jpg b/static/img/camp.jpg
new file mode 100644
index 0000000..3cc7be6
Binary files /dev/null and b/static/img/camp.jpg differ
diff --git a/static/img/chris.jpg b/static/img/chris.jpg
new file mode 100644
index 0000000..f959c47
Binary files /dev/null and b/static/img/chris.jpg differ
diff --git a/static/img/ethan.jpg b/static/img/ethan.jpg
new file mode 100644
index 0000000..517fe6a
Binary files /dev/null and b/static/img/ethan.jpg differ
diff --git a/static/img/janice.jpg b/static/img/janice.jpg
new file mode 100644
index 0000000..e56ea0f
Binary files /dev/null and b/static/img/janice.jpg differ
diff --git a/static/img/jenny.png b/static/img/jenny.png
new file mode 100644
index 0000000..020b45d
Binary files /dev/null and b/static/img/jenny.png differ
diff --git a/static/img/lavonne.jpg b/static/img/lavonne.jpg
new file mode 100644
index 0000000..07aa6ba
Binary files /dev/null and b/static/img/lavonne.jpg differ
diff --git a/static/img/lewis.jpg b/static/img/lewis.jpg
new file mode 100644
index 0000000..f4b58e7
Binary files /dev/null and b/static/img/lewis.jpg differ
diff --git a/static/img/logan.jpg b/static/img/logan.jpg
new file mode 100644
index 0000000..64e25de
Binary files /dev/null and b/static/img/logan.jpg differ
diff --git a/static/img/logo.png b/static/img/logo.png
new file mode 100644
index 0000000..36e04dc
Binary files /dev/null and b/static/img/logo.png differ
diff --git a/static/img/markers_default.png b/static/img/markers_default.png
new file mode 100755
index 0000000..2c81d15
Binary files /dev/null and b/static/img/markers_default.png differ
diff --git a/static/img/markers_default@2x.png b/static/img/markers_default@2x.png
new file mode 100755
index 0000000..35ff61b
Binary files /dev/null and b/static/img/markers_default@2x.png differ
diff --git a/static/img/markers_shadow.png b/static/img/markers_shadow.png
new file mode 100755
index 0000000..33cf955
Binary files /dev/null and b/static/img/markers_shadow.png differ
diff --git a/static/img/markers_shadow@2x.png b/static/img/markers_shadow@2x.png
new file mode 100755
index 0000000..1116503
Binary files /dev/null and b/static/img/markers_shadow@2x.png differ
diff --git a/static/img/mt.jpg b/static/img/mt.jpg
new file mode 100644
index 0000000..5913bde
Binary files /dev/null and b/static/img/mt.jpg differ
diff --git a/static/img/mt2.jpg b/static/img/mt2.jpg
new file mode 100644
index 0000000..040bf25
Binary files /dev/null and b/static/img/mt2.jpg differ
diff --git a/static/img/nv.jpg b/static/img/nv.jpg
new file mode 100644
index 0000000..777690a
Binary files /dev/null and b/static/img/nv.jpg differ
diff --git a/static/img/pburg.jpg b/static/img/pburg.jpg
new file mode 100644
index 0000000..df5dc61
Binary files /dev/null and b/static/img/pburg.jpg differ
diff --git a/static/img/rob.jpg b/static/img/rob.jpg
new file mode 100644
index 0000000..b31ebac
Binary files /dev/null and b/static/img/rob.jpg differ
diff --git a/static/img/shawn-dust.jpg b/static/img/shawn-dust.jpg
new file mode 100644
index 0000000..4a143a8
Binary files /dev/null and b/static/img/shawn-dust.jpg differ
diff --git a/static/img/smith-center.jpg b/static/img/smith-center.jpg
new file mode 100644
index 0000000..b9dc39c
Binary files /dev/null and b/static/img/smith-center.jpg differ
diff --git a/static/img/staff.jpg b/static/img/staff.jpg
new file mode 100644
index 0000000..35c0cee
Binary files /dev/null and b/static/img/staff.jpg differ
diff --git a/static/img/sv.jpg b/static/img/sv.jpg
new file mode 100644
index 0000000..8f208ce
Binary files /dev/null and b/static/img/sv.jpg differ
diff --git a/static/img/tr.png b/static/img/tr.png
new file mode 100644
index 0000000..5510f66
Binary files /dev/null and b/static/img/tr.png differ
diff --git a/static/img/wakeeney.jpg b/static/img/wakeeney.jpg
new file mode 100644
index 0000000..2d45192
Binary files /dev/null and b/static/img/wakeeney.jpg differ
diff --git a/static/js/leaflet.elevation.js b/static/js/leaflet.elevation.js
new file mode 100644
index 0000000..1b0bf91
--- /dev/null
+++ b/static/js/leaflet.elevation.js
@@ -0,0 +1,1660 @@
+/*
+ * Copyright (c) 2019, GPL-3.0+ Project, altrdev
+ *
+ *  This file is free software: you may copy, redistribute and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation, either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This file is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see .
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ *      Copyright (c) 2019, GPL-3.0+ Project, Raruto
+ *
+ *      This file is free software: you may copy, redistribute and/or modify it
+ *      under the terms of the GNU General Public License as published by the
+ *      Free Software Foundation, either version 2 of the License, or (at your
+ *      option) any later version.
+ *
+ *      This file is distributed in the hope that it will be useful, but
+ *      WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program.  If not, see .
+ *
+ *      This file incorporates work covered by the following copyright and
+ *      permission notice:
+ *
+ *          Copyright (c) 2013-2016, MIT License, Felix “MrMufflon” Bache
+ *
+ *          Permission to use, copy, modify, and/or distribute this software
+ *          for any purpose with or without fee is hereby granted, provided
+ *          that the above copyright notice and this permission notice appear
+ *          in all copies.
+ *
+ *          THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ *          WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ *          WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ *          AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+ *          CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ *          OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ *          NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ *          CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+L.Control.Elevation = L.Control.extend({
+
+    includes: L.Evented ? L.Evented.prototype : L.Mixin.Events,
+
+    options: {
+        autohide: true,
+        autohideMarker: true,
+        collapsed: false,
+        controlButton: {
+            iconCssClass: "elevation-toggle-icon",
+            title: "Elevation"
+        },
+        detached: true,
+        distanceFactor: 1,
+        downloadLink: 'link',
+        elevationDiv: "#elevation-div",
+        followMarker: true,
+        forceAxisBounds: false,
+        gpxOptions: {
+            async: true,
+            marker_options: {
+                startIconUrl: null,
+                endIconUrl: null,
+                shadowUrl: null,
+                wptIcons: {
+                    '': L.divIcon({
+                        className: 'elevation-waypoint-marker',
+                        html: '<i class="elevation-waypoint-icon"></i>',
+                        iconSize: [30, 30],
+                        iconAnchor: [8, 30],
+                    })
+                },
+            },
+            polyline_options: {
+                className: '',
+                color: '#566B13',
+                opacity: 0.75,
+                weight: 5,
+                lineCap: 'round'
+            },
+        },
+        height: 200,
+        heightFactor: 1,
+        hoverNumber: {
+            decimalsX: 2,
+            decimalsY: 0,
+            formatter: undefined
+        },
+        imperial: false,
+        interpolation: "curveLinear",
+        lazyLoadJS: true,
+        legend: true,
+        loadData: {
+            defer: false,
+            lazy: false,
+        },
+        marker: 'elevation-line',
+        markerIcon: L.divIcon({
+            className: 'elevation-position-marker',
+            html: '<i class="elevation-position-icon"></i>',
+            iconSize: [32, 32],
+            iconAnchor: [16, 16],
+        }),
+        placeholder: false,
+        position: "topright",
+        reverseCoords: false,
+        skipNullZCoords: false,
+        theme: "lightblue-theme",
+        margins: {
+            top: 10,
+            right: 20,
+            bottom: 30,
+            left: 50
+        },
+        responsive: true,
+        summary: 'inline',
+        width: 600,
+        xLabel: "km",
+        xTicks: undefined,
+        yAxisMax: undefined,
+        yAxisMin: undefined,
+        yLabel: "m",
+        yTicks: undefined,
+        zFollow: 13,
+    },
+    __mileFactor: 0.621371,
+    __footFactor: 3.28084,
+
+    /*
+     * Add data to the diagram either from GPX or GeoJSON and update the axis domain and data
+     */
+    addData: function(d, layer) {
+        this._addData(d);
+
+        if (this._container) {
+            this._applyData();
+        }
+        if ((typeof layer === "undefined" || layer === null) && d.on) {
+            layer = d;
+        }
+        if (layer) {
+            if (layer._path) {
+                L.DomUtil.addClass(layer._path, 'elevation-polyline ' + this.options.theme);
+            }
+            layer
+                .on("mousemove", this._mousemoveLayerHandler, this)
+                .on("mouseout", this._mouseoutHandler, this);
+        }
+
+        this.track_info = this.track_info || {};
+        this.track_info.distance = this._distance;
+        this.track_info.elevation_max = this._maxElevation;
+        this.track_info.elevation_min = this._minElevation;
+
+        this._layers = this._layers || {};
+        this._layers[L.Util.stamp(layer)] = layer;
+
+        var evt = {
+            data: d,
+            layer: layer,
+            track_info: this.track_info,
+        };
+        if (this.fire) this.fire("eledata_added", evt, true);
+        if (this._map) this._map.fire("eledata_added", evt, true);
+    },
+
+    addTo: function(map) {
+        if (this.options.detached) {
+            this._addToChartDiv(map);
+        } else {
+            L.Control.prototype.addTo.call(this, map);
+        }
+        return this;
+    },
+
+    /*
+     * Reset data and display
+     */
+    clear: function() {
+
+        this._clearPath();
+        this._clearChart();
+        this._clearData();
+
+        if (this.fire) this.fire("eledata_clear");
+        if (this._map) this._map.fire("eledata_clear");
+    },
+
+    disableDragging: function() {
+        this._draggingEnabled = false;
+        this._resetDrag();
+    },
+
+    enableDragging: function() {
+        this._draggingEnabled = true;
+    },
+
+    fitBounds: function(bounds) {
+        bounds = bounds || this._fullExtent;
+        if (this._map && bounds) this._map.fitBounds(bounds);
+    },
+
+    getZFollow: function() {
+        return this._zFollow;
+    },
+
+    hide: function() {
+        this._container.style.display = "none";
+    },
+
+    initialize: function(options) {
+        this.options.autohide = typeof options.autohide !== "undefined" ? options.autohide : !L.Browser.mobile;
+
+        // Aliases.
+        if (typeof options.detachedView !== "undefined") this.options.detached = options.detachedView;
+        if (typeof options.responsiveView !== "undefined") this.options.responsive = options.responsiveView;
+        if (typeof options.showTrackInfo !== "undefined") this.options.summary = options.showTrackInfo;
+        if (typeof options.summaryType !== "undefined") this.options.summary = options.summaryType;
+        if (typeof options.autohidePositionMarker !== "undefined") this.options.autohideMarker = options.autohidePositionMarker;
+        if (typeof options.followPositionMarker !== "undefined") this.options.followMarker = options.followPositionMarker;
+        if (typeof options.useLeafletMarker !== "undefined") this.options.marker = options.useLeafletMarker ? 'position-marker' : 'elevation-line';
+        if (typeof options.leafletMarkerIcon !== "undefined") this.options.markerIcon = options.leafletMarkerIcon;
+        if (typeof options.download !== "undefined") this.options.downloadLink = options.download;
+
+        // L.Util.setOptions(this, options);
+        this.options = this._deepMerge({}, this.options, options);
+
+        this._draggingEnabled = !L.Browser.mobile;
+        this._chartEnabled = true;
+
+        if (options.imperial) {
+            this._distanceFactor = this.__mileFactor;
+            this._heightFactor = this.__footFactor;
+            this._xLabel = "mi";
+            this._yLabel = "ft";
+        } else {
+            this._distanceFactor = this.options.distanceFactor;
+            this._heightFactor = this.options.heightFactor;
+            this._xLabel = this.options.xLabel;
+            this._yLabel = this.options.yLabel;
+        }
+
+        this._zFollow = this.options.zFollow;
+
+        if (this.options.followMarker) this._setMapView = L.Util.throttle(this._setMapView, 300, this);
+        if (this.options.placeholder) this.options.loadData.lazy = this.options.loadData.defer = true;
+    },
+
+    /**
+     * Alias for loadData
+     */
+    load: function(data, opts) {
+        this.loadData(data, opts);
+    },
+
+    /**
+     * Alias for addTo
+     */
+    loadChart: function(map) {
+        this.addTo(map);
+    },
+
+    loadData: function(data, opts) {
+        opts = L.extend({}, this.options.loadData, opts);
+        if (opts.defer) {
+            this.loadDefer(data, opts);
+        } else if (opts.lazy) {
+            this.loadLazy(data, opts);
+        } else if (this._isXMLDoc(data)) {
+            this.loadGPX(data);
+        } else if (this._isJSONDoc(data)) {
+            this.loadGeoJSON(data);
+        } else {
+            this.loadFile(data);
+        }
+    },
+
+    loadDefer: function(data, opts) {
+        opts = L.extend({}, this.options.loadData, opts);
+        opts.defer = false;
+        if (document.readyState !== 'complete') window.addEventListener("load", L.bind(this.loadData, this, data, opts), { once: true });
+        else this.loadData(data, opts)
+    },
+
+    loadFile: function(url) {
+        this._downloadURL = url; // TODO: handle multiple urls?
+        try {
+            var xhr = new XMLHttpRequest();
+            xhr.responseType = "text";
+            xhr.open('GET', url);
+            xhr.onload = function() {
+                if (xhr.status !== 200) {
+                    throw "Error " + xhr.status + " while fetching remote file: " + url;
+                } else {
+                    this.loadData(xhr.response, { lazy: false, defer: false });
+                }
+            }.bind(this);
+            xhr.send();
+        } catch (e) {
+            console.warn(e);
+        }
+    },
+
+    loadGeoJSON: function(data) {
+        if (typeof data === "string") {
+            data = JSON.parse(data);
+        }
+
+        this.layer = this.geojson = L.geoJson(data, {
+            style: function(feature) {
+                return {
+                    color: '#566B13',
+                    className: 'elevation-polyline ' + this.options.theme,
+                };
+            }.bind(this),
+            onEachFeature: function(feature, layer) {
+                this.addData(feature, layer);
+
+                this.track_info = this.track_info || {};
+                this.track_info.type = "geojson";
+                this.track_info.name = data.name;
+                this.track_info.distance = this._distance;
+                this.track_info.elevation_max = this._maxElevation;
+                this.track_info.elevation_min = this._minElevation;
+
+            }.bind(this),
+        });
+        if (this._map) {
+            this._map.once('layeradd', function(e) {
+                this.fitBounds(this.layer.getBounds());
+                var evt = {
+                    data: data,
+                    layer: this.layer,
+                    name: this.track_info.name,
+                    track_info: this.track_info,
+                };
+                if (this.fire) this.fire("eledata_loaded", evt, true);
+                if (this._map) this._map.fire("eledata_loaded", evt, true);
+            }, this);
+
+            this.layer.addTo(this._map);
+        } else {
+            console.warn("Undefined elevation map object");
+        }
+    },
+
+    loadGPX: function(data) {
+        var callback = function(data) {
+            this.options.gpxOptions.polyline_options.className += 'elevation-polyline ' + this.options.theme;
+
+            this.layer = this.gpx = new L.GPX(data, this.options.gpxOptions);
+
+            this.layer.on('loaded', function(e) {
+                this.fitBounds(e.target.getBounds());
+            }, this);
+            this.layer.on('addpoint', function(e) {
+
+                if(e.point_type === "start" || e.point_type === "end") {
+                    e.point.setZIndexOffset(10000);
+                }
+                if (e.point._popup) {
+                    e.point._popup.options.className = 'elevation-popup';
+                    e.point._popup._content = decodeURI(e.point._popup._content);
+                }
+                if (e.point._popup && e.point._popup._content) {
+                    e.point.bindTooltip(e.point._popup._content, { direction: 'top', sticky: true, opacity: 1, className: 'elevation-tooltip' }).openTooltip();
+                }
+            });
+            this.layer.once("addline", function(e) {
+                this.addData(e.line /*, this.layer*/ );
+
+                this.track_info = this.track_info || {};
+                this.track_info.type = "gpx";
+                this.track_info.name = this.layer.get_name();
+                this.track_info.distance = this._distance;
+                this.track_info.elevation_max = this._maxElevation;
+                this.track_info.elevation_min = this._minElevation;
+
+                var evt = {
+                    data: data,
+                    layer: this.layer,
+                    name: this.track_info.name,
+                    track_info: this.track_info,
+                };
+
+                if (this.fire) this.fire("eledata_loaded", evt, true);
+                if (this._map) this._map.fire("eledata_loaded", evt, true);
+            }, this);
+
+            if (this._map) {
+                this.layer.addTo(this._map);
+            } else {
+                console.warn("Undefined elevation map object");
+            }
+        }.bind(this, data);
+        if (typeof L.GPX !== 'function' && this.options.lazyLoadJS) {
+            L.Control.Elevation._gpxLazyLoader = this._lazyLoadJS('https://cdnjs.cloudflare.com/ajax/libs/leaflet-gpx/1.5.0/gpx.js', L.Control.Elevation._gpxLazyLoader);
+            L.Control.Elevation._gpxLazyLoader.then(callback);
+        } else {
+            callback.call();
+        }
+    },
+
+    loadLazy: function(data, opts) {
+        opts = L.extend({}, this.options.loadData, opts);
+        opts.lazy = false;
+        let ticking = false;
+        let scrollFn = L.bind(function(data) {
+            if (!ticking) {
+                L.Util.requestAnimFrame(function() {
+                    if (this._isVisible(this.placeholder)) {
+                        window.removeEventListener('scroll', scrollFn);
+                        this.loadData(data, opts);
+                        this.once('eledata_loaded', function() {
+                            if (this.placeholder && this.placeholder.parentNode) {
+                                this.placeholder.parentNode.removeChild(this.placeholder);
+                            }
+                        }, this)
+                    }
+                    ticking = false;
+                }, this);
+                ticking = true;
+            }
+        }, this, data);
+        window.addEventListener('scroll', scrollFn);
+        if (this.placeholder) this.placeholder.addEventListener('mouseenter', scrollFn, { once: true });
+        scrollFn();
+    },
+
+    onAdd: function(map) {
+        this._map = map;
+
+        var container = this._container = L.DomUtil.create("div", "elevation-control elevation");
+
+        if (!this.options.detached) {
+            L.DomUtil.addClass(container, 'leaflet-control');
+        }
+
+        if (this.options.theme) {
+            L.DomUtil.addClass(container, this.options.theme); // append theme to control
+        }
+
+        if (this.options.placeholder && !this._data) {
+            this.placeholder = L.DomUtil.create('img', 'elevation-placeholder');
+            if (typeof this.options.placeholder === 'string') {
+                this.placeholder.src = this.options.placeholder;
+                this.placeholder.alt = '';
+            } else {
+                for (let i in this.options.placeholder) { this.placeholder.setAttribute(i, this.options.placeholder[i]); }
+            }
+            container.insertBefore(this.placeholder, container.firstChild);
+        }
+
+        var callback = function(map, container) {
+            this._initToggle(container);
+            this._initChart(container);
+
+            this._applyData();
+
+            this._map.on('zoom viewreset zoomanim', this._hidePositionMarker, this);
+            this._map.on('resize', this._resetView, this);
+            this._map.on('resize', this._resizeChart, this);
+            this._map.on('mousedown', this._resetDrag, this);
+
+            this._map.on('eledata_loaded', this._updateSummary, this);
+
+            L.DomEvent.on(this._map._container, 'mousewheel', this._resetDrag, this);
+            L.DomEvent.on(this._map._container, 'touchstart', this._resetDrag, this);
+
+        }.bind(this, map, container);
+        if (typeof d3 !== 'object' && this.options.lazyLoadJS) {
+            L.Control.Elevation._d3LazyLoader = this._lazyLoadJS('https://unpkg.com/d3@4.13.0/build/d3.min.js', L.Control.Elevation._d3LazyLoader);
+            L.Control.Elevation._d3LazyLoader.then(callback);
+        } else {
+            callback.call();
+        }
+        return container;
+    },
+
+    onRemove: function(map) {
+        this._container = null;
+    },
+
+    redraw: function() {
+        this._resizeChart();
+    },
+
+    setZFollow: function(zoom) {
+        this._zFollow = zoom;
+    },
+
+    show: function() {
+        this._container.style.display = "block";
+    },
+
+    /*
+     * Parsing data either from GPX or GeoJSON and update the diagram data
+     */
+    _addData: function(d) {
+        var geom = d && d.geometry && d.geometry;
+        var i;
+
+        if (geom) {
+            switch (geom.type) {
+                case 'LineString':
+                    this._addGeoJSONData(geom.coordinates);
+                    break;
+
+                case 'MultiLineString':
+                    for (i = 0; i < geom.coordinates.length; i++) {
+                        this._addGeoJSONData(geom.coordinates[i]);
+                    }
+                    break;
+
+                default:
+                    console.warn('Unsopperted GeoJSON feature geometry type:' + geom.type);
+            }
+        }
+
+        var feat = d && d.type === "FeatureCollection";
+        if (feat) {
+            for (i = 0; i < d.features.length; i++) {
+                this._addData(d.features[i]);
+            }
+        }
+
+        if (d && d._latlngs) {
+            this._addGPXdata(d._latlngs);
+        }
+    },
+
+    /*
+     * Parsing of GeoJSON data lines and their elevation in z-coordinate
+     */
+    _addGeoJSONData: function(coords) {
+        if (coords) {
+            for (var i = 0; i < coords.length; i++) {
+                this._addPoint(coords[i][1], coords[i][0], coords[i][2]);
+            }
+        }
+    },
+
+    /*
+     * Parsing function for GPX data and their elevation in z-coordinate
+     */
+    _addGPXdata: function(coords) {
+        if (coords) {
+            for (var i = 0; i < coords.length; i++) {
+                this._addPoint(coords[i].lat, coords[i].lng, coords[i].meta.ele);
+            }
+        }
+    },
+
+    _addPoint: function(x, y, z) {
+        if (this.options.reverseCoords) {
+            var tmp = x;
+            x = y;
+            y = tmp;
+        }
+
+        var data = this._data || [];
+        var eleMax = this._maxElevation || -Infinity;
+        var eleMin = this._minElevation || +Infinity;
+        var dist = this._distance || 0;
+
+        var curr = new L.LatLng(x, y);
+        var prev = data.length ? data[data.length - 1].latlng : curr;
+
+        var delta = curr.distanceTo(prev) * this._distanceFactor;
+
+        dist = dist + Math.round(delta / 1000 * 100000) / 100000;
+
+        // check and fix missing elevation data on last added point
+        if (!this.options.skipNullZCoords && data.length > 0) {
+            var prevZ = data[data.length - 1].z;
+            if (isNaN(prevZ)) {
+                var lastZ = this._lastValidZ;
+                var currZ = z * this._heightFactor;
+                if (!isNaN(lastZ) && !isNaN(currZ)) {
+                    prevZ = (lastZ + currZ) / 2;
+                } else if (!isNaN(lastZ)) {
+                    prevZ = lastZ;
+                } else if (!isNaN(currZ)) {
+                    prevZ = currZ;
+                }
+                if (!isNaN(prevZ)) data[data.length - 1].z = prevZ;
+                else data.splice(data.length - 1, 1);
+            }
+        }
+
+        z = z * this._heightFactor;
+
+        // skip point if it has not elevation
+        if (!isNaN(z)) {
+            eleMax = eleMax < z ? z : eleMax;
+            eleMin = eleMin > z ? z : eleMin;
+            this._lastValidZ = z;
+        }
+
+        data.push({
+            dist: dist,
+            x: x,
+            y: y,
+            z: z,
+            latlng: curr
+        });
+
+        this._data = data;
+        this._distance = dist;
+        this._maxElevation = eleMax;
+        this._minElevation = eleMin;
+    },
+
+    _addToChartDiv: function(map) {
+        this._appendElevationDiv(map._container).appendChild(this.onAdd(map));
+    },
+
+    _appendChart: function(svg) {
+        var g = svg
+            .append("g")
+            .attr("transform", "translate(" + this.options.margins.left + "," + this.options.margins.top + ")");
+
+        this._appendGrid(g);
+        this._appendAreaPath(g);
+        this._appendAxis(g);
+        this._appendFocusRect(g);
+        this._appendMouseFocusG(g);
+        this._appendLegend(g);
+    },
+
+    _appendElevationDiv: function(container) {
+        var eleDiv = document.querySelector(this.options.elevationDiv);
+        if (!eleDiv) {
+            eleDiv = L.DomUtil.create('div', 'leaflet-control elevation elevation-div');
+            this.options.elevationDiv = '#elevation-div_' + Math.random().toString(36).substr(2, 9);
+            eleDiv.id = this.options.elevationDiv.substr(1);
+            container.parentNode.insertBefore(eleDiv, container.nextSibling); // insert after end of container.
+        }
+        if (this.options.detached) {
+            L.DomUtil.addClass(eleDiv, 'elevation-detached');
+            L.DomUtil.removeClass(eleDiv, 'leaflet-control');
+        }
+        this.eleDiv = eleDiv;
+        return this.eleDiv;
+    },
+
+    _appendXaxis: function(axis) {
+        axis
+            .append("g")
+            .attr("class", "x axis")
+            .attr("transform", "translate(0," + this._height() + ")")
+            .call(
+                d3
+                    .axisBottom()
+                    .scale(this._x)
+                    .ticks(this.options.xTicks)
+            )
+            .append("text")
+            .attr("x", this._width() + 6)
+            .attr("y", 30)
+            .text(this._xLabel);
+    },
+
+    _appendXGrid: function(grid) {
+        grid.append("g")
+            .attr("class", "x grid")
+            .attr("transform", "translate(0," + this._height() + ")")
+            .call(
+                d3
+                    .axisBottom()
+                    .scale(this._x)
+                    .ticks(this.options.xTicks)
+                    .tickSize(-this._height())
+                    .tickFormat("")
+            );
+
+    },
+
+    _appendYaxis: function(axis) {
+        axis
+            .append("g")
+            .attr("class", "y axis")
+            .call(
+                d3
+                    .axisLeft()
+                    .scale(this._y)
+                    .ticks(this.options.yTicks)
+            )
+            .append("text")
+            .attr("x", -30)
+            .attr("y", -5)
+            .text(this._yLabel);
+    },
+
+    _appendYGrid: function(grid) {
+        grid.append("g")
+            .attr("class", "y grid")
+            .call(
+                d3
+                    .axisLeft()
+                    .scale(this._y)
+                    .ticks(this.options.yTicks)
+                    .tickSize(-this._width())
+                    .tickFormat("")
+            );
+    },
+
+    _appendAreaPath: function(g) {
+        this._areapath = g.append("path")
+            .attr("class", "area");
+    },
+
+    _appendAxis: function(g) {
+        this._axis = g.append("g")
+            .attr("class", "axis");
+        this._appendXaxis(this._axis);
+        this._appendYaxis(this._axis);
+    },
+
+    _appendFocusRect: function(g) {
+        var focusRect = this._focusRect = g.append("rect")
+            .attr("width", this._width())
+            .attr("height", this._height())
+            .style("fill", "none")
+            .style("stroke", "none")
+            .style("pointer-events", "all");
+
+        if (L.Browser.mobile) {
+            focusRect
+                .on("touchmove.drag", this._dragHandler.bind(this))
+                .on("touchstart.drag", this._dragStartHandler.bind(this))
+                .on("touchstart.focus", this._mousemoveHandler.bind(this))
+                .on("touchmove.focus", this._mousemoveHandler.bind(this));
+            L.DomEvent.on(this._container, 'touchend', this._dragEndHandler, this);
+        }
+
+        focusRect
+            .on("mousemove.drag", this._dragHandler.bind(this))
+            .on("mousedown.drag", this._dragStartHandler.bind(this))
+            .on("mouseenter.focus", this._mouseenterHandler.bind(this))
+            .on("mousemove.focus", this._mousemoveHandler.bind(this))
+            .on("mouseout.focus", this._mouseoutHandler.bind(this));
+        L.DomEvent.on(this._container, 'mouseup', this._dragEndHandler, this);
+    },
+
+    _appendGrid: function(g) {
+        this._grid = g.append("g")
+            .attr("class", "grid");
+        this._appendXGrid(this._grid);
+        this._appendYGrid(this._grid);
+    },
+
+    _appendMouseFocusG: function(g) {
+        var focusG = this._focusG = g.append("g")
+            .attr("class", "mouse-focus-group");
+
+        this._mousefocus = focusG.append('svg:line')
+            .attr('class', 'mouse-focus-line')
+            .attr('x2', '0')
+            .attr('y2', '0')
+            .attr('x1', '0')
+            .attr('y1', '0');
+
+        this._focuslabelrect = focusG.append("rect")
+            .attr('class', 'mouse-focus-label')
+            .attr("x", 0)
+            .attr("y", 0)
+            .attr("width", 0)
+            .attr("height", 0)
+            .attr("rx", 3)
+            .attr("ry", 3);
+
+        this._focuslabeltext = focusG.append("svg:text")
+            .attr("class", "mouse-focus-label-text");
+        this._focuslabelY = this._focuslabeltext.append("svg:tspan")
+            .attr("class", "mouse-focus-label-y")
+            .attr("dy", "-1em");
+        this._focuslabelX = this._focuslabeltext.append("svg:tspan")
+            .attr("class", "mouse-focus-label-x")
+            .attr("dy", "2em");
+    },
+
+    _appendLegend: function(g) {
+        if (!this.options.legend) return;
+
+        var legend = this._legend = g.append('g')
+            .attr("class", "legend");
+
+        var altitude = this._altitudeLegend = this._legend.append('g')
+            .attr("class", "legend-altitude");
+
+        altitude.append("rect")
+            .attr("class", "area")
+            .attr("x", (this._width() / 2) - 50)
+            .attr("y", this._height() + this.options.margins.bottom - 17)
+            .attr("width", 50)
+            .attr("height", 5)
+            .attr("opacity", 0.75);
+
+        altitude.append('text')
+            .text('Altitude')
+            .attr("x", (this._width() / 2) + 5)
+            .attr("font-size", 10)
+            .style("text-decoration-thickness", "2px")
+            .style("font-weight", "700")
+            .attr('y', this._height() + this.options.margins.bottom - 11);
+
+    },
+
+    _appendPositionMarker: function(pane) {
+        var theme = this.options.theme;
+        var heightG = pane.select("g");
+
+        this._mouseHeightFocus = heightG.append('svg:line')
+            .attr("class", theme + " height-focus line")
+            .attr("x2", 0)
+            .attr("y2", 0)
+            .attr("x1", 0)
+            .attr("y1", 0);
+
+        this._pointG = heightG.append("g");
+        this._pointG.append("svg:circle")
+            .attr("class", theme + " height-focus circle-lower")
+            .attr("r", 6)
+            .attr("cx", 0)
+            .attr("cy", 0);
+
+        this._mouseHeightFocusLabel = heightG.append("svg:text")
+            .attr("class", theme + " height-focus-label")
+            .style("pointer-events", "none");
+    },
+
+    _applyData: function() {
+        if (!this._data) return;
+
+        var xdomain = d3.extent(this._data, function(d) {
+            return d.dist;
+        });
+        var ydomain = d3.extent(this._data, function(d) {
+            return d.z;
+        });
+        var opts = this.options;
+
+        if (opts.yAxisMin !== undefined && (opts.yAxisMin < ydomain[0] || opts.forceAxisBounds)) {
+            ydomain[0] = opts.yAxisMin;
+        }
+        if (opts.yAxisMax !== undefined && (opts.yAxisMax > ydomain[1] || opts.forceAxisBounds)) {
+            ydomain[1] = opts.yAxisMax;
+        }
+
+        this._x.domain(xdomain);
+        this._y.domain(ydomain);
+        this._areapath.datum(this._data)
+            .attr("d", this._area);
+        this._updateAxis();
+
+        this._fullExtent = this._calculateFullExtent(this._data);
+    },
+
+    /*
+     * Calculates the full extent of the data array
+     */
+    _calculateFullExtent: function(data) {
+        if (!data || data.length < 1) {
+            throw new Error("no data in parameters");
+        }
+
+        var ext = new L.latLngBounds(data[0].latlng, data[0].latlng);
+
+        data.forEach(function(item) {
+            ext.extend(item.latlng);
+        });
+
+        return ext;
+    },
+
+    _clearChart: function() {
+        this._resetDrag();
+        if (this._areapath) {
+            // workaround for 'Error: Problem parsing d=""' in Webkit when empty data
+            // https://groups.google.com/d/msg/d3-js/7rFxpXKXFhI/HzIO_NPeDuMJ
+            //this._areapath.datum(this._data).attr("d", this._area);
+            this._areapath.attr("d", "M0 0");
+
+            this._x.domain([0, 1]);
+            this._y.domain([0, 1]);
+            this._updateAxis();
+        }
+        if (this._altitudeLegend) {
+            this._altitudeLegend.select('text').style("text-decoration-line", "line-through");
+        }
+    },
+
+    /*
+     * Reset data
+     */
+    _clearData: function() {
+        this._data = null;
+        this._distance = null;
+        this._maxElevation = null;
+        this._minElevation = null;
+        this.track_info = null;
+        this._layers = null;
+        // if (this.layer) {
+        // 	this.layer.removeFrom(this._map);
+        // }
+    },
+
+    _clearPath: function() {
+        this._hidePositionMarker();
+        for (var id in this._layers) {
+            L.DomUtil.removeClass(this._layers[id]._path, "elevation-polyline");
+            L.DomUtil.removeClass(this._layers[id]._path, this.options.theme);
+        }
+    },
+
+    _collapse: function() {
+        if (this._container) {
+            L.DomUtil.removeClass(this._container, 'elevation-expanded');
+            L.DomUtil.addClass(this._container, 'elevation-collapsed');
+        }
+    },
+
+    _deepMerge: function(target, ...sources) {
+        if (!sources.length) return target;
+        const source = sources.shift();
+        if (this._isObject(target) && this._isObject(source)) {
+            for (const key in source) {
+                if (this._isObject(source[key])) {
+                    if (!target[key]) Object.assign(target, {
+                        [key]: {}
+                    });
+                    this._deepMerge(target[key], source[key]);
+                } else {
+                    Object.assign(target, {
+                        [key]: source[key]
+                    });
+                }
+            }
+        }
+        return this._deepMerge(target, ...sources);
+    },
+
+    _saveFile: function(fileUrl) {
+        var d = document,
+            a = d.createElement('a'),
+            b = d.body;
+        a.href = fileUrl;
+        a.target = '_new';
+        a.download = ""; // fileName
+        a.style.display = 'none';
+        b.appendChild(a);
+        a.click();
+        b.removeChild(a);
+    },
+
+    _dragHandler: function() {
+        //we don't want map events to occur here
+        d3.event.preventDefault();
+        d3.event.stopPropagation();
+
+        this._gotDragged = true;
+        this._drawDragRectangle();
+    },
+
+    /*
+     * Handles end of drag operations. Zooms the map to the selected items extent.
+     */
+    _dragEndHandler: function() {
+        if (!this._dragStartCoords || !this._dragCurrentCoords || !this._gotDragged) {
+            this._dragStartCoords = null;
+            this._gotDragged = false;
+            if (this._draggingEnabled) this._resetDrag();
+            // autotoggle chart data on single click
+            /*if (this._chartEnabled) {
+                this._clearChart();
+                this._clearPath();
+                this._chartEnabled = false;
+            } else {
+                this._resizeChart();
+                this._chartEnabled = true;
+            }*/
+            return;
+        }
+
+        var item1 = this._findItemForX(this._dragStartCoords[0]),
+            item2 = this._findItemForX(this._dragCurrentCoords[0]);
+
+        if (item1 == item2) return;
+
+        this._hidePositionMarker();
+
+        this._fitSection(item1, item2);
+
+        this._dragStartCoords = null;
+        this._gotDragged = false;
+
+        var evt = {
+            data: {
+                dragstart: this._data[item1],
+                dragend: this._data[item2]
+            }
+        };
+        if (this.fire) this.fire("elechart_dragged", evt, true);
+        if (this._map) this._map.fire("elechart_dragged", evt, true);
+    },
+
+    _dragStartHandler: function() {
+        d3.event.preventDefault();
+        d3.event.stopPropagation();
+
+        this._gotDragged = false;
+        this._dragStartCoords = d3.mouse(this._focusRect.node());
+    },
+
+    /*
+     * Draws the currently dragged rectangle over the chart.
+     */
+    _drawDragRectangle: function() {
+        if (!this._dragStartCoords || !this._draggingEnabled) {
+            return;
+        }
+
+        var dragEndCoords = this._dragCurrentCoords = d3.mouse(this._focusRect.node());
+
+        var x1 = Math.min(this._dragStartCoords[0], dragEndCoords[0]),
+            x2 = Math.max(this._dragStartCoords[0], dragEndCoords[0]);
+
+        if (!this._dragRectangle && !this._dragRectangleG) {
+            var g = d3.select(this._container).select("svg").select("g");
+
+            this._dragRectangleG = g.insert("g", ".mouse-focus-group");
+
+            this._dragRectangle = this._dragRectangleG.append("rect")
+                .attr("width", x2 - x1)
+                .attr("height", this._height())
+                .attr("x", x1)
+                .attr('class', 'mouse-drag')
+                .style("pointer-events", "none");
+        } else {
+            this._dragRectangle.attr("width", x2 - x1)
+                .attr("x", x1);
+        }
+    },
+
+    _expand: function() {
+        if (this._container) {
+            L.DomUtil.removeClass(this._container, 'elevation-collapsed');
+            L.DomUtil.addClass(this._container, 'elevation-expanded');
+        }
+    },
+
+    /*
+     * Finds an item with the smallest delta in distance to the given latlng coords
+     */
+    _findItemForLatLng: function(latlng) {
+        var result = null,
+            d = Infinity;
+        this._data.forEach(function(item) {
+            var dist = latlng.distanceTo(item.latlng);
+            if (dist < d) {
+                d = dist;
+                result = item;
+            }
+        });
+        return result;
+    },
+
+    /*
+     * Finds a data entry for a given x-coordinate of the diagram
+     */
+    _findItemForX: function(x) {
+        var bisect = d3.bisector(function(d) {
+            return d.dist;
+        }).left;
+        var xinvert = this._x.invert(x);
+        return bisect(this._data, xinvert);
+    },
+
+    /**
+     * Make the map fit the route section between given indexes.
+     */
+    _fitSection: function(index1, index2) {
+        var start = Math.min(index1, index2);
+        var end   = Math.max(index1, index2);
+        var ext   = this._calculateFullExtent(this._data.slice(start, end));
+        this.fitBounds(ext);
+    },
+
+    /*
+     * Fromatting funciton using the given decimals and seperator
+     */
+    _formatter: function(num, dec, sep) {
+        var res;
+        if (dec === 0) {
+            res = Math.round(num) + "";
+        } else {
+            res = L.Util.formatNum(num, dec) + "";
+        }
+        var numbers = res.split(".");
+        if (numbers[1]) {
+            var d = dec - numbers[1].length;
+            for (; d > 0; d--) {
+                numbers[1] += "0";
+            }
+            res = numbers.join(sep || ".");
+        }
+        return res;
+    },
+
+    _height: function() {
+        var opts = this.options;
+        return opts.height - opts.margins.top - opts.margins.bottom;
+    },
+
+    /*
+     * Hides the position/height indicator marker drawn onto the map
+     */
+    _hidePositionMarker: function() {
+        if (!this.options.autohideMarker) {
+            return;
+        }
+
+        this._selectedItem = null;
+
+        if (this._marker) {
+            if (this._map) this._map.removeLayer(this._marker);
+            this._marker = null;
+        }
+        if (this._mouseHeightFocus) {
+            this._mouseHeightFocus.style("visibility", "hidden");
+            this._mouseHeightFocusLabel.style("visibility", "hidden");
+        }
+        if (this._pointG) {
+            this._pointG.style("visibility", "hidden");
+        }
+        if (this._focusG) {
+            this._focusG.style("visibility", "hidden");
+        }
+    },
+
+    _initChart: function() {
+        var opts = this.options;
+        opts.xTicks = opts.xTicks || Math.round(this._width() / 75);
+        opts.yTicks = opts.yTicks || Math.round(this._height() / 30);
+        opts.hoverNumber.formatter = opts.hoverNumber.formatter || this._formatter;
+
+        if (opts.responsive) {
+            if (opts.detached) {
+                var offWi = this.eleDiv.offsetWidth;
+                var offHe = this.eleDiv.offsetHeight;
+                opts.width = offWi > 0 ? offWi : opts.width;
+                opts.height = (offHe - 20) > 0 ? offHe - 20 : opts.height; // 20 = horizontal scrollbar size.
+            } else {
+                opts._maxWidth = opts._maxWidth > opts.width ? opts._maxWidth : opts.width;
+                var containerWidth = this._map._container.clientWidth;
+                opts.width = opts._maxWidth > containerWidth ? containerWidth - 30 : opts.width;
+            }
+        }
+
+        var x = this._x = d3.scaleLinear().range([0, this._width()]);
+        var y = this._y = d3.scaleLinear().range([this._height(), 0]);
+
+        var interpolation = typeof opts.interpolation === 'function' ? opts.interpolation : d3[opts.interpolation];
+
+        var area = this._area = d3.area().curve(interpolation)
+            .x(function(d) {
+                return (d.xDiagCoord = x(d.dist));
+            })
+            .y0(this._height())
+            .y1(function(d) {
+                return y(d.z);
+            });
+        var line = this._line = d3.line()
+            .x(function(d) {
+                return d3.mouse(svg.select("g"))[0];
+            })
+            .y(function(d) {
+                return this._height();
+            });
+
+        var container = d3.select(this._container);
+
+        var svg = container.append("svg")
+            .attr("class", "background")
+            .attr("width", opts.width)
+            .attr("height", opts.height);
+
+        var summary = this.summaryDiv = container.append("div")
+            .attr("class", "elevation-summary " + this.options.summary + "-summary").node();
+
+        this._appendChart(svg);
+        this._updateSummary();
+
+    },
+
+    /**
+     * Inspired by L.Control.Layers
+     */
+    _initToggle: function(container) {
+        //Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released
+        container.setAttribute('aria-haspopup', true);
+
+        if (!this.options.detached) {
+            L.DomEvent
+                .disableClickPropagation(container);
+            //.disableScrollPropagation(container);
+        }
+
+        if (L.Browser.mobile) {
+            L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
+        }
+
+        //L.DomEvent.on(container, 'mousewheel', this._mousewheelHandler, this);
+
+        if (!this.options.detached) {
+            var iconCssClass = "elevation-toggle " + this.options.controlButton.iconCssClass + (this.options.autohide ? "" : " close-button");
+            var link = this._button = L.DomUtil.create('a', iconCssClass, container);
+            link.href = '#';
+            link.title = this.options.controlButton.title;
+
+            if (this.options.collapsed) {
+                this._collapse();
+                if (this.options.autohide) {
+                    L.DomEvent
+                        .on(container, 'mouseover', this._expand, this)
+                        .on(container, 'mouseout', this._collapse, this);
+                } else {
+                    L.DomEvent
+                        .on(link, 'click', L.DomEvent.stop)
+                        .on(link, 'click', this._toggle, this);
+                }
+
+                L.DomEvent.on(link, 'focus', this._toggle, this);
+
+                this._map.on('click', this._collapse, this);
+                // TODO: keyboard accessibility
+            }
+        } else {
+            // TODO: handle autohide when detached=true
+        }
+    },
+
+    _isObject: function(item) {
+        return (item && typeof item === 'object' && !Array.isArray(item));
+    },
+
+    _isJSONDoc: function(doc, lazy) {
+        lazy = typeof lazy === "undefined" ? true : lazy;
+        if (typeof doc === "string" && lazy) {
+            doc = doc.trim();
+            return doc.indexOf("{") == 0 || doc.indexOf("[") == 0;
+        } else {
+            try {
+                JSON.parse(doc.toString());
+            } catch (e) {
+                if (typeof doc === "object" && lazy) return true;
+                console.warn(e);
+                return false;
+            }
+            return true;
+        }
+    },
+
+    _isXMLDoc: function(doc, lazy) {
+        lazy = typeof lazy === "undefined" ? true : lazy;
+        if (typeof doc === "string" && lazy) {
+            doc = doc.trim();
+            return doc.indexOf("<") == 0;
+        } else {
+            var documentElement = (doc ? doc.ownerDocument || doc : 0).documentElement;
+            return documentElement ? documentElement.nodeName !== "HTML" : false;
+        }
+    },
+
+    _isDomVisible: function(elem) {
+        return !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
+    },
+
+    _isVisible: function(elem) {
+        if (!elem) return false;
+
+        let styles = window.getComputedStyle(elem);
+
+        function isVisibleByStyles(elem, styles) {
+            return styles.visibility !== 'hidden' && styles.display !== 'none';
+        }
+
+        function isAboveOtherElements(elem, styles) {
+            let boundingRect = elem.getBoundingClientRect();
+            let left = boundingRect.left + 1;
+            let right = boundingRect.right - 1;
+            let top = boundingRect.top + 1;
+            let bottom = boundingRect.bottom - 1;
+            let above = true;
+
+            let pointerEvents = elem.style.pointerEvents;
+
+            if (styles['pointer-events'] == 'none') elem.style.pointerEvents = 'auto';
+
+            if (document.elementFromPoint(left, top) !== elem) above = false;
+            if (document.elementFromPoint(right, top) !== elem) above = false;
+
+            // Only for completely visible elements
+            // if (document.elementFromPoint(left, bottom) !== elem) above = false;
+            // if (document.elementFromPoint(right, bottom) !== elem) above = false;
+
+            elem.style.pointerEvents = pointerEvents;
+
+            return above;
+        }
+
+        if (!isVisibleByStyles(elem, styles)) return false;
+        if (!isAboveOtherElements(elem, styles)) return false;
+        return true;
+    },
+
+    _lazyLoadJS: function(url, skip) {
+        if (typeof skip == "undefined") {
+            skip = false;
+        }
+        if (skip instanceof Promise) {
+            return skip;
+        }
+        return new Promise(function(resolve, reject) {
+            if (skip) return resolve();
+            var tag = document.createElement("script");
+            tag.addEventListener('load', resolve, { once: true });
+            tag.src = url;
+            document.head.appendChild(tag);
+        });
+    },
+
+    _mouseenterHandler: function() {
+        if (this.fire) {
+            this.fire("elechart_enter", null, true);
+        }
+        if (this._map) {
+            this._map.fire("elechart_enter", null, true);
+        }
+    },
+
+    /*
+     * Handles the moueseover the chart and displays distance and altitude level
+     */
+    _mousemoveHandler: function(d, i, ctx) {
+        if (!this._data || this._data.length === 0 || !this._chartEnabled) {
+            return;
+        }
+        var coords = d3.mouse(this._focusRect.node());
+        var xCoord = coords[0];
+        var item = this._data[this._findItemForX(xCoord)];
+
+        this._hidePositionMarker();
+        this._showDiagramIndicator(item, xCoord);
+        this._showPositionMarker(item);
+        this._setMapView(item);
+
+        if (this._map && this._map._container) {
+            L.DomUtil.addClass(this._map._container, 'elechart-hover');
+        }
+
+        var evt = {
+            data: item
+        };
+        if (this.fire) {
+            this.fire("elechart_change", evt, true);
+            this.fire("elechart_hover", evt, true);
+        }
+        if (this._map) {
+            this._map.fire("elechart_change", evt, true);
+            this._map.fire("elechart_hover", evt, true);
+        }
+    },
+
+    /*
+     * Handles mouseover events of the data layers on the map.
+     */
+    _mousemoveLayerHandler: function(e) {
+        if (!this._data || this._data.length === 0) {
+            return;
+        }
+        var latlng = e.latlng;
+        var item = this._findItemForLatLng(latlng);
+        if (item) {
+            var xCoord = item.xDiagCoord;
+
+            this._hidePositionMarker();
+            this._showDiagramIndicator(item, xCoord);
+            this._showPositionMarker(item);
+        }
+    },
+
+    _mouseoutHandler: function() {
+        if (!this.options.detached) {
+            this._hidePositionMarker();
+        }
+
+        if (this._map && this._map._container) {
+            L.DomUtil.removeClass(this._map._container, 'elechart-hover');
+        }
+
+        if (this.fire) this.fire("elechart_leave", null, true);
+        if (this._map) this._map.fire("elechart_leave", null, true);
+    },
+
+    _mousewheelHandler: function(e) {
+        if (this._map.gestureHandling && this._map.gestureHandling._enabled) return;
+        var ll = this._selectedItem ? this._selectedItem.latlng : this._map.getCenter();
+        var z = e.deltaY > 0 ? this._map.getZoom() - 1 : this._map.getZoom() + 1;
+        this._resetDrag();
+        this._map.flyTo(ll, z);
+
+    },
+
+    /*
+     * Removes the drag rectangle and zoms back to the total extent of the data.
+     */
+    _resetDrag: function() {
+        if (this._dragRectangleG) {
+            this._dragRectangleG.remove();
+            this._dragRectangleG = null;
+            this._dragRectangle = null;
+            this._hidePositionMarker();
+        }
+    },
+
+    _resetView: function() {
+        if (this._map && this._map._isFullscreen) return;
+        this._resetDrag();
+        this._hidePositionMarker();
+        this.fitBounds(this._fullExtent);
+    },
+
+    _resizeChart: function() {
+        if (this.options.responsive) {
+            if (this.options.detached) {
+                var newWidth = this.eleDiv.offsetWidth; // - 20;
+
+                if (newWidth <= 0) return;
+
+                this.options.width = newWidth;
+                this.eleDiv.innerHTML = "";
+                this.eleDiv.appendChild(this.onAdd(this._map));
+            } else {
+                this._map.removeControl(this._container);
+                this.addTo(this._map);
+            }
+        }
+    },
+
+    _showDiagramIndicator: function(item, xCoordinate) {
+        if (!this._chartEnabled) return;
+
+        var opts = this.options;
+        this._focusG.style("visibility", "visible");
+
+        this._mousefocus.attr('x1', xCoordinate)
+            .attr('y1', 0)
+            .attr('x2', xCoordinate)
+            .attr('y2', this._height())
+            .classed('hidden', false);
+
+        var alt = item.z,
+            dist = item.dist,
+            ll = item.latlng,
+            numY = opts.hoverNumber.formatter(alt, opts.hoverNumber.decimalsY),
+            numX = opts.hoverNumber.formatter(dist, opts.hoverNumber.decimalsX);
+
+        this._focuslabeltext
+            // .attr("x", xCoordinate)
+            .attr("y", this._y(item.z))
+            .style("font-weight", "700");
+
+        this._focuslabelX
+            .text(numX + " " + this._xLabel)
+            .attr("x", xCoordinate + 10);
+
+        this._focuslabelY
+            .text(numY + " " + this._yLabel)
+            .attr("x", xCoordinate + 10);
+
+        var focuslabeltext = this._focuslabeltext.node();
+        if (this._isDomVisible(focuslabeltext)) {
+            var bbox = focuslabeltext.getBBox();
+            var padding = 2;
+
+            this._focuslabelrect
+                .attr("x", bbox.x - padding)
+                .attr("y", bbox.y - padding)
+                .attr("width", bbox.width + (padding * 2))
+                .attr("height", bbox.height + (padding * 2));
+
+            // move focus label to left
+            if (xCoordinate >= this._width() / 2) {
+                this._focuslabelrect.attr("x", this._focuslabelrect.attr("x") - this._focuslabelrect.attr("width") - (padding * 2) - 10);
+                this._focuslabelX.attr("x", this._focuslabelX.attr("x") - this._focuslabelrect.attr("width") - (padding * 2) - 10);
+                this._focuslabelY.attr("x", this._focuslabelY.attr("x") - this._focuslabelrect.attr("width") - (padding * 2) - 10);
+            }
+        }
+
+    },
+
+    _toggle: function() {
+        if (L.DomUtil.hasClass(this._container, "elevation-expanded"))
+            this._collapse();
+        else
+            this._expand();
+    },
+
+    _setMapView: function(item) {
+        if (!this.options.followMarker || !this._map) return;
+        var zoom = this._map.getZoom();
+        zoom = zoom < this._zFollow ? this._zFollow : zoom;
+        this._map.setView(item.latlng, zoom, { animate: true, duration: 0.25 });
+    },
+
+    _showPositionMarker: function(item) {
+        this._selectedItem = item;
+
+        if (this._map && !this._map.getPane('elevationPane')) {
+            this._map.createPane('elevationPane');
+            this._map.getPane('elevationPane').style.zIndex = 625; // This pane is above markers but below popups.
+            this._map.getPane('elevationPane').style.pointerEvents = 'none';
+        }
+
+        if (this.options.marker == 'elevation-line') {
+            this._updatePositionMarker(item);
+        } else if (this.options.marker == 'position-marker') {
+            this._updateLeafletMarker(item);
+        }
+    },
+
+    _updateAxis: function() {
+        this._grid.selectAll("g").remove();
+        this._axis.selectAll("g").remove();
+        this._appendXGrid(this._grid);
+        this._appendYGrid(this._grid);
+        this._appendXaxis(this._axis);
+        this._appendYaxis(this._axis);
+    },
+
+    _updateHeightIndicator: function(item) {
+        var opts = this.options;
+
+        var numY = opts.hoverNumber.formatter(item.z, opts.hoverNumber.decimalsY),
+            numX = opts.hoverNumber.formatter(item.dist, opts.hoverNumber.decimalsX);
+
+        var normalizedAlt = this._height() / this._maxElevation * item.z,
+            normalizedY = item.y - normalizedAlt;
+
+        this._mouseHeightFocus
+            .attr("x1", item.x)
+            .attr("x2", item.x)
+            .attr("y1", item.y)
+            .attr("y2", normalizedY)
+            .style("visibility", "visible");
+
+        this._mouseHeightFocusLabel
+            .attr("x", item.x)
+            .attr("y", normalizedY)
+            .text(numY + " " + this._yLabel)
+            .style("visibility", "visible");
+    },
+
+    _updateLeafletMarker: function(item) {
+        var ll = item.latlng;
+
+        if (!this._marker) {
+            this._marker = new L.Marker(ll, {
+                icon: this.options.markerIcon,
+                zIndexOffset: 1000000,
+            });
+            this._marker.addTo(this._map, {
+                pane: 'elevationPane',
+            });
+        } else {
+            this._marker.setLatLng(ll);
+        }
+    },
+
+    _updatePointG: function(item) {
+        this._pointG
+            .attr("transform", "translate(" + item.x + "," + item.y + ")")
+            .style("visibility", "visible");
+    },
+
+    _updatePositionMarker: function(item) {
+        var point = this._map.latLngToLayerPoint(item.latlng);
+        var layerpoint = {
+            dist: item.dist,
+            x: point.x,
+            y: point.y,
+            z: item.z,
+        };
+
+        if (!this._mouseHeightFocus) {
+            L.svg({ pane: "elevationPane" }).addTo(this._map); // default leaflet svg renderer
+            var layerpane = d3.select(this._map.getContainer()).select(".leaflet-elevation-pane svg");
+            this._appendPositionMarker(layerpane);
+        }
+
+        this._updatePointG(layerpoint);
+        this._updateHeightIndicator(layerpoint);
+    },
+
+    _updateSummary: function() {
+        if (this.options.summary && this.summaryDiv) {
+            this.track_info = this.track_info || {};
+            this.track_info.distance = this._distance || 0;
+            this.track_info.elevation_max = this._maxElevation || 0;
+            this.track_info.elevation_min = this._minElevation || 0;
+            d3.select(this.summaryDiv).html('<span class="totlen"><span class="summarylabel">Total Length: </span><span class="summaryvalue">' + this.track_info.distance.toFixed(2) + ' ' + this._xLabel + '</span></span><span class="maxele"><span class="summarylabel">Max Elevation: </span><span class="summaryvalue">' + this.track_info.elevation_max.toFixed(2) + ' ' + this._yLabel + '</span></span><span class="minele"><span class="summarylabel">Min Elevation: </span><span class="summaryvalue">' + this.track_info.elevation_min.toFixed(2) + ' ' + this._yLabel + '</span></span>');
+        }
+        if (this.options.downloadLink && this._downloadURL) { // TODO: generate dynamically file content instead of using static file urls.
+            var span = document.createElement('span');
+            span.className = 'download';
+            var save = document.createElement('a');
+            save.innerHTML = "Download";
+            save.href = "#";
+            save.onclick = function(e) {
+                e.preventDefault();
+                var evt = { confirm: this._saveFile.bind(this, this._downloadURL) };
+                var type = this.options.downloadLink;
+                if (type == 'modal') {
+                    if (typeof CustomEvent === "function") document.dispatchEvent(new CustomEvent("eletrack_download", { detail: evt }));
+                    if (this.fire) this.fire('eletrack_download', evt);
+                    if (this._map) this._map.fire('eletrack_download', evt);
+                } else if (type == 'link' || type === true) {
+                    evt.confirm();
+                }
+            }.bind(this);
+
+            this.summaryDiv.appendChild(span).appendChild(save);
+        }
+    },
+
+    _width: function() {
+        var opts = this.options;
+        return opts.width - opts.margins.left - opts.margins.right;
+    },
+
+});
+
+L.control.elevation = function(options) {
+    return new L.Control.Elevation(options);
+};
diff --git a/static/js/leaflet.extra-markers.js.map b/static/js/leaflet.extra-markers.js.map
new file mode 100755
index 0000000..05faeeb
--- /dev/null
+++ b/static/js/leaflet.extra-markers.js.map
@@ -0,0 +1 @@
+{"version":3,"file":null,"sources":["/Users/cory/Projects/Leaflet.ExtraMarkers/src/assets/js/leaflet.extra-markers.js"],"sourcesContent":["export var ExtraMarkers = L.ExtraMarkers = {};\nExtraMarkers.version = L.ExtraMarkers.version = \"1.2.1\";\nExtraMarkers.Icon = L.ExtraMarkers.Icon = L.Icon.extend({\n    options: {\n        iconSize: [ 35, 45 ],\n        iconAnchor: [ 17, 42 ],\n        popupAnchor: [ 1, -32 ],\n        shadowAnchor: [ 10, 12 ],\n        shadowSize: [ 36, 16 ],\n        className: \"\",\n        prefix: \"\",\n        extraClasses: \"\",\n        shape: \"circle\",\n        icon: \"\",\n        innerHTML: \"\",\n        markerColor: \"red\",\n        svgBorderColor: \"#fff\",\n        svgOpacity: 1,\n        iconColor: \"#fff\",\n        iconRotate: 0,\n        number: \"\",\n        svg: false\n    },\n    initialize: function(options) {\n        options = L.Util.setOptions(this, options);\n    },\n    createIcon: function() {\n        var div = document.createElement(\"div\"), options = this.options;\n        if (options.icon) {\n            div.innerHTML = this._createInner();\n        }\n        if (options.innerHTML) {\n            div.innerHTML = options.innerHTML;\n        }\n        if (options.bgPos) {\n            div.style.backgroundPosition = -options.bgPos.x + \"px \" + -options.bgPos.y + \"px\";\n        }\n        if (!options.svg) {\n            this._setIconStyles(div, options.shape + \"-\" + options.markerColor);\n        } else {\n            this._setIconStyles(div, \"svg\");\n        }\n        return div;\n    },\n    _getColorHex: function (color) {\n        var colorMap = {\n            red: \"#a23337\",\n            \"orange-dark\": \"#d73e29\",\n            orange: \"#ef9227\",\n            yellow: \"#f5bb39\",\n            \"blue-dark\": \"#276273\",\n            cyan: \"#32a9dd\",\n            purple: \"#440444\",\n            violet: \"#90278d\",\n            pink: \"#c057a0\",\n            green: \"#006838\",\n            white: \"#e8e8e8\",\n            black: \"#211c1d\"\n        };\n        return colorMap[color] || color;\n    },\n    _createSvg: function (shape, markerColor) {\n        var svgMap = {\n            circle: '<svg width=\"32\" height=\"44\" viewBox=\"0 0 35 45\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M17.5 2.746c-8.284 0-15 6.853-15 15.307 0 .963.098 1.902.265 2.816a15.413 15.413 0 002.262 5.684l.134.193 12.295 17.785 12.439-17.863.056-.08a15.422 15.422 0 002.343-6.112c.123-.791.206-1.597.206-2.423 0-8.454-6.716-15.307-15-15.307\" fill=\"' + markerColor + '\" /><path d=\"M17.488 2.748c-8.284 0-15 6.853-15 15.307 0 .963.098 1.902.265 2.816a15.413 15.413 0 002.262 5.684l.134.193 12.295 17.785 12.44-17.863.055-.08a15.422 15.422 0 002.343-6.112c.124-.791.206-1.597.206-2.423 0-8.454-6.716-15.307-15-15.307m0 1.071c7.68 0 13.929 6.386 13.929 14.236 0 .685-.064 1.423-.193 2.258-.325 2.075-1.059 3.99-2.164 5.667l-.055.078-11.557 16.595L6.032 26.14l-.12-.174a14.256 14.256 0 01-2.105-5.29 14.698 14.698 0 01-.247-2.62c0-7.851 6.249-14.237 13.928-14.237\" fill=\"#231f20\" opacity=\".15\" /></svg>',\n            square: '<svg width=\"33\" height=\"44\" viewBox=\"0 0 35 45\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M28.205 3.217H6.777c-2.367 0-4.286 1.87-4.286 4.179v19.847c0 2.308 1.919 4.179 4.286 4.179h5.357l5.337 13.58 5.377-13.58h5.357c2.366 0 4.285-1.87 4.285-4.179V7.396c0-2.308-1.919-4.179-4.285-4.179\" fill=\"' + markerColor + '\" /><g opacity=\".15\" transform=\"matrix(1.0714 0 0 -1.0714 -233.22 146.783)\"><path d=\"M244 134h-20c-2.209 0-4-1.746-4-3.9v-18.525c0-2.154 1.791-3.9 4-3.9h5L233.982 95 239 107.675h5c2.209 0 4 1.746 4 3.9V130.1c0 2.154-1.791 3.9-4 3.9m0-1c1.654 0 3-1.301 3-2.9v-18.525c0-1.599-1.346-2.9-3-2.9h-5.68l-.25-.632-4.084-10.318-4.055 10.316-.249.634H224c-1.654 0-3 1.301-3 2.9V130.1c0 1.599 1.346 2.9 3 2.9h20\" fill=\"#231f20\" /></g></svg>',\n            star:   '<svg width=\"34\" height=\"44\" viewBox=\"0 0 35 45\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M32.92 16.93l-3.525-3.525V8.419a1.983 1.983 0 00-1.983-1.982h-4.985L18.9 2.91a1.984 1.984 0 00-2.803 0l-3.524 3.526H7.588a1.983 1.983 0 00-1.982 1.982v4.986L2.081 16.93a1.982 1.982 0 000 2.803l3.525 3.526v4.984c0 1.096.888 1.983 1.982 1.983h4.986L17.457 45l4.97-14.773h4.985a1.983 1.983 0 001.983-1.983V23.26l3.525-3.526a1.982 1.982 0 000-2.803\" fill=\"' + markerColor + '\" /><g opacity=\".15\" transform=\"matrix(1.0667 0 0 -1.0667 -347.3 97.26)\"><path d=\"M342 89c-.476 0-.951-.181-1.314-.544l-3.305-3.305h-4.673a1.858 1.858 0 01-1.859-1.858v-4.674l-3.305-3.305a1.857 1.857 0 010-2.627l3.305-3.305v-4.674a1.86 1.86 0 011.859-1.859h4.673L341.959 49l4.659 13.849h4.674a1.86 1.86 0 011.859 1.859v4.674l3.305 3.305a1.858 1.858 0 010 2.627l-3.305 3.305v4.674a1.859 1.859 0 01-1.859 1.858h-4.674l-3.304 3.305A1.851 1.851 0 01342 89m0-1a.853.853 0 00.607-.251l3.304-3.305.293-.293h5.088a.86.86 0 00.859-.858v-5.088l3.598-3.598A.852.852 0 00356 74a.85.85 0 00-.251-.606l-3.598-3.598v-5.088a.86.86 0 00-.859-.859h-5.393l-.229-.681-3.702-11.006-3.637 11.001-.227.686h-5.396a.86.86 0 00-.859.859v5.088l-3.598 3.598c-.162.162-.251.377-.251.606s.089.445.251.607l3.598 3.598v5.088a.86.86 0 00.859.858h5.087l3.598 3.598A.853.853 0 00342 88\" fill=\"#231f20\" /></g></svg>',\n            penta:  '<svg width=\"33\" height=\"44\" viewBox=\"0 0 35 45\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M1.872 17.35L9.679 2.993h15.615L33.1 17.35 17.486 44.992z\" fill=\"' + markerColor + '\" /><g opacity=\".15\" transform=\"matrix(1.0769 0 0 -1.0769 -272.731 48.23)\"><path d=\"M276.75 42h-14.5L255 28.668 269.5 3 284 28.668zm-.595-1l6.701-12.323L269.5 5.033l-13.356 23.644L262.845 41z\" fill=\"#231f20\" /></g></svg>'\n        };\n        return svgMap[shape];\n    },\n    _createInner: function() {\n        var iconStyle = \"\", iconNumber = \"\", iconClass = \"\", result = \"\", options = this.options;\n        if (options.iconColor) {\n            iconStyle = \"color: \" + options.iconColor + \";\";\n        }\n        if (options.iconRotate !== 0) {\n            iconStyle += \"-webkit-transform: rotate(\" + options.iconRotate + \"deg);\";\n            iconStyle += \"-moz-transform: rotate(\" + options.iconRotate + \"deg);\";\n            iconStyle += \"-o-transform: rotate(\" + options.iconRotate + \"deg);\";\n            iconStyle += \"-ms-transform: rotate(\" + options.iconRotate + \"deg);\";\n            iconStyle += \"transform: rotate(\" + options.iconRotate + \"deg);\";\n        }\n        if (options.number) {\n            iconNumber = 'number=\"' + options.number + '\" ';\n        }\n        if (options.extraClasses.length) {\n            iconClass += options.extraClasses + \" \";\n        }\n        if (options.prefix.length) {\n            iconClass += options.prefix + \" \";\n        }\n        if (options.icon.length) {\n            iconClass += options.icon + \" \";\n        }\n        if (options.svg) {\n            result += this._createSvg(options.shape, this._getColorHex(options.markerColor));\n        }\n        result += '<i ' + iconNumber + 'style=\"' + iconStyle + '\" class=\"' + iconClass + '\"></i>';\n        return result;\n    },\n    _setIconStyles: function(img, name) {\n        var options = this.options, size = L.point(options[name === \"shadow\" ? \"shadowSize\" : \"iconSize\"]), anchor, leafletName;\n        if (name === \"shadow\") {\n            anchor = L.point(options.shadowAnchor || options.iconAnchor);\n            leafletName = \"shadow\";\n        } else {\n            anchor = L.point(options.iconAnchor);\n            leafletName = \"icon\";\n        }\n        if (!anchor && size) {\n            anchor = size.divideBy(2, true);\n        }\n        img.className = \"leaflet-marker-\" + leafletName + \" extra-marker extra-marker-\" + name + \" \" + options.className;\n        if (anchor) {\n            img.style.marginLeft = -anchor.x + \"px\";\n            img.style.marginTop = -anchor.y + \"px\";\n        }\n        if (size) {\n            img.style.width = size.x + \"px\";\n            img.style.height = size.y + \"px\";\n        }\n    },\n    createShadow: function() {\n        var div = document.createElement(\"div\");\n        this._setIconStyles(div, \"shadow\");\n        return div;\n    }\n});\nExtraMarkers.icon = L.ExtraMarkers.icon = function(options) {\n    return new L.ExtraMarkers.Icon(options);\n};"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/static/js/leaflet.extra-markers.min.js b/static/js/leaflet.extra-markers.min.js
new file mode 100755
index 0000000..5b30f12
--- /dev/null
+++ b/static/js/leaflet.extra-markers.min.js
@@ -0,0 +1,10 @@
+/*!
+ * leaflet-extra-markers
+ * Custom Markers for Leaflet JS based on Awesome Markers
+ * Leaflet ExtraMarkers
+ * https://github.com/coryasilva/Leaflet.ExtraMarkers/
+ * @author coryasilva <https://github.com/coryasilva>
+ * @version 1.2.1
+ */
+
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e.leaflet=e.leaflet||{},e.leaflet["extra-markers"]={}))}(this,function(e){"use strict";var t=L.ExtraMarkers={};t.version=L.ExtraMarkers.version="1.2.1",t.Icon=L.ExtraMarkers.Icon=L.Icon.extend({options:{iconSize:[35,45],iconAnchor:[17,42],popupAnchor:[1,-32],shadowAnchor:[10,12],shadowSize:[36,16],className:"",prefix:"",extraClasses:"",shape:"circle",icon:"",innerHTML:"",markerColor:"red",svgBorderColor:"#fff",svgOpacity:1,iconColor:"#fff",iconRotate:0,number:"",svg:!1},initialize:function(e){e=L.Util.setOptions(this,e)},createIcon:function(){var e=document.createElement("div"),t=this.options;return t.icon&&(e.innerHTML=this._createInner()),t.innerHTML&&(e.innerHTML=t.innerHTML),t.bgPos&&(e.style.backgroundPosition=-t.bgPos.x+"px "+-t.bgPos.y+"px"),t.svg?this._setIconStyles(e,"svg"):this._setIconStyles(e,t.shape+"-"+t.markerColor),e},_getColorHex:function(e){return{red:"#a23337","orange-dark":"#d73e29",orange:"#ef9227",yellow:"#f5bb39","blue-dark":"#276273",cyan:"#32a9dd",purple:"#440444",violet:"#90278d",pink:"#c057a0",green:"#006838",white:"#e8e8e8",black:"#211c1d"}[e]||e},_createSvg:function(e,t){return{circle:'<svg width="32" height="44" viewBox="0 0 35 45" xmlns="http://www.w3.org/2000/svg"><path d="M17.5 2.746c-8.284 0-15 6.853-15 15.307 0 .963.098 1.902.265 2.816a15.413 15.413 0 002.262 5.684l.134.193 12.295 17.785 12.439-17.863.056-.08a15.422 15.422 0 002.343-6.112c.123-.791.206-1.597.206-2.423 0-8.454-6.716-15.307-15-15.307" fill="'+t+'" /><path d="M17.488 2.748c-8.284 0-15 6.853-15 15.307 0 .963.098 1.902.265 2.816a15.413 15.413 0 002.262 5.684l.134.193 12.295 17.785 12.44-17.863.055-.08a15.422 15.422 0 002.343-6.112c.124-.791.206-1.597.206-2.423 0-8.454-6.716-15.307-15-15.307m0 1.071c7.68 0 13.929 6.386 13.929 14.236 0 .685-.064 1.423-.193 2.258-.325 2.075-1.059 3.99-2.164 5.667l-.055.078-11.557 16.595L6.032 26.14l-.12-.174a14.256 14.256 0 01-2.105-5.29 14.698 14.698 0 01-.247-2.62c0-7.851 6.249-14.237 13.928-14.237" fill="#231f20" opacity=".15" /></svg>',square:'<svg width="33" height="44" viewBox="0 0 35 45" xmlns="http://www.w3.org/2000/svg"><path d="M28.205 3.217H6.777c-2.367 0-4.286 1.87-4.286 4.179v19.847c0 2.308 1.919 4.179 4.286 4.179h5.357l5.337 13.58 5.377-13.58h5.357c2.366 0 4.285-1.87 4.285-4.179V7.396c0-2.308-1.919-4.179-4.285-4.179" fill="'+t+'" /><g opacity=".15" transform="matrix(1.0714 0 0 -1.0714 -233.22 146.783)"><path d="M244 134h-20c-2.209 0-4-1.746-4-3.9v-18.525c0-2.154 1.791-3.9 4-3.9h5L233.982 95 239 107.675h5c2.209 0 4 1.746 4 3.9V130.1c0 2.154-1.791 3.9-4 3.9m0-1c1.654 0 3-1.301 3-2.9v-18.525c0-1.599-1.346-2.9-3-2.9h-5.68l-.25-.632-4.084-10.318-4.055 10.316-.249.634H224c-1.654 0-3 1.301-3 2.9V130.1c0 1.599 1.346 2.9 3 2.9h20" fill="#231f20" /></g></svg>',star:'<svg width="34" height="44" viewBox="0 0 35 45" xmlns="http://www.w3.org/2000/svg"><path d="M32.92 16.93l-3.525-3.525V8.419a1.983 1.983 0 00-1.983-1.982h-4.985L18.9 2.91a1.984 1.984 0 00-2.803 0l-3.524 3.526H7.588a1.983 1.983 0 00-1.982 1.982v4.986L2.081 16.93a1.982 1.982 0 000 2.803l3.525 3.526v4.984c0 1.096.888 1.983 1.982 1.983h4.986L17.457 45l4.97-14.773h4.985a1.983 1.983 0 001.983-1.983V23.26l3.525-3.526a1.982 1.982 0 000-2.803" fill="'+t+'" /><g opacity=".15" transform="matrix(1.0667 0 0 -1.0667 -347.3 97.26)"><path d="M342 89c-.476 0-.951-.181-1.314-.544l-3.305-3.305h-4.673a1.858 1.858 0 01-1.859-1.858v-4.674l-3.305-3.305a1.857 1.857 0 010-2.627l3.305-3.305v-4.674a1.86 1.86 0 011.859-1.859h4.673L341.959 49l4.659 13.849h4.674a1.86 1.86 0 011.859 1.859v4.674l3.305 3.305a1.858 1.858 0 010 2.627l-3.305 3.305v4.674a1.859 1.859 0 01-1.859 1.858h-4.674l-3.304 3.305A1.851 1.851 0 01342 89m0-1a.853.853 0 00.607-.251l3.304-3.305.293-.293h5.088a.86.86 0 00.859-.858v-5.088l3.598-3.598A.852.852 0 00356 74a.85.85 0 00-.251-.606l-3.598-3.598v-5.088a.86.86 0 00-.859-.859h-5.393l-.229-.681-3.702-11.006-3.637 11.001-.227.686h-5.396a.86.86 0 00-.859.859v5.088l-3.598 3.598c-.162.162-.251.377-.251.606s.089.445.251.607l3.598 3.598v5.088a.86.86 0 00.859.858h5.087l3.598 3.598A.853.853 0 00342 88" fill="#231f20" /></g></svg>',penta:'<svg width="33" height="44" viewBox="0 0 35 45" xmlns="http://www.w3.org/2000/svg"><path d="M1.872 17.35L9.679 2.993h15.615L33.1 17.35 17.486 44.992z" fill="'+t+'" /><g opacity=".15" transform="matrix(1.0769 0 0 -1.0769 -272.731 48.23)"><path d="M276.75 42h-14.5L255 28.668 269.5 3 284 28.668zm-.595-1l6.701-12.323L269.5 5.033l-13.356 23.644L262.845 41z" fill="#231f20" /></g></svg>'}[e]},_createInner:function(){var e="",t="",o="",r="",a=this.options;return a.iconColor&&(e="color: "+a.iconColor+";"),0!==a.iconRotate&&(e+="-webkit-transform: rotate("+a.iconRotate+"deg);",e+="-moz-transform: rotate("+a.iconRotate+"deg);",e+="-o-transform: rotate("+a.iconRotate+"deg);",e+="-ms-transform: rotate("+a.iconRotate+"deg);",e+="transform: rotate("+a.iconRotate+"deg);"),a.number&&(t='number="'+a.number+'" '),a.extraClasses.length&&(o+=a.extraClasses+" "),a.prefix.length&&(o+=a.prefix+" "),a.icon.length&&(o+=a.icon+" "),a.svg&&(r+=this._createSvg(a.shape,this._getColorHex(a.markerColor))),r+="<i "+t+'style="'+e+'" class="'+o+'"></i>'},_setIconStyles:function(e,t){var o,r,a=this.options,n=L.point(a["shadow"===t?"shadowSize":"iconSize"]);r="shadow"===t?(o=L.point(a.shadowAnchor||a.iconAnchor),"shadow"):(o=L.point(a.iconAnchor),"icon"),!o&&n&&(o=n.divideBy(2,!0)),e.className="leaflet-marker-"+r+" extra-marker extra-marker-"+t+" "+a.className,o&&(e.style.marginLeft=-o.x+"px",e.style.marginTop=-o.y+"px"),n&&(e.style.width=n.x+"px",e.style.height=n.y+"px")},createShadow:function(){var e=document.createElement("div");return this._setIconStyles(e,"shadow"),e}}),t.icon=L.ExtraMarkers.icon=function(e){return new L.ExtraMarkers.Icon(e)},e.ExtraMarkers=t,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/static/js/leaflet.hugo.js b/static/js/leaflet.hugo.js
new file mode 100644
index 0000000..8326bbc
--- /dev/null
+++ b/static/js/leaflet.hugo.js
@@ -0,0 +1,108 @@
+let leafletMapsObj = {};
+let leafletMarkersObj = {};
+
+function drawTrack(trackOpts, elevationOpts, markerOpts) {
+    var opts = {
+        elevationControl: {
+            options: {
+                position: elevationOpts.graphPosition,
+                theme: elevationOpts.graphTheme,
+                width: elevationOpts.graphWidth,
+                height: elevationOpts.graphHeight,
+                margins: {
+                    top: 20,
+                    right: 20,
+                    bottom: 35,
+                    left: 50
+                },
+                followMarker: elevationOpts.graphFollowMarker,
+                collapsed: elevationOpts.graphCollapsed,
+                detached: elevationOpts.graphDetached,
+                legend: false,
+                summary: false,
+                downloadLink: '',
+                gpxOptions: {
+                    polyline_options: {
+                        className: 'track-' + trackOpts.trackId + '-',
+                        color: trackOpts.lineColor,
+                        opacity: trackOpts.lineOpacity,
+                        weight: trackOpts.lineWeight,
+                    },
+                    marker_options: {
+                        startIcon: new L.ExtraMarkers.icon({
+                            icon: markerOpts.iconStart,
+                            markerColor: markerOpts.iconStartColor,
+                            shape: markerOpts.iconStartShape,
+                            prefix: 'fa',
+                            extraClasses: markerOpts.iconStartClasses
+                        }),
+                        endIcon: new L.ExtraMarkers.icon({
+                            icon: markerOpts.iconEnd,
+                            markerColor: markerOpts.iconEndColor,
+                            shape: markerOpts.iconEndShape,
+                            prefix: 'fa',
+                            extraClasses: markerOpts.iconEndClasses
+                        }),
+                        wptIcons: {
+                            '': new L.ExtraMarkers.icon({
+                                icon: markerOpts.icon,
+                                markerColor: markerOpts.iconColor,
+                                shape: markerOpts.iconShape,
+                                prefix: 'fa',
+                                extraClasses:  markerOpts.iconClasses,
+                            })
+                        }
+                    }
+                },
+
+            },
+        },
+    };
+
+    L.control.elevation(opts.elevationControl.options).addTo(leafletMapsObj[trackOpts.mapId]).load(trackOpts.trackPath);
+
+    /*map.on('eledata_loaded', function(e) {
+        track = e.track_info;
+    });*/
+}
+
+window.downloadFile = function (sUrl) {
+
+    //iOS devices do not support downloading. We have to inform user about this.
+    if (/(iP)/g.test(navigator.userAgent)) {
+        alert('Your device does not support files downloading. Please try again in desktop browser.');
+        return false;
+    }
+
+    //If in Chrome or Safari - download via virtual link click
+    if (window.downloadFile.isChrome || window.downloadFile.isSafari) {
+        //Creating new link node.
+        var link = document.createElement('a');
+        link.href = sUrl;
+
+        if (link.download !== undefined) {
+            //Set HTML5 download attribute. This will prevent file from opening if supported.
+            var fileName = sUrl.substring(sUrl.lastIndexOf('/') + 1, sUrl.length);
+            link.download = fileName;
+        }
+
+        //Dispatching click event.
+        if (document.createEvent) {
+            var e = document.createEvent('MouseEvents');
+            e.initEvent('click', true, true);
+            link.dispatchEvent(e);
+            return true;
+        }
+    }
+
+    // Force file download (whether supported by server).
+    if (sUrl.indexOf('?') === -1) {
+        sUrl += '?download';
+    }
+
+    window.open(sUrl, '_self');
+    return true;
+};
+
+window.downloadFile.isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
+window.downloadFile.isSafari = navigator.userAgent.toLowerCase().indexOf('safari') > -1;
diff --git a/static/js/map.js b/static/js/map.js
new file mode 100644
index 0000000..d6fab8b
--- /dev/null
+++ b/static/js/map.js
@@ -0,0 +1,41 @@
+function initMap() {
+    if ($('#map').length) {
+        let map_provider = $('#map-provider').val();
+        let lat = $('#map-lat').val();
+        let lng = $('#map-lng').val();
+        let zoom = parseInt($('#map-zoom').val());
+        let address = $('#map-dir').val();
+        let api_key = $('#map-api-key').val();
+        
+        let map = new L.map('map').setView([lat, lng], zoom);
+        if (map_provider === 'mapbox' && api_key.length) {
+            L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
+                attribution:
+                'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://mapbox.com">Mapbox</a>',
+                tileSize: 512,
+                maxZoom: 18,
+                zoomOffset: -1,
+                id: 'mapbox/streets-v11',
+                accessToken: api_key,
+            }).addTo(map);
+        } else {
+            L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+                maxZoom: 19,
+                attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
+            }).addTo(map);
+        }
+        let marker = L.marker([lat, lng]).addTo(map);
+        let url = lat + ',' + lng + '#map=' + zoom + '/' + lat + '/' + lng + '&layers=N';
+        marker.bindPopup(
+            address +
+                '<p><a href="https://www.openstreetmap.org/directions?engine=osrm_car&route=' +
+                url +
+                '">Routing via OpenStreetMap</a></p>',
+        );
+    }
+}
+
+document.addEventListener('DOMContentLoaded', function () {
+    // Initialise street maps if necessary.
+    initMap();
+});
diff --git a/static/site.webmanifest b/static/site.webmanifest
new file mode 100644
index 0000000..45dc8a2
--- /dev/null
+++ b/static/site.webmanifest
@@ -0,0 +1 @@
+{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
\ No newline at end of file