diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..9abecd6 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "rules": {} +} diff --git a/.gitignore b/.gitignore index 2608ec2..e194fa6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store -.vscode \ No newline at end of file +.vscode +node_modules \ No newline at end of file diff --git a/index.htm b/index.htm index fe0b630..0eca3b9 100644 --- a/index.htm +++ b/index.htm @@ -627,6 +627,6 @@

integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous" > - + diff --git a/index.js b/index.js index 2322fb3..230d287 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,10 @@ +import { parseCoordinatesText } from "./js/coordinates.js"; +import { CHSV, CRGB } from "./js/fastled.js"; +import { parseLayoutText } from "./js/layout.js"; +import { palettes } from "./js/palettes.js"; +import { getPatternCode } from "./js/patterns.js"; +import { parsePixelblazeText } from "./js/pixelblaze.js"; + // get some elements by id const buttonPlayPause = document.getElementById("buttonPlayPause"); @@ -86,223 +93,6 @@ let renderFunction = undefined; const currentPalette = null; -const palettes = { - rainbow: [ - "#ff0000", - "#ff4000", - "#ff8000", - "#ffbf00", - "#ffff00", - "#bfff00", - "#80ff00", - "#40ff00", - "#00ff00", - "#00ff40", - "#00ff80", - "#00ffbf", - "#00ffff", - "#00bfff", - "#0080ff", - "#0040ff", - "#0000ff", - "#4000ff", - "#8000ff", - "#bf00ff", - "#ff00ff", - "#ff00bf", - "#ff0080", - "#ff0040", - "#ff0000", - ], - "rainbow stripe": [ - "#ff0000", - "#000000", - "#ff4000", - "#000000", - "#ff8000", - "#000000", - "#ffbf00", - "#000000", - "#ffff00", - "#000000", - "#bfff00", - "#000000", - "#80ff00", - "#000000", - "#40ff00", - "#000000", - "#00ff00", - "#000000", - "#00ff40", - "#000000", - "#00ff80", - "#000000", - "#00ffbf", - "#000000", - "#00ffff", - "#000000", - "#00bfff", - "#000000", - "#0080ff", - "#000000", - "#0040ff", - "#000000", - "#0000ff", - "#000000", - "#4000ff", - "#000000", - "#8000ff", - "#000000", - "#bf00ff", - "#000000", - "#ff00ff", - "#000000", - "#ff00bf", - "#000000", - "#ff0080", - "#000000", - "#ff0040", - "#000000", - "#ff0000", - ], - cloud: [ - "Blue", - "DarkBlue", - "DarkBlue", - "DarkBlue", - "DarkBlue", - "DarkBlue", - "DarkBlue", - "DarkBlue", - "Blue", - "DarkBlue", - "SkyBlue", - "SkyBlue", - "LightBlue", - "White", - "LightBlue", - "SkyBlue", - ], - lava: ["Black", "Maroon", "Black", "Maroon", "DarkRed", "Maroon", "DarkRed", "DarkRed", "DarkRed", "Red", "Orange", "White", "Orange", "Red", "DarkRed"], - ocean: [ - "MidnightBlue", - "DarkBlue", - "MidnightBlue", - "Navy", - "DarkBlue", - "MediumBlue", - "SeaGreen", - "Teal", - "CadetBlue", - "Blue", - "DarkCyan", - "CornflowerBlue", - "Aquamarine", - "SeaGreen", - "Aqua", - "LightSkyBlue", - ], - forest: [ - "DarkGreen", - "DarkGreen", - "DarkOliveGreen", - "DarkGreen", - "Green", - "ForestGreen", - "OliveDrab", - "Green", - "SeaGreen", - "MediumAquamarine", - "LimeGreen", - "YellowGreen", - "LightGreen", - "LawnGreen", - "MediumAquamarine", - "ForestGreen", - ], - party: [ - "#5500ab", - "#84007c", - "#b5004b", - "#e5001b", - "#e81700", - "#b84700", - "#ab7700", - "#abab00", - "#ab5500", - "#dd2200", - "#f2000e", - "#c2003e", - "#8f0071", - "#5f00a1", - "#2f00d0", - "#0007f9", - ], - heat: [ - "#000000", - "#330000", - "#660000", - "#990000", - "#cc0000", - "#ff0000", - "#ff3300", - "#ff6600", - "#ff9900", - "#ffcc00", - "#ffff00", - "#ffff33", - "#ffff66", - "#ffff99", - "#ffffcc", - "#ffffff", - ], - - // Gradient "cpt-city/arendal/temperature", originally from - // http://soliton.vm.bytemark.co.uk/pub/cpt-city/arendal/tn/temperature.png.index.html - temperature: [ - "rgb( 30, 92,179) 0.000%", - "rgb( 30, 92,179) 5.500%", - "rgb( 23,111,193) 5.500%", - "rgb( 23,111,193) 11.170%", - "rgb( 11,142,216) 11.170%", - "rgb( 11,142,216) 16.670%", - "rgb( 4,161,230) 16.670%", - "rgb( 4,161,230) 22.170%", - "rgb( 25,181,241) 22.170%", - "rgb( 25,181,241) 27.830%", - "rgb( 51,188,207) 27.830%", - "rgb( 51,188,207) 33.330%", - "rgb(102,204,206) 33.330%", - "rgb(102,204,206) 38.830%", - "rgb(153,219,184) 38.830%", - "rgb(153,219,184) 44.500%", - "rgb(192,229,136) 44.500%", - "rgb(192,229,136) 50.000%", - "rgb(204,230, 75) 50.000%", - "rgb(204,230, 75) 55.500%", - "rgb(243,240, 29) 55.500%", - "rgb(243,240, 29) 61.170%", - "rgb(254,222, 39) 61.170%", - "rgb(254,222, 39) 66.670%", - "rgb(252,199, 7) 66.670%", - "rgb(252,199, 7) 72.170%", - "rgb(248,157, 14) 72.170%", - "rgb(248,157, 14) 77.830%", - "rgb(245,114, 21) 77.830%", - "rgb(245,114, 21) 83.330%", - "rgb(241, 71, 28) 83.330%", - "rgb(241, 71, 28) 88.830%", - "rgb(219, 30, 38) 88.830%", - "rgb(219, 30, 38) 94.500%", - "rgb(164, 38, 44) 94.500%", - "rgb(164, 38, 44) 100.000%", - ], - - // Gradient "cpt-city/ing/xmas/ib_jul01", originally from - // http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/xmas/tn/ib_jul01.png.index.html - ib_jul01: ["rgb(230, 6, 17) 0.000%", "rgb( 37, 96, 90) 37.010%", "rgb(144,189,106) 52.000%", "rgb(187, 3, 13) 100.000%"], -}; - // event handlers function onCopyCodeClick() { copyElementToClipboard(codeFastLED); @@ -356,8 +146,6 @@ function onFormSubmit(event) { } function onGenerateCode() { - centerX = inputCenterX.value; - centerY = inputCenterY.value; generateCode(); } @@ -410,65 +198,7 @@ function onPaletteChange() { } function onPatternChange() { - let code; - - switch (selectPattern.value) { - case "palette": - code = "return ColorFromPalette(currentPalette, i - offset);"; - break; - case "clockwise palette": - code = "return ColorFromPalette(currentPalette, angles[i] - offset);"; - break; - case "counter-clockwise palette": - code = "return ColorFromPalette(currentPalette, angles[i] + offset);"; - break; - case "outward palette": - code = "return ColorFromPalette(currentPalette, radii[i] - offset);"; - break; - case "inward palette": - code = "return ColorFromPalette(currentPalette, radii[i] + offset);"; - break; - case "north palette": - code = "return ColorFromPalette(currentPalette, coordsY[i] + offset);"; - break; - case "northeast palette": - code = "return ColorFromPalette(currentPalette, coordsX[i] - coordsY[i] - offset);"; - break; - case "east palette": - code = "return ColorFromPalette(currentPalette, coordsX[i] - offset);"; - break; - case "southeast palette": - code = "return ColorFromPalette(currentPalette, coordsX[i] + coordsY[i] - offset);"; - break; - case "south palette": - code = "return ColorFromPalette(currentPalette, coordsY[i] - offset);"; - break; - case "southwest palette": - code = "return ColorFromPalette(currentPalette, coordsX[i] - coordsY[i] + offset);"; - break; - case "west palette": - code = "return ColorFromPalette(currentPalette, coordsX[i] + offset);"; - break; - case "northwest palette": - code = "return ColorFromPalette(currentPalette, coordsX[i] + coordsY[i] + offset);"; - break; - case "red": - code = "return CRGB(255, 0, 0)"; - break; - case "green": - code = "return CRGB(0, 255, 0)"; - break; - case "blue": - code = "return CRGB(0, 0, 255)"; - break; - case "white": - code = "return CRGB(255, 255, 255)"; - break; - case "custom": - code = ""; - break; - } - + const code = getPatternCode(selectPalette.value); inputPreviewCode.value = code; onPreviewCodeChange(); if (!running) window.requestAnimationFrame(render); @@ -486,7 +216,7 @@ function onPreviewCodeChange() { renderError.innerText = ""; try { - renderFunction = Function("i", "coordsX", "coordsY", "angles", "radii", code); + renderFunction = Function("i", "coordsX", "coordsY", "angles", "radii", "ColorFromPalette", "currentPalette", "offset", "CRGB", "CHSV", code); window.requestAnimationFrame(render); } catch (error) { handleRenderFunctionError(error); @@ -555,43 +285,6 @@ function onWindowResize() { } // functions -function hsvToRgb(h, s, v) { - var r, g, b, i, f, p, q, t; - if (arguments.length === 1) { - (s = h.s), (v = h.v), (h = h.h); - } - i = Math.floor(h * 6); - f = h * 6 - i; - p = v * (1 - s); - q = v * (1 - f * s); - t = v * (1 - (1 - f) * s); - switch (i % 6) { - case 0: - (r = v), (g = t), (b = p); - break; - case 1: - (r = q), (g = v), (b = p); - break; - case 2: - (r = p), (g = v), (b = t); - break; - case 3: - (r = p), (g = q), (b = v); - break; - case 4: - (r = t), (g = p), (b = v); - break; - case 5: - (r = v), (g = p), (b = q); - break; - } - return { - r: Math.round(r * 255), - g: Math.round(g * 255), - b: Math.round(b * 255), - }; -} - function ColorFromPalette(palette, index) { while (index > 255) index -= 256; while (index < 0) index += 256; @@ -601,25 +294,6 @@ function ColorFromPalette(palette, index) { return color; } -function CHSV(hue, saturation, value) { - while (hue > 255) hue -= 256; - while (hue < 0) hue += 256; - while (saturation > 255) saturation -= 256; - while (saturation < 0) saturation += 256; - while (value > 255) value -= 256; - while (value < 0) value += 256; - const h = mapNumber(hue, 0, 255, 0.0, 1.0); - const s = mapNumber(saturation, 0, 255, 0.0, 1.0); - const v = mapNumber(value, 0, 255, 0.0, 1.0); - - const { r, g, b } = hsvToRgb(h, s, v); - return `rgb(${r}, ${g}, ${b})`; -} - -function CRGB(r, g, b) { - return `rgb(${r}, ${g}, ${b})`; -} - function copyElementToClipboard(element) { var range = document.createRange(); range.selectNode(element); @@ -639,16 +313,19 @@ function copyLayoutValueToClipboard(element) { } function generateCode() { - let minX256 = (minY256 = minAngle256 = minRadius256 = 1000000); - let maxX256 = (maxY256 = maxAngle256 = maxRadius256 = -1000000); + let minX256, minY256, minAngle256, minRadius256; + let maxX256, maxY256, maxAngle256, maxRadius256; + + minX256 = minY256 = minAngle256 = minRadius256 = 1000000; + maxX256 = maxY256 = maxAngle256 = maxRadius256 = -1000000; // use the center defined by the user const centerX = inputCenterX.value; const centerY = inputCenterY.value; // calculate the angle and radius for each LED, using the defined center - for (led of leds) { - const { index, x, y } = led; + for (const led of leds) { + const { x, y } = led; const radius = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)); const radians = Math.atan2(centerY - y, centerX - x); @@ -667,7 +344,7 @@ function generateCode() { led.radius = radius; } - for (led of leds) { + for (const led of leds) { const { x, y, angle, radius } = led; let x256 = mapNumber(x, minX, maxX, 0, 255); @@ -753,39 +430,12 @@ function mapNumber(l, inMin, inMax, outMin, outMax) { } function parseCoordinates() { - rows = textAreaCoordinates.value?.split("\n").map((line) => line.split("\t").map((s) => parseFloat(s))); - - document.getElementById("codeParsedCoordinates").innerText = JSON.stringify(rows); - - leds = []; - - minX = minY = minAngle = minRadius = 1000000; - maxX = maxY = maxAngle = maxRadius = -1000000; - - let y = -1; - - for (let row of rows) { - const index = parseInt(row[0]); - const x = row[1]; - const y = row[2]; - - if (isNaN(index) || isNaN(x) || isNaN(y)) continue; - - if (x < minX) minX = x; - if (x > maxX) maxX = x; - - if (y < minY) minY = y; - if (y > maxY) maxY = y; + const results = parseCoordinatesText(textAreaCoordinates.value); - leds.push({ - index, - x, - y, - }); - } + // destructure the results into our global (yuck, sorry, working on it) variables + ({ height, leds, maxX, maxY, minX, minY, rows, width } = results); - width = maxX - minX; - height = maxY - minY; + document.getElementById("codeParsedCoordinates").innerText = JSON.stringify(rows); inputWidth.value = width; inputHeight.value = height; @@ -794,42 +444,12 @@ function parseCoordinates() { } function parseLayout() { - rows = textAreaLayout.value?.split("\n").map((line) => line.split("\t").map((s) => parseInt(s))); - - document.getElementById("codeParsedLayout").innerText = JSON.stringify(rows); - - leds = []; - - minX = minY = minAngle = minRadius = 1000000; - maxX = maxY = maxAngle = maxRadius = -1000000; + const results = parseLayoutText(textAreaLayout.value); - let y = -1; + // destructure the results into our global (yuck, sorry, working on it) variables + ({ height, leds, maxX, maxY, minX, minY, rows, width } = results); - for (let y = 0; y < rows.length; y++) { - const row = rows[y]; - for (let x = 0; x < row.length; x++) { - const cell = row[x]; - - if (!cell && cell !== 0) continue; - - const index = parseInt(cell); - - if (x < minX) minX = x; - if (x > maxX) maxX = x; - - if (y < minY) minY = y; - if (y > maxY) maxY = y; - - leds.push({ - index, - x, - y, - }); - } - } - - width = maxX - minX; - height = maxY - minY; + document.getElementById("codeParsedLayout").innerText = JSON.stringify(rows); inputWidth.value = width; inputHeight.value = height; @@ -838,36 +458,12 @@ function parseLayout() { } function parsePixelblaze() { - rows = JSON.parse(textAreaPixelblaze.value); + const results = parsePixelblazeText(textAreaPixelblaze.value); document.getElementById("codeParsedPixelblaze").innerText = JSON.stringify(rows); - leds = []; - - minX = minY = minAngle = minRadius = 1000000; - maxX = maxY = maxAngle = maxRadius = -1000000; - - let index = 0; - - for (let row of rows) { - const x = row[0]; - const y = row[1]; - - if (x < minX) minX = x; - if (x > maxX) maxX = x; - - if (y < minY) minY = y; - if (y > maxY) maxY = y; - - leds.push({ - index: index++, - x, - y, - }); - } - - width = maxX - minX; - height = maxY - minY; + // destructure the results into our global (yuck, sorry, working on it) variables + ({ height, leds, maxX, maxY, minX, minY, rows, width } = results); inputWidth.value = width; inputHeight.value = height; @@ -896,11 +492,11 @@ function render() { context.clearRect(0, 0, canvasWidth, canvasHeight); - for (let led of leds || []) { + for (const led of leds || []) { let fillStyle; try { - fillStyle = renderFunction(led.index, coordsX, coordsY, angles, radii); + fillStyle = renderFunction(led.index, coordsX, coordsY, angles, radii, ColorFromPalette, currentPalette, offset, CRGB, CHSV); } catch (error) { handleRenderFunctionError(error); return; diff --git a/js/coordinates.js b/js/coordinates.js new file mode 100644 index 0000000..f246f00 --- /dev/null +++ b/js/coordinates.js @@ -0,0 +1,52 @@ +export function parseCoordinatesText(text) { + // split the newline delimited text into lines + const lines = text?.split("\n"); + + // map over the lines, convert to rows (array of float number columns) + const rows = lines.map((line) => { + const columns = line.split("\t"); + // parse the string columns into integer numbers + return columns.map((s) => parseFloat(s)); + }); + + const leds = []; + + let minX, minY, maxX, maxY, width, height; + + minX = minY = 1000000; + maxX = maxY = -1000000; + + for (const row of rows) { + const index = parseInt(row[0]); + const x = row[1]; + const y = row[2]; + + if (isNaN(index) || isNaN(x) || isNaN(y)) continue; + + if (x < minX) minX = x; + if (x > maxX) maxX = x; + + if (y < minY) minY = y; + if (y > maxY) maxY = y; + + leds.push({ + index, + x, + y, + }); + } + + width = maxX - minX; + height = maxY - minY; + + return { + height, + leds, + maxX, + maxY, + minX, + minY, + rows, + width, + }; +} diff --git a/js/fastled.js b/js/fastled.js new file mode 100644 index 0000000..a884427 --- /dev/null +++ b/js/fastled.js @@ -0,0 +1,55 @@ +function hsvToRgb(h, s, v) { + var r, g, b, i, f, p, q, t; + if (arguments.length === 1) { + (s = h.s), (v = h.v), (h = h.h); + } + i = Math.floor(h * 6); + f = h * 6 - i; + p = v * (1 - s); + q = v * (1 - f * s); + t = v * (1 - (1 - f) * s); + switch (i % 6) { + case 0: + (r = v), (g = t), (b = p); + break; + case 1: + (r = q), (g = v), (b = p); + break; + case 2: + (r = p), (g = v), (b = t); + break; + case 3: + (r = p), (g = q), (b = v); + break; + case 4: + (r = t), (g = p), (b = v); + break; + case 5: + (r = v), (g = p), (b = q); + break; + } + return { + r: Math.round(r * 255), + g: Math.round(g * 255), + b: Math.round(b * 255), + }; +} + +export function CHSV(hue, saturation, value) { + while (hue > 255) hue -= 256; + while (hue < 0) hue += 256; + while (saturation > 255) saturation -= 256; + while (saturation < 0) saturation += 256; + while (value > 255) value -= 256; + while (value < 0) value += 256; + const h = mapNumber(hue, 0, 255, 0.0, 1.0); + const s = mapNumber(saturation, 0, 255, 0.0, 1.0); + const v = mapNumber(value, 0, 255, 0.0, 1.0); + + const { r, g, b } = hsvToRgb(h, s, v); + return `rgb(${r}, ${g}, ${b})`; +} + +export function CRGB(r, g, b) { + return `rgb(${r}, ${g}, ${b})`; +} diff --git a/js/layout.js b/js/layout.js new file mode 100644 index 0000000..68b6d2d --- /dev/null +++ b/js/layout.js @@ -0,0 +1,56 @@ +export function parseLayoutText(text) { + // split the newline delimited text into lines + const lines = text?.split("\n"); + + // map over the lines, convert to rows (array of int number columns) + const rows = lines.map((line) => { + // split the tab-delimited line into columns + const columns = line.split("\t"); + // parse the string columns into integer numbers + return columns.map((s) => parseInt(s)); + }); + + const leds = []; + + let minX, minY, maxX, maxY, width, height; + + minX = minY = 1000000; + maxX = maxY = -1000000; + + for (let y = 0; y < rows.length; y++) { + const row = rows[y]; + for (let x = 0; x < row.length; x++) { + const cell = row[x]; + + if (!cell && cell !== 0) continue; + + const index = parseInt(cell); + + if (x < minX) minX = x; + if (x > maxX) maxX = x; + + if (y < minY) minY = y; + if (y > maxY) maxY = y; + + leds.push({ + index, + x, + y, + }); + } + } + + width = maxX - minX; + height = maxY - minY; + + return { + height, + leds, + maxX, + maxY, + minX, + minY, + rows, + width, + }; +} diff --git a/js/palettes.js b/js/palettes.js new file mode 100644 index 0000000..da75d71 --- /dev/null +++ b/js/palettes.js @@ -0,0 +1,216 @@ +export const palettes = { + rainbow: [ + "#ff0000", + "#ff4000", + "#ff8000", + "#ffbf00", + "#ffff00", + "#bfff00", + "#80ff00", + "#40ff00", + "#00ff00", + "#00ff40", + "#00ff80", + "#00ffbf", + "#00ffff", + "#00bfff", + "#0080ff", + "#0040ff", + "#0000ff", + "#4000ff", + "#8000ff", + "#bf00ff", + "#ff00ff", + "#ff00bf", + "#ff0080", + "#ff0040", + "#ff0000", + ], + "rainbow stripe": [ + "#ff0000", + "#000000", + "#ff4000", + "#000000", + "#ff8000", + "#000000", + "#ffbf00", + "#000000", + "#ffff00", + "#000000", + "#bfff00", + "#000000", + "#80ff00", + "#000000", + "#40ff00", + "#000000", + "#00ff00", + "#000000", + "#00ff40", + "#000000", + "#00ff80", + "#000000", + "#00ffbf", + "#000000", + "#00ffff", + "#000000", + "#00bfff", + "#000000", + "#0080ff", + "#000000", + "#0040ff", + "#000000", + "#0000ff", + "#000000", + "#4000ff", + "#000000", + "#8000ff", + "#000000", + "#bf00ff", + "#000000", + "#ff00ff", + "#000000", + "#ff00bf", + "#000000", + "#ff0080", + "#000000", + "#ff0040", + "#000000", + "#ff0000", + ], + cloud: [ + "Blue", + "DarkBlue", + "DarkBlue", + "DarkBlue", + "DarkBlue", + "DarkBlue", + "DarkBlue", + "DarkBlue", + "Blue", + "DarkBlue", + "SkyBlue", + "SkyBlue", + "LightBlue", + "White", + "LightBlue", + "SkyBlue", + ], + lava: ["Black", "Maroon", "Black", "Maroon", "DarkRed", "Maroon", "DarkRed", "DarkRed", "DarkRed", "Red", "Orange", "White", "Orange", "Red", "DarkRed"], + ocean: [ + "MidnightBlue", + "DarkBlue", + "MidnightBlue", + "Navy", + "DarkBlue", + "MediumBlue", + "SeaGreen", + "Teal", + "CadetBlue", + "Blue", + "DarkCyan", + "CornflowerBlue", + "Aquamarine", + "SeaGreen", + "Aqua", + "LightSkyBlue", + ], + forest: [ + "DarkGreen", + "DarkGreen", + "DarkOliveGreen", + "DarkGreen", + "Green", + "ForestGreen", + "OliveDrab", + "Green", + "SeaGreen", + "MediumAquamarine", + "LimeGreen", + "YellowGreen", + "LightGreen", + "LawnGreen", + "MediumAquamarine", + "ForestGreen", + ], + party: [ + "#5500ab", + "#84007c", + "#b5004b", + "#e5001b", + "#e81700", + "#b84700", + "#ab7700", + "#abab00", + "#ab5500", + "#dd2200", + "#f2000e", + "#c2003e", + "#8f0071", + "#5f00a1", + "#2f00d0", + "#0007f9", + ], + heat: [ + "#000000", + "#330000", + "#660000", + "#990000", + "#cc0000", + "#ff0000", + "#ff3300", + "#ff6600", + "#ff9900", + "#ffcc00", + "#ffff00", + "#ffff33", + "#ffff66", + "#ffff99", + "#ffffcc", + "#ffffff", + ], + + // Gradient "cpt-city/arendal/temperature", originally from + // http://soliton.vm.bytemark.co.uk/pub/cpt-city/arendal/tn/temperature.png.index.html + temperature: [ + "rgb( 30, 92,179) 0.000%", + "rgb( 30, 92,179) 5.500%", + "rgb( 23,111,193) 5.500%", + "rgb( 23,111,193) 11.170%", + "rgb( 11,142,216) 11.170%", + "rgb( 11,142,216) 16.670%", + "rgb( 4,161,230) 16.670%", + "rgb( 4,161,230) 22.170%", + "rgb( 25,181,241) 22.170%", + "rgb( 25,181,241) 27.830%", + "rgb( 51,188,207) 27.830%", + "rgb( 51,188,207) 33.330%", + "rgb(102,204,206) 33.330%", + "rgb(102,204,206) 38.830%", + "rgb(153,219,184) 38.830%", + "rgb(153,219,184) 44.500%", + "rgb(192,229,136) 44.500%", + "rgb(192,229,136) 50.000%", + "rgb(204,230, 75) 50.000%", + "rgb(204,230, 75) 55.500%", + "rgb(243,240, 29) 55.500%", + "rgb(243,240, 29) 61.170%", + "rgb(254,222, 39) 61.170%", + "rgb(254,222, 39) 66.670%", + "rgb(252,199, 7) 66.670%", + "rgb(252,199, 7) 72.170%", + "rgb(248,157, 14) 72.170%", + "rgb(248,157, 14) 77.830%", + "rgb(245,114, 21) 77.830%", + "rgb(245,114, 21) 83.330%", + "rgb(241, 71, 28) 83.330%", + "rgb(241, 71, 28) 88.830%", + "rgb(219, 30, 38) 88.830%", + "rgb(219, 30, 38) 94.500%", + "rgb(164, 38, 44) 94.500%", + "rgb(164, 38, 44) 100.000%", + ], + + // Gradient "cpt-city/ing/xmas/ib_jul01", originally from + // http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/xmas/tn/ib_jul01.png.index.html + ib_jul01: ["rgb(230, 6, 17) 0.000%", "rgb( 37, 96, 90) 37.010%", "rgb(144,189,106) 52.000%", "rgb(187, 3, 13) 100.000%"], +}; diff --git a/js/patterns.js b/js/patterns.js new file mode 100644 index 0000000..3b8967e --- /dev/null +++ b/js/patterns.js @@ -0,0 +1,40 @@ +export function getPatternCode(patternName) { + switch (selectPattern.value) { + case "palette": + return "return ColorFromPalette(currentPalette, i - offset);"; + case "clockwise palette": + return "return ColorFromPalette(currentPalette, angles[i] - offset);"; + case "counter-clockwise palette": + return "return ColorFromPalette(currentPalette, angles[i] + offset);"; + case "outward palette": + return "return ColorFromPalette(currentPalette, radii[i] - offset);"; + case "inward palette": + return "return ColorFromPalette(currentPalette, radii[i] + offset);"; + case "north palette": + return "return ColorFromPalette(currentPalette, coordsY[i] + offset);"; + case "northeast palette": + return "return ColorFromPalette(currentPalette, coordsX[i] - coordsY[i] - offset);"; + case "east palette": + return "return ColorFromPalette(currentPalette, coordsX[i] - offset);"; + case "southeast palette": + return "return ColorFromPalette(currentPalette, coordsX[i] + coordsY[i] - offset);"; + case "south palette": + return "return ColorFromPalette(currentPalette, coordsY[i] - offset);"; + case "southwest palette": + return "return ColorFromPalette(currentPalette, coordsX[i] - coordsY[i] + offset);"; + case "west palette": + return "return ColorFromPalette(currentPalette, coordsX[i] + offset);"; + case "northwest palette": + return "return ColorFromPalette(currentPalette, coordsX[i] + coordsY[i] + offset);"; + case "red": + return "return CRGB(255, 0, 0)"; + case "green": + return "return CRGB(0, 255, 0)"; + case "blue": + return "return CRGB(0, 0, 255)"; + case "white": + return "return CRGB(255, 255, 255)"; + case "custom": + return ""; + } +} diff --git a/js/pixelblaze.js b/js/pixelblaze.js new file mode 100644 index 0000000..2fd9560 --- /dev/null +++ b/js/pixelblaze.js @@ -0,0 +1,44 @@ +export function parsePixelblazeText(text) { + // pixelblaze layout should already be JSON formatted 2D array + const rows = JSON.parse(text); + + const leds = []; + + let minX, minY, maxX, maxY, width, height; + + minX = minY = 1000000; + maxX = maxY = -1000000; + + let index = 0; + + for (const row of rows) { + const x = row[0]; + const y = row[1]; + + if (x < minX) minX = x; + if (x > maxX) maxX = x; + + if (y < minY) minY = y; + if (y > maxY) maxY = y; + + leds.push({ + index: index++, + x, + y, + }); + } + + width = maxX - minX; + height = maxY - minY; + + return { + height, + leds, + maxX, + maxY, + minX, + minY, + rows, + width, + }; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..253237a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,689 @@ +{ + "name": "led-mapper", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@eslint/eslintrc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.1.0.tgz", + "integrity": "sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", + "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.9.0.tgz", + "integrity": "sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.1.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + } + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json index e23f755..f05ff6f 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,12 @@ "description": "", "main": "index.js", "scripts": { - "start": "npx serve" + "start": "npx serve", + "lint": "eslint --ext .js --ignore-path .gitignore ." }, "author": "Jason Coon", - "license": "ISC" -} \ No newline at end of file + "license": "ISC", + "devDependencies": { + "eslint": "^8.9.0" + } +}