diff --git a/main.js b/main.js index 6e06fb1c..17447830 100644 --- a/main.js +++ b/main.js @@ -128,9 +128,23 @@ let color = d3.scaleSequential(d3.interpolateSpectral); // default color scheme const lineGen = d3.line().curve(d3.curveBasis); // d3 line generator with default curve interpolation // d3 zoom behavior -let scale = 1, - viewX = 0, - viewY = 0; +let scale = 1; +let viewX = 0; +let viewY = 0; + +const zoomThrottled = throttle(doWorkOnZoom, 100); +function zoomed() { + const {k, x, y} = d3.event.transform; + + const isScaleChanged = Boolean(scale - k); + const isPositionChanged = Boolean(viewX - x || viewY - y); + + scale = k; + viewX = x; + viewY = y; + + zoomThrottled(isScaleChanged, isPositionChanged); +} const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", zoomed); // default options @@ -414,30 +428,24 @@ function showWelcomeMessage() { }); } -function zoomed() { - const transform = d3.event.transform; - const scaleDiff = scale - transform.k; - const positionDiff = (viewX - transform.x) | (viewY - transform.y); - if (!positionDiff && !scaleDiff) return; +function doWorkOnZoom(isScaleChanged, isPositionChanged) { + viewbox.attr("transform", `translate(${viewX} ${viewY}) scale(${scale})`); - scale = transform.k; - viewX = transform.x; - viewY = transform.y; - viewbox.attr("transform", transform); + if (isPositionChanged) drawCoordinates(); - // update grid only if view position - if (positionDiff) drawCoordinates(); - - // rescale only if zoom is changed - if (scaleDiff) { + if (isScaleChanged) { invokeActiveZooming(); drawScaleBar(); } // zoom image converter overlay - const canvas = document.getElementById("canvas"); - if (canvas && +canvas.style.opacity) { - const img = document.getElementById("image"); + if (customization === 1) { + const canvas = document.getElementById("canvas"); + if (!canvas || canvas.style.opacity === "0") return; + + const img = document.getElementById("imageToConvert"); + if (!img) return; + const ctx = canvas.getContext("2d"); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.setTransform(scale, 0, 0, scale, viewX, viewY); diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js index 7c8cf5b1..053dc477 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -131,15 +131,8 @@ function editHeightmap() { // Exit customization mode function finalizeHeightmap() { - if (viewbox.select("#heights").selectAll("*").size() < 200) { - tip("Insufficient land area! There should be at least 200 land cells to finalize the heightmap", null, "error"); - return; - } - - if (document.getElementById("imageConverter").offsetParent) { - tip("Please exit the Image Conversion mode first", null, "error"); - return; - } + if (viewbox.select("#heights").selectAll("*").size() < 200) return tip("Insufficient land area! There should be at least 200 land cells to finalize the heightmap", null, "error"); + if (document.getElementById("imageConverter").offsetParent) return tip("Please exit the Image Conversion mode first", null, "error"); delete window.edits; // remove global variable redo.disabled = templateRedo.disabled = true; @@ -1103,6 +1096,10 @@ function editHeightmap() { const reader = new FileReader(); const img = new Image(); + img.id = "imageToConvert"; + img.style.display = "none"; + document.body.appendChild(img); + img.onload = function () { const ctx = document.getElementById("canvas").getContext("2d"); ctx.drawImage(img, 0, 0, graphWidth, graphHeight); @@ -1295,10 +1292,7 @@ function editHeightmap() { } function applyConversion() { - if (colorsAssigned.childElementCount < 3) { - tip("Please do the assignment first", false, "error"); - return; - } + if (colorsAssigned.childElementCount < 3) return tip("Please do the assignment first", false, "error"); viewbox .select("#heights") @@ -1324,6 +1318,9 @@ function editHeightmap() { const canvas = document.getElementById("canvas"); if (canvas) canvas.remove(); + const image = document.getElementById("imageToConvert"); + if (image) image.remove(); + d3.select("#imageConverter").selectAll("div.color-div").remove(); colorsAssigned.style.display = "none"; colorsUnassigned.style.display = "none"; diff --git a/modules/utils.js b/modules/utils.js index afe22287..0194a1ff 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -11,11 +11,11 @@ function getBoundaryPoints(width, height, spacing) { const numberY = Math.ceil(h / bSpacing) - 1; let points = []; for (let i = 0.5; i < numberX; i++) { - let x = Math.ceil(w * i / numberX + offset); + let x = Math.ceil((w * i) / numberX + offset); points.push([x, offset], [x, h + offset]); } for (let i = 0.5; i < numberY; i++) { - let y = Math.ceil(h * i / numberY + offset); + let y = Math.ceil((h * i) / numberY + offset); points.push([offset, y], [w + offset, y]); } return points; @@ -24,7 +24,7 @@ function getBoundaryPoints(width, height, spacing) { // get points on a regular square grid and jitter them a bit function getJitteredGrid(width, height, spacing) { const radius = spacing / 2; // square radius - const jittering = radius * .9; // max deviation + const jittering = radius * 0.9; // max deviation const jitter = () => Math.random() * 2 * jittering - jittering; let points = []; @@ -40,7 +40,7 @@ function getJitteredGrid(width, height, spacing) { // return cell index on a regular square grid function findGridCell(x, y) { - return Math.floor(Math.min(y / grid.spacing, grid.cellsY -1)) * grid.cellsX + Math.floor(Math.min(x / grid.spacing, grid.cellsX-1)); + return Math.floor(Math.min(y / grid.spacing, grid.cellsY - 1)) * grid.cellsX + Math.floor(Math.min(x / grid.spacing, grid.cellsX - 1)); } // return array of cell indexes in radius on a regular square grid @@ -55,14 +55,12 @@ function findGridAll(x, y, radius) { while (r > 1) { let cycle = frontier.slice(); frontier = []; - cycle.forEach(function(s) { - - c[s].forEach(function(e) { + cycle.forEach(function (s) { + c[s].forEach(function (e) { if (found.indexOf(e) !== -1) return; found.push(e); frontier.push(e); }); - }); r--; } @@ -100,7 +98,7 @@ function getGridPolygon(i) { // mbostock's poissonDiscSampler function* poissonDiscSampler(x0, y0, x1, y1, r, k = 3) { - if (!(x1 >= x0) || !(y1 >= y0) || !(r > 0)) throw new Error; + if (!(x1 >= x0) || !(y1 >= y0) || !(r > 0)) throw new Error(); const width = x1 - x0; const height = y1 - y0; @@ -113,8 +111,8 @@ function* poissonDiscSampler(x0, y0, x1, y1, r, k = 3) { const queue = []; function far(x, y) { - const i = x / cellSize | 0; - const j = y / cellSize | 0; + const i = (x / cellSize) | 0; + const j = (y / cellSize) | 0; const i0 = Math.max(i - 2, 0); const j0 = Math.max(j - 2, 0); const i1 = Math.min(i + 3, gridWidth); @@ -134,14 +132,14 @@ function* poissonDiscSampler(x0, y0, x1, y1, r, k = 3) { } function sample(x, y) { - queue.push(grid[gridWidth * (y / cellSize | 0) + (x / cellSize | 0)] = [x, y]); + queue.push((grid[gridWidth * ((y / cellSize) | 0) + ((x / cellSize) | 0)] = [x, y])); return [x + x0, y + y0]; } yield sample(width / 2, height / 2); pick: while (queue.length) { - const i = Math.random() * queue.length | 0; + const i = (Math.random() * queue.length) | 0; const parent = queue[i]; for (let j = 0; j < k; ++j) { @@ -171,20 +169,19 @@ function isWater(i) { } // convert RGB color string to HEX without # -function toHEX(rgb){ - if (rgb.charAt(0) === "#") {return rgb;} +function toHEX(rgb) { + if (rgb.charAt(0) === "#") { + return rgb; + } rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); - return (rgb && rgb.length === 4) ? "#" + - ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + - ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + - ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : ''; + return rgb && rgb.length === 4 ? "#" + ("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) + ("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) + ("0" + parseInt(rgb[3], 10).toString(16)).slice(-2) : ""; } // return array of standard shuffled colors function getColors(number) { - const c12 = ["#dababf","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#c6b9c1","#bc80bd","#ccebc5","#ffed6f","#8dd3c7","#eb8de7"]; + const c12 = ["#dababf", "#fb8072", "#80b1d3", "#fdb462", "#b3de69", "#fccde5", "#c6b9c1", "#bc80bd", "#ccebc5", "#ffed6f", "#8dd3c7", "#eb8de7"]; const cRB = d3.scaleSequential(d3.interpolateRainbow); - const colors = d3.shuffle(d3.range(number).map(i => i < 12 ? c12[i] : d3.color(cRB((i-12)/(number-12))).hex())); + const colors = d3.shuffle(d3.range(number).map(i => (i < 12 ? c12[i] : d3.color(cRB((i - 12) / (number - 12))).hex()))); return colors; } @@ -193,30 +190,42 @@ function getRandomColor() { } // mix a color with a random color -function getMixedColor(color, mix = .2, bright = .3) { +function getMixedColor(color, mix = 0.2, bright = 0.3) { const c = color && color[0] === "#" ? color : getRandomColor(); // if provided color is not hex (e.g. harching), generate random one return d3.color(d3.interpolate(c, getRandomColor())(mix)).brighter(bright).hex(); } // conver temperature from °C to other scales function convertTemperature(c) { - switch(temperatureScale.value) { - case "°C": return c + "°C"; - case "°F": return rn(c * 9 / 5 + 32) + "°F"; - case "K": return rn(c + 273.15) + "K"; - case "°R": return rn((c + 273.15) * 9 / 5) + "°R"; - case "°De": return rn((100 - c) * 3 / 2) + "°De"; - case "°N": return rn(c * 33 / 100) + "°N"; - case "°Ré": return rn(c * 4 / 5) + "°Ré"; - case "°Rø": return rn(c * 21 / 40 + 7.5) + "°Rø"; - default: return c + "°C"; + switch (temperatureScale.value) { + case "°C": + return c + "°C"; + case "°F": + return rn((c * 9) / 5 + 32) + "°F"; + case "K": + return rn(c + 273.15) + "K"; + case "°R": + return rn(((c + 273.15) * 9) / 5) + "°R"; + case "°De": + return rn(((100 - c) * 3) / 2) + "°De"; + case "°N": + return rn((c * 33) / 100) + "°N"; + case "°Ré": + return rn((c * 4) / 5) + "°Ré"; + case "°Rø": + return rn((c * 21) / 40 + 7.5) + "°Rø"; + default: + return c + "°C"; } } // random number in a range function rand(min, max) { if (min === undefined && max === undefined) return Math.random(); - if (max === undefined) {max = min; min = 0;} + if (max === undefined) { + max = min; + min = 0; + } return Math.floor(Math.random() * (max - min + 1)) + min; } @@ -245,7 +254,9 @@ function rn(v, d = 0) { // round string to d decimals function round(s, d = 1) { - return s.replace(/[\d\.-][\d\.e-]*/g, function(n) {return rn(n, d);}) + return s.replace(/[\d\.-][\d\.e-]*/g, function (n) { + return rn(n, d); + }); } // corvent number to short string with SI postfix @@ -279,52 +290,50 @@ function capitalize(string) { // transform string to array [translateX,translateY,rotateDeg,rotateX,rotateY,scale] function parseTransform(string) { - if (!string) {return [0,0,0,0,0,1];} - const a = string.replace(/[a-z()]/g, "").replace(/[ ]/g, ",").split(","); + if (!string) { + return [0, 0, 0, 0, 0, 1]; + } + const a = string + .replace(/[a-z()]/g, "") + .replace(/[ ]/g, ",") + .split(","); return [a[0] || 0, a[1] || 0, a[2] || 0, a[3] || 0, a[4] || 0, a[5] || 1]; } // findAll d3.quandtree search from https://bl.ocks.org/lwthatcher/b41479725e0ff2277c7ac90df2de2b5e -void function addFindAll() { - const Quad = function(node, x0, y0, x1, y1) { +void (function addFindAll() { + const Quad = function (node, x0, y0, x1, y1) { this.node = node; this.x0 = x0; this.y0 = y0; this.x1 = x1; this.y1 = y1; - } + }; - const tree_filter = function(x, y, radius) { + const tree_filter = function (x, y, radius) { var t = {x, y, x0: this._x0, y0: this._y0, x3: this._x1, y3: this._y1, quads: [], node: this._root}; - if (t.node) {t.quads.push(new Quad(t.node, t.x0, t.y0, t.x3, t.y3))}; + if (t.node) { + t.quads.push(new Quad(t.node, t.x0, t.y0, t.x3, t.y3)); + } radiusSearchInit(t, radius); var i = 0; - while (t.q = t.quads.pop()) { + while ((t.q = t.quads.pop())) { i++; // Stop searching if this quadrant can’t contain a closer node. - if (!(t.node = t.q.node) - || (t.x1 = t.q.x0) > t.x3 - || (t.y1 = t.q.y0) > t.y3 - || (t.x2 = t.q.x1) < t.x0 - || (t.y2 = t.q.y1) < t.y0) continue; + if (!(t.node = t.q.node) || (t.x1 = t.q.x0) > t.x3 || (t.y1 = t.q.y0) > t.y3 || (t.x2 = t.q.x1) < t.x0 || (t.y2 = t.q.y1) < t.y0) continue; // Bisect the current quadrant. if (t.node.length) { t.node.explored = true; var xm = (t.x1 + t.x2) / 2, - ym = (t.y1 + t.y2) / 2; + ym = (t.y1 + t.y2) / 2; - t.quads.push( - new Quad(t.node[3], xm, ym, t.x2, t.y2), - new Quad(t.node[2], t.x1, ym, xm, t.y2), - new Quad(t.node[1], xm, t.y1, t.x2, ym), - new Quad(t.node[0], t.x1, t.y1, xm, ym) - ); + t.quads.push(new Quad(t.node[3], xm, ym, t.x2, t.y2), new Quad(t.node[2], t.x1, ym, xm, t.y2), new Quad(t.node[1], xm, t.y1, t.x2, ym), new Quad(t.node[0], t.x1, t.y1, xm, ym)); // Visit the closest quadrant first. - if (t.i = (y >= ym) << 1 | (x >= xm)) { + if ((t.i = ((y >= ym) << 1) | (x >= xm))) { t.q = t.quads[t.quads.length - 1]; t.quads[t.quads.length - 1] = t.quads[t.quads.length - 1 - t.i]; t.quads[t.quads.length - 1 - t.i] = t.q; @@ -334,29 +343,32 @@ void function addFindAll() { // Visit this point. (Visiting coincident points isn’t necessary!) else { var dx = x - +this._x.call(null, t.node.data), - dy = y - +this._y.call(null, t.node.data), - d2 = dx * dx + dy * dy; + dy = y - +this._y.call(null, t.node.data), + d2 = dx * dx + dy * dy; radiusSearchVisit(t, d2); } } return t.result; - } + }; d3.quadtree.prototype.findAll = tree_filter; - var radiusSearchInit = function(t, radius) { + var radiusSearchInit = function (t, radius) { t.result = []; - t.x0 = t.x - radius, t.y0 = t.y - radius; - t.x3 = t.x + radius, t.y3 = t.y + radius; + (t.x0 = t.x - radius), (t.y0 = t.y - radius); + (t.x3 = t.x + radius), (t.y3 = t.y + radius); t.radius = radius * radius; - } + }; - var radiusSearchVisit = function(t, d2) { + var radiusSearchVisit = function (t, d2) { t.node.data.scanned = true; if (d2 < t.radius) { - do {t.result.push(t.node.data); t.node.data.selected = true;} while (t.node = t.node.next); + do { + t.result.push(t.node.data); + t.node.data.selected = true; + } while ((t.node = t.node.next)); } - } -}() + }; +})(); // get segment of any point on polyline function getSegmentId(points, point, step = 10) { @@ -366,23 +378,23 @@ function getSegmentId(points, point, step = 10) { let minSegment = 1; let minDist = Infinity; - for (let i=0; i < points.length-1; i++) { + for (let i = 0; i < points.length - 1; i++) { const p1 = points[i]; - const p2 = points[i+1]; + const p2 = points[i + 1]; const length = Math.sqrt(d2(p1, p2)); const segments = Math.ceil(length / step); const dx = (p2[0] - p1[0]) / segments; const dy = (p2[1] - p1[1]) / segments; - for (let s=0; s < segments; s++) { + for (let s = 0; s < segments; s++) { const x = p1[0] + s * dx; const y = p1[1] + s * dy; const dist2 = d2(point, [x, y]); if (dist2 >= minDist) continue; minDist = dist2; - minSegment = i+1; + minSegment = i + 1; } } @@ -418,7 +430,9 @@ function vowel(c) { // remove vowels from the end of the string function trimVowels(string) { - while (string.length > 3 && vowel(last(string))) {string = string.slice(0,-1);} + while (string.length > 3 && vowel(last(string))) { + string = string.slice(0, -1); + } return string; } @@ -427,7 +441,7 @@ function getAdjective(string) { // special cases for some suffixes if (string.length > 8 && string.slice(-6) === "orszag") return string.slice(0, -6); if (string.length > 6 && string.slice(-4) === "stan") return string.slice(0, -4); - if (P(.5) && string.slice(-4) === "land") return string + "ic"; + if (P(0.5) && string.slice(-4) === "land") return string + "ic"; if (string.slice(-4) === " Guo") string = string.slice(0, -4); // don't change is name ends on suffix @@ -436,16 +450,16 @@ function getAdjective(string) { if (string.slice(-1) === "i") return string; const end = string.slice(-1); // last letter of string - if (end === "a") return string += "n"; - if (end === "o") return string = trimVowels(string) + "an"; - if (vowel(end) || end === "c") return string += "an"; // ceiuy - if (end === "m" || end === "n") return string += "ese"; - if (end === "q") return string += "i"; + if (end === "a") return (string += "n"); + if (end === "o") return (string = trimVowels(string) + "an"); + if (vowel(end) || end === "c") return (string += "an"); // ceiuy + if (end === "m" || end === "n") return (string += "ese"); + if (end === "q") return (string += "i"); return trimVowels(string) + "ian"; } // get ordinal out of integer: 1 => 1st -const nth = n => n+(["st","nd","rd"][((n+90)%100-10)%10-1]||"th"); +const nth = n => n + (["st", "nd", "rd"][((((n + 90) % 100) - 10) % 10) - 1] || "th"); // get two-letters code (abbreviation) from string function abbreviate(name, restricted = []) { @@ -453,8 +467,8 @@ function abbreviate(name, restricted = []) { const words = parsed.split(" "); const letters = words.join(""); - let code = words.length === 2 ? words[0][0]+words[1][0] : letters.slice(0,2); - for (let i = 1; i < letters.length-1 && restricted.includes(code); i++) { + let code = words.length === 2 ? words[0][0] + words[1][0] : letters.slice(0, 2); + for (let i = 1; i < letters.length - 1 && restricted.includes(code); i++) { code = letters[0] + letters[i].toUpperCase(); } return code; @@ -463,7 +477,7 @@ function abbreviate(name, restricted = []) { // conjunct array: [A,B,C] => "A, B and C" function list(array) { if (!Intl.ListFormat) return array.join(", "); - const conjunction = new Intl.ListFormat(window.lang || "en", {style:"long", type:"conjunction"}); + const conjunction = new Intl.ListFormat(window.lang || "en", {style: "long", type: "conjunction"}); return conjunction.format(array); } @@ -472,7 +486,10 @@ function splitInTwo(str) { const half = str.length / 2; const ar = str.split(" "); if (ar.length < 2) return ar; // only one word - let first = "", last = "", middle = "", rest = ""; + let first = "", + last = "", + middle = "", + rest = ""; ar.forEach((w, d) => { if (d + 1 !== ar.length) w += " "; @@ -501,10 +518,10 @@ function ra(array) { function rw(object) { const array = []; for (const key in object) { - for (let i=0; i < object[key]; i++) { + for (let i = 0; i < object[key]; i++) { array.push(key); } - }; + } return array[Math.floor(Math.random() * array.length)]; } @@ -515,33 +532,55 @@ function lim(v) { // get number from string in format "1-3" or "2" or "0.5" function getNumberInRange(r) { - if (typeof r !== "string") {ERROR && console.error("The value should be a string", r); return 0;} + if (typeof r !== "string") { + ERROR && console.error("The value should be a string", r); + return 0; + } if (!isNaN(+r)) return ~~r + +P(r - ~~r); const sign = r[0] === "-" ? -1 : 1; if (isNaN(+r[0])) r = r.slice(1); const range = r.includes("-") ? r.split("-") : null; - if (!range) {ERROR && console.error("Cannot parse the number. Check the format", r); return 0;} + if (!range) { + ERROR && console.error("Cannot parse the number. Check the format", r); + return 0; + } const count = rand(range[0] * sign, +range[1]); - if (isNaN(count) || count < 0) {ERROR && console.error("Cannot parse number. Check the format", r); return 0;} + if (isNaN(count) || count < 0) { + ERROR && console.error("Cannot parse number. Check the format", r); + return 0; + } return count; } // helper function non-used for the generation function drawCellsValue(data) { debug.selectAll("text").remove(); - debug.selectAll("text").data(data).enter().append("text") - .attr("x", (d,i) => pack.cells.p[i][0]).attr("y", (d,i) => pack.cells.p[i][1]).text(d => d); + debug + .selectAll("text") + .data(data) + .enter() + .append("text") + .attr("x", (d, i) => pack.cells.p[i][0]) + .attr("y", (d, i) => pack.cells.p[i][1]) + .text(d => d); } // helper function non-used for the generation function drawPolygons(data) { - const max = d3.max(data), min = d3.min(data), scheme = getColorScheme(); + const max = d3.max(data), + min = d3.min(data), + scheme = getColorScheme(); data = data.map(d => 1 - normalize(d, min, max)); debug.selectAll("polygon").remove(); - debug.selectAll("polygon").data(data).enter().append("polygon") - .attr("points", (d,i) => getPackPolygon(i)) - .attr("fill", d => scheme(d)).attr("stroke", d => scheme(d)); + debug + .selectAll("polygon") + .data(data) + .enter() + .append("polygon") + .attr("points", (d, i) => getPackPolygon(i)) + .attr("fill", d => scheme(d)) + .attr("stroke", d => scheme(d)); } // polyfill for composedPath @@ -552,56 +591,86 @@ function getComposedPath(node) { else if (node.defaultView) parent = node.defaultView; if (parent !== undefined) return [node].concat(getComposedPath(parent)); return [node]; -}; +} // polyfill for replaceAll if (!String.prototype.replaceAll) { - String.prototype.replaceAll = function(str, newStr){ - if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') return this.replace(str, newStr); - return this.replace(new RegExp(str, 'g'), newStr); + String.prototype.replaceAll = function (str, newStr) { + if (Object.prototype.toString.call(str).toLowerCase() === "[object regexp]") return this.replace(str, newStr); + return this.replace(new RegExp(str, "g"), newStr); }; } // get next unused id function getNextId(core, i = 1) { - while (document.getElementById(core+i)) i++; + while (document.getElementById(core + i)) i++; return core + i; } -function debounce(f, ms) { +function debounce(func, ms) { let isCooldown = false; - return function() { + return function () { if (isCooldown) return; - f.apply(this, arguments); + func.apply(this, arguments); isCooldown = true; - setTimeout(() => isCooldown = false, ms); + setTimeout(() => (isCooldown = false), ms); }; } +function throttle(func, ms) { + let isThrottled = false; + let savedArgs; + let savedThis; + + function wrapper() { + if (isThrottled) { + savedArgs = arguments; + savedThis = this; + return; + } + + func.apply(this, arguments); + isThrottled = true; + + setTimeout(function () { + isThrottled = false; + if (savedArgs) { + wrapper.apply(savedThis, savedArgs); + savedArgs = savedThis = null; + } + }, ms); + } + + return wrapper; +} + // parse error to get the readable string in Chrome and Firefox function parseError(error) { - const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1; const errorString = isFirefox ? error.toString() + " " + error.stack : error.stack; - const regex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; - const errorNoURL = errorString.replace(regex, url => '' + last(url.split("/")) + ''); - const errorParsed = errorNoURL.replace(/at /ig, "
  at "); + const regex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi; + const errorNoURL = errorString.replace(regex, url => "" + last(url.split("/")) + ""); + const errorParsed = errorNoURL.replace(/at /gi, "
  at "); return errorParsed; } // polyfills if (Array.prototype.flat === undefined) { - Array.prototype.flat = function() { - return this.reduce((acc, val) => Array.isArray(val) ? acc.concat(val.flat()) : acc.concat(val), []); - } + Array.prototype.flat = function () { + return this.reduce((acc, val) => (Array.isArray(val) ? acc.concat(val.flat()) : acc.concat(val)), []); + }; } // check if string is a valid for JSON parse JSON.isValid = str => { - try {JSON.parse(str);} - catch(e) {return false;} + try { + JSON.parse(str); + } catch (e) { + return false; + } return true; -} +}; function getBase64(url, callback) { const xhr = new XMLHttpRequest(); @@ -640,16 +709,16 @@ function wiki(page) { // wrap URL into html a element function link(URL, description) { - return `${description}` + return `${description}`; } function isCtrlClick(event) { // meta key is cmd key on MacOs - return event.ctrlKey || event.metaKey; + return event.ctrlKey || event.metaKey; } function generateDate(from = 100, to = 1000) { - return new Date(rand(from, to),rand(12),rand(31)).toLocaleDateString("en", {year:'numeric', month:'long', day:'numeric'}); + return new Date(rand(from, to), rand(12), rand(31)).toLocaleDateString("en", {year: "numeric", month: "long", day: "numeric"}); } function getQGIScoordinates(x, y) { @@ -659,15 +728,18 @@ function getQGIScoordinates(x, y) { } // prompt replacer (prompt does not work in Electron) -void function() { +void (function () { const prompt = document.getElementById("prompt"); const form = prompt.querySelector("#promptForm"); - window.prompt = function(promptText = "Please provide an input", options = {default:1, step:.01, min:0, max:100}, callback) { - if (options.default === undefined) {ERROR && console.error("Prompt: options object does not have default value defined"); return;} + window.prompt = function (promptText = "Please provide an input", options = {default: 1, step: 0.01, min: 0, max: 100}, callback) { + if (options.default === undefined) { + ERROR && console.error("Prompt: options object does not have default value defined"); + return; + } const input = prompt.querySelector("#promptInput"); prompt.querySelector("#promptText").innerHTML = promptText; - const type = typeof(options.default) === "number" ? "number" : "text"; + const type = typeof options.default === "number" ? "number" : "text"; input.type = type; if (options.step !== undefined) input.step = options.step; if (options.min !== undefined) input.min = options.min; @@ -676,17 +748,56 @@ void function() { input.value = options.default; prompt.style.display = "block"; - form.addEventListener("submit", event => { - prompt.style.display = "none"; - const v = type === "number" ? +input.value : input.value; - event.preventDefault(); - if (callback) callback(v); - }, {once: true}); - } + form.addEventListener( + "submit", + event => { + prompt.style.display = "none"; + const v = type === "number" ? +input.value : input.value; + event.preventDefault(); + if (callback) callback(v); + }, + {once: true} + ); + }; const cancel = prompt.querySelector("#promptCancel"); - cancel.addEventListener("click", () => prompt.style.display = "none"); -}() + cancel.addEventListener("click", () => (prompt.style.display = "none")); +})(); // indexedDB; ldb object -!function(){function e(t,o){return n?void(n.transaction("s").objectStore("s").get(t).onsuccess=function(e){var t=e.target.result&&e.target.result.v||null;o(t)}):void setTimeout(function(){e(t,o)},100)}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;if(!t)return void ERROR && console.error("indexedDB not supported");var n,o={k:"",v:""},r=t.open("d2",1);r.onsuccess=function(e){n=this.result},r.onerror=function(e){ERROR && console.error("indexedDB request error"),INFO && console.log(e)},r.onupgradeneeded=function(e){n=null;var t=e.target.result.createObjectStore("s",{keyPath:"k"});t.transaction.oncomplete=function(e){n=e.target.db}},window.ldb={get:e,set:function(e,t){o.k=e,o.v=t,n.transaction("s","readwrite").objectStore("s").put(o)}}}(); \ No newline at end of file +!(function () { + function e(t, o) { + return n + ? void (n.transaction("s").objectStore("s").get(t).onsuccess = function (e) { + var t = (e.target.result && e.target.result.v) || null; + o(t); + }) + : void setTimeout(function () { + e(t, o); + }, 100); + } + var t = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + if (!t) return void ERROR && console.error("indexedDB not supported"); + var n, + o = {k: "", v: ""}, + r = t.open("d2", 1); + (r.onsuccess = function (e) { + n = this.result; + }), + (r.onerror = function (e) { + ERROR && console.error("indexedDB request error"), INFO && console.log(e); + }), + (r.onupgradeneeded = function (e) { + n = null; + var t = e.target.result.createObjectStore("s", {keyPath: "k"}); + t.transaction.oncomplete = function (e) { + n = e.target.db; + }; + }), + (window.ldb = { + get: e, + set: function (e, t) { + (o.k = e), (o.v = t), n.transaction("s", "readwrite").objectStore("s").put(o); + } + }); +})();