'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; const message = `Are you sure you want to remove ${basic ? 'all elements in the group' : 'the entire route group'}?

Routes to be removed: ${count}`; const onConfirm = () => { $('#routeEditor').dialog('close'); hideGroupSection(); if (basic) routes .select('#' + group) .selectAll('path') .remove(); else routes.select('#' + group).remove(); }; confirmationDialog({title: 'Remove route group', message, confirm: 'Remove', onConfirm}); } 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(); }); 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() { const message = 'Are you sure you want to remove the route?
This action cannot be reverted'; const onConfirm = () => { elSelected.remove(); $('#routeEditor').dialog('close'); }; confirmationDialog({title: 'Remove route', message, confirm: 'Remove', onConfirm}); } function closeRoutesEditor() { elSelected.attr('data-new', null).on('click', null); clearMainTip(); routeSplit.classList.remove('pressed'); routeNew.classList.remove('pressed'); debug.select('#controlPoints').remove(); unselect(); } }