This commit is contained in:
Azgaar 2019-08-31 12:16:36 +03:00
parent 5f9cab4f84
commit cab429a346
58 changed files with 6413 additions and 1489 deletions

View file

@ -10,6 +10,7 @@ function restoreDefaultEvents() {
.on(".drag", null)
.on("click", clicked)
.on("touchmove mousemove", moved);
legend.call(d3.drag().on("start", dragLegendBox));
}
// on viewbox click event - run function based on target
@ -19,7 +20,7 @@ function clicked() {
const parent = el.parentElement, grand = parent.parentElement;
if (parent.id === "rivers") editRiver(); else
if (grand.id === "routes") editRoute(); else
if (el.tagName === "textPath" && grand.parentNode.id === "labels") editLabel(); else
if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel(); else
if (grand.id === "burgLabels") editBurg(); else
if (grand.id === "burgIcons") editBurg(); else
if (parent.id === "terrain") editReliefIcon(); else
@ -65,63 +66,37 @@ function fitContent() {
return !window.chrome ? "-moz-max-content" : "fit-content";
}
// DOM elements sorting on header click
$(".sortable").on("click", function() {
const el = $(this);
// remove sorting for all siblings except of clicked element
el.siblings().removeClass("icon-sort-name-up icon-sort-name-down icon-sort-number-up icon-sort-number-down");
const type = el.hasClass("alphabetically") ? "name" : "number";
let state = "no";
if (el.is("[class*='down']")) state = "asc";
if (el.is("[class*='up']")) state = "desc";
const sortby = el.attr("data-sortby");
const list = el.parent().next(); // get list container element (e.g. "countriesBody")
const lines = list.children("div"); // get list elements
if (state === "no" || state === "asc") { // sort desc
el.removeClass("icon-sort-" + type + "-down");
el.addClass("icon-sort-" + type + "-up");
lines.sort(function(a, b) {
let an = a.getAttribute("data-" + sortby);
if (an === "bottom") {return 1;}
let bn = b.getAttribute("data-" + sortby);
if (bn === "bottom") {return -1;}
if (type === "number") {an = +an; bn = +bn;}
if (an > bn) {return 1;}
if (an < bn) {return -1;}
return 0;
});
}
if (state === "desc") { // sort asc
el.removeClass("icon-sort-" + type + "-up");
el.addClass("icon-sort-" + type + "-down");
lines.sort(function(a, b) {
let an = a.getAttribute("data-" + sortby);
if (an === "bottom") {return 1;}
let bn = b.getAttribute("data-" + sortby);
if (bn === "bottom") {return -1;}
if (type === "number") {an = +an; bn = +bn;}
if (an < bn) {return 1;}
if (an > bn) {return -1;}
return 0;
});
}
lines.detach().appendTo(list);
// apply sorting behaviour for lines on Editor header click
document.querySelectorAll(".sortable").forEach(function(e) {
e.addEventListener("click", function(e) {sortLines(this);});
});
function sortLines(header) {
const type = header.classList.contains("alphabetically") ? "name" : "number";
let order = header.className.includes("-down") ? "-up" : "-down";
if (!header.className.includes("icon-sort") && type === "name") order = "-up";
const headers = header.parentNode;
headers.querySelectorAll("div.sortable").forEach(e => {
e.classList.forEach(c => {if(c.includes("icon-sort")) e.classList.remove(c);});
});
header.classList.add("icon-sort-" + type + order);
applySorting(headers);
}
function applySorting(headers) {
const header = headers.querySelector("[class*='icon-sort']");
const header = headers.querySelector("div[class*='icon-sort']");
if (!header) return;
const sortby = header.dataset.sortby;
const type = header.classList.contains("alphabetically") ? "name" : "number";
const desc = headers.querySelector("[class*='-down']") ? -1 : 1;
const name = header.classList.contains("alphabetically");
const desc = header.className.includes("-down") ? -1 : 1;
const list = headers.nextElementSibling;
const lines = Array.from(list.children);
lines.sort(function(a, b) {
let an = a.getAttribute("data-" + sortby);
let bn = b.getAttribute("data-" + sortby);
if (type === "number") {an = +an; bn = +bn;}
return (an - bn) * desc;
lines.sort((a, b) => {
const an = name ? a.dataset[sortby] : +a.dataset[sortby];
const bn = name ? b.dataset[sortby] : +b.dataset[sortby];
return (an > bn ? 1 : an < bn ? -1 : 0) * desc;
}).forEach(line => list.appendChild(line));
}
@ -187,4 +162,250 @@ function removeBurg(id) {
pack.burgs[id].removed = true;
const cell = pack.burgs[id].cell;
pack.cells.burg[cell] = 0;
}
// draw legend box
function drawLegend(name, data) {
legend.selectAll("*").remove(); // fully redraw every time
legend.attr("data", data.join("|")); // store data
const itemsInCol = +styleLegendColItems.value;
const fontSize = +legend.attr("font-size");
const backClr = styleLegendBack.value;
const opacity = +styleLegendOpacity.value;
const lineHeight = Math.round(fontSize * 1.7);
const colorBoxSize = Math.round(fontSize / 1.7);
const colOffset = fontSize;
const vOffset = fontSize / 2;
// append items
const boxes = legend.append("g").attr("stroke-width", .5).attr("stroke", "#111111").attr("stroke-dasharray", "none");
const labels = legend.append("g").attr("fill", "#000000").attr("stroke", "none");
const columns = Math.ceil(data.length / itemsInCol);
for (let column=0, i=0; column < columns; column++) {
const linesInColumn = Math.ceil(data.length / columns);
const offset = column ? colOffset * 2 + legend.node().getBBox().width : colOffset;
for (let l=0; l < linesInColumn && data[i]; l++, i++) {
boxes.append("rect").attr("fill", data[i][1])
.attr("x", offset).attr("y", lineHeight + l*lineHeight + vOffset)
.attr("width", colorBoxSize).attr("height", colorBoxSize);
labels.append("text").text(data[i][2])
.attr("x", offset + colorBoxSize * 1.6).attr("y", fontSize/1.6 + lineHeight + l*lineHeight + vOffset);
}
}
// append label
const offset = colOffset + legend.node().getBBox().width / 2;
labels.append("text")
.attr("text-anchor", "middle").attr("font-weight", "bold").attr("font-size", "1.2em")
.attr("id", "legendLabel").text(name).attr("x", offset).attr("y", fontSize * 1.1 + vOffset / 2);
// append box
const bbox = legend.node().getBBox();
const width = bbox.width + colOffset * 2;
const height = bbox.height + colOffset / 2 + vOffset;
legend.insert("rect", ":first-child").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height)
.attr("fill", backClr).attr("fill-opacity", opacity);
fitLegendBox();
}
// fit Legend box to map size
function fitLegendBox() {
if (!legend.selectAll("*").size()) return;
const px = isNaN(+legend.attr("data-x")) ? 99 : legend.attr("data-x") / 100;
const py = isNaN(+legend.attr("data-y")) ? 93 : legend.attr("data-y") / 100;
const bbox = legend.node().getBBox();
const x = rn(svgWidth * px - bbox.width), y = rn(svgHeight * py - bbox.height);
legend.attr("transform", `translate(${x},${y})`);
}
// draw legend with the same data, but using different settings
function redrawLegend() {
const name = legend.select("#legendLabel").text();
const data = legend.attr("data").split("|").map(l => l.split(","));
drawLegend(name, data);
}
function dragLegendBox() {
const tr = parseTransform(this.getAttribute("transform"));
const x = +tr[0] - d3.event.x, y = +tr[1] - d3.event.y;
const bbox = legend.node().getBBox();
d3.event.on("drag", function() {
const px = rn((x + d3.event.x + bbox.width) / svgWidth * 100, 2);
const py = rn((y + d3.event.y + bbox.height) / svgHeight * 100, 2);
const transform = `translate(${(x + d3.event.x)},${(y + d3.event.y)})`;
legend.attr("transform", transform).attr("data-x", px).attr("data-y", py);
});
}
function clearLegend() {
legend.selectAll("*").remove();
legend.attr("data", null);
}
// draw color (fill) picker
function createPicker() {
const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%");
const curtain = contaiter.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("opacity", .2);
curtain.on("click", () => contaiter.style("display", "none")).on("mousemove", () => tip("Click to close the picker"));
const picker = contaiter.append("g").attr("id", "picker").call(d3.drag().on("start", dragPicker));
const controls = picker.append("g").attr("id", "pickerControls");
const h = controls.append("g");
h.append("text").attr("x", 4).attr("y", 14).text("H:");
h.append("line").attr("x1", 18).attr("y1", 10).attr("x2", 107).attr("y2", 10);
h.append("circle").attr("cx", 75).attr("cy", 10).attr("r", 5).attr("id", "pickerH");
h.on("mousemove", () => tip("Set palette hue"));
const s = controls.append("g");
s.append("text").attr("x", 113).attr("y", 14).text("S:");
s.append("line").attr("x1", 124).attr("y1", 10).attr("x2", 206).attr("y2", 10)
s.append("circle").attr("cx", 181.4).attr("cy", 10).attr("r", 5).attr("id", "pickerS");
s.on("mousemove", () => tip("Set palette saturation"));
const l = controls.append("g");
l.append("text").attr("x", 213).attr("y", 14).text("L:");
l.append("line").attr("x1", 226).attr("y1", 10).attr("x2", 306).attr("y2", 10);
l.append("circle").attr("cx", 282).attr("cy", 10).attr("r", 5).attr("id", "pickerL");
l.on("mousemove", () => tip("Set palette lightness"));
controls.selectAll("line").on("click", clickPickerControl);
controls.selectAll("circle").call(d3.drag().on("start", dragPickerControl));
const colors = picker.append("g").attr("id", "pickerColors").attr("stroke", "#333333");
const hatches = picker.append("g").attr("id", "pickerHatches").attr("stroke", "#333333");
const hatching = d3.selectAll("g#hatching > pattern");
const number = hatching.size();
const clr = d3.range(number).map(i => d3.hsl(i/number*360, .7, .7).hex());
clr.forEach(function(d, i) {
colors.append("rect").attr("id", "picker_" + d).attr("fill", d).attr("class", i?"":"selected")
.attr("x", i*22+4).attr("y", 20).attr("width", 16).attr("height", 16);
});
hatching.each(function(d, i) {
hatches.append("rect").attr("id", "picker_" + this.id).attr("fill", "url(#" + this.id + ")")
.attr("x", i*22+4).attr("y", 41).attr("width", 16).attr("height", 16);
});
colors.selectAll("rect").on("click", pickerFillClicked).on("mousemove", () => tip("Click to fill with the color"));
hatches.selectAll("rect").on("click", pickerFillClicked).on("mousemove", () => tip("Click to fill with the hatching"));
// append box
const bbox = picker.node().getBBox();
const width = bbox.width + 8;
const height = bbox.height + 9;
const pos = () => tip("Drag to change the picker position");
picker.insert("rect", ":first-child").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "#ffffff").on("mousemove", pos);
picker.insert("text", ":first-child").attr("x", 12).attr("y", -10).attr("id", "pickerLabel").text("Color Picker").on("mousemove", pos);
picker.insert("rect", ":first-child").attr("x", 0).attr("y", -30).attr("width", width).attr("height", 30).attr("id", "pickerHeader").on("mousemove", pos);
picker.attr("transform", `translate(${(svgWidth-width)/2},${(svgHeight-height)/2})`);
}
function updateSelectedRect(fill) {
document.getElementById("picker").querySelector("rect.selected").classList.remove("selected");
document.getElementById("picker").querySelector("rect[fill='"+fill+"']").classList.add("selected");
}
function updatePickerColors() {
const colors = d3.select("#picker > #pickerColors").selectAll("rect");
const number = colors.size();
const h = getPickerControl(pickerH, 360);
const s = getPickerControl(pickerS, 1);
const l = getPickerControl(pickerL, 1);
colors.each(function(d, i) {
const clr = d3.hsl(i/number*180+h, s, l).hex();
this.setAttribute("id", "picker_" + clr);
this.setAttribute("fill", clr);
});
}
function openPicker(fill, callback) {
const picker = d3.select("#picker");
if (!picker.size()) createPicker();
d3.select("#pickerContainer").style("display", "block");
if (fill[0] === "#") {
const hsl = d3.hsl(fill);
if (!isNaN(hsl.h)) setPickerControl(pickerH, hsl.h, 360);
if (!isNaN(hsl.s)) setPickerControl(pickerS, hsl.s, 1);
if (!isNaN(hsl.l)) setPickerControl(pickerL, hsl.l, 1);
updatePickerColors();
}
updateSelectedRect(fill);
openPicker.updateFill = function() {
const selected = document.getElementById("picker").querySelector("rect.selected");
if (!selected) return;
callback(selected.getAttribute("fill"));
}
}
function setPickerControl(control, value, max) {
const min = +control.previousSibling.getAttribute("x1");
const delta = +control.previousSibling.getAttribute("x2") - min;
const percent = value / max;
control.setAttribute("cx", min + delta * percent);
}
function getPickerControl(control, max) {
const min = +control.previousSibling.getAttribute("x1");
const delta = +control.previousSibling.getAttribute("x2") - min;
const current = +control.getAttribute("cx") - min;
return current / delta * max;
}
function dragPicker() {
const tr = parseTransform(this.getAttribute("transform"));
const x = +tr[0] - d3.event.x, y = +tr[1] - d3.event.y;
const picker = d3.select("#picker");
const bbox = picker.node().getBBox();
d3.event.on("drag", function() {
const px = rn((x + d3.event.x + bbox.width) / svgWidth * 100, 2);
const py = rn((y + d3.event.y + bbox.height) / svgHeight * 100, 2);
const transform = `translate(${(x + d3.event.x)},${(y + d3.event.y)})`;
picker.attr("transform", transform).attr("data-x", px).attr("data-y", py);
});
}
function pickerFillClicked() {
updateSelectedRect(this.getAttribute("fill"));
openPicker.updateFill();
}
function clickPickerControl() {
const min = this.getScreenCTM().e;
this.nextSibling.setAttribute("cx", d3.event.x - min);
updatePickerColors();
openPicker.updateFill();
}
function dragPickerControl() {
const min = +this.previousSibling.getAttribute("x1");
const max = +this.previousSibling.getAttribute("x2");
d3.event.on("drag", function() {
const x = Math.max(Math.min(d3.event.x, max), min);
this.setAttribute("cx", x);
updatePickerColors();
openPicker.updateFill();
});
}
// remove all fogging
function unfog() {
defs.select("#fog").selectAll("path").remove();
fogging.selectAll("path").remove();
fogging.attr("display", "none");
}