'use strict'; function editRoute(onClick) { if (customization) return; if (!onClick && elSelected && d3.event.target.id === elSelected.attr('id')) return; closeDialogs('.stable'); if (!layerIsOn('toggleRoutes')) toggleRoutes(); $("#routeEditor").dialog({ title: "Edit Route", resizable: false, position: {my: "center top+60", at: "top", of: d3.event, collision: "fit"}, close: closeRoutesEditor }); debug.append('g').attr('id', 'controlPoints'); const node = onClick ? elSelected.node() : d3.event.target; elSelected = d3.select(node).on('click', addInterimControlPoint); drawControlPoints(node); selectRouteGroup(node); viewbox.on('touchmove mousemove', showEditorTips); if (onClick) toggleRouteCreationMode(); if (modules.editRoute) return; modules.editRoute = true; // add listeners document.getElementById('routeGroupsShow').addEventListener('click', showGroupSection); document.getElementById('routeGroup').addEventListener('change', changeRouteGroup); document.getElementById('routeGroupAdd').addEventListener('click', toggleNewGroupInput); document.getElementById('routeGroupName').addEventListener('change', createNewGroup); document.getElementById('routeGroupRemove').addEventListener('click', removeRouteGroup); document.getElementById('routeGroupsHide').addEventListener('click', hideGroupSection); document.getElementById('routeElevationProfile').addEventListener('click', showElevationProfile); document.getElementById('routeEditStyle').addEventListener('click', editGroupStyle); document.getElementById('routeSplit').addEventListener('click', toggleRouteSplitMode); document.getElementById('routeLegend').addEventListener('click', editRouteLegend); document.getElementById('routeNew').addEventListener('click', toggleRouteCreationMode); document.getElementById('routeRemove').addEventListener('click', removeRoute); function showEditorTips() { showMainTip(); if (routeNew.classList.contains("pressed")) return; if (d3.event.target.id === elSelected.attr("id")) tip("Click to add a control point"); else if (d3.event.target.parentNode.id === "controlPoints") tip("Drag to move, click to delete the control point"); } function drawControlPoints(node) { const l = node.getTotalLength(); const increment = l / Math.ceil(l / 4); for (let i = 0; i <= l; i += increment) { const point = node.getPointAtLength(i); addControlPoint([point.x, point.y]); } routeLength.innerHTML = rn(l * distanceScaleInput.value) + ' ' + distanceUnitInput.value; } function addControlPoint(point, before = null) { debug .select("#controlPoints") .insert("circle", before) .attr("cx", point[0]) .attr("cy", point[1]) .attr("r", 0.6) .call(d3.drag().on("drag", dragControlPoint)) .on("click", clickControlPoint); } function addInterimControlPoint() { const point = d3.mouse(this); const controls = document.getElementById('controlPoints').querySelectorAll('circle'); const points = Array.from(controls).map((circle) => [+circle.getAttribute('cx'), +circle.getAttribute('cy')]); const index = getSegmentId(points, point, 2); addControlPoint(point, ":nth-child(" + (index + 1) + ")"); redrawRoute(); } function dragControlPoint() { this.setAttribute('cx', d3.event.x); this.setAttribute('cy', d3.event.y); redrawRoute(); } function redrawRoute() { lineGen.curve(d3.curveCatmullRom.alpha(0.1)); const points = []; debug .select("#controlPoints") .selectAll("circle") .each(function () { points.push([this.getAttribute("cx"), this.getAttribute("cy")]); }); elSelected.attr('d', round(lineGen(points))); const l = elSelected.node().getTotalLength(); routeLength.innerHTML = rn(l * distanceScaleInput.value) + ' ' + distanceUnitInput.value; if (modules.elevation) showEPForRoute(elSelected.node()); } function showElevationProfile() { modules.elevation = true; showEPForRoute(elSelected.node()); } function showGroupSection() { document.querySelectorAll("#routeEditor > button").forEach(el => (el.style.display = "none")); document.getElementById("routeGroupsSelection").style.display = "inline-block"; } function hideGroupSection() { document.querySelectorAll("#routeEditor > button").forEach(el => (el.style.display = "inline-block")); document.getElementById("routeGroupsSelection").style.display = "none"; document.getElementById("routeGroupName").style.display = "none"; document.getElementById("routeGroupName").value = ""; document.getElementById("routeGroup").style.display = "inline-block"; } function selectRouteGroup(node) { const group = node.parentNode.id; const select = document.getElementById('routeGroup'); select.options.length = 0; // remove all options routes.selectAll("g").each(function () { select.options.add(new Option(this.id, this.id, false, this.id === group)); }); } function changeRouteGroup() { document.getElementById(this.value).appendChild(elSelected.node()); } function toggleNewGroupInput() { if (routeGroupName.style.display === 'none') { routeGroupName.style.display = 'inline-block'; routeGroupName.focus(); routeGroup.style.display = 'none'; } else { routeGroupName.style.display = "none"; routeGroup.style.display = "inline-block"; } } function createNewGroup() { if (!this.value) { tip("Please provide a valid group name"); return; } const group = this.value .toLowerCase() .replace(/ /g, "_") .replace(/[^\w\s]/gi, ""); if (document.getElementById(group)) { tip('Element with this id already exists. Please provide a unique name', false, 'error'); return; } if (Number.isFinite(+group.charAt(0))) { tip('Group name should start with a letter', false, 'error'); return; } // just rename if only 1 element left const oldGroup = elSelected.node().parentNode; const basic = ['roads', 'trails', 'searoutes'].includes(oldGroup.id); if (!basic && oldGroup.childElementCount === 1) { document.getElementById("routeGroup").selectedOptions[0].remove(); document.getElementById("routeGroup").options.add(new Option(group, group, false, true)); oldGroup.id = group; toggleNewGroupInput(); document.getElementById("routeGroupName").value = ""; return; } const newGroup = elSelected.node().parentNode.cloneNode(false); document.getElementById('routes').appendChild(newGroup); newGroup.id = group; document.getElementById('routeGroup').options.add(new Option(group, group, false, true)); document.getElementById(group).appendChild(elSelected.node()); toggleNewGroupInput(); document.getElementById('routeGroupName').value = ''; } function removeRouteGroup() { const group = elSelected.node().parentNode.id; const basic = ['roads', 'trails', 'searoutes'].includes(group); const count = elSelected.node().parentNode.childElementCount; alertMessage.innerHTML = /* html */ `Are you sure you want to remove ${ basic ? "all elements in the group" : "the entire route group" }?

Routes to be removed: ${count}`; $("#alert").dialog({ resizable: false, title: "Remove route group", buttons: { Remove: function () { $(this).dialog("close"); $("#routeEditor").dialog("close"); hideGroupSection(); if (basic) routes .select("#" + group) .selectAll("path") .remove(); else routes.select("#" + group).remove(); }, Cancel: function () { $(this).dialog("close"); } } }); } function editGroupStyle() { const g = elSelected.node().parentNode.id; editStyle('routes', g); } function toggleRouteSplitMode() { document.getElementById('routeNew').classList.remove('pressed'); this.classList.toggle('pressed'); } function clickControlPoint() { if (routeSplit.classList.contains("pressed")) splitRoute(this); else { this.remove(); redrawRoute(); } } function splitRoute(clicked) { lineGen.curve(d3.curveCatmullRom.alpha(0.1)); const group = d3.select(elSelected.node().parentNode); routeSplit.classList.remove("pressed"); const points1 = [], points2 = []; let points = points1; debug .select("#controlPoints") .selectAll("circle") .each(function () { points.push([this.getAttribute("cx"), this.getAttribute("cy")]); if (this === clicked) { points = points2; points.push([this.getAttribute("cx"), this.getAttribute("cy")]); } this.remove(); }); function splitRoute(clicked) { lineGen.curve(d3.curveCatmullRom.alpha(0.1)); const group = d3.select(elSelected.node().parentNode); routeSplit.classList.remove('pressed'); const points1 = [], points2 = []; let points = points1; debug .select('#controlPoints') .selectAll('circle') .each(function () { points.push([this.getAttribute('cx'), this.getAttribute('cy')]); if (this === clicked) { points = points2; points.push([this.getAttribute('cx'), this.getAttribute('cy')]); } this.remove(); }); elSelected.attr('d', round(lineGen(points1))); const id = getNextId('route'); group.append('path').attr('id', id).attr('d', lineGen(points2)); debug.select('#controlPoints').selectAll('circle').remove(); drawControlPoints(elSelected.node()); } function toggleRouteCreationMode() { document.getElementById('routeSplit').classList.remove('pressed'); document.getElementById('routeNew').classList.toggle('pressed'); if (document.getElementById('routeNew').classList.contains('pressed')) { tip('Click on map to add control points', true); viewbox.on('click', addPointOnClick).style('cursor', 'crosshair'); elSelected.on('click', null); } else { clearMainTip(); viewbox.on('click', clicked).style('cursor', 'default'); elSelected.on('click', addInterimControlPoint).attr('data-new', null); } } function addPointOnClick() { // create new route if (!elSelected.attr('data-new')) { debug.select('#controlPoints').selectAll('circle').remove(); const parent = elSelected.node().parentNode; const id = getNextId('route'); elSelected = d3.select(parent).append('path').attr('id', id).attr('data-new', 1); } addControlPoint(d3.mouse(this)); redrawRoute(); } function editRouteLegend() { const id = elSelected.attr('id'); editNotes(id, id); } function removeRoute() { alertMessage.innerHTML = "Are you sure you want to remove the route?"; $("#alert").dialog({ resizable: false, title: "Remove route", buttons: { Remove: function () { $(this).dialog("close"); elSelected.remove(); $("#routeEditor").dialog("close"); }, Cancel: function () { $(this).dialog("close"); } } }); } function closeRoutesEditor() { elSelected.attr('data-new', null).on('click', null); clearMainTip(); routeSplit.classList.remove('pressed'); routeNew.classList.remove('pressed'); debug.select('#controlPoints').remove(); unselect(); } } }