[
  {
    "path": ".github/workflows/env.yml",
    "content": "version_string: \"Version ${{ inputs.version }}\"\n"
  },
  {
    "path": ".github/workflows/manual.yml",
    "content": "# This is a basic workflow that is manually triggered\n\nname: Manual workflow\n\n# Controls when the action will run. Workflow runs when manually triggered using the UI\n# or API.\non:\n  workflow_dispatch:\n    # Inputs the workflow accepts.\n    inputs:\n      version:\n        # Friendly description to be shown in the UI instead of 'name'\n        description: 'Version (YY.M.X)'\n        # Default value if no value is explicitly provided\n        default: '99.1.1'\n        # Input has to be provided for the workflow to run\n        required: true\n\n# A workflow run is made up of one or more jobs that can run sequentially or in parallel\njobs:\n  # This workflow contains a single job called \"greet\"\n  greet:\n    # The type of runner that the job will run on\n    runs-on: ubuntu-latest\n\n    # Steps represent a sequence of tasks that will be executed as part of the job\n    steps:\n    # Runs a single command using the runners shell\n    - name: Checkout\n      uses: actions/checkout@v3.3.0\n      with:\n        ref: main\n    - name: Pull the latest changes\n      run: git pull\n    - name: Check if the file is present\n      run: ls  \n    - name: Check if the file is present\n      run: ls html/\n    - name: Copy and Rename File\n      run: cp html/index.html pixartmin.htm\n    - name: Check if the file is present\n      run: ls  \n    - name: Check if the file is present\n      run: ls html/      \n    - name: Add the changes\n      run: git add pixartmin.htm\n    - name: Use env file\n      env:\n        envFile: manualenv.yml\n      run: |\n        echo \"The version is ${{ manualenv.version_string }}\"\n    - name: Commit the changes\n      run: git -c user.name=${{ secrets.NAME }} -c user.email=${{ secrets.EMAIL }} commit -m ${{ manualenv.version_string }}\n    - name: Push the changes\n      run: git push \n      #run: git push git@github.com:myname/myproject.git\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Henrik Werkström\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Download instructions\nFor stable (1.0.8)  \n***Right***-Click [here](https://raw.githubusercontent.com/werkstrom/WLED-PixelArtConverter/main/pixart.htm) and select save to your local computer.  \n\nFor beta (1.1.0)  \n***Right***-Click [here](https://raw.githubusercontent.com/werkstrom/WLED-PixelArtConverter/main/beta/pixart.htm) and select save to your local computer.\n\nAcctual text may differ depending on browser, but for Chrome (as of writing) the menu item would to select be *[Save link as...]*.\n\n# Additional instructions\nThere are now instructions directly in the WLED documentation [here](https://kno.wled.ge/features/pixel-art-converter/). As the Pixel Art Converter project migrates to main WLED project, documentation will most likely primarily be done there as it is beneficial to have a complete, end-to-end documentation on how to set up WLED and use Pixel Art Converter together.\n\n# WLED-PixelArtConverter\nConvert any image your browser can render into pixel art for WLED\n\nThis is a tool for making it easy to show pixel art on a LED panel run by WLED\n\nFind out more about the awesome [WLED software](https://kno.wled.ge/) or their [GitHub](https://github.com/Aircoookie/WLED)\n\n* Please read \"Current notes\" at the end of the document for known issues and important hints.*\n\n* *To run* ***directly*** *from WLED device use pixartmin.html according to* ***instructions below.***  \n\n* As the PixelArtConverter has moved into the main WLED project, sourcecode will no longer be available here. Only downloadable stand alones and documentation.  \n  \n* With the official WLED V0.14.0-b1 there are some issues with the 2D matrix implementation. It's the first release, so no surprise. Many (all known) are fixed and available in the nightly builds. You can get one here but be aware it is development builds. [https://wled-install.github.io/](https://wled-install.github.io/)\n\n## Most notable functions\n\n- You can convert almost any image (tested with at least PNG, JPG, WEBP, GIF)\n- You can upload the generated settings directly to your WLED device, with a click of a button\n- If you want to trigger your image from Home Assistant the tool also generates the entire YAML for your configuration.yaml\n- If you want to trigger your image from any other place/system the tool generates a complete CURL command\n- You can adjust the format you send the data to WLED in(to fit your specific setup, i.e. slow or limited devices)\n- Generates several types led orders, to fit your setup (though with 2D setup you should not need to anymore)\n- Very small footprint. Only about 35 kB excluding favicons\n- 100% pure Javascript/html/CSS\n- Runs in your browser only, no sending iimages to any cloud services\n- Communicates with your device(s) to get valid options.\n\n## Basic Operations\n\n1. First download the pixart.htm file and save it to some where on your computer. Any OS should do really since its purely run inside the browser. *Note: As of V 1.0.8 only the minified version is distributed as it has all the functionality, but in a smaller package.*\n2. And open the pixart.htm file in any modern browser.\n3. Select the settings according to your settup and what you want to do.\n4. Select an image (or drag and drop one) and make sure the preview is visable (the very small image). This indicates your image can be read by the tool\n5. Click the button below to convert your image.\n6. You should be able to se a large preview of the generated led data at the bottom of the page  \n\n**A. Direct upload**  \n\n7. Press the button to send your image to your WLED device.\n8. Validate the image reached the device and is looking as expected. If not change the parameters in PixelArtConverer and try again.\n9. Create a preset on your device from the current setting (follow the instructions [here](https://kno.wled.ge/features/presets/) TL;DR: Press the \"Create Preset\" button.\n10. If you want to upload the same image to another device, simply change the Device IP/host name and click \"Send to device\" again  \n\n**B. Use elsewhere**  \n\n7. Copy the generated code if you want to use it outside of the PixelArtConverter/WLED Note: Since you are running the tool localy, you will not be allowed to copy to your clipboard automatically. You will ned to mark the entire text (ctrl+a) and copy it (ctrl+c).\n8. Paste the text into your tool of choice. **Note: The Preset API setup (where you can paste JSON and create a preset) does currently not support multiple commands, i.e. your entire settings JSON *must* be in one single command string.**  \n\n## Running directly on your WLED device  \n*As PixelArtConverter is no merged into the main WLED project. This option will shortly be available as default*\nMost WLED devices are low power very limited compute so it is important to make life as easy as possible for it. That's why there is a one-file-solution for you to use if you want to make PixelArtConverter available by accessing your WLED device. Follow these steps:  \n\n1. Download the *pixart.htm* file\n2. Direct you browser to http://[your.device.ip]/edit\n3. Upload the *pixart.htm* file using the UI\n4. Direct you browser to http://[your.device.ip]/pixart.htm  \n\nAnd you should be good to go.\n\n## Notes and tips\n\n### Image Size\nYou can resize your images to fit your led setup. The image is projected exactly as it is, onto exactly the size you provide. That means that if your source file aspect ratio does not fit your target size aspect ratio, your image will get destorted. Still in many cases it is not noticable.\n\n### Output format\nPretty self explainatory. If you are to upload to your device from PixelArtConverter you should select WLED JSON\n\n### Color code format\nThough WLED can handle both. The documentation explicitly states HEX is faster and requires less resources. Use it whenever you can.\n\n### Addressing\nPixelArtConverter can either generate one color code per led/pixel, Or, it can identify ranges within your image. Many times the ranges option generates fewer commands and is thus faster and more reliable. But not allways. If you upload to your device and saves it as a preset, it doesn't matter. The preset is based on WLED's internal format and is thus optimized. But if you are to trigger the image from another place, like Home Assistant it can make a lot of difference, depending on your image. The Ukranian flag (that everybody knows by now) is MUCH faster and easier in range mode, but a chess board migt actually be slower...\n\nAs of version 1.0.1 there is a hybrid mode that utilizes the ability to mix single and range. this should be the default for anyone to use from now on.\n\n### Number of colors per command\nWLED is running on very limited devices, and you're throwing a massive amount af data on them in a very short time. To make things easy on the devices, limit your number of colors. You can test with your device how much it can handle. But this will vary.\n\n## Target segment id  \nIf multiple segements are set up in WLED you can send it to any one of them. The most convenient way to set a valid target is to query the device for valid IDs. Simply make sure your device's IP/host is set in the field above and click the \"Get from cloud\" icon. If connection is successful, the icon briefly turns green and you get a selection of available segment IDs to select from. If connection is unsuccesfull the icon turs red briefly in which case you can still manually set a segment and generate code for later use. To reset the selection change the IP/host and click the button again.\n\n### Home Assistant\nThe code generated will create a switch for you within home Assistant. What you do with it, how you trigger it, how you make it look in Home Assistant... Anything you wonder about that. Head over to the [Home Assistant Forum](https://community.home-assistant.io/)\n\n### Where to find images\nIf you want to create your own or modify/edit then [www.pixilart.com](https://www.pixilart.com) is an excelent tool\nIf you want to find old sprites from old games try out [www.spriters-resource.com](https://www.spriters-resource.com) or [opengameart.org](https://opengameart.org/)\nThere are also some (a few) here in the folder \"examples\"\n\n## Current notes\n\n### http, https and the issue with **mixed content** in web browsers\nAs a security meassure web browser do not allow you to access resources (web panges and web services) through http (un encrypted) from a page (like PixelArtConverter) when it is loaded through https (encrypted). Also public webservers (like [ledcalculator.werkstrom.com](https://ledcalculator.werkstrom.com)) \"must\" (in practise) use https. This means two things.\n\n1. In order to upload directly to your device, PixelArtConverter **must** be loaded in the same security context (http/https) as WLED. Since most WLED devices are local IoT devices it's mainly http. That's why you need to download the files and run them locally. A more convenient sollution is to have WLED serve you the page directly as that will solve any issues with possible missmatch. As of today you can set this up manually by uploading the files to your device @ [your.device.ip]/edit and then loading it to your browser using [your.device.ip]/index.html. We're looking into adding it in a more convenient way.\n2. You cannot use the tool @ [ledcalculator.werkstrom.com](https://ledcalculator.werkstrom.com) to upload **directly** to your device. The generated JSON, CURL and Home Assistant code will work just fine though.\n\n### Possible issue with single addressing after WLED V0.14.0-b1\nAs of V0.14.0-b1 there is a bug in WLED that PixelArtConverter have a workaround for that will probably break your upload if you\n\n1. Use **Single** addressing (and you shouldn't)\n2. Have a later build of WLED.\n\nIt should in practise not be a problem, but keep in mind if you get issues\n"
  },
  {
    "path": "RELEASENOTES.md",
    "content": "# Version 1.0.6  \n## Main take aways \n- Streamlined the conversion process  \n  - Image generates as soon as there is enough input to generete an image\n  - Image regenerates on value changes. No need to press convert button\n  - Segments are now named as on the device\n  - If are selected, and rescale is active, w/h will be automatically set from the selected segment\n  - Buttons that communicate with the device is more informative of success (green) or fail (red)\n  - Convertbutton removed (since there is no use for pressing it any more)\n- Minor bugfixes and adjustments\n\n# Version 1.0.5  \n## Main take aways \n- Querying the device for available segments\n- Manually input any segment id (0-63)\n- Preview matrix is now centered also on landscape displays\n- Several UI adjustments\n- Alignment with WLED profile\n- Development moved to main WLED project\n- Changed file ending to htm to align with WLED naming\n\n# Version 1.0.4  \n## Main take aways \n- Minor visual adjustments\n\n# Version 1.0.3  \n## Main take aways \n- Possible to send your image to different segments. There appear to be some sort of issue we havent identified, so currently only segment 0 works. Bur we need to get the function in there to test, so... Test on!\n- Minor adjustments and bugfixes\n\n# Version 1.0.2  \n## Main take aways  \n- Developer mode: Some new and experimental features will be turned off unless you run in developer mode. To enter developermode append \"?dev\" to the url. Example: http://192.168.0.123/pixartmin.htm?dev\n- File to device: Experimental feature to send the generated code as a file to the device instead of using the JSON API. Will not do anything more on the device at the moment.\n- Minor adjustmensts\n"
  },
  {
    "path": "archived/pixart.htm",
    "content": "<!DOCTYPE html> <html> <head> <meta http-equiv=\"Cache-Control\"\ncontent=\"no-cache, no-store, must-revalidate\"> <meta http-equiv=\"Pragma\"\ncontent=\"no-cache\"> <meta http-equiv=\"Expires\" content=\"0\"> <title>\nWLED Pixel Art Converter</title> <style>\n.box{border:2px solid #fff}body{font-family:Arial,sans-serif;background-color:#111}.top-part{width:600px;margin:0 auto}.container{max-width:100% -40px;border-radius:0;padding:20px;text-align:center}h1{font-size:2.3em;color:#ddd;margin:1px 0;font-family:Arial,sans-serif;line-height:.5}h2{font-size:1.1em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:.5;text-align:center}h3{font-size:.7em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:1.4;text-align:center;align-items:center;justify-content:center;display:flex}p{font-size:1em;color:#777;line-height:1.5;font-family:Arial,sans-serif}#fieldTable{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}#scaleTable{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}#drop-zone{display:block;width:100%-40px;border:3px dashed #ddd;border-radius:0;text-align:center;padding:20px;margin:0;cursor:pointer;font-family:Arial,sans-serif;font-size:15px;color:#777}#file-picker{display:none}.adaptiveTD{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center}.mainSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.adaptiveSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:none}.segmentsDiv{width:36px;padding-left:5px}* input[type=range]{appearance:none;-moz-appearance:none;-webkit-appearance:none;flex-grow:1;padding:0;margin:4px 8px 4px 0;background-color:transparent;cursor:pointer;background:linear-gradient(to right,#bbb 50%,#333 50%);border-radius:7px}input[type=range]:focus{outline:0}input[type=range]::-webkit-slider-runnable-track{height:28px;cursor:pointer;background:0 0;border-radius:7px}input[type=range]::-webkit-slider-thumb{height:16px;width:16px;border-radius:50%;background:#fff;cursor:pointer;-webkit-appearance:none;margin-top:4px;border-radius:7px}input[type=range]::-moz-range-track{height:28px;background-color:rgba(0,0,0,0);border-radius:7px}input[type=range]::-moz-range-thumb{border:0 solid transparent;height:16px;width:16px;border-radius:7px;background:#fff}.rangeNumber{width:20px;vertical-align:middle}.fullTextField[type=text]{background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:4px;margin-bottom:4px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.flxTFld{background-color:#222;border:1px solid #333;padding-inline-start:5px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}* input[type=submit]{background-color:#222;border:1px solid #333;padding:.5em;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1.3em;color:#ddd}* button{background-color:#222;border:1px solid #333;padding-inline:5px;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1em;color:#ddd;display:flex;align-items:center;justify-content:center;cursor:pointer}#scaleDiv{display:flex;align-items:center;vertical-align:middle}textarea{grid-row:1/2;width:100%;height:200px;background-color:#222;border:1px solid #333;color:#ddd}.hide{display:none}.svg-icon{vertical-align:middle}#image-container{display:grid;grid-template-rows:1fr 1fr}#button-container{display:flex;padding-bottom:10px;padding-top:10px}.buttonclass{flex:1;padding-top:5px;padding-bottom:5px}.gap{width:10px}#submitConvert::before{content:\"\";display:inline-block;background-image:url('data:image/svg+xml;utf8, <svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\" <path fill=\"currentColor\" d=\"M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z\" /></svg>');width:36px;height:36px}#sizeDiv *{display:inline-block}.sizeInputFields{width:50px;background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:-5px;height:24px;border-radius:7px;font-family:Arial,sans-serif;font-size:15px;color:#ddd}a:link{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:visited{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:hover{color:#ddd;background-color:transparent;text-decoration:none}a:active{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}\n</style> <link rel=\"shortcut icon\"\nhref=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAkNJREFUOE+lUstO21AUnHNsF0V+0EXVivAXfAe7qlL7EZEcQ5cQ8nCICbDJF7Trvhas6YbyDdBu2hSk7kihscK1fU91HRKKaFf1xhrP8ejMnCH850P/+v/cC2qcqw4gsGD3Hk/G23+bvSdw7nk1K887BeCQ4AGMAiEjcGaB7gndEfhRqdRQSC9ncQikAVisAWGBFggR5QvQ7UcTNd9mLvDdC2p2rhIN2G4U/vRbneyynzhps70oAPlhNPLjZna1v4/xVmtQHU8tzQXO3CCk4nrPD8MLN+6qd8ef8erwBKvH750XK1Xy4li9/fQFrw9PAGSDg8azuwJDN6jbuep7a+GFF3ezN0en5fDq8Qfn+UqVgjhW5bePpwCKwUHj6a2ACY7zrCdCC34UjrzutjLZiQC/kh0HlyPyu9tKzM4iGPf7uG5sDEwWpYWvrh85RZYAYAIp7+XayNtqytXeLqWbjUUNcLC+fuG2mpLu7tPl5sZDAnpLk0mzFDir+BFJlkCECaKFuSBYSlA4pLWtiRjEygIyErFzyIKjreYTlbZKgaHrRlzoHotweXeAhEhIxPBTDBhQYgEYhNbybINhxY1sbSwwkbHOZrzs3VRmjmnOk6bOkrqxMHT9yCqyxNybAFMg1mWRBGYlg2eboOSFGdT+IwM3Eil6zBwXZF1xkSWkOa6qtHleceuQvC+g4pZXiaOt9jyDb24Q2qKC5XTSvrlK3ZHMW04nLdMPKtQOMzoGTzPz69YNP2vi7D11ftvQWYCzyt7jfwO10TQgL5hT6QAAAABJRU5ErkJggg==\">\n <script type=\"text/javascript\">\nfunction gId(e){return d.getElementById(e)}function cE(e){return d.createElement(e)}var d=document\n</script> </head> <body> <body> <div class=\"top-part\"> <div\nstyle=\"display:flex;justify-content:center\"> <h1\nstyle=\"display:flex;align-items:center\"> <svg\nstyle=\"width:36px;height:36px;margin-right:6px\" viewBox=\"0 0 32 32\"><path\nfill=\"#003fff\" d=\"M6 22h8v4H6zM14 14h4v8h-4zM18 10h4v8h-4zM22 6h8v4h-8z\"/></svg>\n WLED Pixel Art Converter </h1> </div> <h2>\nConvert image to WLED JSON (pixel art on WLED matrix)\n</h2> <p> <table id=\"fieldTable\"\nstyle=\"width:100%;table-layout:fixed;align-content:center\"> <tr> <td\nstyle=\"vertical-align:middle\"> <label for=\"ledSetupSelector\">Led setup:</label>\n</td> <td class=\"adaptiveTD\"> <select id=\"ledSetupSelector\"\nclass=\"mainSelector\"> <option value=\"matrix\" selected=\"selected\">2D Matrix\n</option> <option value=\"r2l\">Serpentine, first row right to left &lt;-</option>\n <option value=\"l2r\">Serpentine, first row left to right -&gt;</option>\n</select> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label\nfor=\"formatSelector\">Output format:</label> </td> <td class=\"adaptiveTD\">\n<select id=\"formatSelector\" class=\"mainSelector\"> <option value=\"wled\"\nselected=\"selected\">WLED JSON</option> <option value=\"curl\">CURL</option>\n<option value=\"ha\">Home Assistant YAML</option> </select> </td> </tr> <tr> <td\nstyle=\"vertical-align:middle\"> <label for=\"colorFormatSelector\">\nColor code format:</label> </td> <td class=\"adaptiveTD\"> <select\nid=\"colorFormatSelector\" class=\"mainSelector\"> <option value=\"hex\"\nselected=\"selected\">HEX (#f4f4f4)</option> <option value=\"dec\">DEC (244,244,244)\n</option> </select> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label\nfor=\"addressingSelector\">Addressing:</label> </td> <td class=\"adaptiveTD\">\n<select id=\"addressingSelector\" class=\"mainSelector\"> <option value=\"hybrid\"\nselected=\"selected\">Hybrid (#f0f0f0,10, 17, #f4f4f4)</option> <option\nvalue=\"range\">Range (10, 17, #f4f4f4)</option> <option value=\"single\">\nSingle (#f4f4f4)</option> </select> </td> </tr> <tr> <td\nstyle=\"vertical-align:middle\"> <label for=\"brightnessNumber\">Brightness:</label>\n </td> <td style=\"vertical-align:middle;display:flex;align-items:center\"> <input\n type=\"range\" id=\"brightnessNumber\" min=\"1\" max=\"255\" value=\"128\"> <span\nid=\"brightnessValue\">128</span> </td> </tr> <tr> <td\nstyle=\"vertical-align:middle\"> <label for=\"colorLimitNumber\">\nMax no of colors/JSON:</label> </td> <td\nstyle=\"vertical-align:middle;display:flex;align-items:center\"> <input\ntype=\"range\" id=\"colorLimitNumber\" min=\"1\" max=\"512\" value=\"256\"> <span\nid=\"colorLimitValue\">256</span> </td> </tr> <tr class=\"ha-hide\"> <td\nstyle=\"vertical-align:middle\"> <label for=\"haID\">HA Device ID:</label> </td> <td\n class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" id=\"haID\"\nvalue=\"pixel_art_controller_001\"> </td> </tr> <tr class=\"ha-hide\"> <td\nstyle=\"vertical-align:middle\"> <label for=\"haUID\">HA Device Unique ID:</label>\n</td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\"\nid=\"haUID\" value=\"pixel_art_controller_001a\"> </td> </tr> <tr class=\"ha-hide\">\n<td style=\"vertical-align:middle\"> <label for=\"haName\">HA Device Name:</label>\n</td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\"\nid=\"haName\" value=\"Pixel Art Kitchen\"> </td> </tr> <tr> <td\nstyle=\"vertical-align:middle\"> <label for=\"curlUrl\">Device IP/host name:</label>\n </td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\"\nid=\"curlUrl\" value> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label\nfor=\"targetSegment\">Target segment id:</label> </td> <td class=\"adaptiveTD\">\n<input class=\"flxTFld\" type=\"number\" id=\"segID\" value=\"0\" min=\"0\" max=\"63\">\n<select id=\"targetSegment\" class=\"adaptiveSelector\"> </select> <div\nid=\"getSegmentsDiv\" class=\"segmentsDiv\"></div> </td> </tr> </table> <table\nclass=\"scaleTableClass\" id=\"scaleTable\"\nstyle=\"width:100%;table-layout:fixed;align-content:center\"> <tr> <td\nstyle=\"vertical-align:middle\"> <div id=\"scaleDiv\"> <svg\nstyle=\"width:36px;height:36px\" viewBox=\"0 0 24 24\" onclick=\"switchScale()\"\ncursor=\"pointer\"><path fill=\"currentColor\"\nd=\"M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5M7 15a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3z\"/>\n</svg> &nbsp;Scale image </div> </td> <td style=\"vertical-align:middle\"> <div\nid=\"sizeDiv\" style=\"display:none\"> <label for=\"sizeX\">W : </label> &nbsp;<input\nclass=\"sizeInputFields\" type=\"number\" id=\"sizeX\" value=\"0\"> &nbsp;&nbsp;&nbsp;\n<label for=\"sizeY\">H : </label> &nbsp;<input class=\"sizeInputFields\"\ntype=\"number\" id=\"sizeY\" value=\"0\"> </div> </td> </tr> </table> </p> <p> <label\nfor=\"file-picker\"> <div id=\"drop-zone\"> Drop image here <br>or <br>\n Click to select a file </div> </label> </p> <p> <input type=\"file\"\nid=\"file-picker\" style=\"display:none\"> </p><div\nstyle=\"width:100%;text-align:center\"> <img id=\"preview\"\nstyle=\"display:none;margin:0 auto\" src=\"data:text/html;base64,\"> </div> <div\nid=\"submitConvertDiv\" style=\"display:none\"> <button id=\"convertbutton\"\nclass=\"buttonclass\"></button> </div> <div id=\"raw-image-container\"\nstyle=\"display:none\"> <img id=\"image\" src=\"data:text/html;base64,\"\nalt=\"RawImage image\"> </div> <p></p> <div id=\"image-container\"\nstyle=\"display:none\"> <div id=\"image-info\" style=\"display:none\"></div> <textarea\n id=\"JSONled\"></textarea> </div> <div id=\"button-container\"\nstyle=\"display:none\"> <button id=\"copyJSONledbutton\" class=\"buttonclass\">\n</button> <div id=\"gap1\" class=\"gap\"></div> <button id=\"sendJSONledbutton\"\nclass=\"buttonclass\"></button> <div id=\"gap2\" class=\"gap\"></div> <button\nid=\"fileJSONledbutton\" class=\"buttonclass\"></button> </div> <div> <h3><div\nid=\"version\">Version 1.0.5</div>&nbsp;-&nbsp; <a\nhref=\"https://github.com/werkstrom/WLED-PixelArtConverter/blob/main/README.md\"\ntarget=\"_blank\">Help/About</a></h3> </div> </div> <div id=\"bottom-part\"\nstyle=\"display:none\" class=\"bottom-part\"></div> <canvas id=\"pixelCanvas\">\n</canvas> <script type=\"text/javascript\">\nvar curlStart='curl -X POST \"http://',curlMid1=\"/json/state\\\" -d '\",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    \",haMid1=\"\\n      friendly_name: \",haMid2=\"\\n      unique_id: \",haMid3=\"\\n      command_on: >\\n        \",haMid4='\\n      command_off: >\\n        curl -X POST \"http://',haEnd='/json/state\" -d \\'{\"on\":false}\\' -H \"Content-Type: application/json\"',haCommandLeading=\"        \",JSONledStringStart='{\"on\":true,\"bri\":',JSONledStringMid1=',\"seg\":{\"id\":',JSONledStringMid2=',\"i\":[',JSONledStringEnd=\"]}}\";var accentColor=\"#eee\",accentTextColor=\"#777\",greenColor=\"#056b0a\",redColor=\"#6b050c\",scaleToggleOffd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z\",scaleToggleOnd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z\"\n</script> <script type=\"text/javascript\">\nfunction getPixelRGBValues(e){httpArray=[],fileJSON=JSONledStringStart+gId(\"brightnessNumber\").value+JSONledStringMid1+gId(\"targetSegment\").value+JSONledStringMid2;const t=gId(\"targetSegment\");let l=0;console.log(t.style.display),l=\"flex\"==t.style.display?t.value:gId(\"segID\").value,console.log(l);const a=gId(\"JSONled\"),d=gId(\"colorLimitNumber\").value;let r=!1,i=-1,n=gId(\"formatSelector\");i=n.selectedIndex;const o=n.options[i].value;n=gId(\"ledSetupSelector\"),i=n.selectedIndex;const g=n.options[i].value;n=gId(\"colorFormatSelector\"),i=n.selectedIndex;let s=!0;\"dec\"==n.options[i].value&&(s=!1),n=gId(\"addressingSelector\"),i=n.selectedIndex;let u=!0;\"single\"==n.options[i].value?u=!1:\"hybrid\"==n.options[i].value&&(r=!0);let c=\"\",h=\"\",S=\"'\",I=\"'\";s||(S=\"[\",I=\"]\");let v=!1,p=\"\";var f=cE(\"canvas\"),m=f.getContext(\"2d\"),N=new Image;N.src=e,N.onload=function(){let e=gId(\"scaleDiv\").children[0].children[0].getAttribute(\"fill\"),t=gId(\"sizeX\").value,i=gId(\"sizeY\").value;(e!=accentColor||t<1||i<1)&&(t=N.width,i=N.height),f.width=t,f.height=i,p=\"<p>Width: \"+t+\", Height: \"+i+\" (make sure this matches your led matrix setup)</p>\",m.drawImage(N,0,0,t,i);var n=m.getImageData(0,0,t,i).data,y=[];let O=1;\"l2r\"==g&&(O=0);for(var J=0;J<n.length;J+=4){var b=n[J],M=n[J+1],x=n[J+2],w=n[J+3];let e=J/4,l=Math.floor(e/t),a=e;if(\"matrix\"==g);else if((l+O)%2==0);else{a=l*t+(t-1-(a-l*t))}y.push([b,M,x,w,a,e,l])}y.sort((e,t)=>e[5]-t[5]);let E=[...y];E.sort((e,t)=>e[4]-t[4]);let D=\"\",R=-1,A=E.length,T=0,k=[];for(let e=0;e<A;e++){let t=E[e],l=t[0],a=t[1],i=t[2],n=t[3],o=\"\",g=-1;if(u)if(R<0&&(R=e),e<A-1){let t=E[e+1];t[0]==l&&t[1]==a&&t[2]==i||(g=e+1,o=R==e&&r?\"\"==D?e+\",\":\"\":R+\",\"+g+\",\")}else g=e+1,o=R+1==g&&r?\"\"==D?e+\",\":\"\":R+\",\"+g+\",\";else\"\"==D&&(D=e),R=e,g=e;if(n<255&&(v=!0),g>-1){let t=l+\",\"+a+\",\"+i;if(s){const[e,d,r]=[l,a,i];t=\"\"+[e,d,r].map(e=>e.toString(16).padStart(2,\"0\")).join(\"\")}D=D+o+S+t+I,fileJSON=D+o+S+t+I,T+=1,T%d==0||e==A-1?(k.push(D),D=\"\"):D+=\",\",R=-1}}D=\"\";for(let e=0;e<k.length;e++){let t=JSONledStringStart+gId(\"brightnessNumber\").value+JSONledStringMid1+l+JSONledStringMid2+k[e]+JSONledStringEnd;httpArray.push(t);let a=curlStart+gId(\"curlUrl\").value+curlMid1+t+curlEnd;e>0&&(D+=\"\\n\",c+=\" && \"),D+=t,c+=a}h=haStart+gId(\"haID\").value+haMid1+gId(\"haName\").value+haMid2+gId(\"haUID\").value+haMid3+c+haMid3+gId(\"curlUrl\").value+haEnd,a.value=\"wled\"==o?D:\"curl\"==o?c:\"ha\"==o?h:\"ERROR!/n\"+o+\" is an unknown format.\",fileJSON+=JSONledStringEnd;let U=gId(\"image-info\"),z=gId(\"image-info\");v&&(p+=\"<p><b>WARNING!</b> 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.</p>\"),U.innerHTML=p,z.style.display=\"block\",drawBoxes(y,t,i)}}\n</script> <script type=\"text/javascript\">\nfunction drawBoxes(t,e,i){var n=gId(\"pixelCanvas\"),l=n.getContext(\"2d\");window.innerHeight<window.innerWidth?n.width=Math.floor(.98*window.innerHeight):n.width=Math.floor(.98*window.innerWidth);let o=Math.floor(n.width/e),d=(window.innerWidth-e*o)/2;n.height=o*i+10;for(let n=0;n<i;n++)for(let i=0;i<e;i++){let d=t[n*e+i],h=\"rgb(\"+d[0]+\", \"+d[1]+\", \"+d[2]+\")\",r=(d[0],d[1],d[2],d[4]),w=\"rgb(128,128,128)\";l.fillStyle=h,l.fillRect(i*o,n*o,o,o),l.strokeStyle=\"#888888\",l.lineWidth=1,l.strokeRect(i*o,n*o,o,o),l.font=\"10px Arial\",l.fillStyle=w,l.textAlign=\"center\",l.textBaseline=\"middle\",l.fillText(r+1,i*o+o/2,n*o+o/2)}var h=l.getImageData(0,0,n.width,n.height);l.clearRect(0,0,n.width,n.height),n.width=window.innerWidth,l.putImageData(h,d,0),console.log(d)}\n</script> <script type=\"text/javascript\">\ngId(\"curlUrl\").value=location.host;let devMode=!1;const urlParams=new URLSearchParams(window.location.search);urlParams.has(\"dev\")&&(devMode=!0),devMode?console.log(\"Developer mode active. Experimental and unstable functions active.\"):console.log('Developer mode inactive. Append \"?dev\" to the URL.'),devMode?(gId(\"fileJSONledbutton\").style.display=\"buttonclass\",gId(\"gap2\").style.display=\"gap\"):(gId(\"fileJSONledbutton\").style.display=\"none\",gId(\"gap2\").style.display=\"none\");let httpArray=[],fileJSON=\"\";async function postPixels(){for(let e of httpArray)try{devMode&&console.log(e),devMode&&console.log(e.length);const t=await fetch(\"http://\"+gId(\"curlUrl\").value+\"/json/state\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:e}),l=await t.json();devMode&&console.log(l)}catch(e){console.error(e)}}gId(\"convertbutton\").addEventListener(\"click\",()=>{let e=gId(\"preview\").src;if(isValidBase64Gif(e))gId(\"image\").src=e,getPixelRGBValues(e),gId(\"image-container\").style.display=\"block\",gId(\"button-container\").style.display=\"\";else{let t=gId(\"image-info\"),l=\"<p><b>WARNING!</b> File does not appear to be a valid image</p>\";t.innerHTML=l,t.style.display=\"block\",gId(\"image-container\").style.display=\"none\",gId(\"JSONled\").value=\"\",devMode&&console.log(\"The string '\"+e+\"' is not a valid base64 image.\")}}),gId(\"copyJSONledbutton\").addEventListener(\"click\",async()=>{let e=gId(\"JSONled\");e.select();try{await navigator.clipboard.writeText(e.value)}catch(e){try{await d.execCommand(\"copy\")}catch(e){console.error(\"Failed to copy text: \",e)}}}),gId(\"sendJSONledbutton\").addEventListener(\"click\",async()=>{\"https:\"===window.location.protocol?alert(\"Will only be available when served over http (or WLED is run over https)\"):postPixels()}),gId(\"fileJSONledbutton\").addEventListener(\"click\",async()=>{if(\"https:\"===window.location.protocol)alert(\"Will only be available when served over http (or WLED is run over https)\");else{let e=\"TheName.json\",t=\"http://\"+gId(\"curlUrl\").value+\"/upload\";sendAsFile(fileJSON,e,t)}});const dropZone=gId(\"drop-zone\"),filePicker=gId(\"file-picker\"),preview=gId(\"preview\");function zoneClicked(e){e.preventDefault(),filePicker.click()}function dragEnter(e){e.preventDefault(),this.classList.add(\"drag-over\")}function dragOver(e){e.preventDefault()}function dropped(e){e.preventDefault(),this.classList.remove(\"drag-over\");updatePreview(e.dataTransfer.files[0])}function filePicked(e){updatePreview(e.target.files[0])}function updatePreview(e){const t=new FileReader;t.onload=()=>{preview.src=t.result,gId(\"submitConvertDiv\").style.display=\"\",gId(\"preview\").style.display=\"\"},t.readAsDataURL(e)}function isValidBase64Gif(e){return!0}dropZone.addEventListener(\"dragenter\",dragEnter),dropZone.addEventListener(\"dragover\",dragOver),dropZone.addEventListener(\"drop\",dropped),dropZone.addEventListener(\"click\",zoneClicked),filePicker.addEventListener(\"change\",filePicked),gId(\"brightnessNumber\").oninput=()=>{let e=gId(\"brightnessNumber\");gId(\"brightnessValue\").textContent=e.value;let t=100*parseInt(e.value)/255;var l=`linear-gradient(90deg, #bbb ${t}%, #333 ${t}%)`;e.style.backgroundImage=l},gId(\"colorLimitNumber\").oninput=()=>{let e=gId(\"colorLimitNumber\");gId(\"colorLimitValue\").textContent=e.value;let t=100*parseInt(e.value)/512;var l=`linear-gradient(90deg, #bbb ${t}%, #333 ${t}%)`;e.style.backgroundImage=l};for(var hideableRows=d.querySelectorAll(\".ha-hide\"),i=0;i<hideableRows.length;i++)hideableRows[i].classList.add(\"hide\");function switchScale(){let e=gId(\"scaleDiv\").children[0].children[0],t=e.getAttribute(\"fill\"),l=\"\";t===accentColor?(t=accentTextColor,l=scaleToggleOffd,gId(\"sizeDiv\").style.display=\"none\"):(t=accentColor,l=scaleToggleOnd,gId(\"sizeDiv\").style.display=\"\"),e.setAttribute(\"fill\",t),e.setAttribute(\"d\",l)}function sendAsFile(e,t,l){var o=new Blob([e],{type:\"application/json\"});devMode&&(console.log(e),console.log(t),console.log(l));var n=new FormData;n.append(\"file\",o,t);var i=new XMLHttpRequest;i.open(\"POST\",l,!0),i.onload=()=>{200===i.status?devMode&&console.log(\"File uploaded successfully!\"):devMode&&console.log(\"File upload failed!\")},i.send(n)}function generateSegmentOptions(e){var t=gId(\"targetSegment\");t.innerHTML=\"\";for(var l=0;l<e.length;l++){var o=cE(\"option\");o.value=e[l].value,o.text=e[l].text,t.appendChild(o),0===l&&(o.selected=!0)}}async function getSegments(){try{var e=[];const o=await fetch(\"http://\"+gId(\"curlUrl\").value+\"/json/state\"),n=await o.json();console.log(n);let i=n.seg.map(e=>e.id);console.log(i);for(var t=0;t<i.length;t++){e.push({value:i[t],text:\"Segment index \"+i[t]}),generateSegmentOptions(e),gId(\"targetSegment\").style.display=\"flex\",gId(\"segID\").style.display=\"none\";var l=gId(\"getSegmentsSVGpath\");l.setAttribute(\"fill\",greenColor),setTimeout((function(){l.setAttribute(\"fill\",accentTextColor)}),1e3)}}catch(e){console.error(e);var o=gId(\"getSegmentsSVGpath\");o.setAttribute(\"fill\",redColor),setTimeout((function(){o.setAttribute(\"fill\",accentTextColor)}),1e3),gId(\"targetSegment\").style.display=\"none\",gId(\"segID\").style.display=\"flex\"}}function generateSegmentArray(e){for(var t=[],l=0;l<e;l++)t.push({value:l,text:\"Segment index \"+l});return t}gId(\"formatSelector\").addEventListener(\"change\",()=>{for(var e=0;e<hideableRows.length;e++)hideableRows[e].classList.toggle(\"hide\",\"ha\"!==gId(\"formatSelector\").value)});var segmentData=generateSegmentArray(10);generateSegmentOptions(segmentData),gId(\"getSegmentsDiv\").innerHTML='<svg id=getSegmentsSVG style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\" onclick=\"getSegments()\"><path id=getSegmentsSVGpath fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.68 7.35 7.38 5.73 9.07 4.1 11 4.1 11.83 4.1 12.41 4.69 13 5.28 13 6.1V12.15L14.6 10.6L16 12L12 16L8 12L9.4 10.6L11 12.15V6.1Q9.1 6.45 8.05 7.94 7 9.43 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 9.8 16.45 8.76 15.9 7.73 15 7V4.68Q16.85 5.55 17.93 7.26 19 9 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20M12 11.05Z\" /></svg>',gId(\"fileJSONledbutton\").innerHTML='<svg style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M20 18H4V8H20M20 6H12L10 4H4A2 2 0 0 0 2 6V18A2 2 0 0 0 4 20H20A2 2 0 0 0 22 18V8A2 2 0 0 0 20 6M16 17H14V13H11L15 9L19 13H16Z\" /></svg>&nbsp; File to device',gId(\"convertbutton\").innerHTML='<svg style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z\" /> </svg>&nbsp; Convert to WLED JSON ',gId(\"copyJSONledbutton\").innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\" /> </svg>&nbsp; Copy to clipboard',gId(\"sendJSONledbutton\").innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z\" /> </svg>&nbsp; Send to device'\n</script> </body> </body></html>\n"
  },
  {
    "path": "archived/pixartmin.htm",
    "content": "<!DOCTYPE html> <html> <head> <meta http-equiv=\"Cache-Control\" \ncontent=\"no-cache, no-store, must-revalidate\"> <meta http-equiv=\"Pragma\" \ncontent=\"no-cache\"> <meta http-equiv=\"Expires\" content=\"0\"> <title>\nWLED Pixel Art Converter</title> <style>\n.box{border:2px solid #fff}body{font-family:Arial,sans-serif;background-color:#111}.top-part{width:600px;margin:0 auto}.container{max-width:100% -40px;border-radius:0;padding:20px;text-align:center}h1{font-size:2.3em;color:#ddd;margin:1px 0;font-family:Arial,sans-serif;line-height:.5}h2{font-size:1.1em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:.5;text-align:center}h3{font-size:.7em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:1.4;text-align:center;align-items:center;justify-content:center;display:flex}p{font-size:1em;color:#777;line-height:1.5;font-family:Arial,sans-serif}#fieldTable{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}#scaleTable{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}#drop-zone{display:block;width:100%-40px;border:3px dashed #ddd;border-radius:0;text-align:center;padding:20px;margin:0;cursor:pointer;font-family:Arial,sans-serif;font-size:15px;color:#777}#file-picker{display:none}.adaptiveTD{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center}.mainSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.adaptiveSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:none}.segmentsDiv{width:36px;padding-left:5px}* input[type=range]{appearance:none;-moz-appearance:none;-webkit-appearance:none;flex-grow:1;padding:0;margin:4px 8px 4px 0;background-color:transparent;cursor:pointer;background:linear-gradient(to right,#bbb 50%,#333 50%);border-radius:7px}input[type=range]:focus{outline:0}input[type=range]::-webkit-slider-runnable-track{height:28px;cursor:pointer;background:0 0;border-radius:7px}input[type=range]::-webkit-slider-thumb{height:16px;width:16px;border-radius:50%;background:#fff;cursor:pointer;-webkit-appearance:none;margin-top:4px;border-radius:7px}input[type=range]::-moz-range-track{height:28px;background-color:rgba(0,0,0,0);border-radius:7px}input[type=range]::-moz-range-thumb{border:0 solid transparent;height:16px;width:16px;border-radius:7px;background:#fff}.rangeNumber{width:20px;vertical-align:middle}.fullTextField[type=text]{background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:4px;margin-bottom:4px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.flxTFld{background-color:#222;border:1px solid #333;padding-inline-start:5px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}* input[type=submit]{background-color:#222;border:1px solid #333;padding:.5em;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1.3em;color:#ddd}* button{background-color:#222;border:1px solid #333;padding-inline:5px;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1em;color:#ddd;display:flex;align-items:center;justify-content:center;cursor:pointer}#scaleDiv{display:flex;align-items:center;vertical-align:middle}textarea{grid-row:1/2;width:100%;height:200px;background-color:#222;border:1px solid #333;color:#ddd}.hide{display:none}.svg-icon{vertical-align:middle}#image-container{display:grid;grid-template-rows:1fr 1fr}#button-container{display:flex;padding-bottom:10px;padding-top:10px}.buttonclass{flex:1;padding-top:5px;padding-bottom:5px}.gap{width:10px}#submitConvert::before{content:\"\";display:inline-block;background-image:url('data:image/svg+xml;utf8, <svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\" <path fill=\"currentColor\" d=\"M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z\" /></svg>');width:36px;height:36px}#sizeDiv *{display:inline-block}.sizeInputFields{width:50px;background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:-5px;height:24px;border-radius:7px;font-family:Arial,sans-serif;font-size:15px;color:#ddd}a:link{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:visited{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:hover{color:#ddd;background-color:transparent;text-decoration:none}a:active{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}\n</style> <link rel=\"shortcut icon\" \nhref=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAkNJREFUOE+lUstO21AUnHNsF0V+0EXVivAXfAe7qlL7EZEcQ5cQ8nCICbDJF7Trvhas6YbyDdBu2hSk7kihscK1fU91HRKKaFf1xhrP8ejMnCH850P/+v/cC2qcqw4gsGD3Hk/G23+bvSdw7nk1K887BeCQ4AGMAiEjcGaB7gndEfhRqdRQSC9ncQikAVisAWGBFggR5QvQ7UcTNd9mLvDdC2p2rhIN2G4U/vRbneyynzhps70oAPlhNPLjZna1v4/xVmtQHU8tzQXO3CCk4nrPD8MLN+6qd8ef8erwBKvH750XK1Xy4li9/fQFrw9PAGSDg8azuwJDN6jbuep7a+GFF3ezN0en5fDq8Qfn+UqVgjhW5bePpwCKwUHj6a2ACY7zrCdCC34UjrzutjLZiQC/kh0HlyPyu9tKzM4iGPf7uG5sDEwWpYWvrh85RZYAYAIp7+XayNtqytXeLqWbjUUNcLC+fuG2mpLu7tPl5sZDAnpLk0mzFDir+BFJlkCECaKFuSBYSlA4pLWtiRjEygIyErFzyIKjreYTlbZKgaHrRlzoHotweXeAhEhIxPBTDBhQYgEYhNbybINhxY1sbSwwkbHOZrzs3VRmjmnOk6bOkrqxMHT9yCqyxNybAFMg1mWRBGYlg2eboOSFGdT+IwM3Eil6zBwXZF1xkSWkOa6qtHleceuQvC+g4pZXiaOt9jyDb24Q2qKC5XTSvrlK3ZHMW04nLdMPKtQOMzoGTzPz69YNP2vi7D11ftvQWYCzyt7jfwO10TQgL5hT6QAAAABJRU5ErkJggg==\">\n <script type=\"text/javascript\">\nfunction gId(e){return d.getElementById(e)}function cE(e){return d.createElement(e)}var d=document\n</script> </head> <body> <body> <div class=\"top-part\"> <div \nstyle=\"display:flex;justify-content:center\"> <h1 \nstyle=\"display:flex;align-items:center\"> <svg \nstyle=\"width:36px;height:36px;margin-right:6px\" viewBox=\"0 0 32 32\"><path \nfill=\"#003fff\" d=\"M6 22h8v4H6zM14 14h4v8h-4zM18 10h4v8h-4zM22 6h8v4h-8z\"/></svg>\n WLED Pixel Art Converter </h1> </div> <h2>\nConvert image to WLED JSON (pixel art on WLED matrix)</h2> <p> <table \nid=\"fieldTable\" style=\"width:100%;table-layout:fixed;align-content:center\"> <tr>\n <td style=\"vertical-align:middle\"> <label for=\"ledSetupSelector\">Led setup:\n</label> </td> <td class=\"adaptiveTD\"> <select id=\"ledSetupSelector\" \nclass=\"mainSelector\"> <option value=\"matrix\" selected=\"selected\">2D Matrix\n</option> <option value=\"r2l\">Serpentine, first row right to left &lt;-</option>\n <option value=\"l2r\">Serpentine, first row left to right -&gt;</option> \n</select> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label \nfor=\"formatSelector\">Output format:</label> </td> <td class=\"adaptiveTD\"> \n<select id=\"formatSelector\" class=\"mainSelector\"> <option value=\"wled\" \nselected=\"selected\">WLED JSON</option> <option value=\"curl\">CURL</option> \n<option value=\"ha\">Home Assistant YAML</option> </select> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"colorFormatSelector\">\nColor code format:</label> </td> <td class=\"adaptiveTD\"> <select \nid=\"colorFormatSelector\" class=\"mainSelector\"> <option value=\"hex\" \nselected=\"selected\">HEX (#f4f4f4)</option> <option value=\"dec\">DEC (244,244,244)\n</option> </select> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label \nfor=\"addressingSelector\">Addressing:</label> </td> <td class=\"adaptiveTD\"> \n<select id=\"addressingSelector\" class=\"mainSelector\"> <option value=\"hybrid\" \nselected=\"selected\">Hybrid (#f0f0f0,10, 17, #f4f4f4)</option> <option \nvalue=\"range\">Range (10, 17, #f4f4f4)</option> <option value=\"single\">\nSingle (#f4f4f4)</option> </select> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"brightnessNumber\">Brightness:</label>\n </td> <td style=\"vertical-align:middle;display:flex;align-items:center\"> <input\n type=\"range\" id=\"brightnessNumber\" min=\"1\" max=\"255\" value=\"128\"> <span \nid=\"brightnessValue\">128</span> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"colorLimitNumber\">\nMax no of colors/JSON:</label> </td> <td \nstyle=\"vertical-align:middle;display:flex;align-items:center\"> <input \ntype=\"range\" id=\"colorLimitNumber\" min=\"1\" max=\"512\" value=\"256\"> <span \nid=\"colorLimitValue\">256</span> </td> </tr> <tr class=\"ha-hide\"> <td \nstyle=\"vertical-align:middle\"> <label for=\"haID\">HA Device ID:</label> </td> <td\n class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" id=\"haID\" \nvalue=\"pixel_art_controller_001\"> </td> </tr> <tr class=\"ha-hide\"> <td \nstyle=\"vertical-align:middle\"> <label for=\"haUID\">HA Device Unique ID:</label> \n</td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" \nid=\"haUID\" value=\"pixel_art_controller_001a\"> </td> </tr> <tr class=\"ha-hide\"> \n<td style=\"vertical-align:middle\"> <label for=\"haName\">HA Device Name:</label> \n</td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" \nid=\"haName\" value=\"Pixel Art Kitchen\"> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"curlUrl\">Device IP/host name:</label>\n </td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" \nid=\"curlUrl\" value> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label \nfor=\"targetSegment\">Target segment id:</label> </td> <td class=\"adaptiveTD\"> \n<input class=\"flxTFld\" type=\"number\" id=\"segID\" value=\"0\" min=\"0\" max=\"63\"> \n<select id=\"targetSegment\" class=\"adaptiveSelector\"> </select> <div \nid=\"getSegmentsDiv\" class=\"segmentsDiv\"></div> </td> </tr> </table> <table \nclass=\"scaleTableClass\" id=\"scaleTable\" \nstyle=\"width:100%;table-layout:fixed;align-content:center\"> <tr> <td \nstyle=\"vertical-align:middle\"> <div id=\"scaleDiv\"> <svg \nstyle=\"width:36px;height:36px\" viewBox=\"0 0 24 24\" onclick=\"switchScale()\" \ncursor=\"pointer\"><path fill=\"currentColor\" \nd=\"M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5M7 15a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3z\"/>\n</svg> &nbsp;Scale image </div> </td> <td style=\"vertical-align:middle\"> <div \nid=\"sizeDiv\" style=\"display:none\"> <label for=\"sizeX\">W : </label> &nbsp;<input \nclass=\"sizeInputFields\" type=\"number\" id=\"sizeX\" min=\"1\" value=\"16\">\n &nbsp;&nbsp;&nbsp; <label for=\"sizeY\">H : </label> &nbsp;<input \nclass=\"sizeInputFields\" type=\"number\" id=\"sizeY\" min=\"1\" value=\"16\"> </div> \n</td> </tr> </table> </p> <p> <label for=\"file-picker\"> <div id=\"drop-zone\">\n Drop image here <br>or <br> Click to select a file </div> </label> </p> <p> \n<input type=\"file\" id=\"file-picker\" style=\"display:none\"> </p><div \nstyle=\"width:100%;text-align:center\"> <img id=\"preview\" \nstyle=\"display:none;margin:0 auto\" src=\"data:text/html;base64,\"> </div> <div \nid=\"raw-image-container\" style=\"display:none\"> <img id=\"image\" \nsrc=\"data:text/html;base64,\" alt=\"RawImage image\"> </div> <p></p> <div \nid=\"image-container\" style=\"display:none\"> <div id=\"image-info\" \nstyle=\"display:none\"></div> <textarea id=\"JSONled\" readonly=\"readonly\">\n</textarea> </div> <div id=\"button-container\" style=\"display:none\"> <button \nid=\"copyJSONledbutton\" class=\"buttonclass\"></button> <div id=\"gap1\" class=\"gap\">\n</div> <button id=\"sendJSONledbutton\" class=\"buttonclass\"></button> <div \nid=\"gap2\" class=\"gap\"></div> <button id=\"fileJSONledbutton\" class=\"buttonclass\">\n</button> </div> <div> <h3><div id=\"version\">Version 1.0.6</div>&nbsp;-&nbsp; <a\n href=\"https://github.com/werkstrom/WLED-PixelArtConverter/blob/main/README.md\" \ntarget=\"_blank\">Help/About</a></h3> </div> </div> <div id=\"bottom-part\" \nstyle=\"display:none\" class=\"bottom-part\"></div> <canvas id=\"pixelCanvas\">\n</canvas> <script type=\"text/javascript\">\nvar curlStart='curl -X POST \"http://',curlMid1=\"/json/state\\\" -d '\",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    \",haMid1=\"\\n      friendly_name: \",haMid2=\"\\n      unique_id: \",haMid3=\"\\n      command_on: >\\n        \",haMid4='\\n      command_off: >\\n        curl -X POST \"http://',haEnd='/json/state\" -d \\'{\"on\":false}\\' -H \"Content-Type: application/json\"',haCommandLeading=\"        \",JSONledStringStart='{\"on\":true,\"bri\":',JSONledStringMid1=',\"seg\":{\"id\":',JSONledStringMid2=',\"i\":[',JSONledStringEnd=\"]}}\";var accentColor=\"#eee\",accentTextColor=\"#777\",prsCol=\"#ccc\",greenColor=\"#056b0a\",redColor=\"#6b050c\",scaleToggleOffd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z\",scaleToggleOnd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z\"\n</script> <script type=\"text/javascript\">\nfunction getPixelRGBValues(e){httpArray=[],fileJSON=JSONledStringStart+gId(\"brightnessNumber\").value+JSONledStringMid1+gId(\"targetSegment\").value+JSONledStringMid2;const t=gId(\"targetSegment\");let l=0;l=\"flex\"==t.style.display?t.value:gId(\"segID\").value;const a=gId(\"JSONled\"),d=gId(\"colorLimitNumber\").value;let i=!1,r=-1,n=gId(\"formatSelector\");r=n.selectedIndex;const g=n.options[r].value;n=gId(\"ledSetupSelector\"),r=n.selectedIndex;const o=n.options[r].value;n=gId(\"colorFormatSelector\"),r=n.selectedIndex;let s=!0;\"dec\"==n.options[r].value&&(s=!1),n=gId(\"addressingSelector\"),r=n.selectedIndex;let u=!0;\"single\"==n.options[r].value?u=!1:\"hybrid\"==n.options[r].value&&(i=!0);let h=\"\",c=\"\",S=\"'\",I=\"'\";s||(S=\"[\",I=\"]\");let v=!1,p=\"\";var f=cE(\"canvas\"),m=f.getContext(\"2d\",{willReadFrequently:!0}),N=new Image;N.src=e,N.onload=function(){let e=gId(\"scaleDiv\").children[0].children[0].getAttribute(\"fill\"),t=gId(\"sizeX\").value,r=gId(\"sizeY\").value;(e!=accentColor||t<1||r<1)&&(t=N.width,r=N.height,(N.width>512||N.height>512)&&(t=16,r=16)),f.width=t,f.height=r,p=\"<p>Width: \"+t+\", Height: \"+r+\" (make sure this matches your led matrix setup)</p>\",m.drawImage(N,0,0,t,r);var n=m.getImageData(0,0,t,r).data,y=[];let O=1;\"l2r\"==o&&(O=0);for(var J=0;J<n.length;J+=4){var b=n[J],M=n[J+1],x=n[J+2],w=n[J+3];let e=J/4,l=Math.floor(e/t),a=e;if(\"matrix\"==o);else if((l+O)%2==0);else{a=l*t+(t-1-(a-l*t))}y.push([b,M,x,w,a,e,l])}y.sort((e,t)=>e[5]-t[5]);let E=[...y];E.sort((e,t)=>e[4]-t[4]);let R=\"\",D=-1,A=E.length,T=0,k=[];for(let e=0;e<A;e++){let t=E[e],l=t[0],a=t[1],r=t[2],n=t[3],g=\"\",o=-1;if(u)if(D<0&&(D=e),e<A-1){let t=E[e+1];t[0]==l&&t[1]==a&&t[2]==r||(o=e+1,g=D==e&&i?\"\"==R?e+\",\":\"\":D+\",\"+o+\",\")}else o=e+1,g=D+1==o&&i?\"\"==R?e+\",\":\"\":D+\",\"+o+\",\";else\"\"==R&&(R=e),D=e,o=e;if(n<255&&(v=!0),o>-1){let t=l+\",\"+a+\",\"+r;if(s){const[e,d,i]=[l,a,r];t=\"\"+[e,d,i].map(e=>e.toString(16).padStart(2,\"0\")).join(\"\")}R=R+g+S+t+I,fileJSON=R+g+S+t+I,T+=1,T%d==0||e==A-1?(k.push(R),R=\"\"):R+=\",\",D=-1}}R=\"\";for(let e=0;e<k.length;e++){let t=JSONledStringStart+gId(\"brightnessNumber\").value+JSONledStringMid1+l+JSONledStringMid2+k[e]+JSONledStringEnd;httpArray.push(t);let a=curlStart+gId(\"curlUrl\").value+curlMid1+t+curlEnd;e>0&&(R+=\"\\n\",h+=\" && \"),R+=t,h+=a}c=haStart+gId(\"haID\").value+haMid1+gId(\"haName\").value+haMid2+gId(\"haUID\").value+haMid3+h+haMid3+gId(\"curlUrl\").value+haEnd,a.value=\"wled\"==g?R:\"curl\"==g?h:\"ha\"==g?c:\"ERROR!/n\"+g+\" is an unknown format.\",fileJSON+=JSONledStringEnd;let U=gId(\"image-info\"),z=gId(\"image-info\");v&&(p+=\"<p><b>WARNING!</b> 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.</p>\"),U.innerHTML=p,z.style.display=\"block\",drawBoxes(y,t,r)}}\n</script> <script type=\"text/javascript\">\nfunction drawBoxes(t,e,i){var l=gId(\"pixelCanvas\"),n=l.getContext(\"2d\",{willReadFrequently:!0});window.innerHeight<window.innerWidth?l.width=Math.floor(.98*window.innerHeight):l.width=Math.floor(.98*window.innerWidth);let d=Math.floor(l.width/e),h=(window.innerWidth-e*d)/2;l.height=d*i+10;for(let l=0;l<i;l++)for(let i=0;i<e;i++){let h=t[l*e+i],r=\"rgb(\"+h[0]+\", \"+h[1]+\", \"+h[2]+\")\",o=(h[0],h[1],h[2],h[4]),w=\"rgb(128,128,128)\";n.fillStyle=r,n.fillRect(i*d,l*d,d,d),n.strokeStyle=\"#888888\",n.lineWidth=1,n.strokeRect(i*d,l*d,d,d),n.font=\"10px Arial\",n.fillStyle=w,n.textAlign=\"center\",n.textBaseline=\"middle\",n.fillText(o+1,i*d+d/2,l*d+d/2)}var r=n.getImageData(0,0,l.width,l.height);n.clearRect(0,0,l.width,l.height),l.width=window.innerWidth,n.putImageData(r,h,0)}\n</script> <script type=\"text/javascript\">\ngId(\"curlUrl\").value=location.host;let devMode=!1;const urlParams=new URLSearchParams(window.location.search);urlParams.has(\"dev\")&&(devMode=!0),devMode?console.log(\"Developer mode active. Experimental and unstable functions active.\"):console.log('Developer mode inactive. Append \"?dev\" to the URL.'),devMode?(gId(\"fileJSONledbutton\").style.display=\"buttonclass\",gId(\"gap2\").style.display=\"gap\"):(gId(\"fileJSONledbutton\").style.display=\"none\",gId(\"gap2\").style.display=\"none\");let httpArray=[],fileJSON=\"\";function gen(){if((gId(\"sizeX\").value>0&&gId(\"sizeY\").value>0||\"none\"==gId(\"sizeDiv\").style.display)&&gId(\"curlUrl\").value.length>0&&\"none\"!=gId(\"preview\").style.display){let e=gId(\"preview\").src;if(isValidBase64Gif(e))gId(\"image\").src=e,getPixelRGBValues(e),gId(\"image-container\").style.display=\"block\",gId(\"button-container\").style.display=\"\";else{let t=gId(\"image-info\"),n=\"<p><b>WARNING!</b> File does not appear to be a valid image</p>\";t.innerHTML=n,t.style.display=\"block\",gId(\"image-container\").style.display=\"none\",gId(\"JSONled\").value=\"\",devMode&&console.log(\"The string '\"+e+\"' is not a valid base64 image.\")}}var e=gId(\"getSegmentsSVGpath\");if(gId(\"curlUrl\").value.length>0)e.setAttribute(\"fill\",accentColor);else{e.setAttribute(\"fill\",accentTextColor);let t=gId(\"targetSegment\");t.style.display=\"none\",t.innerHTML=\"\",gId(\"segID\").style.display=\"flex\"}}async function postPixels(){(svg=gId(\"sendSvgP\")).setAttribute(\"fill\",prsCol);let e=!1;for(let t of httpArray)try{devMode&&console.log(t),devMode&&console.log(t.length);const e=await fetch(\"http://\"+gId(\"curlUrl\").value+\"/json/state\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:t}),n=await e.json();devMode&&console.log(n)}catch(t){console.error(t),e=!0}e?(svg.setAttribute(\"fill\",redColor),setTimeout((function(){svg.setAttribute(\"fill\",accentTextColor)}),1e3)):(svg.setAttribute(\"fill\",greenColor),setTimeout((function(){svg.setAttribute(\"fill\",accentColor)}),1e3))}function sendAsFile(e,t,n){(svg=gId(\"fileSvgP\")).setAttribute(\"fill\",prsCol);var l=new Blob([e],{type:\"application/json\"});devMode&&(console.log(e),console.log(t),console.log(n));var i=new FormData;i.append(\"file\",l,t);var o=new XMLHttpRequest;o.open(\"POST\",n,!0),o.onload=()=>{200===o.status?(svg.setAttribute(\"fill\",greenColor),setTimeout((function(){svg.setAttribute(\"fill\",accentTextColor)}),1e3),devMode&&console.log(\"File uploaded successfully!\")):(svg.setAttribute(\"fill\",redColor),setTimeout((function(){svg.setAttribute(\"fill\",accentColor)}),1e3),devMode&&console.log(\"File upload failed!\"))},o.send(i)}gId(\"copyJSONledbutton\").addEventListener(\"click\",async()=>{let e=gId(\"JSONled\");e.select();try{await navigator.clipboard.writeText(e.value)}catch(e){try{await d.execCommand(\"copy\")}catch(e){console.error(\"Failed to copy text: \",e)}}}),gId(\"ledSetupSelector\").addEventListener(\"change\",()=>{gen()}),gId(\"sizeY\").addEventListener(\"change\",()=>{gen()}),gId(\"sizeX\").addEventListener(\"change\",()=>{gen()}),gId(\"formatSelector\").addEventListener(\"change\",()=>{gen()}),gId(\"colorFormatSelector\").addEventListener(\"change\",()=>{gen()}),gId(\"addressingSelector\").addEventListener(\"change\",()=>{gen()}),gId(\"brightnessNumber\").addEventListener(\"change\",()=>{gen()}),gId(\"colorLimitNumber\").addEventListener(\"change\",()=>{gen()}),gId(\"haID\").addEventListener(\"change\",()=>{gen()}),gId(\"haUID\").addEventListener(\"change\",()=>{gen()}),gId(\"haName\").addEventListener(\"change\",()=>{gen()}),gId(\"curlUrl\").addEventListener(\"change\",()=>{gen()}),gId(\"targetSegment\").addEventListener(\"change\",(function(){sop=this.options[this.selectedIndex],gId(\"sizeX\").value=sop.dataset.x,gId(\"sizeY\").value=sop.dataset.y,gen()})),gId(\"segID\").addEventListener(\"change\",()=>{gen()}),gId(\"preview\").addEventListener(\"load\",(function(){gen()})),gId(\"sendJSONledbutton\").addEventListener(\"click\",async()=>{\"https:\"===window.location.protocol?alert(\"Will only be available when served over http (or WLED is run over https)\"):postPixels()}),gId(\"fileJSONledbutton\").addEventListener(\"click\",async()=>{if(\"https:\"===window.location.protocol)alert(\"Will only be available when served over http (or WLED is run over https)\");else{let e=\"TheName.json\",t=\"http://\"+gId(\"curlUrl\").value+\"/upload\";sendAsFile(fileJSON,e,t)}});const dropZone=gId(\"drop-zone\"),filePicker=gId(\"file-picker\"),preview=gId(\"preview\");function zoneClicked(e){e.preventDefault(),filePicker.click()}function dragEnter(e){e.preventDefault(),this.classList.add(\"drag-over\")}function dragOver(e){e.preventDefault()}function dropped(e){e.preventDefault(),this.classList.remove(\"drag-over\");updatePreview(e.dataTransfer.files[0])}function filePicked(e){updatePreview(e.target.files[0])}function updatePreview(e){const t=new FileReader;t.onload=()=>{preview.src=t.result,gId(\"preview\").style.display=\"\"},t.readAsDataURL(e)}function isValidBase64Gif(e){return!0}dropZone.addEventListener(\"dragenter\",dragEnter),dropZone.addEventListener(\"dragover\",dragOver),dropZone.addEventListener(\"drop\",dropped),dropZone.addEventListener(\"click\",zoneClicked),filePicker.addEventListener(\"change\",filePicked),gId(\"brightnessNumber\").oninput=()=>{let e=gId(\"brightnessNumber\");gId(\"brightnessValue\").textContent=e.value;let t=100*parseInt(e.value)/255;var n=`linear-gradient(90deg, #bbb ${t}%, #333 ${t}%)`;e.style.backgroundImage=n},gId(\"colorLimitNumber\").oninput=()=>{let e=gId(\"colorLimitNumber\");gId(\"colorLimitValue\").textContent=e.value;let t=100*parseInt(e.value)/512;var n=`linear-gradient(90deg, #bbb ${t}%, #333 ${t}%)`;e.style.backgroundImage=n};for(var hideableRows=d.querySelectorAll(\".ha-hide\"),i=0;i<hideableRows.length;i++)hideableRows[i].classList.add(\"hide\");function switchScale(){let e=gId(\"scaleDiv\").children[0].children[0],t=e.getAttribute(\"fill\"),n=\"\";t===accentColor?(t=accentTextColor,n=scaleToggleOffd,gId(\"sizeDiv\").style.display=\"none\"):(t=accentColor,n=scaleToggleOnd,gId(\"sizeDiv\").style.display=\"\"),e.setAttribute(\"fill\",t),e.setAttribute(\"d\",n),gen()}function generateSegmentOptions(e){var t=gId(\"targetSegment\");t.innerHTML=\"\";for(var n=0;n<e.length;n++){var l=cE(\"option\");l.value=e[n].value,l.text=e[n].text,l.dataset.x=e[n].x,l.dataset.y=e[n].y,t.appendChild(l),0===n&&(l.selected=!0)}}async function getSegments(){cv=gId(\"curlUrl\").value;var e=gId(\"getSegmentsSVGpath\");if(cv.length>0)try{var t=[];const l=await fetch(\"http://\"+cv+\"/json/state\");let i=(await l.json()).seg.map(e=>({id:e.id,n:e.n,xs:e.start,xe:e.stop,ys:e.startY,ye:e.stopY}));for(var n=0;n<i.length;n++)t.push({value:i[n].id,text:i[n].n+\" (index: \"+i[n].id+\")\",x:i[n].xe-i[n].xs,y:i[n].ye-i[n].ys});generateSegmentOptions(t),gId(\"targetSegment\").style.display=\"flex\",gId(\"segID\").style.display=\"none\",e.setAttribute(\"fill\",greenColor),setTimeout((function(){e.setAttribute(\"fill\",accentColor)}),1e3)}catch(t){console.error(t),e.setAttribute(\"fill\",redColor),setTimeout((function(){e.setAttribute(\"fill\",accentColor)}),1e3),gId(\"targetSegment\").style.display=\"none\",gId(\"segID\").style.display=\"flex\"}else e.setAttribute(\"fill\",redColor),setTimeout((function(){e.setAttribute(\"fill\",accentTextColor)}),1e3),gId(\"targetSegment\").style.display=\"none\",gId(\"segID\").style.display=\"flex\"}function generateSegmentArray(e){for(var t=[],n=0;n<e;n++)t.push({value:n,text:\"Segment index \"+n});return t}gId(\"formatSelector\").addEventListener(\"change\",()=>{for(var e=0;e<hideableRows.length;e++)hideableRows[e].classList.toggle(\"hide\",\"ha\"!==gId(\"formatSelector\").value)});var segmentData=generateSegmentArray(10);generateSegmentOptions(segmentData),gId(\"getSegmentsDiv\").innerHTML='<svg id=getSegmentsSVG style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\" onclick=\"getSegments()\"><path id=getSegmentsSVGpath fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.68 7.35 7.38 5.73 9.07 4.1 11 4.1 11.83 4.1 12.41 4.69 13 5.28 13 6.1V12.15L14.6 10.6L16 12L12 16L8 12L9.4 10.6L11 12.15V6.1Q9.1 6.45 8.05 7.94 7 9.43 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 9.8 16.45 8.76 15.9 7.73 15 7V4.68Q16.85 5.55 17.93 7.26 19 9 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20M12 11.05Z\" /></svg>',gId(\"fileJSONledbutton\").innerHTML='<svg style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"><path id=fileSvgP fill=\"currentColor\" d=\"M20 18H4V8H20M20 6H12L10 4H4A2 2 0 0 0 2 6V18A2 2 0 0 0 4 20H20A2 2 0 0 0 22 18V8A2 2 0 0 0 20 6M16 17H14V13H11L15 9L19 13H16Z\" /></svg>&nbsp; File to device',gId(\"copyJSONledbutton\").innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\" /> </svg>&nbsp; Copy to clipboard',gId(\"sendJSONledbutton\").innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path id=sendSvgP fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z\" /> </svg>&nbsp; Send to device';var svg=gId(\"getSegmentsSVGpath\");gId(\"curlUrl\").value.length>0&&svg.setAttribute(\"fill\",accentColor)\n</script> </body> </body></html>\n"
  },
  {
    "path": "beta/pixart.htm",
    "content": "<!DOCTYPE html> <html> <head> <meta http-equiv=\"Cache-Control\" \ncontent=\"no-cache, no-store, must-revalidate\"> <meta http-equiv=\"Pragma\" \ncontent=\"no-cache\"> <meta http-equiv=\"Expires\" content=\"0\"> <title>\nWLED Pixel Art Converter</title> <style>\n.box{border:2px solid #fff}body{font-family:Arial,sans-serif;background-color:#111}.top-part{width:600px;margin:0 auto}.container{max-width:100% -40px;border-radius:0;padding:20px;text-align:center}h1{font-size:2.3em;color:#ddd;margin:1px 0;font-family:Arial,sans-serif;line-height:.5}h2{font-size:1.1em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:.5;text-align:center}h3{font-size:.7em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:1.4;text-align:center;align-items:center;justify-content:center;display:flex}p{font-size:1em;color:#777;line-height:1.5;font-family:Arial,sans-serif}#fieldTable{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}.scaleTableClass{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}#drop-zone{display:block;width:100%-40px;border:3px dashed #ddd;border-radius:0;text-align:center;padding:20px;margin:0;cursor:pointer;font-family:Arial,sans-serif;font-size:15px;color:#777}#file-picker{display:none}.adaptiveTD{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center;vertical-align:middle}.mainSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.adaptiveSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:none}.segmentsDiv{width:36px;padding-left:5px}.sndRnDiv{width:36px;padding-left:5px}* input[type=range]{appearance:none;-moz-appearance:none;-webkit-appearance:none;flex-grow:1;padding:0;margin:4px 8px 4px 0;background-color:transparent;cursor:pointer;background:linear-gradient(to right,#bbb 50%,#333 50%);border-radius:7px}input[type=range]:focus{outline:0}input[type=range]::-webkit-slider-runnable-track{height:28px;cursor:pointer;background:0 0;border-radius:7px}input[type=range]::-webkit-slider-thumb{height:16px;width:16px;border-radius:50%;background:#fff;cursor:pointer;-webkit-appearance:none;margin-top:4px;border-radius:7px}input[type=range]::-moz-range-track{height:28px;background-color:rgba(0,0,0,0);border-radius:7px}input[type=range]::-moz-range-thumb{border:0 solid transparent;height:16px;width:16px;border-radius:7px;background:#fff}.rangeNumber{width:20px;vertical-align:middle}.fullTextField[type=text]{background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:4px;margin-bottom:4px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.flxTFld{background-color:#222;border:1px solid #333;padding-inline-start:5px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center;width:100%;box-sizing:border-box}.durFld{background-color:#222;border:1px solid #333;padding-inline-start:5px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}* input[type=submit]{background-color:#222;border:1px solid #333;padding:.5em;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1.3em;color:#ddd}* button{background-color:#222;border:1px solid #333;padding-inline:5px;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1em;color:#ddd;display:flex;align-items:center;justify-content:center;cursor:pointer}#scaleDiv{display:flex;align-items:center;vertical-align:middle}#runDiv{display:flex;align-items:center;vertical-align:middle}textarea{grid-row:1/2;width:100%;height:200px;background-color:#222;border:1px solid #333;color:#ddd}.hide{display:none}.svg-icon{vertical-align:middle}#image-container{display:grid;grid-template-rows:1fr 1fr}#button-container{display:flex;padding-bottom:10px;padding-top:10px}.buttonclass{flex:1;padding-top:5px;padding-bottom:5px}.gap{width:10px}#submitConvert::before{content:\"\";display:inline-block;background-image:url('data:image/svg+xml;utf8, <svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\" <path fill=\"currentColor\" d=\"M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z\" /></svg>');width:36px;height:36px}#sizeDiv *{display:inline-block}.sizeInputFields{width:50px;background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:-5px;height:24px;border-radius:7px;font-family:Arial,sans-serif;font-size:15px;color:#ddd}a:link{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:visited{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:hover{color:#ddd;background-color:transparent;text-decoration:none}a:active{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}.stpIconTD{width:36px;margin:0}.stpImgTD{width:36px;margin:0}.stpDur{display:flex;align-items:center;justify-content:center}\n</style> <link rel=\"shortcut icon\" \nhref=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAkNJREFUOE+lUstO21AUnHNsF0V+0EXVivAXfAe7qlL7EZEcQ5cQ8nCICbDJF7Trvhas6YbyDdBu2hSk7kihscK1fU91HRKKaFf1xhrP8ejMnCH850P/+v/cC2qcqw4gsGD3Hk/G23+bvSdw7nk1K887BeCQ4AGMAiEjcGaB7gndEfhRqdRQSC9ncQikAVisAWGBFggR5QvQ7UcTNd9mLvDdC2p2rhIN2G4U/vRbneyynzhps70oAPlhNPLjZna1v4/xVmtQHU8tzQXO3CCk4nrPD8MLN+6qd8ef8erwBKvH750XK1Xy4li9/fQFrw9PAGSDg8azuwJDN6jbuep7a+GFF3ezN0en5fDq8Qfn+UqVgjhW5bePpwCKwUHj6a2ACY7zrCdCC34UjrzutjLZiQC/kh0HlyPyu9tKzM4iGPf7uG5sDEwWpYWvrh85RZYAYAIp7+XayNtqytXeLqWbjUUNcLC+fuG2mpLu7tPl5sZDAnpLk0mzFDir+BFJlkCECaKFuSBYSlA4pLWtiRjEygIyErFzyIKjreYTlbZKgaHrRlzoHotweXeAhEhIxPBTDBhQYgEYhNbybINhxY1sbSwwkbHOZrzs3VRmjmnOk6bOkrqxMHT9yCqyxNybAFMg1mWRBGYlg2eboOSFGdT+IwM3Eil6zBwXZF1xkSWkOa6qtHleceuQvC+g4pZXiaOt9jyDb24Q2qKC5XTSvrlK3ZHMW04nLdMPKtQOMzoGTzPz69YNP2vi7D11ftvQWYCzyt7jfwO10TQgL5hT6QAAAABJRU5ErkJggg==\">\n <script type=\"text/javascript\">\nfunction gId(e){return d.getElementById(e)}function cE(e){return d.createElement(e)}var d=document\n</script> </head> <body> <body> <div class=\"top-part\"> <div \nstyle=\"display:flex;justify-content:center\"> <h1 \nstyle=\"display:flex;align-items:center\"> <svg \nstyle=\"width:36px;height:36px;margin-right:6px\" viewBox=\"0 0 32 32\"><path \nfill=\"#003fff\" d=\"M6 22h8v4H6zM14 14h4v8h-4zM18 10h4v8h-4zM22 6h8v4h-8z\"/></svg>\n WLED Pixel Art Converter </h1> </div> <h2>\nConvert image to WLED JSON (pixel art on WLED matrix)</h2> <p> <table \nid=\"fieldTable\" style=\"width:100%;table-layout:fixed;align-content:center\"> <tr>\n <td style=\"vertical-align:middle\"> <label for=\"ledSetupSelector\">Led setup:\n</label> </td> <td class=\"adaptiveTD\"> <select id=\"ledSetupSelector\" \nclass=\"mainSelector\"> <option value=\"matrix\" selected=\"selected\">2D Matrix\n</option> <option value=\"r2l\">Serpentine, first row right to left &lt;-</option>\n <option value=\"l2r\">Serpentine, first row left to right -&gt;</option> \n</select> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label \nfor=\"formatSelector\">Output format:</label> </td> <td class=\"adaptiveTD\"> \n<select id=\"formatSelector\" class=\"mainSelector\"> <option value=\"wled\" \nselected=\"selected\">WLED JSON</option> <option value=\"curl\">CURL</option> \n<option value=\"ha\">Home Assistant YAML</option> </select> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"colorFormatSelector\">\nColor code format:</label> </td> <td class=\"adaptiveTD\"> <select \nid=\"colorFormatSelector\" class=\"mainSelector\"> <option value=\"hex\" \nselected=\"selected\">HEX (&quot;f4f4f4&quot;)</option> <option value=\"dec\">\nDEC (244,244,244)</option> </select> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"addressingSelector\">Addressing:\n</label> </td> <td class=\"adaptiveTD\"> <select id=\"addressingSelector\" \nclass=\"mainSelector\"> <option value=\"hybrid\" selected=\"selected\">\nHybrid (&quot;f0f0f0&quot;,10, 17, &quot;f4f4f4&quot;)</option> <option \nvalue=\"range\">Range (10, 17, &quot;f4f4f4&quot;)</option> <option \nvalue=\"single\">Single (&quot;f4f4f4&quot;)</option> </select> </td> </tr> <tr> \n<td style=\"vertical-align:middle\"> <label for=\"brightnessNumber\">Brightness:\n</label> </td> <td \nstyle=\"vertical-align:middle;display:flex;align-items:center\"> <input \ntype=\"range\" id=\"brightnessNumber\" min=\"1\" max=\"255\" value=\"128\"> <span \nid=\"brightnessValue\">128</span> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"colorLimitNumber\">\nMax no of colors/JSON:</label> </td> <td \nstyle=\"vertical-align:middle;display:flex;align-items:center\"> <input \ntype=\"range\" id=\"colorLimitNumber\" min=\"1\" max=\"512\" value=\"256\"> <span \nid=\"colorLimitValue\">256</span> </td> </tr> <tr class=\"ha-hide\"> <td \nstyle=\"vertical-align:middle\"> <label for=\"haID\">HA Device ID:</label> </td> <td\n class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" id=\"haID\" \nvalue=\"pixel_art_controller_001\"> </td> </tr> <tr class=\"ha-hide\"> <td \nstyle=\"vertical-align:middle\"> <label for=\"haUID\">HA Device Unique ID:</label> \n</td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" \nid=\"haUID\" value=\"pixel_art_controller_001a\"> </td> </tr> <tr class=\"ha-hide\"> \n<td style=\"vertical-align:middle\"> <label for=\"haName\">HA Device Name:</label> \n</td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" \nid=\"haName\" value=\"Pixel Art Kitchen\"> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"curlUrl\">Device IP/host name:</label>\n </td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" \nid=\"curlUrl\" value> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label \nfor=\"targetSegment\">Target segment id:</label> </td> <td class=\"adaptiveTD\"> \n<input class=\"flxTFld\" type=\"number\" id=\"segID\" value=\"0\" min=\"0\" max=\"63\"> \n<select id=\"targetSegment\" class=\"adaptiveSelector\"> </select> <div \nid=\"getSegmentsDiv\" class=\"segmentsDiv\"></div> </td> </tr> </table> <table \nclass=\"scaleTableClass\" id=\"scaleTable\" \nstyle=\"width:100%;table-layout:fixed;align-content:center\"> <tr> <td \nstyle=\"vertical-align:middle\"> <div id=\"scaleDiv\"> <svg \nstyle=\"width:36px;height:36px\" viewBox=\"0 0 24 24\" onclick=\"switchScale()\" \ncursor=\"pointer\"><path fill=\"currentColor\" \nd=\"M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5M7 15a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3z\"/>\n</svg> &nbsp;Scale image </div> </td> <td style=\"vertical-align:middle\"> <div \nid=\"sizeDiv\" style=\"display:none\"> <label for=\"sizeX\">W : </label> &nbsp;<input \nclass=\"sizeInputFields\" type=\"number\" id=\"sizeX\" min=\"1\" value=\"16\">\n &nbsp;&nbsp;&nbsp; <label for=\"sizeY\">H : </label> &nbsp;<input \nclass=\"sizeInputFields\" type=\"number\" id=\"sizeY\" min=\"1\" value=\"16\"> </div> \n</td> </tr> </table> <table class=\"scaleTableClass\" id=\"runTable\" \nstyle=\"width:100%;table-layout:fixed;align-content:center\"> <tr \nstyle=\"height:40px\"> <td style=\"vertical-align:middle\"> <div id=\"runDiv\"> <svg \nstyle=\"width:36px;height:36px\" viewBox=\"0 0 24 24\" onclick=\"switchRun()\" \ncursor=\"pointer\"><path fill=\"currentColor\" \nd=\"M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5M7 15a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3z\"/>\n</svg> &nbsp;Generate flow file </div> </td> <td id=\"rnTD\" class=\"adaptiveTD\"> \n<input class=\"flxTFld\" type=\"text\" id=\"rnID\" value=\"ID\"> <div id=\"repRnDiv\" \nclass=\"sndRnDiv\" title=\"Toggle repeat on/off\" onclick=\"setRep()\"> </div> <div \nid=\"prwRnDiv\" class=\"sndRnDiv\" title=\"Preview animation\"> </div> <div \nid=\"sndRnDiv\" class=\"sndRnDiv\" title=\"Send animation file to device\"> </div> \n</td> </tr> <tr id=\"rnLstTR\" style=\"display:none\"> <td \nstyle=\"vertical-align:middle\"> <table id=\"rnLst\" \nstyle=\"width:100%;table-layout:fixed;align-content:center;vertical-align:middle\">\n <tr id=\"stp1\" data-json style=\"display:flex;align-items:center\"> </tr> </table>\n </td> </tr> </table> </p> <p> <label for=\"file-picker\"> <div id=\"drop-zone\">\n Drop image here <br>or <br> Click to select a file </div> </label> </p> <p> \n<input type=\"file\" id=\"file-picker\" style=\"display:none\"> </p><div id=\"lib\" \nstyle=\"width:100%;text-align:center\"> <img id=\"i0\" \nstyle=\"display:none;margin:0 auto\" alt=\"Library image 1\" width=\"64\" height=\"64\" \nsrc=\"data:text/html;base64,\"> </div> <div id=\"raw-image-container\" \nstyle=\"display:none\"> <img id=\"image\" src=\"data:text/html;base64,\" \nalt=\"RawImage image\"> </div> <p></p> <div id=\"image-container\" \nstyle=\"display:none\"> <div id=\"image-info\" style=\"display:none\"></div> <textarea\n id=\"JSONled\" readonly=\"readonly\"></textarea> </div> <div id=\"button-container\" \nstyle=\"display:none\"> <button id=\"copyJSONledbutton\" class=\"buttonclass\">\n</button> <div id=\"gap1\" class=\"gap\"></div> <button id=\"sendJSONledbutton\" \nclass=\"buttonclass\"></button> </div> <div> <h3><div id=\"version\" \ntitle=\"PAC version 2023.04.20.19.18.22\">Version 1.1.0.b</div>&nbsp;-&nbsp; <a \nhref=\"https://github.com/werkstrom/WLED-PixelArtConverter/blob/main/README.md\" \ntarget=\"_blank\">Help/About</a></h3> </div> </div> <div id=\"bottom-part\" \nstyle=\"display:none\" class=\"bottom-part\"></div> <canvas id=\"pixelCanvas\">\n</canvas> <p> </p><div style=\"width:100%;text-align:center\"> <div> <h3><div \nid=\"orig\">Original image</div></h3> </div> <img id=\"preview\" \nstyle=\"display:none;margin:0 auto\" src=\"data:text/html;base64,\"> </div> <p></p> \n<script type=\"text/javascript\">\nfor(var gurl=gId(\"curlUrl\"),szX=gId(\"sizeX\"),szY=gId(\"sizeY\"),szDiv=gId(\"sizeDiv\"),prw=gId(\"preview\"),lib=gId(\"lib\"),sID=gId(\"segID\"),JLD=gId(\"JSONled\"),tSg=gId(\"targetSegment\"),brgh=gId(\"brightnessNumber\"),seDiv=gId(\"getSegmentsDiv\"),cjb=gId(\"copyJSONledbutton\"),frm=gId(\"formatSelector\"),cLN=gId(\"colorLimitNumber\"),haIDe=gId(\"haID\"),haUe=gId(\"haUID\"),haNe=gId(\"haName\"),aS=gId(\"addressingSelector\"),cFS=gId(\"colorFormatSelector\"),lSS=gId(\"ledSetupSelector\"),imin=gId(\"image-info\"),imcn=gId(\"image-container\"),bcn=gId(\"button-container\"),im=gId(\"image\"),scDiv=gId(\"scaleDiv\"),rnDiv=gId(\"runDiv\"),w=window,canvas=gId(\"pixelCanvas\"),brgV=gId(\"brightnessValue\"),cLV=gId(\"colorLimitValue\"),sndRnDivgId=gId(\"sndRnDiv\"),httpArray=[],rawRGBArray=[],fileJSON=\"\",hideableRows=d.querySelectorAll(\".ha-hide\"),i=0;i<hideableRows.length;i++)hideableRows[i].classList.add(\"hide\");var accentColor=\"#eeeeee\",inactiveColor=\"#444444\",accentTextColor=\"#777\",prsCol=\"#ccc\",greenColor=\"#056b0a\",redColor=\"#6b050c\",scaleToggleOffd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z\",scaleToggleOnd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z\",repSVG='<svg id=\"repSVG\" style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\"><path id=\"repPath\" fill=\"#44444\" d=\"M17,17H7V14L3,18L7,22V19H19V13H17M7,7H17V10L21,6L17,2V5H5V11H7V7Z\"/></svg>',playSVG='<svg id=playSVG style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\"><path fill=\"#eeeeee\" d=\"M8,5.14V19.14L19,12.14L8,5.14Z\"/></svg>',stopSVGPath='<path id=\"stopSVGPath\" fill=\"#eee\" d=\"M18,18H6V6H18V18Z\"/>',playSVGPath='<path id=\"playSVGPath\" fill=\"#eee\" d=\"M8,5.14V19.14L19,12.14L8,5.14Z\"/>',sendSVG='<svg class=\"svg-icon\" style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\"> <path id=sendSvgP fill=\"#eee\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z\" /> </svg>',sSg=gId(\"getSegmentsSVGpath\"),sRnSVG=gId(\"sendRunnerSVGpath\"),stpTR='<td id=\"stpXxXLoad\" class=\"stpIconTD\"  title=\"Set selected image to step\"><svg id=loadImgSVGXxX style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\"><path fill=\"#eee\" d=\"M15 3h4V0l5 5-5 5V7h-4V3m6 8.94V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7.06c-.06.33-.06.67-.06 1a8 8 0 0 0 8 8c.33 0 .67 0 1-.06M19 18l-4.5-6-3.5 4.5-2.5-3L5 18h14Z\"/></svg></td><td id=\"stpXxXImgTD\" class=\"stpImgTD\"><svg id=empImgSVGXxX style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\"><path fill=\"#eee\" d=\"M15,5H17V3H15M15,21H17V19H15M11,5H13V3H11M19,5H21V3H19M19,9H21V7H19M19,21H21V19H19M19,13H21V11H19M19,17H21V15H19M3,5H5V3H3M3,9H5V7H3M3,13H5V11H3M3,17H5V15H3M3,21H5V19H3M11,21H13V19H11M7,21H9V19H7M7,5H9V3H7V5Z\"/></svg></td><td id=\"stpXxXDur\" title=\"Duration in ms\" class=\"stpDur\" style=\"vertical-align: middle;\">Duration:&nbsp;<input class=\"durFld\" type=\"number\" id=\"stpXxXDurFld\" value=\"50\" min=\"0\">&nbsp;1&#47;100s</td><td id=\"stpXxXAdd\" title=\"Add new step below\" class=\"stpIconTD\"><svg id=addRowSVGXxX style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\"><path fill=\"#eee\" d=\"M2,16H10V14H2M18,14V10H16V14H12V16H16V20H18V16H22V14M14,6H2V8H14M14,10H2V12H14V10Z\"/></svg></td><td id=\"stpXxXDel\" title=\"Delete step\" class=\"stpIconTD\"><svg id=delRowSVGXxX style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\"><path fill=\"#eee\" d=\"M2,6V8H14V6H2M2,10V12H11V10H2M14.17,10.76L12.76,12.17L15.59,15L12.76,17.83L14.17,19.24L17,16.41L19.83,19.24L21.24,17.83L18.41,15L21.24,12.17L19.83,10.76L17,13.59L14.17,10.76M2,14V16H11V14H2Z\"/></svg></td>'\n</script> <script type=\"text/javascript\">\nfunction getPixelRGBValues(e){httpArray=[],rawRGBArray=[],console.log(\"Generating RGB\"),fileJSON=`{\"on\":true,\"bri\":${brgh.value},\"seg\":{\"id\":${tSg.value},\"i\":[`;let t=0;t=\"flex\"==tSg.style.display?tSg.value:sID.value;const l=parseInt(cLN.value);let n=!1,a=-1;a=frm.selectedIndex;const i=frm.options[a].value;a=lSS.selectedIndex;const o=lSS.options[a].value;a=cFS.selectedIndex;let r=!0;\"dec\"==cFS.options[a].value&&(r=!1),a=aS.selectedIndex;let s=!0;\"single\"==aS.options[a].value?s=!1:\"hybrid\"==aS.options[a].value&&(n=!0);let u=\"\",d=\"\",c='\"',h='\"';r||(c=\"[\",h=\"]\");let p=!1,g=\"\";var f=cE(\"canvas\"),m=f.getContext(\"2d\",{willReadFrequently:!0}),v=new Image;v.src=e,v.onload=function(){let e=scDiv.children[0].children[0].getAttribute(\"fill\"),a=szX.value,y=szY.value;(e!=accentColor||a<1||y<1)&&(a=v.width,y=v.height,(v.width>512||v.height>512)&&(a=16,y=16)),f.width=a,f.height=y;new Uint8Array(a*y);g=\"<p>Width: \"+a+\", Height: \"+y+\" (make sure this matches your led matrix setup)</p>\",m.drawImage(v,0,0,a,y);var S=m.getImageData(0,0,a,y).data;rawRGBArray=[...S.filter((e,t)=>(t+1)%4!=0)];var w=[];let I=1;\"l2r\"==o&&(I=0);for(var $=0;$<S.length;$+=4){var b=S[$],x=S[$+1],A=S[$+2],N=S[$+3];let e=$/4,t=Math.floor(e/a),l=e;if(\"matrix\"==o);else if((t+I)%2==0);else{l=t*a+(a-1-(l-t*a))}w.push([b,x,A,N,l,e,t])}w.sort((e,t)=>e[5]-t[5]);let R=[...w];R.sort((e,t)=>e[4]-t[4]);let T=\"\",O=-1,D=R.length,G=0,j=[];for(let e=0;e<D;e++){let t=R[e],a=t[0],i=t[1],o=t[2],u=t[3],d=\"\",g=-1;if(s)if(O<0&&(O=e),e<D-1){let t=R[e+1];t[0]==a&&t[1]==i&&t[2]==o||(g=e+1,d=O==e&&n?\"\"==T?e+\",\":\"\":O+\",\"+g+\",\")}else g=e+1,d=O+1==g&&n?\"\"==T?e+\",\":\"\":O+\",\"+g+\",\";else\"\"==T&&(T=e+\",\"),O=e,g=e;if(u<255&&(p=!0),g>-1){let t=a+\",\"+i+\",\"+o;if(r){const[e,l,n]=[a,i,o];t=\"\"+[e,l,n].map(e=>e.toString(16).padStart(2,\"0\")).join(\"\")}T+=d+c+t+h,fileJSON=T+d+c+t+h,G+=1,G%l==0||e==D-1?(j.push(T),T=\"\"):T+=\",\",O=-1}}T=\"\";for(let e=0;e<j.length;e++){let l=`{\"on\":true,\"bri\":${brgh.value},\"seg\":{\"id\":${t},\"i\":[${j[e]}]}}`;httpArray.push(l);let n=`curl -X POST \"http://${gurl.value}/json/state\" -d '${l}' -H \"Content-Type: application/json\"`;e>0&&(T+=\"\\n<NEXT COMMAND (multiple commands not supported in API/preset setup)>\\n\",u+=\" && \"),T+=l,u+=n}d=`#Uncomment if you don't allready have these defined in your switch section of your configuration.yaml \\n#- platform: command_line \\n  #switches: \\n    ${haIDe.value} \\n      friendly_name: ${haNe.value} \\n      unique_id: ${haUe.value} \\n      command_on: > \\n        ${u} \\n      command_off: > \\n        curl -X POST \"http://${gurl.value}/json/state\" -d '{\"on\":false}' -H \"Content-Type: application/json\"`,JLD.value=\"wled\"==i?T:\"curl\"==i?u:\"ha\"==i?d:\"ERROR!/n\"+i+\" is an unknown format.\",fileJSON+=\"]}}\";let B=imin,C=imin;p&&(g+=\"<p><b>WARNING!</b> 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.</p>\"),B.innerHTML=g,C.style.display=\"block\",drawBoxes(w,a,y)}}\n</script> <script type=\"text/javascript\">\nfunction drawBoxes(t,e,a){var i=window,n=canvas.getContext(\"2d\",{willReadFrequently:!0});i.innerHeight<i.innerWidth?canvas.width=Math.floor(.98*i.innerHeight):canvas.width=Math.floor(.98*i.innerWidth);let l=Math.floor(canvas.width/e),h=(i.innerWidth-e*l)/2;canvas.height=l*a+10;for(let i=0;i<a;i++)for(let a=0;a<e;a++){let h=t[i*e+a],r=\"rgb(\"+h[0]+\", \"+h[1]+\", \"+h[2]+\")\",d=\"rgb(128,128,128)\";n.fillStyle=r,n.fillRect(a*l,i*l,l,l),n.strokeStyle=\"#888888\",n.lineWidth=1,n.strokeRect(a*l,i*l,l,l),n.font=\"10px Arial\",n.fillStyle=d,n.textAlign=\"center\",n.textBaseline=\"middle\",n.fillText(h[4]+1,a*l+l/2,i*l+l/2)}var r=n.getImageData(0,0,canvas.width,canvas.height);n.clearRect(0,0,canvas.width,canvas.height),canvas.width=i.innerWidth,n.putImageData(r,h,0)}\n</script> <script type=\"text/javascript\">\ngurl.value=location.host;var animTOId,aAr=[],curStp=0,isRep=0;const urlParams=new URLSearchParams(window.location.search);function gen(e=!1){if((szX.value>0&&szY.value>0||\"none\"==szDiv.style.display)&&gurl.value.length>0&&\"none\"!=prw.style.display){let t=prw.src;if(isValidBase64Gif(t)){if(im.src=t,e)return getPixelRGBValues(t,!0);getPixelRGBValues(t),imcn.style.display=\"block\",bcn.style.display=\"\"}else{let e=\"<p><b>WARNING!</b> File does not appear to be a valid image</p>\";imin.innerHTML=e,imin.style.display=\"block\",imcn.style.display=\"none\",JLD.value=\"\"}}if(gurl.value.length>0)gId(\"sSg\").setAttribute(\"fill\",accentColor);else{gId(\"sSg\").setAttribute(\"fill\",accentTextColor);let e=tSg;e.style.display=\"none\",e.innerHTML=\"\",sID.style.display=\"flex\"}}async function postPixels(){let e=gId(\"sendSvgP\");e.setAttribute(\"fill\",prsCol);let t=!1;for(let e of httpArray)try{const t=await fetch(\"http://\"+gId(\"curlUrl\").value+\"/json/state\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:e});await t.json()}catch(e){console.error(e),t=!0}t?(e.setAttribute(\"fill\",redColor),setTimeout((function(){e.setAttribute(\"fill\",accentTextColor)}),1e3)):(e.setAttribute(\"fill\",greenColor),setTimeout((function(){e.setAttribute(\"fill\",accentColor)}),1e3))}async function postAnim(e){let t=gId(\"sendSvgP\");t.setAttribute(\"fill\",prsCol);let n=!1,i=[`{\"pixart\":{\"anim\": \"${e}\"}}`];console.log(i);for(let e of i)try{const t=await fetch(\"http://\"+gId(\"curlUrl\").value+\"/json/state\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:e});await t.json()}catch(e){console.error(e),n=!0}n?(t.setAttribute(\"fill\",redColor),setTimeout((function(){t.setAttribute(\"fill\",accentTextColor)}),1e3)):(t.setAttribute(\"fill\",greenColor),setTimeout((function(){t.setAttribute(\"fill\",accentColor)}),1e3))}gurl.value.length<1&&(gurl.value=\"Missing_Host\"),cjb.addEventListener(\"click\",async()=>{let e=JLD;e.select();try{await navigator.clipboard.writeText(e.value)}catch(e){try{await d.execCommand(\"copy\")}catch(e){console.error(\"Failed to copy text: \",e)}}}),lSS.addEventListener(\"change\",gen),szY.addEventListener(\"change\",gen),szX.addEventListener(\"change\",gen),cFS.addEventListener(\"change\",gen),aS.addEventListener(\"change\",gen),brgh.addEventListener(\"change\",gen),cLN.addEventListener(\"change\",gen),haIDe.addEventListener(\"change\",gen),haUe.addEventListener(\"change\",gen),haNe.addEventListener(\"change\",gen),gurl.addEventListener(\"change\",gen),sID.addEventListener(\"change\",gen),prw.addEventListener(\"load\",gen),tSg.addEventListener(\"change\",()=>{sop=tSg.options[tSg.selectedIndex],szX.value=sop.dataset.x,szY.value=sop.dataset.y,gen()}),gId(\"sendJSONledbutton\").addEventListener(\"click\",async()=>{\"https:\"===window.location.protocol?alert(\"Will only be available when served over http (or WLED is run over https)\"):postPixels()}),brgh.oninput=()=>{brgV.textContent=brgh.value;let e=100*parseInt(brgh.value)/255;var t=`linear-gradient(90deg, #bbb ${e}%, #333 ${e}%)`;brgh.style.backgroundImage=t},cLN.oninput=()=>{let e=cLN;cLV.textContent=e.value;let t=100*parseInt(e.value)/512;var n=`linear-gradient(90deg, #bbb ${t}%, #333 ${t}%)`;e.style.backgroundImage=n},frm.addEventListener(\"change\",()=>{for(var e=0;e<hideableRows.length;e++)hideableRows[e].classList.toggle(\"hide\",\"ha\"!==frm.value),gen()});const dropZone=gId(\"drop-zone\"),filePicker=gId(\"file-picker\"),preview=prw;function zoneClicked(e){e.preventDefault(),filePicker.click()}function dragEnter(e){e.preventDefault(),this.classList.add(\"drag-over\")}function dragOver(e){e.preventDefault()}function dropped(e){e.preventDefault(),this.classList.remove(\"drag-over\");updatePreview(e.dataTransfer.files[0])}function filePicked(e){updatePreview(e.target.files[0])}function updatePreview(e){let t=\"\";const n=new FileReader;n.onload=()=>{preview.src=n.result;const e=document.querySelectorAll(\"#lib img\");let i=0;e.forEach(e=>{const t=parseInt(e.id.slice(1));t>i&&(i=t)});const s=gId(\"i\"+i);nNo=i+1;let r=\"i\"+nNo;nNo;s.insertAdjacentHTML(\"afterend\",`<img id=\"${r}\" src=\"${preview.src}\" alt=\"Library image ${nNo}\" title=\"Image ${nNo}\" width=\"64\" height=\"64\">`),t=gId(r),t.setAttribute(\"data-issel\",1),t.style.border=\"2px solid #eee\",t.addEventListener(\"click\",clkImg),toDel=gId(\"i0\"),toDel&&toDel.parentNode.removeChild(toDel),prw.style.display=\"\",lib.style.display=\"\"},n.readAsDataURL(e),setFoc(t)}function setFoc(e){document.querySelectorAll(\"#lib img\").forEach(t=>{e.srcElement!=t?(t.style.border=\"2px solid #444\",t.setAttribute(\"data-isSel\",0)):(t.style.border=\"2px solid #eee\",t.setAttribute(\"data-isSel\",1))})}function clkImg(e){setFoc(e),prw.src=e.srcElement.src}function isValidBase64Gif(e){return!0}dropZone.addEventListener(\"dragenter\",dragEnter),dropZone.addEventListener(\"dragover\",dragOver),dropZone.addEventListener(\"drop\",dropped),dropZone.addEventListener(\"click\",zoneClicked),filePicker.addEventListener(\"change\",filePicked);for(var hideableRows=d.querySelectorAll(\".ha-hide\"),i=0;i<hideableRows.length;i++)hideableRows[i].classList.add(\"hide\");function switchScale(){let e=scDiv.children[0].children[0],t=e.getAttribute(\"fill\"),n=\"\";t===accentColor?(t=accentTextColor,n=scaleToggleOffd,szDiv.style.display=\"none\"):(t=accentColor,n=scaleToggleOnd,szDiv.style.display=\"\"),e.setAttribute(\"fill\",t),e.setAttribute(\"d\",n),gen()}function switchRun(){let e=rnDiv.children[0].children[0],t=e.getAttribute(\"fill\"),n=\"\";t===accentColor?(t=accentTextColor,n=scaleToggleOffd,gId(\"rnTD\").style.display=\"none\",gId(\"rnLstTR\").style.display=\"none\"):(t=accentColor,n=scaleToggleOnd,gId(\"rnTD\").style.display=\"\",gId(\"rnLstTR\").style.display=\"\"),e.setAttribute(\"fill\",t),e.setAttribute(\"d\",n),gen()}function setStpImg(e){i=e.srcElement.ownerSVGElement,selim=\"\",nr=i?i.id.replace(\"loadImgSVG\",\"\"):e.srcElement.id.replace(\"loadImgSVG\",\"\");const t=gId(\"lib\").getElementsByTagName(\"img\");for(let e=0;e<t.length;e++){if(1==t[e].getAttribute(\"data-issel\")){selim=t[e];break}}td=gId(`stp${nr}ImgTD`),td.innerHTML=`<img id=\"stp${nr}Img\" src=\"${selim.src}\" alt=\"Step image ${nr}\" title=\"Image: ${selim.title}\" width=\"36\" height=\"36\" data-baseimg=${selim.id}>`}function addStpImg(e){i=e.srcElement.ownerSVGElement,selim=\"\",nr=i?i.id.replace(\"addRowSVG\",\"\"):e.srcElement.id.replace(\"addRowSVG\",\"\");let t=parseInt(nr)+1;thisTR=gId(\"stp\"+nr);const n=stpTR.replace(/XxX/g,e=>t);var s=Array.from(gId(\"rnLst\").getElementsByTagName(\"tr\")),r=[];s.forEach((function(e){thisNr=parseInt(e.id.replace(\"stp\",\"\")),thisNr>nr&&r.push(thisNr)})),r.sort((function(e,t){return t-e})),r.forEach((function(e){moveStep(e,e+1)}));var l=document.createElement(\"tr\");l.id=\"stp\"+t,l.setAttribute(\"data-json\",\"\"),l.style=\"display: flex; align-items: center;\",l.innerHTML=n,thisTR.parentNode.insertBefore(l,thisTR.nextSibling),gId(\"loadImgSVG\"+t).addEventListener(\"click\",setStpImg),gId(\"addRowSVG\"+t).addEventListener(\"click\",addStpImg),gId(\"delRowSVG\"+t).addEventListener(\"click\",delStpImg)}function delStpImg(e){i=e.srcElement.ownerSVGElement,selim=\"\",nr=i?i.id.replace(\"delRowSVG\",\"\"):e.srcElement.id.replace(\"delRowSVG\",\"\"),gId(\"stp\"+nr).remove();var t=Array.from(gId(\"rnLst\").getElementsByTagName(\"tr\")),n=[];t.forEach((function(e){thisNr=parseInt(e.id.replace(\"stp\",\"\")),thisNr>nr&&n.push(thisNr)})),n.sort(),n.forEach((function(e){moveStep(e,e-1)}))}function chkDelOK(){gId(`stp${newN}Del`).style.display=\"\"}function moveStep(e,t){gId(\"stp\"+e).id=\"stp\"+t,gId(`stp${e}Load`).id=`stp${t}Load`,gId(\"loadImgSVG\"+e).id=\"loadImgSVG\"+t,gId(`stp${e}ImgTD`).id=`stp${t}ImgTD`;gId(\"empImgSVG\"+e)?gId(\"empImgSVG\"+e).id=\"empImgSVG\"+t:gId(`stp${e}Img`).id=`stp${t}Img`,gId(`stp${e}Dur`).id=`stp${t}Dur`,gId(`stp${e}DurFld`).id=`stp${t}DurFld`,gId(`stp${e}Add`).id=`stp${t}Add`,gId(\"addRowSVG\"+e).id=\"addRowSVG\"+t,gId(`stp${e}Del`).id=`stp${t}Del`,gId(\"delRowSVG\"+e).id=\"delRowSVG\"+t}function generateSegmentOptions(e){tSg.innerHTML=\"\";for(var t=0;t<e.length;t++){var n=cE(\"option\");n.value=e[t].value,n.text=e[t].text,n.dataset.x=e[t].x,n.dataset.y=e[t].y,tSg.appendChild(n),0===t&&(n.selected=!0,szX.value=n.dataset.x,szY.value=n.dataset.y)}}async function getSegments(){if(cv=gurl.value,cv.length>0)try{var e=[];const n=await fetch(\"http://\"+cv+\"/json/state\");let i=(await n.json()).seg.map(e=>({id:e.id,n:e.n,xs:e.start,xe:e.stop,ys:e.startY,ye:e.stopY}));for(var t=0;t<i.length;t++)e.push({value:i[t].id,text:i[t].n+\" (index: \"+i[t].id+\")\",x:i[t].xe-i[t].xs,y:i[t].ye-i[t].ys});generateSegmentOptions(e),tSg.style.display=\"flex\",sID.style.display=\"none\",gId(\"sSg\").setAttribute(\"fill\",greenColor),setTimeout((function(){gId(\"sSg\").setAttribute(\"fill\",accentColor)}),1e3)}catch(e){console.error(e),gId(\"sSg\").setAttribute(\"fill\",redColor),setTimeout((function(){gId(\"sSg\").setAttribute(\"fill\",accentColor)}),1e3),tSg.style.display=\"none\",sID.style.display=\"flex\"}else gId(\"sSg\").setAttribute(\"fill\",redColor),setTimeout((function(){gId(\"sSg\").setAttribute(\"fill\",accentTextColor)}),1e3),tSg.style.display=\"none\",sID.style.display=\"flex\"}function generateSegmentArray(e){for(var t=[],n=0;n<e;n++)t.push({value:n,text:\"Segment index \"+n});return t}function anim(){gId(\"sendSvgP\").setAttribute(\"fill\",inactiveColor),gId(\"sndRnDiv\").removeEventListener(\"click\",sendAnim),gId(\"sndRnDiv\").title=\"Sending disabled while animating\",gId(\"playSVG\").innerHTML=stopSVGPath,gId(\"prwRnDiv\").removeEventListener(\"click\",anim),gId(\"prwRnDiv\").addEventListener(\"click\",stopAnim),aAr=[],curStp=0,Array.from(gId(\"rnLst\").getElementsByTagName(\"tr\")).forEach((function(e){const t=e.id.replace(\"stp\",\"\"),n=e.getElementsByClassName(\"stpImgTD\")[0].querySelector(\"img\").getAttribute(\"data-baseimg\"),i=10*e.getElementsByClassName(\"stpDur\")[0].querySelector(\"input\").value;aAr.push({stpId:t,imgId:n,dur:i})})),animateSteps(curStp=0)}function animateSteps(e){if(e>=aAr.length||e<0)stopAnim();else{var t=aAr[e];gId(t.imgId).click(),e+1>=aAr.length&&1==isRep?nStp=0:nStp=e+1,animTOId=setTimeout((function(){animateSteps(nStp)}),t.dur)}}function stopAnim(){clearTimeout(animTOId),gId(\"playSVG\").innerHTML=playSVGPath,gId(\"prwRnDiv\").removeEventListener(\"click\",stopAnim),gId(\"prwRnDiv\").addEventListener(\"click\",anim),gId(\"sendSvgP\").setAttribute(\"fill\",accentColor),gId(\"sndRnDiv\").removeEventListener(\"click\",sendAnim),gId(\"sndRnDiv\").addEventListener(\"click\",sendAnim),gId(\"sndRnDiv\").title=\"Send animation file to device\"}function setRep(e){pth=gId(\"repPath\"),console.log(e,pth.getAttribute(\"fill\"),accentColor,inactiveColor),pth.getAttribute(\"fill\")==accentColor?(pth.setAttribute(\"fill\",inactiveColor),isRep=0):(pth.setAttribute(\"fill\",accentColor),isRep=1)}async function sendAnim(e){gId(\"sendSvgP\").setAttribute(\"fill\",inactiveColor);let t=gId(\"rnID\").value,n={description:t,created:(new Date).toLocaleString().substring(0,16).replace(\",\",\"\")};n.repeat=1===isRep,sAr=[],Array.from(gId(\"rnLst\").getElementsByTagName(\"tr\")).forEach((function(e){const t=e.id.replace(\"stp\",\"\"),n=e.getElementsByClassName(\"stpImgTD\")[0].querySelector(\"img\").getAttribute(\"data-baseimg\"),i=e.getElementsByClassName(\"stpDur\")[0].querySelector(\"input\").value;sAr.push({stpId:t,imgId:n,dur:i})}));let i=[...new Set(sAr.map(e=>e.imgId))];sets=[];const s=await itcAr(i);n.sets=s.map(e=>e[2]),sAr.sort((e,t)=>e.stpId-t.stpId);const r=sAr.map((e,t)=>({step:t+1,commandId:e.imgId,duration:e.dur}));n.steps=r;uploadAnimation(s,r,t)}async function itcAr(e){const t=[];for(const n of e){await setImg(n);const e=httpArray.map((e,t)=>({position:t+1,command:JSON.parse(e)})),i=new Uint8Array(rawRGBArray);let s={id:n,commands:e};t.push([n,i,s])}return t}async function setImg(e){return new Promise(t=>{gId(e).click(),setTimeout(()=>{t(e)},200)})}function uploadAnimation(e,t,n){t.sort((e,t)=>e.step-t.step);var i=e.map((function(e){return[e[0],e[1]]}));let s=[],r=[],l=[],a=[];if(t.forEach((function(e,t){a.push([t,parseInt(e.duration)]);const n=i.find(t=>t[0]===e.commandId)[1];let o=[];if(0==t){for(let e=0;e<n.length;e+=3)o.push([t,e/3,n[e],n[e+1],n[e+2]]);l=o.slice(),s=o.slice()}else{let e=[];for(let i=0;i<n.length;i+=3){e.push([t,i/3,n[i],n[i+1],n[i+2]]);let r=s[i/3];r[2]==n[i]&&r[3]==n[i+1]&&r[4]==n[i+2]||o.push([t,i/3,n[i],n[i+1],n[i+2]])}s=e.slice()}r.push(...o)})),1==isRep){let e=[];l.forEach((function(t,n){let i=s[n];i[2]==t[2]&&i[3]==t[3]&&i[4]==t[4]||e.push([255,n,t[2],t[3],t[4]])})),r.push(...e)}let o=[];r.forEach((function(e,t){const n=e[1],i=n>>8&255,s=255&n;o.push(e[0],i,s,e[2],e[3],e[4])}));let d=[];a.forEach((function(e,t){const n=e[1],i=n>>8&255,s=255&n;d.push(e[0],i,s)}));const c=new XMLHttpRequest,g=new Blob([new Uint8Array(o)],{type:\"application/octet-stream\"});fileName=`/${n}.frm`,c.fileName=fileName,console.log(`Writing ${fileName} to device`),addListenerToHTTP(c,fileName),c.open(\"POST\",\"/upload\");var p=new FormData;p.append(\"data\",g,fileName),c.send(p);const u=new XMLHttpRequest,m=new Blob([new Uint8Array(d)],{type:\"application/octet-stream\"});fileName=`/${n}.ani`,u.fileName=fileName,console.log(`Writing ${fileName} to device`),addListenerToHTTP(u,fileName),u.open(\"POST\",\"/upload\");var f=new FormData;return f.append(\"data\",m,fileName),u.send(f),!1}function addListenerToHTTP(e,t){e.addEventListener(\"load\",(function(){console.log(`Writing ${t} succeeded. `,this.responseText,\" - \",this.status),gId(\"sendSvgP\").setAttribute(\"fill\",greenColor),setTimeout((function(){gId(\"sendSvgP\").setAttribute(\"fill\",accentColor)}),1e3)})),e.addEventListener(\"error\",(function(e){console.log(\"Error: \",e),console.log(\" Status: \",this.status),gId(\"sendSvgP\").setAttribute(\"fill\",greenColor),setTimeout((function(){gId(\"sendSvgP\").setAttribute(\"fill\",accentColor)}),2e3)}))}frm.addEventListener(\"change\",()=>{for(var e=0;e<hideableRows.length;e++)hideableRows[e].classList.toggle(\"hide\",\"ha\"!==frm.value)}),document.addEventListener(\"dblclick\",(function(){stopAnim()}));var segmentData=generateSegmentArray(10);generateSegmentOptions(segmentData),seDiv.innerHTML='<svg id=getSegmentsSVG style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\" onclick=\"getSegments()\"><path id=sSg fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.68 7.35 7.38 5.73 9.07 4.1 11 4.1 11.83 4.1 12.41 4.69 13 5.28 13 6.1V12.15L14.6 10.6L16 12L12 16L8 12L9.4 10.6L11 12.15V6.1Q9.1 6.45 8.05 7.94 7 9.43 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 9.8 16.45 8.76 15.9 7.73 15 7V4.68Q16.85 5.55 17.93 7.26 19 9 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20M12 11.05Z\" /></svg>',cjb.innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\" /> </svg>&nbsp; Copy to clipboard',gId(\"sendJSONledbutton\").innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path id=sendSvgP fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z\" /> </svg>&nbsp; Send to device',gId(\"repRnDiv\").innerHTML=repSVG,gId(\"prwRnDiv\").innerHTML=playSVG,gId(\"sndRnDiv\").innerHTML=sendSVG,gId(\"repPath\").setAttribute(\"fill\",accentColor);const regex=/XxX/g;gId(\"stp1\").innerHTML=stpTR.replace(regex,e=>1),gId(\"rnTD\").style.display=\"none\",gId(\"loadImgSVG1\").addEventListener(\"click\",setStpImg),gId(\"addRowSVG1\").addEventListener(\"click\",addStpImg),gId(\"delRowSVG1\").addEventListener(\"click\",delStpImg),gId(\"sndRnDiv\").addEventListener(\"click\",sendAnim),gId(\"prwRnDiv\").addEventListener(\"click\",anim),gurl.value.length>0&&gId(\"sSg\").setAttribute(\"fill\",accentColor)\n</script> </body> </body></html>"
  },
  {
    "path": "beta/releasenotes.md",
    "content": "Beta\n"
  },
  {
    "path": "examples/filename",
    "content": "\n"
  },
  {
    "path": "html/boxdraw.js",
    "content": "function drawBoxes(inputPixelArray, widthPixels, heightPixels) {\n \n    // Get a reference to the canvas element\n    var canvas = document.getElementById('pixelCanvas');\n\n    // Get the canvas context\n    var ctx = canvas.getContext('2d');\n\n    // Set the width and height of the canvas\n    if (window.innerHeight < window.innerWidth) {\n        canvas.width = Math.floor(window.innerHeight * 0.98);\n    }\n    else{\n        canvas.width = Math.floor(window.innerWidth * 0.98);\n    }\n    //canvas.height = window.innerWidth;\n\n    let pixelSize = Math.floor(canvas.width/widthPixels);\n\n    //Set the canvas height to fit the right number of pixelrows\n    canvas.height = (pixelSize * heightPixels) + 10\n    \n    //Iterate through the matrix\n    for (let y = 0; y < heightPixels; y++) {\n        for (let x = 0; x < widthPixels; x++) {\n\n            // Calculate the index of the current pixel\n            let i = (y*widthPixels) + x;\n            \n            //Gets the RGB of the current pixel\n            let pixel = inputPixelArray[i];\n\n            let pixelColor = 'rgb(' + pixel[0] + ', ' + pixel[1] + ', ' + pixel[2] + ')';\n            let r = pixel[0];\n            let g = pixel[1];\n            let b = pixel[2];\n            let pos = pixel[4];\n\n            let textColor = 'rgb(128,128,128)';\n\n            // Set the fill style to the pixel color\n            ctx.fillStyle = pixelColor;\n\n            //Draw the rectangle\n            ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);\n\n            // Draw a border on the box\n            ctx.strokeStyle = '#888888';\n            ctx.lineWidth = 1;\n            ctx.strokeRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);\n\n            //Write text to box\n            ctx.font = \"10px Arial\";\n            ctx.fillStyle = textColor;\n            ctx.textAlign = \"center\";\n            ctx.textBaseline = 'middle';\n            ctx.fillText((pos + 1), (x * pixelSize) + (pixelSize /2), (y * pixelSize) + (pixelSize /2));\n        }\n    }\n}\n\nfunction drawBackground() {\n  const grid = document.createElement(\"div\");\n  grid.id = \"grid\";\n  grid.classList.add(\"grid-class\");\n  grid.style.cssText = \"\";\n\n  const boxSize = 20;\n  const boxCount = Math.ceil(window.innerWidth / boxSize) * Math.ceil(window.innerHeight / boxSize);;\n\n  for (let i = 0; i < boxCount; i++) {\n  const box = document.createElement(\"div\");\n  box.classList.add(\"box\");\n  box.style.backgroundColor = getRandomColor();\n  grid.appendChild(box);\n  }\n  grid.style.zIndex = -1;\n  document.body.appendChild(grid);\n}\n\nfunction getRandomColor() {\n  const letters = \"0123456789ABCDEF\";\n  let color = \"rgba(\";\n  for (let i = 0; i < 3; i++) {\n  color += Math.floor(Math.random() * 256) + \",\";\n  }\n  color += \"0.05)\";\n  return color;\n}\n\n  window.drawBackground = drawBackground;\n"
  },
  {
    "path": "html/getPixelValues.js",
    "content": "function getPixelRGBValues(base64Image) {\n  httpArray = [];\n  fileJSON = JSONledStringStart + document.getElementById('brightnessNumber').value + JSONledStringMid1 + document.getElementById('targetSegment').value + JSONledStringMid2;\n\n  //const copyJSONledbutton = document.getElementById('copyJSONledbutton');\n  const JSONled = document.getElementById('JSONled');\n  const maxNoOfColorsInCommandSting = document.getElementById('colorLimitNumber').value;\n  \n  let hybridAddressing = false;\n  let selectedIndex = -1;\n\n  let selector = document.getElementById(\"formatSelector\");\n  selectedIndex = selector.selectedIndex;\n  const formatSelection = selector.options[selectedIndex].value;\n\n  selector = document.getElementById(\"ledSetupSelector\");\n  selectedIndex = selector.selectedIndex;\n  const ledSetupSelection = selector.options[selectedIndex].value;\n\n  selector = document.getElementById(\"colorFormatSelector\");\n  selectedIndex = selector.selectedIndex;\n  let hexValueCheck = true;\n  if (selector.options[selectedIndex].value == 'dec'){\n    hexValueCheck = false\n  }\n\n  selector = document.getElementById(\"addressingSelector\");\n  selectedIndex = selector.selectedIndex;\n  let segmentValueCheck = true; //If Range or Hybrid\n  if (selector.options[selectedIndex].value == 'single'){\n    segmentValueCheck = false\n  } else if (selector.options[selectedIndex].value == 'hybrid'){\n    hybridAddressing = true;\n  }\n\n  let segmentString = ''\n  let curlString = ''\n  let haString = ''\n  let haCommandCurlString = '';\n  \n\n  let colorSeparatorStart = '\\'';\n  let colorSeparatorEnd = '\\'';\n  if (!hexValueCheck){\n    colorSeparatorStart = '[';\n    colorSeparatorEnd = ']';\n  }\n  // Warnings\n  let hasTransparency = false; //If alpha < 255 is detected on any pixel, this is set to true in code below\n  let imageInfo = '';\n  \n  // Create an off-screen canvas\n  var canvas = document.createElement('canvas');\n  var context = canvas.getContext('2d');\n\n  // Create an image element and set its src to the base64 image\n  var image = new Image();\n  image.src = base64Image;\n\n  // Wait for the image to load before drawing it onto the canvas\n  image.onload = function() {\n    \n    let scalePath = document.getElementById(\"scalePath\");\n    let color = scalePath.getAttribute(\"fill\");\n    let sizeX = document.getElementById(\"sizeX\").value;\n    let sizeY = document.getElementById(\"sizeY\").value;\n\n    if (color != accentColor || sizeX < 1 || sizeY < 1){\n      //image will not be rezised Set desitred size to original size\n      sizeX = image.width;\n      sizeY = image.height;\n    }\n\n    // Set the canvas size to the same as the desired image size\n    canvas.width = sizeX;\n    canvas.height = sizeY;\n\n    imageInfo = '<p>Width: ' + sizeX + ', Height: ' + sizeY + ' (make sure this matches your led matrix setup)</p>'\n\n    // Draw the image onto the canvas\n    context.drawImage(image, 0, 0, sizeX, sizeY);\n\n    // Get the pixel data from the canvas\n    var pixelData = context.getImageData(0, 0, sizeX, sizeY).data;\n  \n    // Create an array to hold the RGB values of each pixel\n    var pixelRGBValues = [];\n\n    // If the first row of the led matrix is right -> left\n    let right2leftAdjust = 1;\n          \n    if (ledSetupSelection == 'l2r'){\n      right2leftAdjust = 0;\n    }\n\n    // Loop through the pixel data and get the RGB values of each pixel\n    for (var i = 0; i < pixelData.length; i += 4) {\n      var r = pixelData[i];\n      var g = pixelData[i + 1];\n      var b = pixelData[i + 2];\n      var a = pixelData[i + 3];\n\n      let pixel = i/4\n      let row = Math.floor(pixel/sizeX);\n      let led = pixel;\n      if (ledSetupSelection == 'matrix'){\n          //Do nothing, the matrix is set upp like the index in the image\n          //Every row starts from the left, i.e. no zigzagging\n      }\n      else if ((row + right2leftAdjust) % 2 === 0) {\n          //Setup is traditional zigzag\n          //right2leftAdjust basically flips the row order if = 1\n          //Row is left to right\n          //Leave led index as pixel index\n        \n      } else {\n          //Setup is traditional zigzag\n          //Row is right to left\n          //Invert index of row for led\n          let indexOnRow = led - (row * sizeX);\n          let maxIndexOnRow = sizeX - 1;\n          let reversedIndexOnRow = maxIndexOnRow - indexOnRow;\n          led = (row * sizeX) + reversedIndexOnRow;\n      }\n\n      // Add the RGB values to the pixel RGB values array\n      pixelRGBValues.push([r, g, b, a, led, pixel, row]);\n    }\n    \n    pixelRGBValues.sort((a, b) => a[5] - b[5]);\n\n    //Copy the values to a new array for resorting\n    let ledRGBValues = [... pixelRGBValues];\n    \n    //Sort the array based on led index\n    ledRGBValues.sort((a, b) => a[4] - b[4]);\n    \n    //Generate JSON in WLED format\n    let JSONledString = '';\n    let JSONledStringShort = '';\n\n    //Set starting values for the segment check to something that is no color\n    let segmentStart = -1;\n    let maxi = ledRGBValues.length;\n    let curentColorIndex = 0\n    let commandArray = [];\n\n    //For evry pixel in the LED array\n    for (let i = 0; i < maxi; i++) {\n      let pixel = ledRGBValues[i];\n      let r = pixel[0];\n      let g = pixel[1];\n      let b = pixel[2];\n      let a = pixel[3];\n      let segmentString = '';\n      let segmentEnd = -1;\n\n      if(segmentValueCheck){\n        if (segmentStart < 0){\n          //This is the first led of a new segment\n          segmentStart = i;\n        } //Else we allready have a start index\n        \n        if (i < maxi - 1){ \n          \n          let iNext = i + 1;\n          let nextPixel = ledRGBValues[iNext];\n\n          if (nextPixel[0] != r || nextPixel[1] != g || nextPixel[2] != b ){\n            //Next pixel has new color\n            //The current segment ends with this pixel\n            segmentEnd = i + 1 //WLED wants the NEXT LED as the stop led...\n            if (segmentStart == i && hybridAddressing){\n              //If only one led/pixel, no segment info needed\n              if (JSONledString == ''){\n                //If addressing is single, we need to start every command with a starting possition\n                segmentString = '' + i + ',';\n                //Fixed to b2\n              } else{\n                segmentString = ''\n              }\n            }\n            else {\n              segmentString = segmentStart + ',' + segmentEnd + ',';\n            }\n          }\n\n        } else {\n          //This is the last pixel, so the segment must end\n          segmentEnd = i + 1;\n\n          if (segmentStart + 1 == segmentEnd && hybridAddressing){\n            //If only one led/pixel, no segment info needed\n            if (JSONledString == ''){\n              //If addressing is single, we need to start every command with a starting possition\n              segmentString = '' + i + ',';\n              //Fixed to b2\n            } else{\n              segmentString = ''\n            }\n          }\n          else {\n            segmentString = segmentStart + ',' + segmentEnd + ','; \n          }\n        }\n      } else{\n        //Write every pixel\n        if (JSONledString == ''){\n          //If addressing is single, we need to start every command with a starting possition\n          JSONledString = i\n          //Fixed to b2\n        }\n\n        segmentStart = i\n        segmentEnd = i   \n        //Segment string should be empty for when addressing single. So no need to set it again.       \n      }\n\n      if (a < 255){\n        hasTransparency = true; //If ANY pixel has alpha < 255 then this is set to true to warn the user\n      }\n\n      if (segmentEnd > -1){\n        //This is the last pixel in the segment, write to the JSONledString\n        //Return color value in selected format\n        let colorValueString = r + ',' + g + ',' + b ;\n\n        if (hexValueCheck){\n          const [red, green, blue] = [r, g, b];\n          colorValueString = `${[red, green, blue].map(x => x.toString(16).padStart(2, '0')).join('')}`;\n        } else{\n          //do nothing, allready set\n        }\n\n        // Check if start and end is the same, in which case remove\n\n        JSONledString = JSONledString + segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd;\n        fileJSON = JSONledString + segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd;\n\n        curentColorIndex = curentColorIndex + 1; // We've just added a new color to the string so up the count with one\n\n        if (curentColorIndex % maxNoOfColorsInCommandSting === 0 || i == maxi - 1) { \n\n          //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\n          commandArray.push(JSONledString);\n          JSONledString = ''; //Start on an new command string\n        } else\n        {\n          //Add a comma to continue the command string\n          JSONledString = JSONledString + ','\n        }\n        //Reset segment values\n        segmentStart = - 1;\n      }\n    }\n    \n    JSONledString = ''\n\n    //For evry commandString in the  array\n    for (let i = 0; i < commandArray.length; i++) {\n      let thisJSONledString = JSONledStringStart + document.getElementById('brightnessNumber').value + JSONledStringMid1 + document.getElementById('targetSegment').value + JSONledStringMid2 + commandArray[i] + JSONledStringEnd;\n      httpArray.push(thisJSONledString);\n\n      let thiscurlString = curlStart + document.getElementById('curlUrl').value + curlMid1 + thisJSONledString + curlEnd;\n\n      //Aggregated Strings That should be returned to the user\n      if (i > 0){\n        JSONledString = JSONledString + '\\n';\n        curlString = curlString + ' && ';\n      }\n      JSONledString = JSONledString + thisJSONledString;\n      curlString = curlString + thiscurlString;\n    }\n\n    \n    haString = haStart + document.getElementById('haID').value + haMid1 + document.getElementById('haName').value + haMid2 + document.getElementById('haUID').value + haMid3 +curlString + haMid3 + document.getElementById('curlUrl').value + haEnd;\n\n    if (formatSelection == 'wled'){\n      JSONled.value = JSONledString;\n    } else if (formatSelection == 'curl'){\n      JSONled.value = curlString;\n    } else if (formatSelection == 'ha'){\n      JSONled.value = haString;\n    } else {\n      JSONled.value = 'ERROR!/n' + formatSelection + ' is an unknown format.'\n    }\n    \n    fileJSON = fileJSON + JSONledStringEnd;\n\n    let infoDiv = document.getElementById('image-info');\n    let canvasDiv = document.getElementById('image-info');\n    if (hasTransparency){\n      imageInfo = imageInfo + '<p><b>WARNING!</b> 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.</p>'\n    }\n    \n    infoDiv.innerHTML = imageInfo;\n    canvasDiv.style.display = \"block\"\n\n\n    //Drawing the image\n    drawBoxes(pixelRGBValues, sizeX, sizeY);\n  }\n}"
  },
  {
    "path": "html/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\">\n    <meta http-equiv=\"Pragma\" content=\"no-cache\">\n    <meta http-equiv=\"Expires\" content=\"0\">\n    <title>Led Matrix Pixel Art Converter</title>\n    <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\">\n    <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\">\n    <link rel=\"manifest\" href=\"/site.webmanifest\">\n  </head>\n\n  <body>\n    <div class=\"top-part\" >\n      <div style=\"display: flex; justify-content: center;\">\n        <h1 style=\"display: flex; align-items: center;\">\n          <svg style=\"width:36px;height:36px\" id=\"logomatrix\" viewBox=\"0 0 24 24\">\n            <path fill=\"#7e4c80\" d=\"M6,5\n                    a2,2 0 1,0 4,0\n                    a2,2 0 1,0 -4,0\" id=\"sc1\"/>\n            <path fill=\"#7e4c80\" d=\"M12,5\n                    a2,2 0 1,0 4,0\n                    a2,2 0 1,0 -4,0\" id=\"sc2\"/>\n            <path fill=\"#7e4c80\" d=\"M18,5\n                    a2,2 0 1,0 4,0\n                    a2,2 0 1,0 -4,0\" id=\"sc3\"/>\n            <path fill=\"#7e4c80\" d=\"M6,11\n                    a2,2 0 1,0 4,0\n                    a2,2 0 1,0 -4,0\" id=\"sc4\"/>\n            <path fill=\"#7e4c80\" d=\"M12,11\n                    a2,2 0 1,0 4,0\n                    a2,2 0 1,0 -4,0\" id=\"sc5\"/>\n            <path fill=\"#7e4c80\" d=\"M18,11\n                    a2,2 0 1,0 4,0\n                    a2,2 0 1,0 -4,0\" id=\"sc6\"/>\n            <path fill=\"#7e4c80\" d=\"M6,17\n                    a2,2 0 1,0 4,0\n                    a2,2 0 1,0 -4,0\" id=\"sc7\"/>\n            <path fill=\"#7e4c80\" d=\"M12,17\n                    a2,2 0 1,0 4,0\n                    a2,2 0 1,0 -4,0\" id=\"sc8\"/>\n            <path fill=\"#7e4c80\" d=\"M18,17\n                    a2,2 0 1,0 4,0\n                    a2,2 0 1,0 -4,0\" id=\"sc9\"/>\n          </svg>\n\n            Led Matrix Pixel Art Converter\n        </h1>\n      </div>\n      <h2>Convert image to WLED JSON (pixel art on WLED matrix)</h2>\n      <p>\n        <table id=\"fieldTable\"  style=\"width: 100%; table-layout: fixed; align-content: center;\">\n          <tr>\n            <td style=\"vertical-align: middle;\">\n              <label for=\"ledSetupSelector\">Led setup:</label>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <select id=\"ledSetupSelector\">\n                <option value=\"matrix\" selected>2D Matrix</option>\n                <option value=\"r2l\">Serpentine, first row right to left &lt;-</option>\n                <option value=\"l2r\">Serpentine, first row left to right -&gt;</option>\n              </select>\n            </td>\n          </tr>        \n          <tr>\n            <td style=\"vertical-align: middle;\">\n              <label for=\"formatSelector\">Output format:</label>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <select id=\"formatSelector\">\n                <option value=\"wled\" selected>WLED JSON</option>\n                <option value=\"curl\">CURL</option>\n                <option value=\"ha\">Home Assistant YAML</option>\n              </select>\n            </td>\n          </tr>        \n          <tr>\n            <td style=\"vertical-align: middle;\">\n              <label for=\"colorFormatSelector\">Color code format:</label>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <select id=\"colorFormatSelector\">\n                <option value=\"hex\" selected>HEX (#f4f4f4)</option>\n                <option value=\"dec\">DEC (244,244,244)</option>\n              </select>\n          </td>\n          </tr>\n          <tr>\n            <td style=\"vertical-align: middle;\">\n              <label for=\"addressingSelector\">Addressing:</label>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <select id=\"addressingSelector\">\n                <option value=\"hybrid\" selected>Hybrid (#f0f0f0,10, 17, #f4f4f4)</option>\n                <option value=\"range\">Range (10, 17, #f4f4f4)</option>\n                <option value=\"single\">Single (#f4f4f4)</option>\n              </select>\n            </td>\n          </tr>\n          <tr>\n            <td style=\"vertical-align: middle;\">\n              <label for=\"brightnessNumber\">Brightness:</label>\n            </td>\n            <td style=\"vertical-align: middle; display: flex; align-items: center;\">\n              <input type=\"range\" id=\"brightnessNumber\" min=\"1\" max=\"255\" value=\"255\">\n              <span id=\"brightnessValue\">255</span>\n            </td>\n          </tr>\n          <tr>\n            <td style=\"vertical-align: middle;\">\n              <label for=\"colorLimitNumber\">Max no of colors/JSON:</label>\n            </td>\n            <td style=\"vertical-align: middle; display: flex; align-items: center;\">\n              <input type=\"range\" id=\"colorLimitNumber\" min=\"1\" max=\"512\" value=\"256\">\n              <span id=\"colorLimitValue\" >256</span>\n            </td>\n          </tr>\n          <tr class=\"ha-hide\">\n            <td style=\"vertical-align: middle;\">\n              <label for=\"haID\">HA Device ID:</label>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <input class=\"fullTextField\" type=\"text\" id=\"haID\" value=\"pixel_art_controller_001\">\n            </td>\n          </tr>\n          <tr class=\"ha-hide\">\n            <td style=\"vertical-align: middle;\">\n              <label for=\"haUID\">HA Device Unique ID:</label>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <input class=\"fullTextField\" type=\"text\" id=\"haUID\" value=\"pixel_art_controller_001a\">\n            </td>\n          </tr>\n          <tr class=\"ha-hide\">\n            <td style=\"vertical-align: middle;\">\n              <label for=\"haName\">HA Device Name:</label>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <input class=\"fullTextField\" type=\"text\" id=\"haName\" value=\"Pixel Art Kitchen\">\n            </td>\n          </tr>\n          <tr>\n            <td style=\"vertical-align: middle;\">\n              <label for=\"curlUrl\">Device IP/host name:</label>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <input class=\"fullTextField\" type=\"text\" id=\"curlUrl\" value=\"\">\n            </td>\n          </tr>\n          <tr>\n            <td style=\"vertical-align: middle;\">\n              <label for=\"targetSegment\">Target segment id:</label>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <select id=\"targetSegment\">\n              </select>\n            </td>\n          </tr>\n        </table>\n        <table  class= \"scaleTableClass\" id=\"scaleTable\"  style=\"width: 100%; table-layout: fixed; align-content: center;\">\n          <tr>\n            <td style=\"vertical-align: middle;\">\n              <div id=\"scaleDiv\">\n                <svg id=\"scaleToggle\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\" onclick=\"switchScale()\">\n                  <path id=\"scaleTogglePath\" fill=\"currentColor\" d=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z\" />\n                </svg>\n                &nbsp;&nbsp;&nbsp;\n                <svg id=\"scaleSvg\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\" onclick=\"switchScale()\">\n                  <path id=\"scalePath\" fill=\"currentColor\" d=\"M21,15H23V17H21V15M21,11H23V13H21V11M23,19H21V21C22,21 23,20 23,19M13,3H15V5H13V3M21,7H23V9H21V7M21,3V5H23C23,4 22,3 21,3M1,7H3V9H1V7M17,3H19V5H17V3M17,19H19V21H17V19M3,3C2,3 1,4 1,5H3V3M9,3H11V5H9V3M5,3H7V5H5V3M1,11V19A2,2 0 0,0 3,21H15V11H1M3,19L5.5,15.79L7.29,17.94L9.79,14.72L13,19H3Z\" />\n                </svg>\n              </div>\n            </td>\n            <td style=\"vertical-align: middle;\">\n              <div id=\"sizeDiv\" style=\"display: none;\">\n                <label for=\"sizeX\">X : </label> &nbsp;<input class=\"sizeInputFields\" type=\"number\" id=\"sizeX\" value=\"0\">\n                &nbsp;&nbsp;&nbsp;\n                <label for=\"sizeY\">Y : </label> &nbsp;<input class=\"sizeInputFields\" type=\"number\" id=\"sizeY\" value=\"0\">\n              </div>\n            </td>\n          </tr>\n        </table>\n      </p>\n\n      <p>\n        <label for=\"file-picker\">\n          <div id=\"drop-zone\">    \n              Drop image here <br>or <br>\n              Click to select a file\n          </div>\n        </label>\n      </p>\n      \n      <p>\n        <input type=\"file\" id=\"file-picker\" style=\"display: none;\">\n        <div style=\"width: 100%; text-align: center;\">\n          <img id=\"preview\" style=\"display: block; margin: 0 auto;\">\n          <img id=\"newimage\" style=\"display: block; margin: 0 auto;\"><br>\n        </div>\n        \n        <div id=\"submitConvertDiv\" style=\"display: none;\">\n          <button id=\"convertbutton\" class=\"buttonclass\"></button>\n        </div>\n        \n        <div id=\"raw-image-container\" style=\"display: none\">\n          <img id=\"image\" src=\"\" alt=\"RawImage image\">\n        </div>\n        \n      </p>\n      \n      <div id=\"image-container\" style=\"display: none;\">\n        <div id=\"image-info\" style=\"display: none\"></div>\n        <textarea id=\"JSONled\"></textarea>\n      </div>\n\n      <div id=\"button-container\" style=\"display: none;\">\n        <button id=\"copyJSONledbutton\" class=\"buttonclass\"></button>\n        <div id=\"gap1\" class=\"gap\"></div>\n        <button id=\"sendJSONledbutton\" class=\"buttonclass\"></button>\n        <div id = \"gap2\" class=\"gap\"></div>\n        <button id=\"fileJSONledbutton\" class=\"buttonclass\"></button>\n      </div>\n      <div>\n        <h3><div id=\"version\">Version 1.0.4</div>&nbsp;-&nbsp; <a href=\"https://github.com/werkstrom/WLED-PixelArtConverter/blob/main/README.md\" target=\"_blank\">Help/About</a></h3>\n      </div>\n    </div>\n    <div id=bottom-part style=\"display: none\" class=bottom-part></div>\n          <canvas id=\"pixelCanvas\"></canvas>\n    </div>\n        \n\n    <script>\n      function loadFiles(fileNames, index) {\n          if (index === fileNames.length) {\n              return;\n          }\n\n          var fileName = fileNames[index];\n          var fileExt = fileName.split('.').pop();\n          var element;\n          if (fileExt === 'js') {\n              element = document.createElement('script');\n              element.type = 'text/javascript';\n              element.src = fileName + '?time=' + new Date().getTime();\n          } else if (fileExt === 'css') {\n              element = document.createElement('link');\n              element.rel = 'stylesheet';\n              element.href = fileName + '?time=' + new Date().getTime();\n          }\n\n          element.onload = function() {\n              loadFiles(fileNames, index + 1);\n          }\n\n          document.getElementsByTagName('head')[0].appendChild(element);\n      }\n\n      var files = [\"statics.js\", \"getPixelValues.js\", \"boxdraw.js\", \"index.js\", \"styles.css\"];\n      loadFiles(files, 0);\n\n    </script>\n  </body>\n</html>"
  },
  {
    "path": "html/index.js",
    "content": "//Start up code\ndocument.getElementById('curlUrl').value = location.host;\n\nlet devMode = false;\nconst urlParams = new URLSearchParams(window.location.search);\nif(urlParams.has('dev')){\n  devMode = true;\n}\nif(devMode){\n  console.log('Developer mode active. Experimental and unstable functions active.')\n} else{\nconsole.log('Developer mode inactive. Append \"?dev\" to the URL.')\n}\n\nif(devMode){\n  document.getElementById(\"fileJSONledbutton\").style.display = 'buttonclass'\n  document.getElementById(\"gap2\").style.display = 'gap'\n} else {\n  document.getElementById(\"fileJSONledbutton\").style.display = 'none'\n  document.getElementById(\"gap2\").style.display = 'none'\n}\n\n\nlet httpArray = [];\nlet fileJSON = '';\n\n\n//On submit button pressed =======================\n\n\ndocument.getElementById(\"convertbutton\").addEventListener(\"click\", function() {\n  \n  let base64Image = document.getElementById('preview').src;\n  if (isValidBase64Gif(base64Image)) {\n    document.getElementById('image').src = base64Image;\n    getPixelRGBValues(base64Image);\n    document.getElementById('image-container').style.display = \"block\"\n    document.getElementById(\"button-container\").style.display = \"\";\n\n  } \n  else {\n    let infoDiv = document.getElementById('image-info');\n    let imageInfo = '<p><b>WARNING!</b> File does not appear to be a valid image</p>'\n    infoDiv.innerHTML = imageInfo;\n    infoDiv.style.display = \"block\"\n    document.getElementById('image-container').style.display = \"none\";\n    document.getElementById('JSONled').value = '';\n    console.log(\"The string '\" + base64Image + \"' is not a valid base64 image.\");\n  }\n\n});\n\n// Code for copying the generated string to clipboard\n\ncopyJSONledbutton.addEventListener('click', async () => {\n  let JSONled = document.getElementById('JSONled');\n  JSONled.select();\n  try {\n    await navigator.clipboard.writeText(JSONled.value);\n  } catch (err) {\n    try {\n      await document.execCommand(\"copy\");\n    } catch (err) {\n      console.error('Failed to copy text: ', err);\n    }\n  }\n});\n\nsendJSONledbutton.addEventListener('click', async () => {\n  if (window.location.protocol === \"https:\") {\n    alert('Will only be available when served over http (or WLED is run over https)');\n  } else {\n    postPixels();\n  }\n});\n\nfileJSONledbutton.addEventListener('click', async () => {\n  if (window.location.protocol === \"https:\") {\n    alert('Will only be available when served over http (or WLED is run over https)');\n  } else {\n    let JSONFileName = 'TheName.json'\n    let urlString = 'http://'+document.getElementById('curlUrl').value+'/upload'\n\n    sendAsFile(fileJSON, JSONFileName, urlString);\n  }\n});\n\nasync function postPixels() {\n  for (let i of httpArray) {\n    try {\n      console.log(i);\n      console.log(i.length);\n      const response = await fetch('http://'+document.getElementById('curlUrl').value+'/json/state', {\n        method: 'POST',\n        headers: {\n          'Content-Type': 'application/json'\n          //'Content-Type': 'text/html; charset=UTF-8'\n        },\n        body: i\n      });\n      const data = await response.json();\n      console.log(data);\n    } catch (error) {\n      console.error(error);\n    }\n  }\n}\n//File uploader code\nconst dropZone = document.getElementById('drop-zone');\nconst filePicker = document.getElementById('file-picker');\nconst preview = document.getElementById('preview');\n\n// Listen for dragenter, dragover, and drop events\ndropZone.addEventListener('dragenter', dragEnter);\ndropZone.addEventListener('dragover', dragOver);\ndropZone.addEventListener('drop', dropped);\ndropZone.addEventListener('click', zoneClicked);\n\n// Listen for change event on file picker\nfilePicker.addEventListener('change', filePicked);\n\n// Handle zone click\nfunction zoneClicked(e) {\n  e.preventDefault();\n  //this.classList.add('drag-over');\n  //alert('Hej');\n  filePicker.click();\n}\n\n// Handle dragenter\nfunction dragEnter(e) {\n  e.preventDefault();\n  this.classList.add('drag-over');\n}\n\n// Handle dragover\nfunction dragOver(e) {\n  e.preventDefault();\n}\n\n// Handle drop\nfunction dropped(e) {\n  e.preventDefault();\n  this.classList.remove('drag-over');\n\n  // Get the dropped file\n  const file = e.dataTransfer.files[0];\n  updatePreview(file);\n}\n\n// Handle file picked\nfunction filePicked(e) {\n  // Get the picked file\n  const file = e.target.files[0];\n  updatePreview(file);\n}\n\n// Update the preview image\nfunction updatePreview(file) {\n  // Use FileReader to read the file\n  const reader = new FileReader();\n  reader.onload = function() {\n    // Update the preview image\n    preview.src = reader.result;\n    document.getElementById(\"submitConvertDiv\").style.display = \"\";\n  };\n  reader.readAsDataURL(file);\n}\n\nfunction isValidBase64Gif(string) {\n  // Use a regular expression to check that the string is a valid base64 string\n  /*\n  const base64gifPattern = /^data:image\\/gif;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;\n  const base64pngPattern = /^data:image\\/png;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;\n  const base64jpgPattern = /^data:image\\/jpg;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;\n  const base64webpPattern = /^data:image\\/webp;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;\n  */\n  //REMOVED, Any image appear to work as long as it can be drawn to the canvas. Leaving code in for future use, possibly\n  if (1==1 || base64gifPattern.test(string) || base64pngPattern.test(string) || base64jpgPattern.test(string) || base64webpPattern.test(string)) {\n    return true;\n  }\n  else {\n    //Not OK\n    return false;\n  }\n}\n\ndocument.getElementById(\"brightnessNumber\").oninput = function() {\n  document.getElementById(\"brightnessValue\").textContent = this.value;\n}\n\ndocument.getElementById(\"colorLimitNumber\").oninput = function() {\n  document.getElementById(\"colorLimitValue\").textContent = this.value;\n}\n\nvar formatSelector = document.getElementById(\"formatSelector\");\nvar hideableRows = document.querySelectorAll(\".ha-hide\");\nfor (var i = 0; i < hideableRows.length; i++) {\n    hideableRows[i].classList.add(\"hide\");\n}\nformatSelector.addEventListener(\"change\", function() {\n     for (var i = 0; i < hideableRows.length; i++) {\n         hideableRows[i].classList.toggle(\"hide\", this.value !== \"ha\");\n     }\n });\n\nfunction switchScale() {\n  let scalePath = document.getElementById(\"scalePath\");\n  let scaleTogglePath = document.getElementById(\"scaleTogglePath\");\n  let color = scalePath.getAttribute(\"fill\");\n  let d = ''\n  if (color === accentColor) {\n    color = accentTextColor;\n    d = scaleToggleOffd\n    document.getElementById(\"sizeDiv\").style.display = \"none\";\n    // Set values to actual XY of image, if possible\n  } else {\n    color = accentColor;\n    d = scaleToggleOnd\n    document.getElementById(\"sizeDiv\").style.display = \"\";\n  }\n  scalePath.setAttribute(\"fill\", color);\n  scaleTogglePath.setAttribute(\"fill\", color);\n  scaleTogglePath.setAttribute(\"d\", d);\n}\n\n\nfunction sendAsFile(jsonStringInput, fileName, urlString) {\n  //var jsonString = JSON.stringify({name: \"value\"});\n  var file = new Blob([jsonStringInput], {type: 'application/json'});\n  console.log(jsonStringInput)\n  console.log(fileName);\n  console.log(urlString);\n  \n  var formData = new FormData();\n  formData.append('file', file, fileName);\n\n  var xhr = new XMLHttpRequest();\n  xhr.open('POST', urlString, true);\n  xhr.onload = function() {\n    if (xhr.status === 200) {\n      console.log('File uploaded successfully!');\n    } else {\n      console.log('File upload failed!');\n    }\n  };\n  xhr.send(formData);\n}\n\nfunction generateSegmentOptions(array) {\n  //This function is prepared for a name property on each segment for easier selection\n  //Currently the name is generated generically based on index\n  var select = document.getElementById(\"targetSegment\");\n  select.innerHTML = \"\";\n  for (var i = 0; i < array.length; i++) {\n      var option = document.createElement(\"option\");\n      option.value = array[i].value;\n      option.text = array[i].text;\n      select.appendChild(option);\n      if(i === 0) {\n        option.selected = true;\n      }\n    }\n}\n\n//Initial population of segment selection\nfunction generateSegmentArray(noOfSegments) {\n  var arr = [];\n  for (var i = 0; i < noOfSegments; i++) {\n      arr.push({\n          value: i,\n          text: \"Segment index \" + i\n      });\n  }\n  return arr;\n}\n\n\n//Animate matrix\nvar matrixcircles = document.querySelectorAll(\"#logomatrix path\");\nvar intervalId;\n// Function that changes the color of a random circle to a lighter purple\nfunction changeColorOfDot() {\n  // Get a random number between 0 and the number of circles\n  var randomIndex = Math.floor(Math.random() * matrixcircles.length);\n  // Get the circle at the random index\n  var randomCircle = matrixcircles[randomIndex];\n\n  \n  // Store the current fill color\n  var currentColor = randomCircle.getAttribute(\"fill\");\n  // Change the color of the circle\n  randomCircle.setAttribute(\"fill\", \"#bb8fbc\");\n  setTimeout(() => {\n    randomCircle.setAttribute(\"fill\", currentColor);\n    clearInterval(intervalId);\n    intervalId = setInterval(changeColorOfDot, randomInterval());\n  }, 500);\n}\n\nfunction randomInterval() {\n  var interval = Math.floor(Math.random() * (5 - 1 + 1)) + 1;\n  return interval * 1000;\n}\n\n// call the function changeColorOfDot every 10 seconds\nintervalId = setInterval(changeColorOfDot, randomInterval());\n\nvar segmentData = generateSegmentArray(10);\n\ngenerateSegmentOptions(segmentData);\n\ndocument.getElementById(\"fileJSONledbutton\").innerHTML = \n'<svg style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M20 18H4V8H20M20 6H12L10 4H4A2 2 0 0 0 2 6V18A2 2 0 0 0 4 20H20A2 2 0 0 0 22 18V8A2 2 0 0 0 20 6M16 17H14V13H11L15 9L19 13H16Z\" /></svg>&nbsp; File to device'\n document.getElementById(\"convertbutton\").innerHTML = \n '<svg style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z\" /> </svg>&nbsp; Convert to WLED JSON '; \n document.getElementById(\"copyJSONledbutton\").innerHTML = \n '<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\" /> </svg>&nbsp; Copy to clipboard'; \n document.getElementById(\"sendJSONledbutton\").innerHTML = \n'<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z\" /> </svg>&nbsp; Send to device';"
  },
  {
    "path": "html/pixartmin.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\">\n    <meta http-equiv=\"Pragma\" content=\"no-cache\">\n    <meta http-equiv=\"Expires\" content=\"0\">\n    <title>Led Matrix Pixel Art Converter</title>\n    <style> \n      h1,h2{line-height:.5;margin:1px 0}h1,h2,h3{margin:1px 0}#fieldTable,#scaleTable,p{color:#777;font-family:Arcade,Arial,sans-serif}#drop-zone,.container{text-align:center;padding:20px}#drop-zone,#fieldTable,#scaleTable,body,h1,h2,h3,p{font-family:Arcade,Arial,sans-serif}.rangeNumber,.svg-icon{vertical-align:middle}a:active,a:link,a:visited,h2,h3{color:rgba(126,76,128,.61)}a:hover,h1{color:#7e4c80}a:active,a:hover,a:link,a:visited{background-color:transparent;text-decoration:none}.box{border:2px solid #fff}body{background-color:#151515}.top-part{width:600px;margin:0 auto}.container{max-width:100% -40px;border-radius:0}h1{font-size:2.3em}h2{font-size:1.1em;text-align:center}h3{font-size:.7em;line-height:1.4;text-align:center;align-items:center;justify-content:center;display:flex}p{font-size:1.2em;line-height:1.5}#fieldTable,#scaleTable{font-size:1 em;line-height:1}#drop-zone{display:block;width:100%-40px;border:3px dashed #7e4c80;border-radius:0;margin:0;cursor:pointer;font-size:15px;color:#777}* select,.fullTextField[type=text]{background-color:#333;border:1px solid silver;width:100%;color:#777;font-size:15px}#file-picker,.hide{display:none}* select{margin-top:.5em;margin-bottom:.5em;padding:0;height:27px;border-radius:0}* input[type=range]{-webkit-appearance:none;flex-grow:1;border-radius:0;background:linear-gradient(to right,#333 0,#333 100%);color:silver;border:1px solid silver;margin-top:.5em;margin-left:0}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:25px;height:25px;background:#7e4c80;position:relative;z-index:3}.rangeNumber{width:20px}.fullTextField[type=text]{padding-inline-start:5px;margin-top:10px;height:24px;border-radius:0;font-family:Arcade,Arial,sans-serif}* button,* input[type=submit]{background-color:#333;border:1px solid silver;width:100%;font-family:Arcade,Arial,sans-serif}* input[type=submit]{padding:.5em;border-radius:0;font-size:1.3em;color:#777}* button{padding-inline:5px;border-radius:0;font-size:1em;color:#777;display:flex;align-items:center;justify-content:center;cursor:pointer}.sizeInputFields,textarea{background-color:#333;border:1px solid silver;color:#777}textarea{grid-row:1/2;width:100%;height:200px}.grids-class{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}.buttondivmid-class,.gap{width:10px}#sizeDiv *,.buttondiv-class,.buttondivmid-class{display:inline-block}.buttondiv-class{flex:1}#image-container{display:grid;grid-template-rows:1fr 1fr}#button-container{display:flex;padding-bottom:10px;padding-top:10px}.buttonclass{flex:1;padding-top:5px;padding-bottom:5px}#submitConvert::before{content:\"\";display:inline-block;background-image:url('data:image/svg+xml;utf8, <svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\" <path fill=\"currentColor\" d=\"M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z\" /></svg>');width:36px;height:36px}.sizeInputFields{width:50px;padding-inline-start:5px;margin-top:-5px;height:24px;border-radius:0;font-family:Arcade,Arial,sans-serif;font-size:15px}\n    </style>\n  </head>\n  <body>\n    <body>\n      <div class=\"top-part\" >\n        <div style=\"display: flex; justify-content: center;\">\n          <h1 style=\"display: flex; align-items: center;\">\n            <svg style=\"width:36px;height:36px\" id=\"logomatrix\" viewBox=\"0 0 24 24\">\n              <path fill=\"#7e4c80\" d=\"M6,5\n                      a2,2 0 1,0 4,0\n                      a2,2 0 1,0 -4,0\" id=\"sc1\"/>\n              <path fill=\"#7e4c80\" d=\"M12,5\n                      a2,2 0 1,0 4,0\n                      a2,2 0 1,0 -4,0\" id=\"sc2\"/>\n              <path fill=\"#7e4c80\" d=\"M18,5\n                      a2,2 0 1,0 4,0\n                      a2,2 0 1,0 -4,0\" id=\"sc3\"/>\n              <path fill=\"#7e4c80\" d=\"M6,11\n                      a2,2 0 1,0 4,0\n                      a2,2 0 1,0 -4,0\" id=\"sc4\"/>\n              <path fill=\"#7e4c80\" d=\"M12,11\n                      a2,2 0 1,0 4,0\n                      a2,2 0 1,0 -4,0\" id=\"sc5\"/>\n              <path fill=\"#7e4c80\" d=\"M18,11\n                      a2,2 0 1,0 4,0\n                      a2,2 0 1,0 -4,0\" id=\"sc6\"/>\n              <path fill=\"#7e4c80\" d=\"M6,17\n                      a2,2 0 1,0 4,0\n                      a2,2 0 1,0 -4,0\" id=\"sc7\"/>\n              <path fill=\"#7e4c80\" d=\"M12,17\n                      a2,2 0 1,0 4,0\n                      a2,2 0 1,0 -4,0\" id=\"sc8\"/>\n              <path fill=\"#7e4c80\" d=\"M18,17\n                      a2,2 0 1,0 4,0\n                      a2,2 0 1,0 -4,0\" id=\"sc9\"/>\n            </svg>\n  \n              Led Matrix Pixel Art Converter\n          </h1>\n        </div>\n        <h2>Convert image to WLED JSON (pixel art on WLED matrix)</h2>\n        <p>\n          <table id=\"fieldTable\"  style=\"width: 100%; table-layout: fixed; align-content: center;\">\n            <tr>\n              <td style=\"vertical-align: middle;\">\n                <label for=\"ledSetupSelector\">Led setup:</label>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <select id=\"ledSetupSelector\">\n                  <option value=\"matrix\" selected>2D Matrix</option>\n                  <option value=\"r2l\">Serpentine, first row right to left &lt;-</option>\n                  <option value=\"l2r\">Serpentine, first row left to right -&gt;</option>\n                </select>\n              </td>\n            </tr>        \n            <tr>\n              <td style=\"vertical-align: middle;\">\n                <label for=\"formatSelector\">Output format:</label>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <select id=\"formatSelector\">\n                  <option value=\"wled\" selected>WLED JSON</option>\n                  <option value=\"curl\">CURL</option>\n                  <option value=\"ha\">Home Assistant YAML</option>\n                </select>\n              </td>\n            </tr>        \n            <tr>\n              <td style=\"vertical-align: middle;\">\n                <label for=\"colorFormatSelector\">Color code format:</label>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <select id=\"colorFormatSelector\">\n                  <option value=\"hex\" selected>HEX (#f4f4f4)</option>\n                  <option value=\"dec\">DEC (244,244,244)</option>\n                </select>\n            </td>\n            </tr>\n            <tr>\n              <td style=\"vertical-align: middle;\">\n                <label for=\"addressingSelector\">Addressing:</label>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <select id=\"addressingSelector\">\n                  <option value=\"hybrid\" selected>Hybrid (#f0f0f0,10, 17, #f4f4f4)</option>\n                  <option value=\"range\">Range (10, 17, #f4f4f4)</option>\n                  <option value=\"single\">Single (#f4f4f4)</option>\n                </select>\n              </td>\n            </tr>\n            <tr>\n              <td style=\"vertical-align: middle;\">\n                <label for=\"brightnessNumber\">Brightness:</label>\n              </td>\n              <td style=\"vertical-align: middle; display: flex; align-items: center;\">\n                <input type=\"range\" id=\"brightnessNumber\" min=\"1\" max=\"255\" value=\"255\">\n                <span id=\"brightnessValue\">255</span>\n              </td>\n            </tr>\n            <tr>\n              <td style=\"vertical-align: middle;\">\n                <label for=\"colorLimitNumber\">Max no of colors/JSON:</label>\n              </td>\n              <td style=\"vertical-align: middle; display: flex; align-items: center;\">\n                <input type=\"range\" id=\"colorLimitNumber\" min=\"1\" max=\"512\" value=\"256\">\n                <span id=\"colorLimitValue\" >256</span>\n              </td>\n            </tr>\n            <tr class=\"ha-hide\">\n              <td style=\"vertical-align: middle;\">\n                <label for=\"haID\">HA Device ID:</label>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <input class=\"fullTextField\" type=\"text\" id=\"haID\" value=\"pixel_art_controller_001\">\n              </td>\n            </tr>\n            <tr class=\"ha-hide\">\n              <td style=\"vertical-align: middle;\">\n                <label for=\"haUID\">HA Device Unique ID:</label>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <input class=\"fullTextField\" type=\"text\" id=\"haUID\" value=\"pixel_art_controller_001a\">\n              </td>\n            </tr>\n            <tr class=\"ha-hide\">\n              <td style=\"vertical-align: middle;\">\n                <label for=\"haName\">HA Device Name:</label>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <input class=\"fullTextField\" type=\"text\" id=\"haName\" value=\"Pixel Art Kitchen\">\n              </td>\n            </tr>\n            <tr>\n              <td style=\"vertical-align: middle;\">\n                <label for=\"curlUrl\">Device IP/host name:</label>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <input class=\"fullTextField\" type=\"text\" id=\"curlUrl\" value=\"\">\n              </td>\n            </tr>\n            <tr>\n              <td style=\"vertical-align: middle;\">\n                <label for=\"targetSegment\">Target segment id:</label>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <select id=\"targetSegment\">\n                </select>\n              </td>\n            </tr>\n          </table>\n          <table  class= \"scaleTableClass\" id=\"scaleTable\"  style=\"width: 100%; table-layout: fixed; align-content: center;\">\n            <tr>\n              <td style=\"vertical-align: middle;\">\n                <div id=\"scaleDiv\">\n                  <svg id=\"scaleToggle\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\" onclick=\"switchScale()\">\n                    <path id=\"scaleTogglePath\" fill=\"currentColor\" d=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z\" />\n                  </svg>\n                  &nbsp;&nbsp;&nbsp;\n                  <svg id=\"scaleSvg\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\" onclick=\"switchScale()\">\n                    <path id=\"scalePath\" fill=\"currentColor\" d=\"M21,15H23V17H21V15M21,11H23V13H21V11M23,19H21V21C22,21 23,20 23,19M13,3H15V5H13V3M21,7H23V9H21V7M21,3V5H23C23,4 22,3 21,3M1,7H3V9H1V7M17,3H19V5H17V3M17,19H19V21H17V19M3,3C2,3 1,4 1,5H3V3M9,3H11V5H9V3M5,3H7V5H5V3M1,11V19A2,2 0 0,0 3,21H15V11H1M3,19L5.5,15.79L7.29,17.94L9.79,14.72L13,19H3Z\" />\n                  </svg>\n                </div>\n              </td>\n              <td style=\"vertical-align: middle;\">\n                <div id=\"sizeDiv\" style=\"display: none;\">\n                  <label for=\"sizeX\">X : </label> &nbsp;<input class=\"sizeInputFields\" type=\"number\" id=\"sizeX\" value=\"0\">\n                  &nbsp;&nbsp;&nbsp;\n                  <label for=\"sizeY\">Y : </label> &nbsp;<input class=\"sizeInputFields\" type=\"number\" id=\"sizeY\" value=\"0\">\n                </div>\n              </td>\n            </tr>\n          </table>\n        </p>\n  \n        <p>\n          <label for=\"file-picker\">\n            <div id=\"drop-zone\">    \n                Drop image here <br>or <br>\n                Click to select a file\n            </div>\n          </label>\n        </p>\n        \n        <p>\n          <input type=\"file\" id=\"file-picker\" style=\"display: none;\">\n          <div style=\"width: 100%; text-align: center;\">\n            <img id=\"preview\" style=\"display: block; margin: 0 auto;\">\n            <img id=\"newimage\" style=\"display: block; margin: 0 auto;\"><br>\n          </div>\n          \n          <div id=\"submitConvertDiv\" style=\"display: none;\">\n            <button id=\"convertbutton\" class=\"buttonclass\"></button>\n          </div>\n          \n          <div id=\"raw-image-container\" style=\"display: none\">\n            <img id=\"image\" src=\"\" alt=\"RawImage image\">\n          </div>\n          \n        </p>\n        \n        <div id=\"image-container\" style=\"display: none;\">\n          <div id=\"image-info\" style=\"display: none\"></div>\n          <textarea id=\"JSONled\"></textarea>\n        </div>\n  \n        <div id=\"button-container\" style=\"display: none;\">\n          <button id=\"copyJSONledbutton\" class=\"buttonclass\"></button>\n          <div id=\"gap1\" class=\"gap\"></div>\n          <button id=\"sendJSONledbutton\" class=\"buttonclass\"></button>\n          <div id = \"gap2\" class=\"gap\"></div>\n          <button id=\"fileJSONledbutton\" class=\"buttonclass\"></button>\n        </div>\n        <div>\n          <h3><div id=\"version\">Version 1.0.4</div>&nbsp;-&nbsp; <a href=\"https://github.com/werkstrom/WLED-PixelArtConverter/blob/main/README.md\" target=\"_blank\">Help/About</a></h3>\n        </div>\n      </div>\n      <div id=bottom-part style=\"display: none\" class=bottom-part></div>\n            <canvas id=\"pixelCanvas\"></canvas>\n      </div>\n    <script>\n    var intervalId,curlStart='curl -X POST \"http://',curlMid1=\"/json/state\\\" -d '\",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    \",haMid1=\"\\n      friendly_name: \",haMid2=\"\\n      unique_id: \",haMid3=\"\\n      command_on: >\\n        \",haMid4='\\n      command_off: >\\n        curl -X POST \"http://',haEnd='/json/state\" -d \\'{\"on\":false}\\' -H \"Content-Type: application/json\"',haCommandLeading=\"        \",JSONledStringStart='{\"on\":true,\"bri\":',JSONledStringMid1=',\"seg\":{\"id\":',JSONledStringMid2=',\"i\":[',JSONledStringEnd=\"]}}\";var accentColor=\"#7E4C80\",accentTextColor=\"#777\",scaleToggleOffd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z\",scaleToggleOnd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z\";document.getElementById(\"curlUrl\").value=location.host;let devMode=!1;const urlParams=new URLSearchParams(window.location.search);urlParams.has(\"dev\")&&(devMode=!0),devMode?console.log(\"Developer mode active. Experimental and unstable functions active.\"):console.log('Developer mode inactive. Append \"?dev\" to the URL.'),devMode?(document.getElementById(\"fileJSONledbutton\").style.display=\"buttonclass\",document.getElementById(\"gap2\").style.display=\"gap\"):(document.getElementById(\"fileJSONledbutton\").style.display=\"none\",document.getElementById(\"gap2\").style.display=\"none\");let httpArray=[],fileJSON=\"\";async function postPixels(){for(let e of httpArray)try{console.log(e),console.log(e.length);let t=await fetch(\"http://\"+document.getElementById(\"curlUrl\").value+\"/json/state\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:e}),l=await t.json();console.log(l)}catch(n){console.error(n)}}document.getElementById(\"convertbutton\").addEventListener(\"click\",function(){let e=document.getElementById(\"preview\").src;if(isValidBase64Gif(e))document.getElementById(\"image\").src=e,getPixelRGBValues(e),document.getElementById(\"image-container\").style.display=\"block\",document.getElementById(\"button-container\").style.display=\"\";else{let t=document.getElementById(\"image-info\");t.innerHTML=\"<p><b>WARNING!</b> File does not appear to be a valid image</p>\",t.style.display=\"block\",document.getElementById(\"image-container\").style.display=\"none\",document.getElementById(\"JSONled\").value=\"\",console.log(\"The string '\"+e+\"' is not a valid base64 image.\")}}),copyJSONledbutton.addEventListener(\"click\",async()=>{let e=document.getElementById(\"JSONled\");e.select();try{await navigator.clipboard.writeText(e.value)}catch(t){try{await document.execCommand(\"copy\")}catch(l){console.error(\"Failed to copy text: \",l)}}}),sendJSONledbutton.addEventListener(\"click\",async()=>{\"https:\"===window.location.protocol?alert(\"Will only be available when served over http (or WLED is run over https)\"):postPixels()}),fileJSONledbutton.addEventListener(\"click\",async()=>{if(\"https:\"===window.location.protocol)alert(\"Will only be available when served over http (or WLED is run over https)\");else{let e;sendAsFile(fileJSON,\"TheName.json\",\"http://\"+document.getElementById(\"curlUrl\").value+\"/upload\")}});const dropZone=document.getElementById(\"drop-zone\"),filePicker=document.getElementById(\"file-picker\"),preview=document.getElementById(\"preview\");function zoneClicked(e){e.preventDefault(),filePicker.click()}function dragEnter(e){e.preventDefault(),this.classList.add(\"drag-over\")}function dragOver(e){e.preventDefault()}function dropped(e){e.preventDefault(),this.classList.remove(\"drag-over\");let t=e.dataTransfer.files[0];updatePreview(t)}function filePicked(e){let t=e.target.files[0];updatePreview(t)}function updatePreview(e){let t=new FileReader;t.onload=function(){preview.src=t.result,document.getElementById(\"submitConvertDiv\").style.display=\"\"},t.readAsDataURL(e)}function isValidBase64Gif(e){return!0}dropZone.addEventListener(\"dragenter\",dragEnter),dropZone.addEventListener(\"dragover\",dragOver),dropZone.addEventListener(\"drop\",dropped),dropZone.addEventListener(\"click\",zoneClicked),filePicker.addEventListener(\"change\",filePicked),document.getElementById(\"brightnessNumber\").oninput=function(){document.getElementById(\"brightnessValue\").textContent=this.value},document.getElementById(\"colorLimitNumber\").oninput=function(){document.getElementById(\"colorLimitValue\").textContent=this.value};for(var formatSelector=document.getElementById(\"formatSelector\"),hideableRows=document.querySelectorAll(\".ha-hide\"),i=0;i<hideableRows.length;i++)hideableRows[i].classList.add(\"hide\");function switchScale(){let e=document.getElementById(\"scalePath\"),t=document.getElementById(\"scaleTogglePath\"),l=e.getAttribute(\"fill\"),n=\"\";l===accentColor?(l=accentTextColor,n=scaleToggleOffd,document.getElementById(\"sizeDiv\").style.display=\"none\"):(l=accentColor,n=scaleToggleOnd,document.getElementById(\"sizeDiv\").style.display=\"\"),e.setAttribute(\"fill\",l),t.setAttribute(\"fill\",l),t.setAttribute(\"d\",n)}function sendAsFile(e,t,l){var n=new Blob([e],{type:\"application/json\"});console.log(e),console.log(t),console.log(l);var a=new FormData;a.append(\"file\",n,t);var o=new XMLHttpRequest;o.open(\"POST\",l,!0),o.onload=function(){200===o.status?console.log(\"File uploaded successfully!\"):console.log(\"File upload failed!\")},o.send(a)}function generateSegmentOptions(e){var t=document.getElementById(\"targetSegment\");t.innerHTML=\"\";for(var l=0;l<e.length;l++){var n=document.createElement(\"option\");n.value=e[l].value,n.text=e[l].text,t.appendChild(n),0===l&&(n.selected=!0)}}function generateSegmentArray(e){for(var t=[],l=0;l<e;l++)t.push({value:l,text:\"Segment index \"+l});return t}formatSelector.addEventListener(\"change\",function(){for(var e=0;e<hideableRows.length;e++)hideableRows[e].classList.toggle(\"hide\",\"ha\"!==this.value)});var matrixcircles=document.querySelectorAll(\"#logomatrix path\");function changeColorOfDot(){var e=matrixcircles[Math.floor(Math.random()*matrixcircles.length)],t=e.getAttribute(\"fill\");e.setAttribute(\"fill\",\"#bb8fbc\"),setTimeout(()=>{e.setAttribute(\"fill\",t),clearInterval(intervalId),intervalId=setInterval(changeColorOfDot,randomInterval())},500)}function randomInterval(){return 1e3*(Math.floor(5*Math.random())+1)}intervalId=setInterval(changeColorOfDot,randomInterval());var segmentData=generateSegmentArray(10);function getPixelRGBValues(e){httpArray=[],fileJSON=JSONledStringStart+document.getElementById(\"brightnessNumber\").value+JSONledStringMid1+document.getElementById(\"targetSegment\").value+JSONledStringMid2;let t=document.getElementById(\"JSONled\"),l=document.getElementById(\"colorLimitNumber\").value,n=!1,a=-1,o=document.getElementById(\"formatSelector\");a=o.selectedIndex;let r=o.options[a].value;a=(o=document.getElementById(\"ledSetupSelector\")).selectedIndex;let d=o.options[a].value;a=(o=document.getElementById(\"colorFormatSelector\")).selectedIndex;let s=!0;\"dec\"==o.options[a].value&&(s=!1),a=(o=document.getElementById(\"addressingSelector\")).selectedIndex;let _=!0;\"single\"==o.options[a].value?_=!1:\"hybrid\"==o.options[a].value&&(n=!0);let c=\"\",g=\"\",$=\"'\",u=\"'\";s||($=\"[\",u=\"]\");let m=!1,p=\"\";var y=document.createElement(\"canvas\"),v=y.getContext(\"2d\"),h=new Image;h.src=e,h.onload=function(){let e=document.getElementById(\"scalePath\").getAttribute(\"fill\"),a=document.getElementById(\"sizeX\").value,o=document.getElementById(\"sizeY\").value;(e!=accentColor||a<1||o<1)&&(a=h.width,o=h.height),y.width=a,y.height=o,p=\"<p>Width: \"+a+\", Height: \"+o+\" (make sure this matches your led matrix setup)</p>\",v.drawImage(h,0,0,a,o);var f=v.getImageData(0,0,a,o).data,E=[];let I=1;\"l2r\"==d&&(I=0);for(var B=0;B<f.length;B+=4){var S=f[B],b=f[B+1],A=f[B+2],w=f[B+3];let x=B/4,L=Math.floor(x/a),C=x;if(\"matrix\"==d);else if((L+I)%2==0);else{let O=C-L*a,M=a-1-O;C=L*a+M}E.push([S,b,A,w,C,x,L])}E.sort((e,t)=>e[5]-t[5]);let H=[...E];H.sort((e,t)=>e[4]-t[4]);let N=\"\",T=-1,k=H.length,J=0,V=[];for(let D=0;D<k;D++){let P=H[D],R=P[0],j=P[1],W=P[2],Z=P[3],U=\"\",z=-1;if(_){if(T<0&&(T=D),D<k-1){let F=H[D+1];(F[0]!=R||F[1]!=j||F[2]!=W)&&(z=D+1,U=T==D&&n?\"\"==N?\"\"+D+\",\":\"\":T+\",\"+z+\",\")}else U=T+1==(z=D+1)&&n?\"\"==N?\"\"+D+\",\":\"\":T+\",\"+z+\",\"}else\"\"==N&&(N=D),T=D,z=D;if(Z<255&&(m=!0),z>-1){let G=R+\",\"+j+\",\"+W;if(s){let[Q,q,X]=[R,j,W];G=`${[Q,q,X].map(e=>e.toString(16).padStart(2,\"0\")).join(\"\")}`}fileJSON=(N=N+U+$+G+u)+U+$+G+u,(J+=1)%l==0||D==k-1?(V.push(N),N=\"\"):N+=\",\",T=-1}}N=\"\";for(let Y=0;Y<V.length;Y++){let K=JSONledStringStart+document.getElementById(\"brightnessNumber\").value+JSONledStringMid1+document.getElementById(\"targetSegment\").value+JSONledStringMid2+V[Y]+\"]}}\";httpArray.push(K);let ee=curlStart+document.getElementById(\"curlUrl\").value+curlMid1+K+curlEnd;Y>0&&(N+=\"\\n\",c+=\" && \"),N+=K,c+=ee}g=\"#Uncomment if you don't allready have these defined in your switch section of your configuration.yaml\\n#- platform: command_line\\n  #switches:\\n    \"+document.getElementById(\"haID\").value+\"\\n      friendly_name: \"+document.getElementById(\"haName\").value+\"\\n      unique_id: \"+document.getElementById(\"haUID\").value+haMid3+c+haMid3+document.getElementById(\"curlUrl\").value+'/json/state\" -d \\'{\"on\":false}\\' -H \"Content-Type: application/json\"',\"wled\"==r?t.value=N:\"curl\"==r?t.value=c:\"ha\"==r?t.value=g:t.value=\"ERROR!/n\"+r+\" is an unknown format.\",fileJSON+=\"]}}\";let et=document.getElementById(\"image-info\"),el=document.getElementById(\"image-info\");m&&(p+=\"<p><b>WARNING!</b> 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.</p>\"),et.innerHTML=p,el.style.display=\"block\",drawBoxes(E,a,o)}}function drawBoxes(e,t,l){var n=document.getElementById(\"pixelCanvas\"),a=n.getContext(\"2d\");window.innerHeight<window.innerWidth?n.width=Math.floor(.98*window.innerHeight):n.width=Math.floor(.98*window.innerWidth);let o=Math.floor(n.width/t);n.height=o*l+10;for(let r=0;r<l;r++)for(let d=0;d<t;d++){let s=e[r*t+d],_=\"rgb(\"+s[0]+\", \"+s[1]+\", \"+s[2]+\")\";s[0],s[1],s[2];let c=s[4];a.fillStyle=_,a.fillRect(d*o,r*o,o,o),a.strokeStyle=\"#888888\",a.lineWidth=1,a.strokeRect(d*o,r*o,o,o),a.font=\"10px Arial\",a.fillStyle=\"rgb(128,128,128)\",a.textAlign=\"center\",a.textBaseline=\"middle\",a.fillText(c+1,d*o+o/2,r*o+o/2)}}function drawBackground(){let e=document.createElement(\"div\");e.id=\"grid\",e.classList.add(\"grid-class\"),e.style.cssText=\"\";let t=Math.ceil(window.innerWidth/20)*Math.ceil(window.innerHeight/20);for(let l=0;l<t;l++){let n=document.createElement(\"div\");n.classList.add(\"box\"),n.style.backgroundColor=getRandomColor(),e.appendChild(n)}e.style.zIndex=-1,document.body.appendChild(e)}function getRandomColor(){let e=\"rgba(\";for(let t=0;t<3;t++)e+=Math.floor(256*Math.random())+\",\";return e+\"0.05)\"}generateSegmentOptions(segmentData),document.getElementById(\"fileJSONledbutton\").innerHTML='<svg style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M20 18H4V8H20M20 6H12L10 4H4A2 2 0 0 0 2 6V18A2 2 0 0 0 4 20H20A2 2 0 0 0 22 18V8A2 2 0 0 0 20 6M16 17H14V13H11L15 9L19 13H16Z\" /></svg>&nbsp; File to device',document.getElementById(\"convertbutton\").innerHTML='<svg style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z\" /> </svg>&nbsp; Convert to WLED JSON ',document.getElementById(\"copyJSONledbutton\").innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\" /> </svg>&nbsp; Copy to clipboard',document.getElementById(\"sendJSONledbutton\").innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z\" /> </svg>&nbsp; Send to device',window.drawBackground=drawBackground;  \n    </script>\n  </body>\n</html>"
  },
  {
    "path": "html/site.webmanifest",
    "content": "{\n    \"name\": \"Led Matrix Pixel Art Convertor\",\n    \"short_name\": \"ledconv\",\n    \"icons\": [\n       {\n          \"src\": \"/favicon-32x32.png\",\n          \"sizes\": \"32x322\",\n          \"type\": \"image/png\"\n       },\n       {\n          \"src\": \"/favicon-32x32.png\",\n          \"sizes\": \"32x32\",\n          \"type\": \"image/png\"\n       }\n    ],\n    \"theme_color\": \"#ffffff\",\n    \"background_color\": \"#ffffff\",\n    \"display\": \"standalone\"\n }"
  },
  {
    "path": "html/statics.js",
    "content": "var curlStart = 'curl -X POST \"http://';\nvar curlMid1 = '/json/state\" -d \\'';\nvar curlEnd = '\\' -H \"Content-Type: application/json\"';\n\nconst 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    ';\nconst haMid1 = '\\n      friendly_name: ';\nconst haMid2 = '\\n      unique_id: ';\nconst haMid3= '\\n      command_on: >\\n        ';\nconst haMid4 = '\\n      command_off: >\\n        curl -X POST \"http://';\nconst haEnd = '/json/state\" -d \\'{\"on\":false}\\' -H \"Content-Type: application/json\"';\nconst haCommandLeading = '        ';\n\nconst JSONledStringStart = '{\"on\":true,\"bri\":';\nconst JSONledStringMid1 = ',\"seg\":{\"id\":';\nconst JSONledStringMid2 = ',\"i\":[';\n// const JSONledShortStringStart = '{';\n// const JSONledShortStringMid1 = '\"seg\":{\"i\":[';\nconst JSONledStringEnd = ']}}';\n\nvar accentColor = '#7E4C80';\nvar accentTextColor = '#777';\n\nvar scaleToggleOffd = \"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z\";\nvar scaleToggleOnd = \"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z\";\n\n"
  },
  {
    "path": "html/styles.css",
    "content": "\n.box {\n  border: 2px solid white;\n}\nbody {\n  font-family: 'Arcade', Arial, sans-serif;\n  background-color: #151515;\n}\n\n.top-part {\n  width: 600px;\n  margin: 0 auto;\n}\n.container {\n  max-width: 100% -40px;\n  border-radius: 0px;\n  padding: 20px;\n  text-align: center;\n}\nh1 {\n  font-size: 2.3em;\n  color: rgb(126, 76, 128);\n  margin: 1px 0;\n  font-family: 'Arcade', Arial, sans-serif;\n  line-height: 0.5;\n  /*text-align: center;*/\n}\nh2 {\n  font-size: 1.1em;\n  color: rgba(126, 76, 128, 0.61);\n  margin: 1px 0;\n  font-family: 'Arcade', Arial, sans-serif;\n  line-height: 0.5;\n  text-align: center;\n}\nh3 {\n  font-size: 0.7em;\n  color: rgba(126, 76, 128, 0.61);\n  margin: 1px 0;\n  font-family: 'Arcade', Arial, sans-serif;\n  line-height: 1.4;\n  text-align: center;\n  align-items: center;\n  justify-content: center;\n  display: flex;\n}\n\np {\n  font-size: 1.2em;\n  color: rgb(119, 119, 119);\n  line-height: 1.5;\n  font-family: 'Arcade', Arial, sans-serif;\n}\n\n#fieldTable {\n  font-size: 1  em;\n  color: #777;\n  line-height: 1;\n  font-family: 'Arcade', Arial, sans-serif;\n}\n\n#scaleTable {\n  font-size: 1  em;\n  color: #777;\n  line-height: 1;\n  font-family: 'Arcade', Arial, sans-serif;\n}\n\n#drop-zone {\n  display: block;\n  width: 100%-40px;\n  border: 3px dashed #7E4C80;\n  border-radius: 0px;\n  text-align: center;\n  padding: 20px;\n  margin: 0px;\n  cursor: pointer;\n  \n  font-family: 'Arcade', Arial, sans-serif;\n  font-size: 15px;\n  color: #777;\n}\n\n#file-picker {\n  display: none;\n}\n\n* select {\n  background-color: #333333;\n  color: #C0C0C0;\n  border: 1px solid #C0C0C0;\n  margin-top: 0.5em;\n  margin-bottom: 0.5em;\n  padding: 0em;\n  width: 100%;\n  height: 27px;\n  font-size: 15px;\n  color: rgb(119, 119, 119);\n  border-radius: 0;\n}\n\n* input[type=range] {\n  -webkit-appearance:none;\n  flex-grow: 1;\n  border-radius: 0px;\n  background: linear-gradient(to right, #333333 0%, #333333 100%);\n  color: #C0C0C0;\n  border: 1px solid #C0C0C0; \n  margin-top: 0.5em; \n  margin-left: 0em;     \n}\n\ninput[type=\"range\"]::-webkit-slider-thumb{\n  -webkit-appearance:none;\n  width: 25px;\n  height:25px;\n  background:#7E4C80;\n  position:relative;\n  z-index:3;\n}\n\n.rangeNumber{\n  width: 20px;\n  vertical-align: middle;\n}\n\n.fullTextField[type=text] {\n  background-color: #333333;\n  border: 1px solid #C0C0C0;\n  padding-inline-start: 5px;\n  margin-top: 10px;\n  width: 100%;\n  height: 24px;\n  border-radius: 0px;\n  font-family: 'Arcade', Arial, sans-serif;\n  font-size: 15px;\n  color: rgb(119, 119, 119);\n}\n\n* input[type=submit] {\n  background-color: #333333;\n  border: 1px solid #C0C0C0;\n  padding: 0.5em;\n  width: 100%;\n  border-radius: 0px;\n  font-family: 'Arcade', Arial, sans-serif;\n  font-size: 1.3em;\n  color: rgb(119, 119, 119);\n}\n\n* button {\n  background-color: #333333;\n  border: 1px solid #C0C0C0;\n  padding-inline: 5px;\n  width: 100%;\n  border-radius: 0px;\n  font-family: 'Arcade', Arial, sans-serif;\n  font-size: 1em;\n  color: rgb(119, 119, 119);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  cursor: pointer;\n}\n\ntextarea {\n  grid-row: 1 / 2;\n  width: 100%;\n  height: 200px;\n  background-color: #333333;\n  border: 1px solid #C0C0C0;\n  \n  color: #777;\n}\n.hide {\n    display: none;\n}\n.grids-class{\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100vw;\n  height: 100vh;\n  display: grid;\n  grid-template-columns: repeat(auto-fill, 20px);\n  grid-template-rows: repeat(auto-fill, 20px);\n  grid-gap: 0px;\n}\n.svg-icon {\n  vertical-align: middle;\n}\n.buttondiv-class {\n  flex: 1;\n  display: inline-block;\n}\n\n.buttondivmid-class {\n  width: 10px;\n  display: inline-block;\n}\n#image-container {\n  display: grid;\n  grid-template-rows: 1fr 1fr;\n}\n#button-container {\n  display: flex;\n  padding-bottom: 10px;\n  padding-top: 10px;\n}\n\n.buttonclass {\n  flex: 1;\n  padding-top: 5px;\n  padding-bottom: 5px;\n}\n\n.gap {\n  width: 10px;\n}\n\n#submitConvert::before {\n  content: \"\";\n  display: inline-block;\n  background-image: url('data:image/svg+xml;utf8, <svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\" <path fill=\"currentColor\" d=\"M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z\" /></svg>');\n  width: 36px;\n  height: 36px;\n}\n\n#sizeDiv * {\n  display: inline-block;\n}\n.sizeInputFields{\n  width: 50px;\n  background-color: #333333;\n  border: 1px solid #C0C0C0;\n  padding-inline-start: 5px;\n  margin-top: -5px;\n  height: 24px;\n  border-radius: 0px;\n  font-family: 'Arcade', Arial, sans-serif;\n  font-size: 15px;\n  color: rgb(119, 119, 119);\n}\na:link {\n  color: rgba(126, 76, 128, 0.61);\n  background-color: transparent;\n  text-decoration: none;\n}\n\na:visited {\n  color: rgba(126, 76, 128, 0.61);\n  background-color: transparent;\n  text-decoration: none;\n}\n\na:hover {\n  color: rgb(126, 76, 128);\n  background-color: transparent;\n  text-decoration: none;\n}\n\na:active {\n  color: rgba(126, 76, 128, 0.61);\n  background-color: transparent;\n  text-decoration: none;\n}"
  },
  {
    "path": "infoFiles/FlowDesignDocument.md",
    "content": "***WLED Command Runner***  \n**Design document**\n\nNaming conventions\nA flow is the “top level” object. It is the container of all other info and is the “thing” we call to start running a command flow.\nA step is the “timetable” that the command runner goes through, from top to bottom deciding what sets to call, in what order and how long to wait until running the next set.\nA set is what the flow internally handles. When the runner goes through the flow it runs one set and then waits until it is time to run the next set. A set is a (any) number of API commands that are to be run in sequence (without delaying in-between)\nA command is any WLED API command, following the standard. \nThe reason for arranging commands in sets is that WLED has a problem with running to massive commands, i.e. setting hundreds of leds. So the desired outcome needs to be split into several commands that can be run one after another.\n\nflows are uploaded as files (to /edit) with the template flow_[someID].JSON\n\nUsage:\nA Flow is made up off steps. Each step references a set (of commands)\nA flow runner should, when triggerd, \n1. find the file with the correct flow\n2. get the array of steps\n3. for each step find the set (of commands)\n4. Send the commands to WLED\n5. Sleep/Wait for the stipulated number of ms\n6. Move to next step (as per 4 above)\n7. repeat until end of steps\n8. If repeat = true start over from step 1\n"
  },
  {
    "path": "infoFiles/runnerFlowTemplate.json",
    "content": "{\n    \"description\" : \"My first flow\",\n    \"created\": \"2023-04-05 14:42\",\n    \"repeat\": true,\n    \"sets\": [\n        {\n            \"id\": \"numeroUno\",\n            \"commands\": [\n                {\n                    \"position\": 1,\n                    \"command\": \"{Valid WLED API COMMAND}\"\n                },\n                {\n                    \"position\": 2,\n                    \"command\": \"{Valid WLED API COMMAND}\"\n                },\n                {\n                    \"position\": 3,\n                    \"command\": \"{Valid WLED API COMMAND}\"\n                }\n            ]\n        },{\n            \"id\": \"SecondToNone\",\n            \"commands\": [\n                {\n                    \"position\": 1,\n                    \"command\": \"{Valid WLED API COMMAND}\"\n                },\n                {\n                    \"position\": 2,\n                    \"command\": \"{Valid WLED API COMMAND}\"\n                },                \n                {\n                    \"position\": 3,\n                    \"command\": \"{Valid WLED API COMMAND}\"\n                }\n            ]\n        },{\n            \"id\": \"4\",\n            \"commands\": [\n                {\n                    \"position\": 1,\n                    \"command\": \"{Valid WLED API COMMAND}\"\n                }\n            ]\n        },{\n            \"id\": \"2\",\n            \"commands\": [\n                {\n                    \"position\": 1,\n                    \"command\": \"{Valid WLED API COMMAND}\"\n                },\n                {\n                    \"position\": 2,\n                    \"command\": \"{Valid WLED API COMMAND}\"\n                }\n            ]\n        }\n    ],\n    \"steps\":[\n        {\n            \"step\": 1,\n            \"commandId\": \"SecondToNone\",\n            \"duration\": 500\n        },\n        {\n            \"step\": 2,\n            \"commandId\": \"numeroUno\",\n            \"duration\": 300\n        },\n        {\n            \"step\": 3,\n            \"commandId\": \"SecondToNone\",\n            \"duration\": 800\n        },\n        {\n            \"step\": 4,\n            \"commandId\": \"4\",\n            \"duration\": 1000\n        },\n        {\n            \"step\": 5,\n            \"commandId\": \"2\",\n            \"duration\": 500\n        },\n        {\n            \"step\": 6,\n            \"commandId\": \"SecondToNone\",\n            \"duration\": 500\n        }\n    ]\n}\n"
  },
  {
    "path": "pixart.htm",
    "content": "<!DOCTYPE html> <html> <head> <meta http-equiv=\"Cache-Control\" \ncontent=\"no-cache, no-store, must-revalidate\"> <meta http-equiv=\"Pragma\" \ncontent=\"no-cache\"> <meta http-equiv=\"Expires\" content=\"0\"> <title>\nWLED Pixel Art Converter</title> <style>\n.box{border:2px solid #fff}body{font-family:Arial,sans-serif;background-color:#111}.top-part{width:600px;margin:0 auto}.container{max-width:100% -40px;border-radius:0;padding:20px;text-align:center}h1{font-size:2.3em;color:#ddd;margin:1px 0;font-family:Arial,sans-serif;line-height:.5}h2{font-size:1.1em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:.5;text-align:center}h3{font-size:.7em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:1.4;text-align:center;align-items:center;justify-content:center;display:flex}p{font-size:1em;color:#777;line-height:1.5;font-family:Arial,sans-serif}#fieldTable{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}#scaleTable{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}#drop-zone{display:block;width:100%-40px;border:3px dashed #ddd;border-radius:0;text-align:center;padding:20px;margin:0;cursor:pointer;font-family:Arial,sans-serif;font-size:15px;color:#777}#file-picker{display:none}.adaptiveTD{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center}.mainSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.adaptiveSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:none}.segmentsDiv{width:36px;padding-left:5px}* input[type=range]{appearance:none;-moz-appearance:none;-webkit-appearance:none;flex-grow:1;padding:0;margin:4px 8px 4px 0;background-color:transparent;cursor:pointer;background:linear-gradient(to right,#bbb 50%,#333 50%);border-radius:7px}input[type=range]:focus{outline:0}input[type=range]::-webkit-slider-runnable-track{height:28px;cursor:pointer;background:0 0;border-radius:7px}input[type=range]::-webkit-slider-thumb{height:16px;width:16px;border-radius:50%;background:#fff;cursor:pointer;-webkit-appearance:none;margin-top:4px;border-radius:7px}input[type=range]::-moz-range-track{height:28px;background-color:rgba(0,0,0,0);border-radius:7px}input[type=range]::-moz-range-thumb{border:0 solid transparent;height:16px;width:16px;border-radius:7px;background:#fff}.rangeNumber{width:20px;vertical-align:middle}.fullTextField[type=text]{background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:4px;margin-bottom:4px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.flxTFld{background-color:#222;border:1px solid #333;padding-inline-start:5px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}* input[type=submit]{background-color:#222;border:1px solid #333;padding:.5em;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1.3em;color:#ddd}* button{background-color:#222;border:1px solid #333;padding-inline:5px;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1em;color:#ddd;display:flex;align-items:center;justify-content:center;cursor:pointer}#scaleDiv{display:flex;align-items:center;vertical-align:middle}textarea{grid-row:1/2;width:100%;height:200px;background-color:#222;border:1px solid #333;color:#ddd}.hide{display:none}.svg-icon{vertical-align:middle}#image-container{display:grid;grid-template-rows:1fr 1fr}#button-container{display:flex;padding-bottom:10px;padding-top:10px}.buttonclass{flex:1;padding-top:5px;padding-bottom:5px}.gap{width:10px}#submitConvert::before{content:\"\";display:inline-block;background-image:url('data:image/svg+xml;utf8, <svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\" <path fill=\"currentColor\" d=\"M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z\" /></svg>');width:36px;height:36px}#sizeDiv *{display:inline-block}.sizeInputFields{width:50px;background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:-5px;height:24px;border-radius:7px;font-family:Arial,sans-serif;font-size:15px;color:#ddd}a:link{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:visited{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:hover{color:#ddd;background-color:transparent;text-decoration:none}a:active{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}\n</style> <link rel=\"shortcut icon\" \nhref=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAkNJREFUOE+lUstO21AUnHNsF0V+0EXVivAXfAe7qlL7EZEcQ5cQ8nCICbDJF7Trvhas6YbyDdBu2hSk7kihscK1fU91HRKKaFf1xhrP8ejMnCH850P/+v/cC2qcqw4gsGD3Hk/G23+bvSdw7nk1K887BeCQ4AGMAiEjcGaB7gndEfhRqdRQSC9ncQikAVisAWGBFggR5QvQ7UcTNd9mLvDdC2p2rhIN2G4U/vRbneyynzhps70oAPlhNPLjZna1v4/xVmtQHU8tzQXO3CCk4nrPD8MLN+6qd8ef8erwBKvH750XK1Xy4li9/fQFrw9PAGSDg8azuwJDN6jbuep7a+GFF3ezN0en5fDq8Qfn+UqVgjhW5bePpwCKwUHj6a2ACY7zrCdCC34UjrzutjLZiQC/kh0HlyPyu9tKzM4iGPf7uG5sDEwWpYWvrh85RZYAYAIp7+XayNtqytXeLqWbjUUNcLC+fuG2mpLu7tPl5sZDAnpLk0mzFDir+BFJlkCECaKFuSBYSlA4pLWtiRjEygIyErFzyIKjreYTlbZKgaHrRlzoHotweXeAhEhIxPBTDBhQYgEYhNbybINhxY1sbSwwkbHOZrzs3VRmjmnOk6bOkrqxMHT9yCqyxNybAFMg1mWRBGYlg2eboOSFGdT+IwM3Eil6zBwXZF1xkSWkOa6qtHleceuQvC+g4pZXiaOt9jyDb24Q2qKC5XTSvrlK3ZHMW04nLdMPKtQOMzoGTzPz69YNP2vi7D11ftvQWYCzyt7jfwO10TQgL5hT6QAAAABJRU5ErkJggg==\">\n <script type=\"text/javascript\">\nfunction gId(e){return d.getElementById(e)}function cE(e){return d.createElement(e)}var d=document\n</script> </head> <body> <body> <div class=\"top-part\"> <div \nstyle=\"display:flex;justify-content:center\"> <h1 \nstyle=\"display:flex;align-items:center\"> <svg \nstyle=\"width:36px;height:36px;margin-right:6px\" viewBox=\"0 0 32 32\"><path \nfill=\"#003fff\" d=\"M6 22h8v4H6zM14 14h4v8h-4zM18 10h4v8h-4zM22 6h8v4h-8z\"/></svg>\n WLED Pixel Art Converter </h1> </div> <h2>\nConvert image to WLED JSON (pixel art on WLED matrix)</h2> <p> <table \nid=\"fieldTable\" style=\"width:100%;table-layout:fixed;align-content:center\"> <tr>\n <td style=\"vertical-align:middle\"> <label for=\"ledSetupSelector\">Led setup:\n</label> </td> <td class=\"adaptiveTD\"> <select id=\"ledSetupSelector\" \nclass=\"mainSelector\"> <option value=\"matrix\" selected=\"selected\">2D Matrix\n</option> <option value=\"r2l\">Serpentine, first row right to left &lt;-</option>\n <option value=\"l2r\">Serpentine, first row left to right -&gt;</option> \n</select> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label \nfor=\"formatSelector\">Output format:</label> </td> <td class=\"adaptiveTD\"> \n<select id=\"formatSelector\" class=\"mainSelector\"> <option value=\"wled\" \nselected=\"selected\">WLED JSON</option> <option value=\"curl\">CURL</option> \n<option value=\"ha\">Home Assistant YAML</option> </select> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"colorFormatSelector\">\nColor code format:</label> </td> <td class=\"adaptiveTD\"> <select \nid=\"colorFormatSelector\" class=\"mainSelector\"> <option value=\"hex\" \nselected=\"selected\">HEX (&quot;f4f4f4&quot;)</option> <option value=\"dec\">\nDEC (244,244,244)</option> </select> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"addressingSelector\">Addressing:\n</label> </td> <td class=\"adaptiveTD\"> <select id=\"addressingSelector\" \nclass=\"mainSelector\"> <option value=\"hybrid\" selected=\"selected\">\nHybrid (&quot;f0f0f0&quot;,10, 17, &quot;f4f4f4&quot;)</option> <option \nvalue=\"range\">Range (10, 17, &quot;f4f4f4&quot;)</option> <option \nvalue=\"single\">Single (&quot;f4f4f4&quot;)</option> </select> </td> </tr> <tr> \n<td style=\"vertical-align:middle\"> <label for=\"brightnessNumber\">Brightness:\n</label> </td> <td \nstyle=\"vertical-align:middle;display:flex;align-items:center\"> <input \ntype=\"range\" id=\"brightnessNumber\" min=\"1\" max=\"255\" value=\"128\"> <span \nid=\"brightnessValue\">128</span> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"colorLimitNumber\">\nMax no of colors/JSON:</label> </td> <td \nstyle=\"vertical-align:middle;display:flex;align-items:center\"> <input \ntype=\"range\" id=\"colorLimitNumber\" min=\"1\" max=\"512\" value=\"256\"> <span \nid=\"colorLimitValue\">256</span> </td> </tr> <tr class=\"ha-hide\"> <td \nstyle=\"vertical-align:middle\"> <label for=\"haID\">HA Device ID:</label> </td> <td\n class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" id=\"haID\" \nvalue=\"pixel_art_controller_001\"> </td> </tr> <tr class=\"ha-hide\"> <td \nstyle=\"vertical-align:middle\"> <label for=\"haUID\">HA Device Unique ID:</label> \n</td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" \nid=\"haUID\" value=\"pixel_art_controller_001a\"> </td> </tr> <tr class=\"ha-hide\"> \n<td style=\"vertical-align:middle\"> <label for=\"haName\">HA Device Name:</label> \n</td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" \nid=\"haName\" value=\"Pixel Art Kitchen\"> </td> </tr> <tr> <td \nstyle=\"vertical-align:middle\"> <label for=\"curlUrl\">Device IP/host name:</label>\n </td> <td class=\"adaptiveTD\"> <input class=\"fullTextField\" type=\"text\" \nid=\"curlUrl\" value> </td> </tr> <tr> <td style=\"vertical-align:middle\"> <label \nfor=\"targetSegment\">Target segment id:</label> </td> <td class=\"adaptiveTD\"> \n<input class=\"flxTFld\" type=\"number\" id=\"segID\" value=\"0\" min=\"0\" max=\"63\"> \n<select id=\"targetSegment\" class=\"adaptiveSelector\"> </select> <div \nid=\"getSegmentsDiv\" class=\"segmentsDiv\"></div> </td> </tr> </table> <table \nclass=\"scaleTableClass\" id=\"scaleTable\" \nstyle=\"width:100%;table-layout:fixed;align-content:center\"> <tr> <td \nstyle=\"vertical-align:middle\"> <div id=\"scaleDiv\"> <svg \nstyle=\"width:36px;height:36px\" viewBox=\"0 0 24 24\" onclick=\"switchScale()\" \ncursor=\"pointer\"><path fill=\"currentColor\" \nd=\"M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5M7 15a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3z\"/>\n</svg> &nbsp;Scale image </div> </td> <td style=\"vertical-align:middle\"> <div \nid=\"sizeDiv\" style=\"display:none\"> <label for=\"sizeX\">W : </label> &nbsp;<input \nclass=\"sizeInputFields\" type=\"number\" id=\"sizeX\" min=\"1\" value=\"16\">\n &nbsp;&nbsp;&nbsp; <label for=\"sizeY\">H : </label> &nbsp;<input \nclass=\"sizeInputFields\" type=\"number\" id=\"sizeY\" min=\"1\" value=\"16\"> </div> \n</td> </tr> </table> </p> <p> <label for=\"file-picker\"> <div id=\"drop-zone\">\n Drop image here <br>or <br> Click to select a file </div> </label> </p> <p> \n<input type=\"file\" id=\"file-picker\" style=\"display:none\"> </p><div \nstyle=\"width:100%;text-align:center\"> <img id=\"preview\" \nstyle=\"display:none;margin:0 auto\" src=\"data:text/html;base64,\"> </div> <div \nid=\"raw-image-container\" style=\"display:none\"> <img id=\"image\" \nsrc=\"data:text/html;base64,\" alt=\"RawImage image\"> </div> <p></p> <div \nid=\"image-container\" style=\"display:none\"> <div id=\"image-info\" \nstyle=\"display:none\"></div> <textarea id=\"JSONled\" readonly=\"readonly\">\n</textarea> </div> <div id=\"button-container\" style=\"display:none\"> <button \nid=\"copyJSONledbutton\" class=\"buttonclass\"></button> <div id=\"gap1\" class=\"gap\">\n</div> <button id=\"sendJSONledbutton\" class=\"buttonclass\"></button> </div> <div>\n <h3><div id=\"version\">Version 1.0.8</div>&nbsp;-&nbsp; <a \nhref=\"https://github.com/werkstrom/WLED-PixelArtConverter/blob/main/README.md\" \ntarget=\"_blank\">Help/About</a></h3> </div> </div> <div id=\"bottom-part\" \nstyle=\"display:none\" class=\"bottom-part\"></div> <canvas id=\"pixelCanvas\">\n</canvas> <script type=\"text/javascript\">\nfor(var gurl=gId(\"curlUrl\"),szX=gId(\"sizeX\"),szY=gId(\"sizeY\"),szDiv=gId(\"sizeDiv\"),prw=gId(\"preview\"),sID=gId(\"segID\"),JLD=gId(\"JSONled\"),tSg=gId(\"targetSegment\"),brgh=gId(\"brightnessNumber\"),seDiv=gId(\"getSegmentsDiv\"),cjb=gId(\"copyJSONledbutton\"),frm=gId(\"formatSelector\"),cLN=gId(\"colorLimitNumber\"),haIDe=gId(\"haID\"),haUe=gId(\"haUID\"),haNe=gId(\"haName\"),aS=gId(\"addressingSelector\"),cFS=gId(\"colorFormatSelector\"),lSS=gId(\"ledSetupSelector\"),imin=gId(\"image-info\"),imcn=gId(\"image-container\"),bcn=gId(\"button-container\"),im=gId(\"image\"),scDiv=gId(\"scaleDiv\"),w=window,canvas=gId(\"pixelCanvas\"),brgV=gId(\"brightnessValue\"),cLV=gId(\"colorLimitValue\"),httpArray=[],fileJSON=\"\",hideableRows=d.querySelectorAll(\".ha-hide\"),i=0;i<hideableRows.length;i++)hideableRows[i].classList.add(\"hide\");var accentColor=\"#eee\",accentTextColor=\"#777\",prsCol=\"#ccc\",greenColor=\"#056b0a\",redColor=\"#6b050c\",scaleToggleOffd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z\",scaleToggleOnd=\"M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z\",sSg=gId(\"getSegmentsSVGpath\")\n</script> <script type=\"text/javascript\">\nfunction getPixelRGBValues(e){httpArray=[],fileJSON=`{\"on\":true,\"bri\":${brgh.value},\"seg\":{\"id\":${tSg.value},\"i\":[`;let t=0;t=\"flex\"==tSg.style.display?tSg.value:sID.value;const l=parseInt(cLN.value);let n=!1,a=-1;a=frm.selectedIndex;const i=frm.options[a].value;a=lSS.selectedIndex;const o=lSS.options[a].value;a=cFS.selectedIndex;let s=!0;\"dec\"==cFS.options[a].value&&(s=!1),a=aS.selectedIndex;let r=!0;\"single\"==aS.options[a].value?r=!1:\"hybrid\"==aS.options[a].value&&(n=!0);let u=\"\",d=\"\",c='\"',h='\"';s||(c=\"[\",h=\"]\");let p=!1,f=\"\";var g=cE(\"canvas\"),m=g.getContext(\"2d\",{willReadFrequently:!0}),v=new Image;v.src=e,v.onload=function(){let e=scDiv.children[0].children[0].getAttribute(\"fill\"),a=szX.value,y=szY.value;(e!=accentColor||a<1||y<1)&&(a=v.width,y=v.height,(v.width>512||v.height>512)&&(a=16,y=16)),g.width=a,g.height=y,f=\"<p>Width: \"+a+\", Height: \"+y+\" (make sure this matches your led matrix setup)</p>\",m.drawImage(v,0,0,a,y);var S=m.getImageData(0,0,a,y).data,I=[];let $=1;\"l2r\"==o&&($=0);for(var w=0;w<S.length;w+=4){var b=S[w],x=S[w+1],N=S[w+2],T=S[w+3];let e=w/4,t=Math.floor(e/a),l=e;if(\"matrix\"==o);else if((t+$)%2==0);else{l=t*a+(a-1-(l-t*a))}I.push([b,x,N,T,l,e,t])}I.sort((e,t)=>e[5]-t[5]);let O=[...I];O.sort((e,t)=>e[4]-t[4]);let A=\"\",D=-1,R=O.length,j=0,C=[];for(let e=0;e<R;e++){let t=O[e],a=t[0],i=t[1],o=t[2],u=t[3],d=\"\",f=-1;if(r)if(D<0&&(D=e),e<R-1){let t=O[e+1];t[0]==a&&t[1]==i&&t[2]==o||(f=e+1,d=D==e&&n?\"\"==A?e+\",\":\"\":D+\",\"+f+\",\")}else f=e+1,d=D+1==f&&n?\"\"==A?e+\",\":\"\":D+\",\"+f+\",\";else\"\"==A&&(A=e),D=e,f=e;if(u<255&&(p=!0),f>-1){let t=a+\",\"+i+\",\"+o;if(s){const[e,l,n]=[a,i,o];t=\"\"+[e,l,n].map(e=>e.toString(16).padStart(2,\"0\")).join(\"\")}A+=d+c+t+h,fileJSON=A+d+c+t+h,j+=1,j%l==0||e==R-1?(C.push(A),A=\"\"):A+=\",\",D=-1}}A=\"\";for(let e=0;e<C.length;e++){let l=`{\"on\":true,\"bri\":${brgh.value},\"seg\":{\"id\":${t},\"i\":[${C[e]}]}}`;httpArray.push(l);let n=`curl -X POST \"http://${gurl.value}/json/state\" -d '${l}' -H \"Content-Type: application/json\"`;e>0&&(A+=\"\\n<NEXT COMMAND (multiple commands not supported in API/preset setup)>\\n\",u+=\" && \"),A+=l,u+=n}d=`#Uncomment if you don't allready have these defined in your switch section of your configuration.yaml \\n#- platform: command_line \\n  #switches: \\n    ${haIDe.value} \\n      friendly_name: ${haNe.value} \\n      unique_id: ${haUe.value} \\n      command_on: > \\n        ${u} \\n      command_off: > \\n        curl -X POST \"http://${gurl.value}/json/state\" -d '{\"on\":false}' -H \"Content-Type: application/json\"`,JLD.value=\"wled\"==i?A:\"curl\"==i?u:\"ha\"==i?d:\"ERROR!/n\"+i+\" is an unknown format.\",fileJSON+=\"]}}\";let _=imin,H=imin;p&&(f+=\"<p><b>WARNING!</b> 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.</p>\"),_.innerHTML=f,H.style.display=\"block\",drawBoxes(I,a,y)}}\n</script> <script type=\"text/javascript\">\nfunction drawBoxes(t,e,a){var i=window,n=canvas.getContext(\"2d\",{willReadFrequently:!0});i.innerHeight<i.innerWidth?canvas.width=Math.floor(.98*i.innerHeight):canvas.width=Math.floor(.98*i.innerWidth);let l=Math.floor(canvas.width/e),h=(i.innerWidth-e*l)/2;canvas.height=l*a+10;for(let i=0;i<a;i++)for(let a=0;a<e;a++){let h=t[i*e+a],r=\"rgb(\"+h[0]+\", \"+h[1]+\", \"+h[2]+\")\",d=\"rgb(128,128,128)\";n.fillStyle=r,n.fillRect(a*l,i*l,l,l),n.strokeStyle=\"#888888\",n.lineWidth=1,n.strokeRect(a*l,i*l,l,l),n.font=\"10px Arial\",n.fillStyle=d,n.textAlign=\"center\",n.textBaseline=\"middle\",n.fillText(h[4]+1,a*l+l/2,i*l+l/2)}var r=n.getImageData(0,0,canvas.width,canvas.height);n.clearRect(0,0,canvas.width,canvas.height),canvas.width=i.innerWidth,n.putImageData(r,h,0)}\n</script> <script type=\"text/javascript\">\nlet devMode=!1;gurl.value=location.host;const urlParams=new URLSearchParams(window.location.search);function gen(){if((szX.value>0&&szY.value>0||\"none\"==szDiv.style.display)&&gurl.value.length>0&&\"none\"!=prw.style.display){let e=prw.src;if(isValidBase64Gif(e))im.src=e,getPixelRGBValues(e),imcn.style.display=\"block\",bcn.style.display=\"\";else{let t=\"<p><b>WARNING!</b> File does not appear to be a valid image</p>\";imin.innerHTML=t,imin.style.display=\"block\",imcn.style.display=\"none\",JLD.value=\"\",devMode&&console.log(\"The string '\"+e+\"' is not a valid base64 image.\")}}if(gurl.value.length>0)gId(\"sSg\").setAttribute(\"fill\",accentColor);else{gId(\"sSg\").setAttribute(\"fill\",accentTextColor);let e=tSg;e.style.display=\"none\",e.innerHTML=\"\",sID.style.display=\"flex\"}}async function postPixels(){let e=gId(\"sendSvgP\");e.setAttribute(\"fill\",prsCol);let t=!1;for(let e of httpArray)try{devMode&&console.log(e),devMode&&console.log(e.length);const t=await fetch(\"http://\"+gId(\"curlUrl\").value+\"/json/state\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:e}),n=await t.json();devMode&&console.log(n)}catch(e){console.error(e),t=!0}t?(e.setAttribute(\"fill\",redColor),setTimeout((function(){e.setAttribute(\"fill\",accentTextColor)}),1e3)):(e.setAttribute(\"fill\",greenColor),setTimeout((function(){e.setAttribute(\"fill\",accentColor)}),1e3))}gurl.value.length<1&&(gurl.value=\"Missing_Host\"),cjb.addEventListener(\"click\",async()=>{let e=JLD;e.select();try{await navigator.clipboard.writeText(e.value)}catch(e){try{await d.execCommand(\"copy\")}catch(e){console.error(\"Failed to copy text: \",e)}}}),lSS.addEventListener(\"change\",gen),szY.addEventListener(\"change\",gen),szX.addEventListener(\"change\",gen),cFS.addEventListener(\"change\",gen),aS.addEventListener(\"change\",gen),brgh.addEventListener(\"change\",gen),cLN.addEventListener(\"change\",gen),haIDe.addEventListener(\"change\",gen),haUe.addEventListener(\"change\",gen),haNe.addEventListener(\"change\",gen),gurl.addEventListener(\"change\",gen),sID.addEventListener(\"change\",gen),prw.addEventListener(\"load\",gen),tSg.addEventListener(\"change\",()=>{sop=tSg.options[tSg.selectedIndex],szX.value=sop.dataset.x,szY.value=sop.dataset.y,gen()}),gId(\"sendJSONledbutton\").addEventListener(\"click\",async()=>{\"https:\"===window.location.protocol?alert(\"Will only be available when served over http (or WLED is run over https)\"):postPixels()}),brgh.oninput=()=>{brgV.textContent=brgh.value;let e=100*parseInt(brgh.value)/255;var t=`linear-gradient(90deg, #bbb ${e}%, #333 ${e}%)`;brgh.style.backgroundImage=t},cLN.oninput=()=>{let e=cLN;cLV.textContent=e.value;let t=100*parseInt(e.value)/512;var n=`linear-gradient(90deg, #bbb ${t}%, #333 ${t}%)`;e.style.backgroundImage=n},frm.addEventListener(\"change\",()=>{for(var e=0;e<hideableRows.length;e++)hideableRows[e].classList.toggle(\"hide\",\"ha\"!==frm.value),gen()});const dropZone=gId(\"drop-zone\"),filePicker=gId(\"file-picker\"),preview=prw;function zoneClicked(e){e.preventDefault(),filePicker.click()}function dragEnter(e){e.preventDefault(),this.classList.add(\"drag-over\")}function dragOver(e){e.preventDefault()}function dropped(e){e.preventDefault(),this.classList.remove(\"drag-over\");updatePreview(e.dataTransfer.files[0])}function filePicked(e){updatePreview(e.target.files[0])}function updatePreview(e){const t=new FileReader;t.onload=()=>{preview.src=t.result,prw.style.display=\"\"},t.readAsDataURL(e)}function isValidBase64Gif(e){return!0}dropZone.addEventListener(\"dragenter\",dragEnter),dropZone.addEventListener(\"dragover\",dragOver),dropZone.addEventListener(\"drop\",dropped),dropZone.addEventListener(\"click\",zoneClicked),filePicker.addEventListener(\"change\",filePicked);for(var hideableRows=d.querySelectorAll(\".ha-hide\"),i=0;i<hideableRows.length;i++)hideableRows[i].classList.add(\"hide\");function switchScale(){let e=scDiv.children[0].children[0],t=e.getAttribute(\"fill\"),n=\"\";t===accentColor?(t=accentTextColor,n=scaleToggleOffd,szDiv.style.display=\"none\"):(t=accentColor,n=scaleToggleOnd,szDiv.style.display=\"\"),e.setAttribute(\"fill\",t),e.setAttribute(\"d\",n),gen()}function generateSegmentOptions(e){tSg.innerHTML=\"\";for(var t=0;t<e.length;t++){var n=cE(\"option\");n.value=e[t].value,n.text=e[t].text,n.dataset.x=e[t].x,n.dataset.y=e[t].y,tSg.appendChild(n),0===t&&(n.selected=!0,szX.value=n.dataset.x,szY.value=n.dataset.y)}}async function getSegments(){if(cv=gurl.value,cv.length>0)try{var e=[];const n=await fetch(\"http://\"+cv+\"/json/state\");let l=(await n.json()).seg.map(e=>({id:e.id,n:e.n,xs:e.start,xe:e.stop,ys:e.startY,ye:e.stopY}));for(var t=0;t<l.length;t++)e.push({value:l[t].id,text:l[t].n+\" (index: \"+l[t].id+\")\",x:l[t].xe-l[t].xs,y:l[t].ye-l[t].ys});generateSegmentOptions(e),tSg.style.display=\"flex\",sID.style.display=\"none\",gId(\"sSg\").setAttribute(\"fill\",greenColor),setTimeout((function(){gId(\"sSg\").setAttribute(\"fill\",accentColor)}),1e3)}catch(e){console.error(e),gId(\"sSg\").setAttribute(\"fill\",redColor),setTimeout((function(){gId(\"sSg\").setAttribute(\"fill\",accentColor)}),1e3),tSg.style.display=\"none\",sID.style.display=\"flex\"}else gId(\"sSg\").setAttribute(\"fill\",redColor),setTimeout((function(){gId(\"sSg\").setAttribute(\"fill\",accentTextColor)}),1e3),tSg.style.display=\"none\",sID.style.display=\"flex\"}function generateSegmentArray(e){for(var t=[],n=0;n<e;n++)t.push({value:n,text:\"Segment index \"+n});return t}frm.addEventListener(\"change\",()=>{for(var e=0;e<hideableRows.length;e++)hideableRows[e].classList.toggle(\"hide\",\"ha\"!==frm.value)});var segmentData=generateSegmentArray(10);generateSegmentOptions(segmentData),seDiv.innerHTML='<svg id=getSegmentsSVG style=\"width:36px;height:36px;cursor:pointer\" viewBox=\"0 0 24 24\" onclick=\"getSegments()\"><path id=sSg fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.68 7.35 7.38 5.73 9.07 4.1 11 4.1 11.83 4.1 12.41 4.69 13 5.28 13 6.1V12.15L14.6 10.6L16 12L12 16L8 12L9.4 10.6L11 12.15V6.1Q9.1 6.45 8.05 7.94 7 9.43 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 9.8 16.45 8.76 15.9 7.73 15 7V4.68Q16.85 5.55 17.93 7.26 19 9 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20M12 11.05Z\" /></svg>',cjb.innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\" /> </svg>&nbsp; Copy to clipboard',gId(\"sendJSONledbutton\").innerHTML='<svg class=\"svg-icon\" style=\"width:36px;height:36px\" viewBox=\"0 0 24 24\"> <path id=sendSvgP fill=\"currentColor\" d=\"M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z\" /> </svg>&nbsp; Send to device',gurl.value.length>0&&gId(\"sSg\").setAttribute(\"fill\",accentColor)\n</script> </body> </body></html>"
  }
]