From 091b5ee8271a1f4d22db4244fb987fa9fee7577a Mon Sep 17 00:00:00 2001 From: Henrik Date: Sat, 14 Jan 2023 18:14:53 +0100 Subject: [PATCH] Add files via upload --- html/boxdraw.js | 100 ++++++++++++++++ html/favicon-16x16.png | Bin 0 -> 649 bytes html/favicon-32x32.png | Bin 0 -> 1802 bytes html/favicon.ico | Bin 0 -> 15406 bytes html/getPixelValues.js | 253 +++++++++++++++++++++++++++++++++++++++++ html/index.html | 236 ++++++++++++++++++++++++++++++++++++++ html/index.js | 179 +++++++++++++++++++++++++++++ html/site.webmanifest | 19 ++++ html/statics.js | 18 +++ html/styles.css | 166 +++++++++++++++++++++++++++ 10 files changed, 971 insertions(+) create mode 100644 html/boxdraw.js create mode 100644 html/favicon-16x16.png create mode 100644 html/favicon-32x32.png create mode 100644 html/favicon.ico create mode 100644 html/getPixelValues.js create mode 100644 html/index.html create mode 100644 html/index.js create mode 100644 html/site.webmanifest create mode 100644 html/statics.js create mode 100644 html/styles.css diff --git a/html/boxdraw.js b/html/boxdraw.js new file mode 100644 index 0000000..4da9572 --- /dev/null +++ b/html/boxdraw.js @@ -0,0 +1,100 @@ +function drawBoxes(inputPixelArray, widthPixels, heightPixels) { + + // Get a reference to the canvas element + var canvas = document.getElementById('pixelCanvas'); + + // Get the canvas context + var ctx = canvas.getContext('2d'); + + // Set the width and height of the canvas + if (window.innerHeight < window.innerWidth) { + canvas.width = Math.floor(window.innerHeight * 0.98); + } + else{ + canvas.width = Math.floor(window.innerWidth * 0.98); + } + //canvas.height = window.innerWidth; + + let pixelSize = Math.floor(canvas.width/widthPixels); + + //Set the canvas height to fit the right number of pixelrows + canvas.height = (pixelSize * heightPixels) + 10 + + //Iterate through the matrix + for (let y = 0; y < heightPixels; y++) { + for (let x = 0; x < widthPixels; x++) { + + // Calculate the index of the current pixel + let i = (y*widthPixels) + x; + + //Gets the RGB of the current pixel + let pixel = inputPixelArray[i]; + + let pixelColor = 'rgb(' + pixel[0] + ', ' + pixel[1] + ', ' + pixel[2] + ')'; + let r = pixel[0]; + let g = pixel[1]; + let b = pixel[2]; + let pos = pixel[4]; + + let textColor = 'rgb(128,128,128)'; + + // Set the fill style to the pixel color + ctx.fillStyle = pixelColor; + + //Draw the rectangle + ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize); + + // Draw a border on the box + ctx.strokeStyle = '#888888'; + ctx.lineWidth = 1; + ctx.strokeRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize); + + //Write text to box + ctx.font = "10px Arial"; + ctx.fillStyle = textColor; + ctx.textAlign = "center"; + ctx.textBaseline = 'middle'; + ctx.fillText((pos + 1), (x * pixelSize) + (pixelSize /2), (y * pixelSize) + (pixelSize /2)); + } + } +} + +function drawBackground() { + const grid = document.createElement("div"); + grid.id = "grid"; + grid.style.cssText = ` + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + display: grid; + grid-template-columns: repeat(auto-fill, 20px); + grid-template-rows: repeat(auto-fill, 20px); + grid-gap: 0px; + `; + + const boxSize = 20; + const boxCount = Math.ceil(window.innerWidth / boxSize) * Math.ceil(window.innerHeight / boxSize);; + + for (let i = 0; i < boxCount; i++) { + const box = document.createElement("div"); + box.classList.add("box"); + box.style.backgroundColor = getRandomColor(); + grid.appendChild(box); + } + grid.style.zIndex = -1; + document.body.appendChild(grid); + } + + function getRandomColor() { + const letters = "0123456789ABCDEF"; + let color = "rgba("; + for (let i = 0; i < 3; i++) { + color += Math.floor(Math.random() * 256) + ","; + } + color += "0.05)"; + return color; + } + + window.drawBackground = drawBackground; diff --git a/html/favicon-16x16.png b/html/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..feb51ca09ff48fe5b76452318b35d36a60b48a15 GIT binary patch literal 649 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s77>k44ofy`glX=O&z~t=d z;uvD#zclEy-|YaAImJ2Ru5}k&uXcS9uVLT4D(JW1L>cGl0-p*xI5(UW-|~8&*qWDZ zpLj3i-4a>y&ST-mLtAV8OJ#++GQz(elRE$L#hE#Zf1W%4|MmY4ch;QMdlCg)~pXEJYei$QdBNTYkbFfSMW#R$}0gL`ssxnOBf^8FeWyNaR@%; zzVOyv*z|sy-iN!~S!L^lcyHv1{QD9;_sypH7MYv(YB2mvH2L&6t!(T5{*PhV0kZzO zXIanPQCRY*>OB8pZu57m%8$?g`04}8>f`U{ife~{dK9tu?-$nf{Qe9n&CSO)?`Co~ zUvYcqtLp5Bt>WdI&E1zj{qX7|`}3b(Q=2Tpo^J17&d_zx@$t*G4V-QEZMrY zQ{u=eCL^JZ#V48;uYD#wb(>e?!q;vwFXXxlD$5&MJUotk2bjTtwXX0PTe0~Ye|~+9*FO8v8{5*l+ZQ4mHlMopxSo0IMH2=68Nn|Z7#J8l MUHx3vIVCg!0DkB+_W%F@ literal 0 HcmV?d00001 diff --git a/html/favicon-32x32.png b/html/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..a3b5cceb11d8560596937052a215427005534ee7 GIT binary patch literal 1802 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>U_0XJ z;usR)el+}cpNywq?};_1)@5(c&VEa`@f3WrCH@*687Cwyc6#&2-Q>BEz& z@0xa0$Nx$AX!K3WMm()5Ci?Q_yH`t3b~r>D_ul>1qp!X2?ewtF6b=75JvN1052gP- zG~Z)iqs#F7H)^XG(RSwr72h zx}b9XHIwH5+ztBfFPoQaeJ$K;l5$P;RiKy1S;5ycPM+Bs-hRe0`%2uUg_fUNg`w32KgOK!Z_CR2Cr)HDzB&4_ zdBU42&MFZm*QtlJ6#3FltxkTp!f8d)ql9&TtXQOV1y%=GtO+o0^zi6lH*ENmxN*m% z&g)DSVmy3ovhRIt=L_XsUHR>HwoBEsmsSa!;z|}OVYM+ht2 zjGJR5xEN=PN5=8IN=t6!a!RaS7CL>?q>@je3wx(?CpDh=nYi!WwWss1cZbi|A#z}G z$rp>P+wXUsa!$)%ZE@4}{P`onOWo$)-BY_gCm^*h-CK1+?T?G+Z#=F_uC}>%wDIJW zOG_7=sw?@uJ(b<+@-{tAMd?4EufM;)vpC&VT`yh1hu>*c*|HUhCz2Ltl`XQdcR$z^ z@QFX~@3M@ePr6n*9QpG&{?CptYh$J7&F!6PxRXhfLuG&A>vC6atLsVJ3?~-1$Cf-l zch;P_!lY+Di*D_1r%?9;8!Dr00_$cdI%q1~?7dxn+HcR9=j%ciZHlk^5`67SPR`Wj zyZ70jaL74-fIt7PZuD^m?~`7eJ}B<}AI5O(;Rl5+XV1ATJfdVX$M0B%4tFU$ME!>Rf?0@`x-|beCcR-9~LOcK4*OEWNXW4Y* zZjbxob^PyMewQ=nZl;9)R1=;kBxsbK+v)Yar@>*{^u5P#nPtzOd@}ie(p-iVg&?M+ zV|xA;))DdbVkW-dj=$Qex8}=%LJ7Gev73!Q@+=Me62ho{Xwtc5$zT4;1bj{BWO)8x ziPhlwdAG=ya#LVR}c(_oz?%e@@Wc)X0XyteP0Uy* zYc1=q(lchWv|lX0+xo4YTUchfO4hP1y$62@zq-_N;>^_WcBhL>rN#{3Zg%%MZLDjZ zCA2Xs&GKX8-8C6bj6b7emriH0C=2eN{dVT3gcGvo0uG;u(x~+h+Lrgq(TT_S?Zpmj zTh^n>-mkXw3;RuN^N`T`qB4bw{}S8;PJ^{zdQgU!AUgpwYv} zp0Yibb^W>Oa7K?apTg=MZ2BXqCAr`;=lQvpxtRMGmDoS3T5VrEDULm9QQbZFgvDGN zyM7+x|FF|yk!p6`7=>t)To=u7GI{53bv zThx7em3+jpmoe^f-?%3-)Lhy&{q=#x6<^ZjrL?iCjVnoThUVR) zPOE}Wsky|pN>#}^uyz`pi9cDsz4^Hqrf{r~-=w%f1!HhSEMnY(^gK)}CWRkAO(dB@*W)cm<-T@`z=fO_@B-0kN@ zoSdZWa#xFPf1TFxFfF}JPK77n*D7z%lh5^^v~RmRPwV*xlbTOX|4v(daB<#USsxc) zEUI_)RXD?~(BFG2ez@y8A?Z@cx{kKHCE|Ky$1Kkx59vH!o*Kdy$u z8aF@BG}HT>m$&$^!OhQ4ukJ`b5o&SqmZ8J@N&A@+B437z{aW?o)89?%_f-F{-Pd#P gY4YdT>;J@s7hH{PcDb{Kfq{X+)78&qol`;+0K`Lpz5oCK literal 0 HcmV?d00001 diff --git a/html/favicon.ico b/html/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bde8945e58b53c78d61b100e93a7a41f87365608 GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/html/getPixelValues.js b/html/getPixelValues.js new file mode 100644 index 0000000..6ab8b2c --- /dev/null +++ b/html/getPixelValues.js @@ -0,0 +1,253 @@ +function getPixelRGBValues(base64Image) { + httpArray = []; + const copyJSONledbutton = document.getElementById('copyJSONledbutton'); + const JSONled = document.getElementById('JSONled'); + const maxNoOfColorsInCommandSting = document.getElementById('colorLimitNumber').value; + + let selectedIndex = -1; + + let selector = document.getElementById("formatSelector"); + selectedIndex = selector.selectedIndex; + const formatSelection = selector.options[selectedIndex].value; + + selector = document.getElementById("ledSetupSelector"); + selectedIndex = selector.selectedIndex; + const ledSetupSelection = selector.options[selectedIndex].value; + + selector = document.getElementById("colorFormatSelector"); + selectedIndex = selector.selectedIndex; + let hexValueCheck = true; + if (selector.options[selectedIndex].value == 'dec'){ + hexValueCheck = false + } + + selector = document.getElementById("addressingSelector"); + selectedIndex = selector.selectedIndex; + let segmentValueCheck = true; + if (selector.options[selectedIndex].value == 'single'){ + segmentValueCheck = false + } + + let segmentString = '' + let curlString = '' + let haString = '' + let haCommandCurlString = ''; + + + let colorSeparatorStart = '\''; + let colorSeparatorEnd = '\''; + if (!hexValueCheck){ + colorSeparatorStart = '['; + colorSeparatorEnd = ']'; + } + // Warnings + let hasTransparency = false; //If alpha < 255 is detected on any pixel, this is set to true in code below + let imageInfo = ''; + + // Create an off-screen canvas + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + + // Create an image element and set its src to the base64 image + var image = new Image(); + image.src = base64Image; + + // Wait for the image to load before drawing it onto the canvas + image.onload = function() { + // Set the canvas size to the same as the image + canvas.width = image.width; + canvas.height = image.height; + imageInfo = '

Width: ' + image.width + ', Height: ' + image.height + ' (make sure this matches your led matrix setup)

' + + // Draw the image onto the canvas + context.drawImage(image, 0, 0); + + // Get the pixel data from the canvas + var pixelData = context.getImageData(0, 0, image.width, image.height).data; + + // Create an array to hold the RGB values of each pixel + var pixelRGBValues = []; + + // If the first row of the led matrix is right -> left + let right2leftAdjust = 1; + + if (ledSetupSelection == 'l2r'){ + right2leftAdjust = 0; + } + + // Loop through the pixel data and get the RGB values of each pixel + for (var i = 0; i < pixelData.length; i += 4) { + var r = pixelData[i]; + var g = pixelData[i + 1]; + var b = pixelData[i + 2]; + var a = pixelData[i + 3]; + + let pixel = i/4 + let row = Math.floor(pixel/image.width); + let led = pixel; + if (ledSetupSelection == 'matrix'){ + //Do nothing, the matrix is set upp like the index in the image + //Every row starts from the left, i.e. no zigzagging + } + else if ((row + right2leftAdjust) % 2 === 0) { + //Setup is traditional zigzag + //right2leftAdjust basically flips the row order if = 1 + //Row is left to right + //Leave led index as pixel index + + } else { + //Setup is traditional zigzag + //Row is right to left + //Invert index of row for led + let indexOnRow = led - (row * image.width); + let maxIndexOnRow = image.width - 1; + let reversedIndexOnRow = maxIndexOnRow - indexOnRow; + led = (row * image.width) + reversedIndexOnRow; + } + + // Add the RGB values to the pixel RGB values array + pixelRGBValues.push([r, g, b, a, led, pixel, row]); + } + + pixelRGBValues.sort((a, b) => a[5] - b[5]); + + //Copy the values to a new array for resorting + let ledRGBValues = [... pixelRGBValues]; + + //Sort the array based on led index + ledRGBValues.sort((a, b) => a[4] - b[4]); + + //Generate JSON in WLED format + let JSONledString = ''; + let JSONledStringShort = ''; + + //Set starting values for the segment check to something that is no color + let segmentStart = -1; + let maxi = ledRGBValues.length; + let curentColorIndex = 0 + let commandArray = []; + + //For evry pixel in the LED array + for (let i = 0; i < maxi; i++) { + let pixel = ledRGBValues[i]; + let r = pixel[0]; + let g = pixel[1]; + let b = pixel[2]; + let a = pixel[3]; + let segmentString = ''; + let segmentEnd = -1; + + if(segmentValueCheck){ + if (segmentStart < 0){ + //This is the first led of a new segment + segmentStart = i; + } //Else we allready have a start index + + if (i < maxi - 1){ + + let iNext = i + 1; + let nextPixel = ledRGBValues[iNext]; + + if (nextPixel[0] != r || nextPixel[1] != g || nextPixel[2] != b ){ + //Next pixel has new color + //The current segment ends with this pixel + segmentEnd = i + 1 //WLED wants the NEXT LED as the stop led... + segmentString = segmentStart + ',' + segmentEnd + ','; + } + + } else { + //This is the last pixel, so the segment must end + segmentEnd = i + 1; + segmentString = segmentStart + ',' + segmentEnd + ','; + } + } else{ + //Write every pixel + if (JSONledString == ''){ + //If addressing is single, we ned to start every command with a starting possition + JSONledString = i + ', \'dummy\','; + } + + segmentStart = i + segmentEnd = i + //Segment string should be empty for when addressing single. So no need to set it again. + } + + if (a < 255){ + hasTransparency = true; //If ANY pixel has alpha < 255 then this is set to true to warn the user + } + + if (segmentEnd > -1){ + //This is the last pixel in the segment, write to the JSONledString + //Return color value in selected format + let colorValueString = r + ',' + g + ',' + b ; + + if (hexValueCheck){ + const [red, green, blue] = [r, g, b]; + colorValueString = `${[red, green, blue].map(x => x.toString(16).padStart(2, '0')).join('')}`; + } else{ + //do nothing, allready set + } + + JSONledString = JSONledString + segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd; + + curentColorIndex = curentColorIndex + 1; // We've just added a new color to the string so up the count with one + + if (curentColorIndex % maxNoOfColorsInCommandSting === 0 || i == maxi - 1) { + + //If we have accumulated the max number of colors to send in a single command or if this is the last pixel, we should write the current colorstring to the array + commandArray.push(JSONledString); + JSONledString = ''; //Start on an new command string + } else + { + //Add a comma to continue the command string + JSONledString = JSONledString + ',' + } + //Reset segment values + segmentStart = - 1; + } + } + + JSONledString = '' + + //For evry commandString in the array + for (let i = 0; i < commandArray.length; i++) { + let thisJSONledString = JSONledStringStart + document.getElementById('brightnessNumber').value + JSONledStringMid1 + commandArray[i] + JSONledStringEnd; + httpArray.push(thisJSONledString); + + let thiscurlString = curlStart + document.getElementById('curlUrl').value + curlMid1 + thisJSONledString + curlEnd; + + //Aggregated Strings That should be returned to the user + if (i > 0){ + JSONledString = JSONledString + '\n'; + curlString = curlString + ' && '; + } + JSONledString = JSONledString + thisJSONledString; + curlString = curlString + thiscurlString; + } + + + haString = haStart + document.getElementById('haID').value + haMid1 + document.getElementById('haName').value + haMid2 + document.getElementById('haUID').value + haMid3 +curlString + haMid3 + document.getElementById('curlUrl').value + haEnd; + + if (formatSelection == 'wled'){ + JSONled.value = JSONledString; + } else if (formatSelection == 'curl'){ + JSONled.value = curlString; + } else if (formatSelection == 'ha'){ + JSONled.value = haString; + } else { + JSONled.value = 'ERROR!/n' + formatSelection + ' is an unknown format.' + } + + let infoDiv = document.getElementById('image-info'); + let canvasDiv = document.getElementById('image-info'); + if (hasTransparency){ + imageInfo = imageInfo + '

