diff --git a/public/main.js b/public/main.js index 75be96b13..9f3e52fd8 100644 --- a/public/main.js +++ b/public/main.js @@ -61,6 +61,7 @@ let zones = viewbox.append("g").attr("id", "zones"); let borders = viewbox.append("g").attr("id", "borders"); let stateBorders = borders.append("g").attr("id", "stateBorders"); let provinceBorders = borders.append("g").attr("id", "provinceBorders"); +let journeys = viewbox.append("g").attr("id", "journeys").style("display", "none"); let routes = viewbox.append("g").attr("id", "routes"); let roads = routes.append("g").attr("id", "roads"); let trails = routes.append("g").attr("id", "trails"); @@ -585,6 +586,9 @@ function invokeActiveZooming() { const size = rn((10 / scale ** 0.3) * 2, 2); ruler.selectAll("text").attr("font-size", size); } + + // journey layer: screen-constant sizing + LOD on tier change + if (layerIsOn("toggleJourney")) syncJourneyZoom(scale); } // add drag to upload logic, pull request from @evyatron @@ -651,6 +655,7 @@ async function generate(options) { else delete grid.cells.h; grid.cells.h = await HeightmapGenerator.generate(grid); pack = {}; // reset pack + pack.journeys = []; Features.markupGrid(); addLakesInDeepDepressions(); diff --git a/public/modules/io/load.js b/public/modules/io/load.js index 2bd8fa807..fc116a0c7 100644 --- a/public/modules/io/load.js +++ b/public/modules/io/load.js @@ -357,6 +357,7 @@ async function parseLoadedData(data, mapVersion) { anchors = icons.select("#anchors"); armies = viewbox.select("#armies"); markers = viewbox.select("#markers"); + journeys = viewbox.select("#journeys"); ruler = viewbox.select("#ruler"); fogging = viewbox.select("#fogging"); debug = viewbox.select("#debug"); @@ -371,6 +372,9 @@ async function parseLoadedData(data, mapVersion) { if (!emblems.size()) { emblems = viewbox.insert("g", "#labels").attr("id", "emblems").style("display", "none"); } + if (!journeys.size()) { + journeys = viewbox.insert("g", "#routes").attr("id", "journeys").style("display", "none"); + } } { @@ -413,6 +417,17 @@ async function parseLoadedData(data, mapVersion) { // data[28] had deprecated cells.crossroad pack.cells.routes = data[36] ? JSON.parse(data[36]) : {}; pack.ice = data[39] ? JSON.parse(data[39]) : []; + { + let parsedJourney = null; + if (data[40]) { + try { + parsedJourney = JSON.parse(data[40]); + } catch { + parsedJourney = null; + } + } + pack.journeys = Array.isArray(parsedJourney) ? parsedJourney : []; + } if (data[31]) { const namesDL = data[31].split("/"); @@ -464,6 +479,8 @@ async function parseLoadedData(data, mapVersion) { if (isVisible(icons)) turnOn("toggleBurgIcons"); if (hasChildren(armies) && isVisible(armies)) turnOn("toggleMilitary"); if (hasChild(markers, "svg")) turnOn("toggleMarkers"); + if (isVisible(journeys) && (hasChild(journeys, "path") || hasChild(journeys, "circle"))) + turnOn("toggleJourney"); if (isVisible(ruler)) turnOn("toggleRulers"); if (isVisible(scaleBar)) turnOn("toggleScaleBar"); if (isVisibleNode(ensureEl("vignette"))) turnOn("toggleVignette"); @@ -735,6 +752,7 @@ async function parseLoadedData(data, mapVersion) { // draw data layers (not kept in svg) if (rulers && layerIsOn("toggleRulers")) rulers.draw(); if (layerIsOn("toggleGrid")) drawGrid(); + if (layerIsOn("toggleJourney")) drawJourney(); } { diff --git a/public/modules/io/save.js b/public/modules/io/save.js index 1d5642d62..f6402a415 100644 --- a/public/modules/io/save.js +++ b/public/modules/io/save.js @@ -103,6 +103,9 @@ function prepareMapData() { const routes = JSON.stringify(pack.routes); const zones = JSON.stringify(pack.zones); const ice = JSON.stringify(pack.ice); + const journey = JSON.stringify( + Array.isArray(pack.journeys) ? pack.journeys : [], + ); // store name array only if not the same as default const defaultNB = Names.getNameBases(); @@ -157,7 +160,8 @@ function prepareMapData() { cellRoutes, routes, zones, - ice + ice, + journey ].join("\r\n"); return mapData; } diff --git a/public/modules/ui/hotkeys.js b/public/modules/ui/hotkeys.js index 2290b6c05..f2ddec442 100644 --- a/public/modules/ui/hotkeys.js +++ b/public/modules/ui/hotkeys.js @@ -55,6 +55,7 @@ function handleKeyup(event) { else if ((shift || altShift) && code === "KeyV") overviewRivers(); else if ((shift || altShift) && code === "KeyM") overviewMilitary(); else if ((shift || altShift) && code === "KeyK") overviewMarkers(); + else if ((shift || altShift) && code === "KeyJ") editJourney(); else if ((shift || altShift) && code === "KeyE") viewCellDetails(); else if (key === "!") toggleAddBurg(); else if (key === "@") toggleAddLabel(); diff --git a/public/modules/ui/layers.js b/public/modules/ui/layers.js index 3c601fe8d..a981df617 100644 --- a/public/modules/ui/layers.js +++ b/public/modules/ui/layers.js @@ -59,11 +59,26 @@ function getDefaultPresets() { "toggleIce", "toggleLakes", "toggleMarkers", + "toggleJourney", "toggleRivers", "toggleRoutes", "toggleScaleBar", "toggleVignette" ], + journeyPath: [ + "toggleHeight", + "toggleLakes", + "toggleCells", + "toggleGrid", + "toggleStates", + "toggleZones", + "toggleBorders", + "toggleJourney", + "toggleRoutes", + "toggleBurgIcons", + "toggleLabels", + "toggleMarkers" + ], military: [ "toggleBorders", "toggleBurgIcons", @@ -206,6 +221,7 @@ function drawLayers() { if (layerIsOn("toggleProvinces")) drawProvinces(); if (layerIsOn("toggleZones")) drawZones(); if (layerIsOn("toggleBorders")) drawBorders(); + if (layerIsOn("toggleJourney")) drawJourney(); if (layerIsOn("toggleRoutes")) drawRoutes(); if (layerIsOn("toggleTemperature")) drawTemperature(); if (layerIsOn("togglePopulation")) drawPopulation(); @@ -853,6 +869,35 @@ function toggleMarkers(event) { } } +function drawJourney() { + TIME && console.time("drawJourney"); + const zm = Math.max(+ensureEl("zoomExtentMin").value, 0.01); + JourneyDraw.redraw(defs, journeys, scale, zm); + // Presets only flip layer buttons; they never call toggleJourney/fadeIn. Match visible state to layerIsOn. + if (layerIsOn("toggleJourney")) journeys.style("display", null); + TIME && console.timeEnd("drawJourney"); +} + +function syncJourneyZoom(zoomScale) { + if (!layerIsOn("toggleJourney")) return; + const jn = journeys.node(); + if (!jn || getComputedStyle(jn).display === "none") return; + const zm = Math.max(+ensureEl("zoomExtentMin").value, 0.01); + JourneyDraw.syncZoom(defs, journeys, zoomScale, zm); +} + +function toggleJourney(event) { + if (!layerIsOn("toggleJourney")) { + turnButtonOn("toggleJourney"); + drawJourney(); + if (event && isCtrlClick(event)) editStyle("journeys"); + } else { + if (event && isCtrlClick(event)) return editStyle("journeys"); + $("#journeys").fadeOut(); + turnButtonOff("toggleJourney"); + } +} + function toggleLabels(event) { if (!layerIsOn("toggleLabels")) { turnButtonOn("toggleLabels"); @@ -1022,5 +1067,6 @@ function getLayer(id) { if (id === "toggleLabels") return $("#labels"); if (id === "toggleBurgIcons") return $("#icons"); if (id === "toggleMarkers") return $("#markers"); + if (id === "toggleJourney") return $("#journeys"); if (id === "toggleRulers") return $("#ruler"); } diff --git a/public/modules/ui/style-presets.js b/public/modules/ui/style-presets.js index f1b6b167d..33570a205 100644 --- a/public/modules/ui/style-presets.js +++ b/public/modules/ui/style-presets.js @@ -160,6 +160,8 @@ function applyStyleWithUiRefresh(style) { drawScaleBar(scaleBar, scale); fitScaleBar(scaleBar, svgWidth, svgHeight); + + if (layerIsOn("toggleJourney")) drawJourney(); } function addStylePreset() { @@ -253,6 +255,17 @@ function addStylePreset() { "filter" ], "#ice": ["opacity", "fill", "stroke", "stroke-width", "filter"], + "#journeys": [ + "opacity", + "filter", + "data-line-screen-px", + "data-waypoint-fill", + "data-waypoint-stroke", + "data-waypoint-r-screen-px", + "data-waypoint-ring-screen-px", + "data-outline-color", + "data-outline-screen-px" + ], "#emblems": ["opacity", "stroke-width", "filter"], "#emblems > #stateEmblems": ["data-size"], "#emblems > #provinceEmblems": ["data-size"], diff --git a/public/modules/ui/style.js b/public/modules/ui/style.js index b94a21c5a..b7e2fa26a 100644 --- a/public/modules/ui/style.js +++ b/public/modules/ui/style.js @@ -77,6 +77,40 @@ function getColor(value, scheme = getColorScheme("bright")) { // Toggle style sections on element select styleElementSelect.on("change", selectStyleElement); +function journeyStyleHexForPicker(raw, fallback) { + const fb = fallback || "#000000"; + const s = (raw != null && String(raw).trim() !== "" ? String(raw).trim() : fb); + if (/^#[\da-fA-F]{6}$/.test(s)) return s; + if (/^#[\da-fA-F]{3}$/.test(s)) { + const r = s[1], + g = s[2], + b = s[3]; + return `#${r}${r}${g}${g}${b}${b}`; + } + return fb; +} + +/** Fallbacks if TS bundle not loaded yet; normally mirror `window.Journey.STYLE_DEFAULTS`. */ +function journeyUiDefaults() { + const d = window.Journey && window.Journey.STYLE_DEFAULTS; + return { + gradientFromHex: (d && d.gradientFromHex) || "#e81416", + gradientToHex: (d && d.gradientToHex) || "#70389d", + lineScreenPx: (d && d.lineScreenPx) || 6, + waypointRScreenPx: (d && d.waypointRScreenPx) || 9, + waypointRingScreenPx: (d && d.waypointRingScreenPx) || 4.5, + outlineScreenPx: (d && d.outlineScreenPx) || 2, + solidStroke: (d && d.solidStroke) || "#5c5c70", + waypointFill: (d && d.waypointFill) || "#ffffff", + waypointStroke: (d && d.waypointStroke) || "#000000", + outlineColor: (d && d.outlineColor) || "#000000", + }; +} + +function redrawJourneyIfVisible() { + if (typeof drawJourney === "function" && layerIsOn("toggleJourney")) drawJourney(); +} + function selectStyleElement() { const styleElement = styleElementSelect.value; let el = d3.select("#" + styleElement); @@ -84,7 +118,10 @@ function selectStyleElement() { styleElements.querySelectorAll("tbody").forEach(e => (e.style.display = "none")); // hide all sections // show alert line if layer is not visible - const isLayerOff = styleElement !== "ocean" && (el.style("display") === "none" || !el.selectAll("*").size()); + const isLayerOff = + styleElement !== "ocean" && + styleElement !== "journeys" && + (el.style("display") === "none" || !el.selectAll("*").size()); styleIsOff.style.display = isLayerOff ? "block" : "none"; // active group element @@ -203,6 +240,32 @@ function selectStyleElement() { styleRescaleMarkers.checked = +markers.attr("rescale"); } + if (styleElement === "journeys") { + ensureEl("styleJourney").style.display = "table-row-group"; + const j = el; + const jd = journeyUiDefaults(); + const numAttr = (name, fb) => { + const v = parseFloat(j.attr(name)); + return Number.isFinite(v) ? v : fb; + }; + ensureEl("styleJourneyLineScreenPx").value = numAttr("data-line-screen-px", jd.lineScreenPx); + const wpf = journeyStyleHexForPicker(j.attr("data-waypoint-fill"), jd.waypointFill); + ensureEl("styleJourneyWaypointFill").value = wpf; + ensureEl("styleJourneyWaypointFillOutput").value = wpf; + const wps = journeyStyleHexForPicker(j.attr("data-waypoint-stroke"), jd.waypointStroke); + ensureEl("styleJourneyWaypointStroke").value = wps; + ensureEl("styleJourneyWaypointStrokeOutput").value = wps; + ensureEl("styleJourneyWaypointRScreenPx").value = numAttr("data-waypoint-r-screen-px", jd.waypointRScreenPx); + ensureEl("styleJourneyWaypointRingScreenPx").value = numAttr( + "data-waypoint-ring-screen-px", + jd.waypointRingScreenPx, + ); + const oc = journeyStyleHexForPicker(j.attr("data-outline-color"), jd.outlineColor); + ensureEl("styleJourneyOutlineColor").value = oc; + ensureEl("styleJourneyOutlineColorOutput").value = oc; + ensureEl("styleJourneyOutlineScreenPx").value = numAttr("data-outline-screen-px", jd.outlineScreenPx); + } + if (styleElement === "gridOverlay") { styleGrid.style.display = "block"; styleGridType.value = el.attr("type"); @@ -557,6 +620,44 @@ styleRescaleMarkers.on("change", function () { invokeActiveZooming(); }); +d3.select("#styleJourneyLineScreenPx").on("input", function () { + svg.select("#journeys").attr("data-line-screen-px", this.value); + redrawJourneyIfVisible(); +}); + +d3.select("#styleJourneyWaypointFill").on("input", function () { + ensureEl("styleJourneyWaypointFillOutput").value = this.value; + svg.select("#journeys").attr("data-waypoint-fill", this.value); + redrawJourneyIfVisible(); +}); + +d3.select("#styleJourneyWaypointStroke").on("input", function () { + ensureEl("styleJourneyWaypointStrokeOutput").value = this.value; + svg.select("#journeys").attr("data-waypoint-stroke", this.value); + redrawJourneyIfVisible(); +}); + +d3.select("#styleJourneyWaypointRScreenPx").on("input", function () { + svg.select("#journeys").attr("data-waypoint-r-screen-px", this.value); + redrawJourneyIfVisible(); +}); + +d3.select("#styleJourneyWaypointRingScreenPx").on("input", function () { + svg.select("#journeys").attr("data-waypoint-ring-screen-px", this.value); + redrawJourneyIfVisible(); +}); + +d3.select("#styleJourneyOutlineColor").on("input", function () { + ensureEl("styleJourneyOutlineColorOutput").value = this.value; + svg.select("#journeys").attr("data-outline-color", this.value); + redrawJourneyIfVisible(); +}); + +d3.select("#styleJourneyOutlineScreenPx").on("input", function () { + svg.select("#journeys").attr("data-outline-screen-px", this.value); + redrawJourneyIfVisible(); +}); + styleCoastlineAuto.on("change", function () { coastline.select("#sea_island").attr("auto-filter", +this.checked); styleFilter.style.display = this.checked ? "none" : "block"; diff --git a/public/modules/ui/tools.js b/public/modules/ui/tools.js index 3707263b1..748996b88 100644 --- a/public/modules/ui/tools.js +++ b/public/modules/ui/tools.js @@ -71,6 +71,7 @@ toolsContent.addEventListener("click", function (event) { else if (button === "addRiver") toggleAddRiver(); else if (button === "addRoute") createRoute(); else if (button === "addMarker") toggleAddMarker(); + else if (button === "addJourney") editJourney(); // click to create a new map buttons else if (button === "openSubmapTool") openSubmapTool(); else if (button === "openTransformTool") openTransformTool(); diff --git a/public/styles/ancient.json b/public/styles/ancient.json index d6ed402ca..9f36bde93 100644 --- a/public/styles/ancient.json +++ b/public/styles/ancient.json @@ -99,6 +99,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/atlas.json b/public/styles/atlas.json index 144362be5..4488b45d6 100644 --- a/public/styles/atlas.json +++ b/public/styles/atlas.json @@ -99,6 +99,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/clean.json b/public/styles/clean.json index 9698edc09..b450fe9e2 100644 --- a/public/styles/clean.json +++ b/public/styles/clean.json @@ -100,6 +100,20 @@ "rescale": null, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/cyberpunk.json b/public/styles/cyberpunk.json index c5796f9c4..bf4da2b48 100644 --- a/public/styles/cyberpunk.json +++ b/public/styles/cyberpunk.json @@ -99,6 +99,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/darkSeas.json b/public/styles/darkSeas.json index 5c1840ca6..e73e887f4 100644 --- a/public/styles/darkSeas.json +++ b/public/styles/darkSeas.json @@ -96,6 +96,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/default.json b/public/styles/default.json index cfca04804..e074397d7 100644 --- a/public/styles/default.json +++ b/public/styles/default.json @@ -99,6 +99,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/gloom.json b/public/styles/gloom.json index f06a67db4..08da58d46 100644 --- a/public/styles/gloom.json +++ b/public/styles/gloom.json @@ -100,6 +100,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/light.json b/public/styles/light.json index c28bbd974..d263a03cd 100644 --- a/public/styles/light.json +++ b/public/styles/light.json @@ -99,6 +99,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/monochrome.json b/public/styles/monochrome.json index f05e2a37c..7f5a35660 100644 --- a/public/styles/monochrome.json +++ b/public/styles/monochrome.json @@ -100,6 +100,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/night.json b/public/styles/night.json index d890db4e0..55abcb2fa 100644 --- a/public/styles/night.json +++ b/public/styles/night.json @@ -99,6 +99,20 @@ "rescale": null, "filter": "url(#dropShadow01)" }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#ffffff", diff --git a/public/styles/pale.json b/public/styles/pale.json index b9ce1a6b7..caf5a93e6 100644 --- a/public/styles/pale.json +++ b/public/styles/pale.json @@ -99,6 +99,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/styles/watercolor.json b/public/styles/watercolor.json index b1632825e..480fd4dd9 100644 --- a/public/styles/watercolor.json +++ b/public/styles/watercolor.json @@ -99,6 +99,20 @@ "rescale": 1, "filter": null }, + "#journeys": { + "opacity": null, + "filter": null, + "data-color-mode": "rainbow", + "data-solid-stroke": "#5c5c70", + "data-rainbow-stops": null, + "data-line-screen-px": 6, + "data-waypoint-fill": "#ffffff", + "data-waypoint-stroke": "#000000", + "data-waypoint-r-screen-px": 9, + "data-waypoint-ring-screen-px": 4.5, + "data-outline-color": "#000000", + "data-outline-screen-px": 2 + }, "#prec": { "opacity": null, "stroke": "#000000", diff --git a/public/versioning.js b/public/versioning.js index ecfdb6764..823af693a 100644 --- a/public/versioning.js +++ b/public/versioning.js @@ -30,6 +30,7 @@ if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format o } const latestPublicChanges = [ + "Journey layer: draw and edit a directional path on the map", "Jagged coastlines", "Heightmap Editor: Fill brush", "Editors: undo button", diff --git a/src/controllers/namesbase-editor.ts b/src/controllers/namesbase-editor.ts index e426c4d22..427507578 100644 --- a/src/controllers/namesbase-editor.ts +++ b/src/controllers/namesbase-editor.ts @@ -1,5 +1,6 @@ import { max as d3max, min as d3min, mean, median } from "d3"; import { ensureEl, openURL, rn, unique } from "../utils"; +import { escapeHtml } from "../utils/stringUtils"; addListeners(); @@ -397,14 +398,6 @@ function namesbaseUpload(dataLoaded: string, override = true): void { const unsafe = /[|/]/g; -const escapeHtml = (str: string): string => - str - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); - interface ParseError { id: number; line: string; diff --git a/src/index.html b/src/index.html index 11dbf51e0..0cc10b174 100644 --- a/src/index.html +++ b/src/index.html @@ -472,6 +472,7 @@ + @@ -625,6 +626,13 @@ > Borders +
  • + Journey +
  • Grid + @@ -1479,6 +1488,55 @@ + + + + Path outline color + + + + + + + Path outline (px) + + + + + + Line (screen px) + + + + + + Waypoint fill + + + + + + + Waypoint stroke + + + + + + + Waypoint radius (px) + + + + + + Waypoint ring (px) + + + + + + @@ -2271,6 +2329,13 @@ River +
    Show
    @@ -3103,6 +3168,89 @@ + +