diff --git a/assets/js/leaflet.elevation.js b/assets/js/leaflet.elevation.js
new file mode 100644
index 0000000..1b0bf91
--- /dev/null
+++ b/assets/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: '',
+ 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: '',
+ 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('Total Length: ' + this.track_info.distance.toFixed(2) + ' ' + this._xLabel + 'Max Elevation: ' + this.track_info.elevation_max.toFixed(2) + ' ' + this._yLabel + 'Min Elevation: ' + this.track_info.elevation_min.toFixed(2) + ' ' + this._yLabel + '');
+ }
+ 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/assets/js/leaflet.extra-markers.js.map b/assets/js/leaflet.extra-markers.js.map
new file mode 100755
index 0000000..05faeeb
--- /dev/null
+++ b/assets/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: '',\n square: '',\n star: '',\n penta: ''\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 += '';\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/assets/js/leaflet.extra-markers.min.js b/assets/js/leaflet.extra-markers.min.js
new file mode 100755
index 0000000..5b30f12
--- /dev/null
+++ b/assets/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
+ * @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:'',square:'',star:'',penta:''}[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+="'},_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/assets/js/leaflet.hugo.js b/assets/js/leaflet.hugo.js
new file mode 100644
index 0000000..8326bbc
--- /dev/null
+++ b/assets/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/content/dates/2021-08-06-FeriAin.md b/content/dates/2021-08-06-FeriAin.md
index de6503b..a9117b8 100644
--- a/content/dates/2021-08-06-FeriAin.md
+++ b/content/dates/2021-08-06-FeriAin.md
@@ -4,3 +4,8 @@ title: "Feri'Ain des Fanfarons 2021 - Thoissey"
date: 2021-08-06T00:00:00+01:00
---
+### Map
+{{}}
+ {{}}
+{{}}
+
diff --git a/content/dates/2021-12-12-PalaisDeJustice.md b/content/dates/2021-12-12-PalaisDeJustice.md
index f0774cf..e7b519f 100644
--- a/content/dates/2021-12-12-PalaisDeJustice.md
+++ b/content/dates/2021-12-12-PalaisDeJustice.md
@@ -4,3 +4,7 @@ title: "Passerelle du Palais de Justice - Lyon"
date: 2021-12-12T00:00:00+01:00
---
+### Map
+{{}}
+ {{}}
+{{}}
diff --git a/content/dates/2022-04-15-PontUSSB-Balances.md b/content/dates/2022-04-15-PontUSSB-Balances.md
index cce58b1..acf6d0a 100644
--- a/content/dates/2022-04-15-PontUSSB-Balances.md
+++ b/content/dates/2022-04-15-PontUSSB-Balances.md
@@ -12,4 +12,9 @@ Balances Publiques en vue de l'Apéro Fanfare du 22 Avril 2022
{{}}
### Vidéo
-{{