WARNING! Transparency info detected in image. Transparency (alpha) has been ignored. To ensure you get the result you desire, use only solid colors in your image.

' + } + + infoDiv.innerHTML = imageInfo; + canvasDiv.style.display = "block" + + //Drawing the image + drawBoxes(pixelRGBValues, image.width, image.width); + } +} \ No newline at end of file diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..5ed09ca --- /dev/null +++ b/html/index.html @@ -0,0 +1,236 @@ + + + + + + + + Led Matrix Pixel Art Convertor + + + + + + +
+

Led Matrix Pixel Art Converter

+

Convert image to WLED JSON (pixel art on WLED matrix)

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + + 100 +
+ + + + 256 +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + +

+

+ + + +

+

+ Drop image here
or
+ Click to select a file +
+ +

+ +

+ +

+
+
+ +
+ +
+ + +

+ + +
+ + + + + + + + + \ No newline at end of file diff --git a/html/index.js b/html/index.js new file mode 100644 index 0000000..b7b6004 --- /dev/null +++ b/html/index.js @@ -0,0 +1,179 @@ +//Start up code +console.log(location.host); +document.getElementById('curlUrl').value = location.host; +let httpArray = []; + + +//On submit button pressed ======================= +document.getElementById('form').addEventListener('submit', function(event) { + + event.preventDefault(); + + let base64Image = document.getElementById('preview').src; + if (isValidBase64Gif(base64Image)) { + document.getElementById('image').src = base64Image; + getPixelRGBValues(base64Image); + document.getElementById('image-container').style.display = "block" + } + else { + let infoDiv = document.getElementById('image-info'); + let imageInfo = '

WARNING! File does not appear to be a valid GIF image

' + infoDiv.innerHTML = imageInfo; + infoDiv.style.display = "block" + document.getElementById('image-container').style.display = "none"; + document.getElementById('JSONled').value = ''; + console.log("The string '" + base64Image + "' is not a valid base64 GIF image."); + } + +}); + +// Code for copying the generated string to clipboard + +copyJSONledbutton.addEventListener('click', async () => { + JSONled.select(); + try { + await navigator.clipboard.writeText('test text'); + console.log('Text copied to clipboard'); + } catch (err) { + console.error('Failed to copy text: ', err); + } +}); + +sendJSONledbutton.addEventListener('click', async () => { + if (window.location.protocol === "https:") { + alert('Will only be available when served over http (or WLED is run over https)'); + } else { + postPixels(); + } +}); + +async function postPixels() { + for (let i of httpArray) { + try { + console.log(i); + console.log(i.length); + const response = await fetch('http://'+document.getElementById('curlUrl').value+'/json/state', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + //'Content-Type': 'text/html; charset=UTF-8' + }, + body: i + }); + const data = await response.json(); + console.log(data); + } catch (error) { + console.error(error); + } + } +} + +let helpCheckbox = document.getElementById("helpCheckbox"); +let helpDiv = document.getElementById("help-container"); + +helpCheckbox.addEventListener("change", function() { + if (helpCheckbox.checked) { + helpDiv.style.display = "block"; + } else { + helpDiv.style.display = "none"; + } +}); + +//File uploader code +const dropZone = document.getElementById('drop-zone'); +const filePicker = document.getElementById('file-picker'); +const preview = document.getElementById('preview'); + +// Listen for dragenter, dragover, and drop events +dropZone.addEventListener('dragenter', dragEnter); +dropZone.addEventListener('dragover', dragOver); +dropZone.addEventListener('drop', dropped); +dropZone.addEventListener('click', zoneClicked); + +// Listen for change event on file picker +filePicker.addEventListener('change', filePicked); + +// Handle zone click +function zoneClicked(e) { + e.preventDefault(); + //this.classList.add('drag-over'); + //alert('Hej'); + filePicker.click(); +} + +// Handle dragenter +function dragEnter(e) { + e.preventDefault(); + this.classList.add('drag-over'); +} + +// Handle dragover +function dragOver(e) { + e.preventDefault(); +} + +// Handle drop +function dropped(e) { + e.preventDefault(); + this.classList.remove('drag-over'); + + // Get the dropped file + const file = e.dataTransfer.files[0]; + updatePreview(file); +} + +// Handle file picked +function filePicked(e) { + // Get the picked file + const file = e.target.files[0]; + updatePreview(file); +} + +// Update the preview image +function updatePreview(file) { + // Use FileReader to read the file + const reader = new FileReader(); + reader.onload = function() { + // Update the preview image + preview.src = reader.result; + document.getElementById("submitConvert").style.display = "block"; + }; + reader.readAsDataURL(file); +} + +function isValidBase64Gif(string) { + // Use a regular expression to check that the string is a valid base64 string + /* + const base64gifPattern = /^data:image\/gif;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/; + const base64pngPattern = /^data:image\/png;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/; + const base64jpgPattern = /^data:image\/jpg;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/; + const base64webpPattern = /^data:image\/webp;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/; + */ + //REMOVED, Any image appear to work as long as it can be drawn to the canvas. Leavingg code in for future use, possibly + if (1==1 || base64gifPattern.test(string) || base64pngPattern.test(string) || base64jpgPattern.test(string) || base64webpPattern.test(string)) { + return true; + } + else { + //Not OK + return false; + } +} + +document.getElementById("brightnessNumber").oninput = function() { + document.getElementById("brightnessValue").textContent = this.value; +} + +document.getElementById("colorLimitNumber").oninput = function() { + document.getElementById("colorLimitValue").textContent = this.value; +} + +var formatSelector = document.getElementById("formatSelector"); +var hideableRows = document.querySelectorAll(".ha-hide"); +for (var i = 0; i < hideableRows.length; i++) { + hideableRows[i].classList.add("hide"); +} +formatSelector.addEventListener("change", function() { + for (var i = 0; i < hideableRows.length; i++) { + hideableRows[i].classList.toggle("hide", this.value !== "ha"); + } + }); \ No newline at end of file diff --git a/html/site.webmanifest b/html/site.webmanifest new file mode 100644 index 0000000..3072091 --- /dev/null +++ b/html/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "Led Matrix Pixel Art Convertor", + "short_name": "ledconv", + "icons": [ + { + "src": "/favicon-32x32.png", + "sizes": "32x322", + "type": "image/png" + }, + { + "src": "/favicon-32x32.png", + "sizes": "32x32", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" + } \ No newline at end of file diff --git a/html/statics.js b/html/statics.js new file mode 100644 index 0000000..07ff027 --- /dev/null +++ b/html/statics.js @@ -0,0 +1,18 @@ +var curlStart = 'curl -X POST "http://'; +var curlMid1 = '/json/state" -d \''; +var curlEnd = '\' -H "Content-Type: application/json"'; + +const haStart = '#Uncomment if you don\'t allready have these defined in your switch section of your configuration.yaml\n#- platform: command_line\n #switches:\n '; +const haMid1 = '\n friendly_name: '; +const haMid2 = '\n unique_id: '; +const haMid3= '\n command_on: >\n '; +const haMid4 = '\n command_off: >\n curl -X POST "http://'; +const haEnd = '/json/state" -d \'{"on":false}\' -H "Content-Type: application/json"'; +const haCommandLeading = ' '; + +const JSONledStringStart = '{"on":true, "bri":'; +const JSONledStringMid1 = ', "seg":{"i":['; +const JSONledShortStringStart = '{'; +const JSONledShortStringMid1 = '"seg":{"i":['; +const JSONledStringEnd = ']}}'; + diff --git a/html/styles.css b/html/styles.css new file mode 100644 index 0000000..4c41037 --- /dev/null +++ b/html/styles.css @@ -0,0 +1,166 @@ + + .box { + border: 2px solid white; + } + body { + font-family: 'Arcade', Arial, sans-serif; + background-color: #151515; + + } + + .top-part { + width: 600px; + margin: 0 auto; + } + .container { + max-width: 100% -40px; + border-radius: 0px; + padding: 20px; + text-align: center; + } + h1 { + font-size: 2.3em; + color: rgb(126, 76, 128); + margin: 20px 0; + font-family: 'Arcade', Arial, sans-serif; + line-height: 0.5; + text-align: center; + } + h2 { + font-size: 1.3em; + color: rgba(126, 76, 128, 0.61); + margin: 20px 0; + font-family: 'Arcade', Arial, sans-serif; + line-height: 0.5; + text-align: center; + } + + p { + font-size: 1.2em; + color: rgb(119, 119, 119); + line-height: 1.5; + font-family: 'Arcade', Arial, sans-serif; + } + + #fieldTable { + font-size: 1 em; + color: #777; + line-height: 1; + font-family: 'Arcade', Arial, sans-serif; + } + + #drop-zone { + display: block; + width: 100%-40px; + border: 3px dashed #7E4C80; + border-radius: 0px; + text-align: center; + padding: 20px; + margin: 0px; + cursor: pointer; + + font-family: 'Arcade', Arial, sans-serif; + font-size: 15px; + color: #777; + } + + + *.button { + display: block; + width: 100% - 40px; + border: 2px dashed #ccc; + border-radius: 20px; + text-align: center; + padding: 20px; + margin: 0px; + cursor: pointer; + } + + #file-picker { + display: none; + } + * select { + background-color: #333333; + color: #C0C0C0; + border: 1px solid #C0C0C0; + margin-top: 0.5em; + margin-bottom: 0.5em; + padding: 0em; + width: 100%; + height: 27px; + font-size: 15px; + color: rgb(119, 119, 119); + border-radius: 0; + } + * input[type=range] { + -webkit-appearance:none; + flex-grow: 1; + border-radius: 0px; + background: linear-gradient(to right, #333333 0%, #333333 100%); + color: #C0C0C0; + border: 1px solid #C0C0C0; + margin-top: 0.5em; + margin-left: 0em; + } + input[type="range"]::-webkit-slider-thumb{ + -webkit-appearance:none; + width: 25px; + height:25px; + background:#7E4C80; + position:relative; + z-index:3; + } + .rangeNumber{ + width: 20px; + vertical-align: middle; + } + * input[type=text] { + background-color: #333333; + border: 1px solid #C0C0C0; + padding-inline-start: 5px; + margin-top: 10px; + width: 100%; + height: 27px; + border-radius: 0px; + font-family: 'Arcade', Arial, sans-serif; + font-size: 15px; + color: rgb(119, 119, 119); + } + * input[type="checkbox"] { + } + * input[type=submit] { + background-color: #333333; + border: 1px solid #C0C0C0; + padding: 0.5em; + width: 100%; + border-radius: 0px; + font-family: 'Arcade', Arial, sans-serif; + font-size: 1.3em; + color: rgb(119, 119, 119); + } + * button { + background-color: #333333; + border: 1px solid #C0C0C0; + padding: 0.5em; + margin-bottom: 15px; + width: 100%; + border-radius: 0px; + font-family: 'Arcade', Arial, sans-serif; + font-size: 1.3em; + color: rgb(119, 119, 119); + } + * textarea { + background-color: #333333; + border: 1px solid #C0C0C0; + padding: 0em; + margin-bottom: 10px; + width: 100%; + height: 200px; + border-radius: 0px; + font-family: 'Courier', Arial, sans-serif; + font-size: 1em; + color: rgb(119, 119, 119); + } + .hide { + display: none; + } \ No newline at end of file