"unrestricted" number of custom palettes (#4932)

- allow more than 10 custom palettes
- move palettes into CPP file
- Fix for minimizing cpal.htm (saves 2k of flash)
- shortened names in cpal, saves about 400 bytes of Flash after packing
- removed async from common.js loading to prevent errors on page loads if the file is not cached
- restricted nubmer of user palettes on ESP8266 to 10
- unrestricted number of user palettes on all other platforms (total max palettes: 256)
- added a warning when adding more than 10 palettes to let the user decide to risk it
- Bugfixes in palette enumeration, fixed AR palette adding
- AR palettes are now also added if there are more than 10 custom palettes

Co-authored-by: Blaž Kristan <blaz@kristan-sp.si>
This commit is contained in:
Damian Schneider
2025-09-22 20:09:54 +02:00
committed by GitHub
parent 4b1b0fe045
commit 529edfc39b
24 changed files with 389 additions and 469 deletions

View File

@@ -143,7 +143,7 @@ async function writeHtmlGzipped(sourceFile, resultFile, page) {
console.info("Minified and compressed " + sourceFile + " from " + originalLength + " to " + result.length + " bytes");
const array = hexdump(result);
let src = singleHeader;
src += `const uint16_t PAGE_${page}_L = ${result.length};\n`;
src += `const uint16_t PAGE_${page}_length = ${result.length};\n`;
src += `const uint8_t PAGE_${page}[] PROGMEM = {\n${array}\n};\n\n`;
console.info("Writing " + resultFile);
fs.writeFileSync(resultFile, src);
@@ -244,9 +244,22 @@ if (isAlreadyBuilt("wled00/data") && process.argv[2] !== '--force' && process.ar
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal');
//writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal');
writeHtmlGzipped("wled00/data/pxmagic/pxmagic.htm", "wled00/html_pxmagic.h", 'pxmagic');
writeChunks(
"wled00/data/cpal",
[
{
file: "cpal.htm",
name: "PAGE_cpal",
method: "gzip",
filter: "html-minify"
}
],
"wled00/html_cpal.h"
);
writeChunks(
"wled00/data",
[

View File

@@ -1981,7 +1981,7 @@ void AudioReactive::createAudioPalettes(void) {
if (palettes) return;
DEBUG_PRINTLN(F("Adding audio palettes."));
for (int i=0; i<MAX_PALETTES; i++)
if (customPalettes.size() < 10) {
if (customPalettes.size() < WLED_MAX_CUSTOM_PALETTES) {
customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
palettes++;
DEBUG_PRINTLN(palettes);

View File

@@ -8,7 +8,6 @@
Parts of the code adapted from WLED Sound Reactive
*/
#include "wled.h"
#include "palettes.h"
// setUpMatrix() - constructs ledmap array from matrix of panels with WxH pixels
// this converts physical (possibly irregular) LED arrangement into well defined

View File

@@ -11,7 +11,6 @@
*/
#include "wled.h"
#include "FXparticleSystem.h" // TODO: better define the required function (mem service) in FX.h?
#include "palettes.h"
/*
Custom per-LED mapping has moved!
@@ -226,8 +225,12 @@ void Segment::resetIfRequired() {
}
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0;
if (pal > 245 && (customPalettes.size() == 0 || 255U-pal > customPalettes.size()-1)) pal = 0;
// there is one randomy generated palette (1) followed by 4 palettes created from segment colors (2-5)
// those are followed by 7 fastled palettes (6-12) and 59 gradient palettes (13-71)
// then come the custom palettes (255,254,...) growing downwards from 255 (255 being 1st custom palette)
// palette 0 is a varying palette depending on effect and may be replaced by segment's color if so
// instructed in color_from_palette()
if (pal > FIXED_PALETTE_COUNT && pal <= 255-customPalettes.size()) pal = 0; // out of bounds palette
//default palette. Differs depending on effect
if (pal == 0) pal = _default_palette; // _default_palette is set in setMode()
switch (pal) {
@@ -263,13 +266,13 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
}
break;}
default: //progmem palettes
if (pal>245) {
if (pal > 255 - customPalettes.size()) {
targetPalette = customPalettes[255-pal]; // we checked bounds above
} else if (pal < 13) { // palette 6 - 12, fastled palettes
targetPalette = *fastledPalettes[pal-6];
} else if (pal < DYNAMIC_PALETTE_COUNT+FASTLED_PALETTE_COUNT+1) { // palette 6 - 12, fastled palettes
targetPalette = *fastledPalettes[pal-DYNAMIC_PALETTE_COUNT-1];
} else {
byte tcp[72];
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72);
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-(DYNAMIC_PALETTE_COUNT+FASTLED_PALETTE_COUNT)-1])), 72);
targetPalette.loadDynamicGradientPalette(tcp);
}
break;
@@ -573,8 +576,7 @@ Segment &Segment::setMode(uint8_t fx, bool loadDefaults) {
}
Segment &Segment::setPalette(uint8_t pal) {
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; // built in palettes
if (pal > 245 && (customPalettes.size() == 0 || 255U-pal > customPalettes.size()-1)) pal = 0; // custom palettes
if (pal <= 255-customPalettes.size() && pal > FIXED_PALETTE_COUNT) pal = 0; // not built in palette or custom palette
if (pal != palette) {
//DEBUG_PRINTF_P(PSTR("- Starting palette transition: %d\n"), pal);
startTransition(strip.getTransition(), blendingStyle != BLEND_STYLE_FADE); // start transition prior to change (no need to copy segment)

View File

@@ -252,7 +252,7 @@ void loadCustomPalettes() {
byte tcp[72]; //support gradient palettes with up to 18 entries
CRGBPalette16 targetPalette;
customPalettes.clear(); // start fresh
for (int index = 0; index<10; index++) {
for (int index = 0; index < WLED_MAX_CUSTOM_PALETTES; index++) {
char fileName[32];
sprintf_P(fileName, PSTR("/palette%d.json"), index);

View File

@@ -123,7 +123,7 @@ CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette);
CRGBPalette16 generateRandomPalette();
void loadCustomPalettes();
extern std::vector<CRGBPalette16> customPalettes;
inline size_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); }
inline size_t getPaletteCount() { return FIXED_PALETTE_COUNT + customPalettes.size(); }
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
void hsv2rgb(const CHSV32& hsv, uint32_t& rgb);
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb);
@@ -141,4 +141,8 @@ void setRandomColor(byte* rgb);
[[gnu::hot, gnu::pure]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video = false);
// palettes
extern const TProgmemRGBPalette16* const fastledPalettes[];
extern const uint8_t* const gGradientPalettes[];
#endif

View File

@@ -6,7 +6,15 @@
* Readability defines and their associated numerical values + compile-time constants
*/
#define GRADIENT_PALETTE_COUNT 59
constexpr size_t FASTLED_PALETTE_COUNT = 7; // = sizeof(fastledPalettes) / sizeof(fastledPalettes[0]);
constexpr size_t GRADIENT_PALETTE_COUNT = 59; // = sizeof(gGradientPalettes) / sizeof(gGradientPalettes[0]);
constexpr size_t DYNAMIC_PALETTE_COUNT = 5; // 1-5 are dynamic palettes (1=random,2=primary,3=primary+secondary,4=primary+secondary+tertiary,5=primary+secondary(+tertiary if not black)
constexpr size_t FIXED_PALETTE_COUNT = DYNAMIC_PALETTE_COUNT + FASTLED_PALETTE_COUNT + GRADIENT_PALETTE_COUNT; // total number of fixed palettes
#ifndef ESP8266
#define WLED_MAX_CUSTOM_PALETTES (255 - FIXED_PALETTE_COUNT) // allow up to 255 total palettes, user is warned about stability issues when adding more than 10
#else
#define WLED_MAX_CUSTOM_PALETTES 10 // ESP8266: limit custom palettes to 10
#endif
// You can define custom product info from build flags.
// This is useful to allow API consumer to identify what type of WLED version

View File

@@ -11,6 +11,7 @@
function gId(e) {return d.getElementById(e);}
function cE(e) {return d.createElement(e);}
</script>
<script src="common.js" type="text/javascript"></script>
<style>
body {
@@ -21,39 +22,39 @@
margin: 0 10px;
line-height: 0.5;
}
#parent-container {
#pCont {
position: relative;
width: 100%;
height: 20px;
}
#bottomContainer {
#bCont {
position: absolute;
margin-top: 50px;
}
#gradient-box {
#gBox {
width: 100%;
height: 100%;
}
.color-marker, .color-picker-marker {
.cMark, .cPickMark {
position: absolute;
border-radius: 3px;
background-color: rgb(192, 192, 192);
border: 2px solid rgba(68, 68, 68, 0.5);
z-index: 2;
}
.color-marker {
.cMark {
height: 30px;
width: 7px;
top: 50%;
transform: translateY(-50%);
touch-action: none;
}
.color-picker-marker {
.cPickMark {
height: 7px;
width: 7px;
top: 150%;
}
.delete-marker {
.dMark {
position: absolute;
height: 5px;
width: 5px;
@@ -63,7 +64,7 @@
top: 220%;
z-index: 2;
}
.color-picker {
.cPick {
position: absolute;
height: 1px;
width: 1px;
@@ -73,13 +74,13 @@
border-color: #111;
background-color: #111;
}
.buttonclass {
.btnCls {
padding: 0;
margin: 0;
vertical-align: bottom;
background-color: #111;
}
#bottomContainer span {
#bCont span {
display: inline-flex;
align-items: center;
color: #fff;
@@ -87,7 +88,6 @@
vertical-align: middle;
}
#info {
display: "";
text-align: center;
color: #fff;
font-size: 12px;
@@ -104,119 +104,76 @@
width: 800px;
}
}
.palette {
height: 20px;
}
.paletteGradients {
flex: 1;
height: 20px;
border-radius: 3px;
}
.palettesMain {
margin-top: 50px;
width: 100%;
}
.palTop {
height: fit-content;
text-align: center;
color: #fff;
font-size: 12px;
position: relative;
line-height: 1;
}
.palGradientParent {
display: flex;
align-items: center;
height: fit-content;
margin-top: 10px;
text-align: center;
color: #fff;
font-size: 12px;
position: relative;
line-height: 1;
}
.buttonsDiv {
display: inline-flex;
margin-left: 5px;
width: 50px;
}
.sendSpan, .editSpan{
cursor: pointer;
}
h1 {
font-size: 1.6rem;
}
.pal {height: 20px;}
.pGrads {flex: 1; height: 20px; border-radius: 3px;}
.pMain {margin-top: 50px; width: 100%;}
.pTop {height: fit-content; text-align: center; color: #fff; font-size: 14px; line-height: 1;}
.pGradPar {display: flex; align-items: center; height: fit-content; margin-top: 10px; text-align: center; color: #fff; font-size: 12px; line-height: 1;}
.btnsDiv {display: inline-flex; margin-left: 5px; width: 50px;}
.sSpan, .eSpan {cursor: pointer;}
h1 {font-size: 1.6rem;}
</style>
</head>
<body>
<div id="wrap" class="wrap">
<div style="display: flex; justify-content: center;">
<h1 style="display: flex; align-items: center;">
<svg style="width:36px;height:36px;margin-right:6px;" viewBox="0 0 32 32">
<rect style="fill:#003FFF" x="6" y="22" width="8" height="4"/>
<rect style="fill:#003FFF" x="14" y="14" width="4" height="8"/>
<rect style="fill:#003FFF" x="18" y="10" width="4" height="8"/>
<rect style="fill:#003FFF" x="22" y="6" width="8" height="4"/>
<svg style="width: 36px; height: 36px; margin-right: 6px;" viewBox="0 0 32 32">
<rect style="fill: #03F" x="6" y="22" width="8" height="4"/>
<rect style="fill: #03F" x="14" y="14" width="4" height="8"/>
<rect style="fill: #03F" x="18" y="10" width="4" height="8"/>
<rect style="fill: #03F" x="22" y="6" width="8" height="4"/>
</svg>
<span id="head">WLED Custom Palette Editor</span>
<span id="head">WLED Palette Editor</span>
</h1>
</div>
<div id="parent-container">
<div id="gradient-box"></div>
</div>
<div id="pCont"><div id="gBox"></div></div>
<div style="display: flex; justify-content: center;">
<div id="palettes" class="palettesMain">
<div id="distDiv" class="palTop"></div>
<div id="palTop" class="palTop">
Currently in use custom palettes
</div>
<div id="pals" class="pMain">
<div id="distDiv" class="pTop"></div>
<div id="memWarn" class="pTop" style="display:none; color:#ff6600; margin-bottom:8px; font-size:16px;">
Warning: Adding many custom palettes might cause stability issues, create <a href="/settings/sec#backup" style="color:#ff9900">backups</a> before proceeding.</div>
<div id="pTop" class="pTop">Custom palettes</div>
</div>
</div>
<div style="display: flex; justify-content: center;">
<div id="info">
Click on the gradient editor to add new color slider, then the colored box below the slider to change its color.
Click the red box below indicator (and confirm) to delete.
Once finished, click the arrow icon to upload into the desired slot.
To edit existing palette, click the pencil icon.
</div>
<div id="info">Click gradient to add. Box = color. Red = delete. Arrow = upload. Pencil = edit.</div>
</div>
<div style="display: flex; justify-content: center;">
<div id="staticPalettes" class="palettesMain">
<div id="statpalTop" class="palTop">
Available static palettes
</div>
<div id="sPals" class="pMain">
<div id="spTop" class="pTop">Static palettes</div>
</div>
</div>
</body>
<script type="text/javascript">
//global variables
var gradientBox = gId('gradient-box');
var cpalc = -1;
var pxCol = {};
var tCol = {};
var rect = gradientBox.getBoundingClientRect();
var gradientLength = rect.width;
var mOffs = Math.round((gradientLength / 256) / 2) - 5;
var paletteArray = []; //Holds the palettes after load.
var paletteName = []; // Holds the names of the palettes after load.
// global vars
var gBox = gId('gBox'); // gradientBox
var cpalc = -1, cpalm = 10; // current palette count, max custom
var pxCol = {}; // pixel color map
var tCol = {}; // true color map
var rect = gBox.getBoundingClientRect(); // bounding rect of gBox
var gLen = rect.width; // gradientLength
var mOffs = Math.round((gLen / 256) / 2) - 5; // marker offset
var palArr = []; // paletteArray
var palNm = []; // paletteName
var svgSave = '<svg style="width:25px;height:25px" viewBox="0 0 24 24"><path fill=#fff d="M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M7,12L12,17V14H16V10H12V7L7,12Z"/></svg>'
var svgEdit = '<svg style="width:25px;height:25px" viewBox="0 0 24 24"><path fill=#fff d="M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2M15.1,7.07C15.24,7.07 15.38,7.12 15.5,7.23L16.77,8.5C17,8.72 17,9.07 16.77,9.28L15.77,10.28L13.72,8.23L14.72,7.23C14.82,7.12 14.96,7.07 15.1,7.07M13.13,8.81L15.19,10.87L9.13,16.93H7.07V14.87L13.13,8.81Z"/></svg>'
var svgDist = '<svg style="width:25px;height:25px" viewBox="0 0 24 24"><path fill=#fff d="M4 22H2V2H4V22M22 2H20V22H22V2M13.5 7H10.5V17H13.5V7Z"/></svg>'
var svgTrash = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="30px" height="30px"><path style="fill:#880000; stroke: #888888; stroke-width: -2px;stroke-dasharray: 0.1, 8;" d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z"/></svg>'
const distDiv = gId("distDiv");
distDiv.addEventListener('click', distribute);
distDiv.setAttribute('title', 'Distribute colors equally');
distDiv.addEventListener('click', distrib);
distDiv.setAttribute('title', 'Distribute equally');
distDiv.innerHTML = svgDist;
function recOf() {
rect = gradientBox.getBoundingClientRect();
gradientLength = rect.width;
mOffs = Math.round((gradientLength / 256) / 2) - 5;
rect = gBox.getBoundingClientRect();
gLen = rect.width;
mOffs = Math.round((gLen / 256) / 2) - 5;
}
//Initiation
@@ -224,277 +181,220 @@
window.addEventListener('load', chkW);
window.addEventListener('resize', chkW);
gradientBox.addEventListener("click", clikOnGradient);
gBox.addEventListener("click", clikGrad);
//Sets start and stop, mandatory
addC(0);
addC(255);
updateGradient(); //Sets the gradient at startup
updGrad(); // updateGradient at startup
function clikOnGradient(e) {
removeTrashcan(e);
addC(Math.round((e.offsetX/gradientLength)*256));
function clikGrad(e) { // clickOnGradient
rmTrash(e); // removeTrashcan
addC(Math.round((e.offsetX/gLen)*256));
}
///////// Add a new colorMarker
function addC(truePos, thisColor = '') {
let position = -1;
let iExist = false;
const colorMarkers = gradientBox.querySelectorAll('.color-marker');
///////// Add a new color marker
function addC(tPos, thisCol = '') {
let pos = -1;
let exist = false;
const cMarks = gBox.querySelectorAll('.cMark'); // color markers
colorMarkers.forEach((colorMarker, i) => {
if (colorMarker.getAttribute("data-truepos") == truePos) {
iExist = true;
}
cMarks.forEach((cm) => {
if (cm.getAttribute("data-tpos") == tPos) exist = true;
});
if (colorMarkers.length > 17) iExist = true;
if (iExist) return; // Exit the function early if iExist is true
if (cMarks.length > 17) exist = true;
if (exist) return;
if (truePos > 0 && truePos < 255) {
//calculate first available > 0
for (var i = 1; i <= 16 && position < 1; i++) {
if (!gId("colorMarker"+i)) {
position = i;
}
if (tPos > 0 && tPos < 255) {
for (var i=1; i<=16 && pos<1; i++) {
if (!gId("cMark"+i)) pos = i;
}
} else{
position = truePos;
} else {
pos = tPos;
}
if (thisColor == ''){
thisColor = `#${(Math.random() * 0xFFFFFF << 0).toString(16).padStart(6, '0')}`;// set random color as default
if (thisCol == '') {
thisCol = `#${(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,'0')}`;
}
const colorMarker = cE('span'); // create a marker for the color position
colorMarker.className = 'color-marker';
colorMarker.id = 'colorMarker' + position.toString();
colorMarker.setAttribute("data-truepos", truePos); //Used to always have a true position no matter what screen or percentage we use
colorMarker.setAttribute("data-truecol", thisColor); //Used to hold the color of the position in the gradient connected to a true position
colorMarker.setAttribute("data-offset", mOffs);
colorMarker.addEventListener('click', stopFurtherProp); //Added to prevent the gradient click to fire when covered by a marker
colorMarker.style.left = `${Math.round((gradientLength / 256) * truePos)+mOffs}px`;
const cMark = cE('span'); // color marker
cMark.className = 'cMark';
cMark.id = 'cMark' + pos;
cMark.setAttribute("data-tpos", tPos);
cMark.setAttribute("data-tcol", thisCol);
cMark.setAttribute("data-offset", mOffs);
cMark.addEventListener('click', stopProp);
cMark.style.left = `${Math.round((gLen/256)*tPos)+mOffs}px`;
const colorPicker = cE('input');
colorPicker.type = 'color';
colorPicker.value = thisColor;
colorPicker.className = 'color-picker';
colorPicker.id = 'colorPicker' + position.toString();
colorPicker.addEventListener('input', updateGradient);
colorPicker.addEventListener('click',cpClk)
const cPick = cE('input'); // colorPicker
cPick.type = 'color';
cPick.value = thisCol;
cPick.className = 'cPick';
cPick.id = 'cPick' + pos;
cPick.addEventListener('input', updGrad);
cPick.addEventListener('click', cpClk);
const colorPickerMarker = cE('span'); // create a marker for the color position
colorPickerMarker.className = 'color-picker-marker';
colorPickerMarker.id = 'colorPickerMarker' + position.toString();
colorPickerMarker.addEventListener('click', colClk);
colorPickerMarker.style.left = colorMarker.style.left;
colorPicker.style.left = colorMarker.style.left;
const cPM = cE('span'); // colorPickerMarker
cPM.className = 'cPickMark';
cPM.id = 'cPM' + pos;
cPM.addEventListener('click', colClk);
cPM.style.left = cMark.style.left;
cPick.style.left = cMark.style.left;
const deleteMarker = cE('span'); // create a delete marker for the color position
if (position > 0 && position < 255) {
deleteMarker.className = 'delete-marker';
deleteMarker.id = 'deleteMarker' + position.toString();
deleteMarker.addEventListener('click', (e) => {
deleteColor(e);
});
deleteMarker.style.left = colorMarker.style.left
if (pos > 0 && pos < 255) {
const dMark = cE('span'); // deleteMarker
dMark.className = 'dMark';
dMark.id = 'dMark' + pos;
dMark.addEventListener('click', (e) => { delCol(e); });
dMark.style.left = cMark.style.left;
gBox.appendChild(dMark);
}
colorMarker.style.backgroundColor = colorPicker.value; // set marker color to match color picker
colorPickerMarker.style.backgroundColor = colorPicker.value;
cMark.style.backgroundColor = cPick.value;
cPM.style.backgroundColor = cPick.value;
gradientBox.appendChild(colorPicker);
gradientBox.appendChild(colorMarker);
gradientBox.appendChild(colorPickerMarker);
if (position != 0 && position != 255) gradientBox.appendChild(deleteMarker); // append the marker if not start or end
//make markers slidable IF they are not the first or last slider
if (position > 0 && position < 255) makeMeDrag(gId(colorMarker.id));
gBox.appendChild(cPick);
gBox.appendChild(cMark);
gBox.appendChild(cPM);
if (pos > 0 && pos < 255) mkDrag(gId(cMark.id)); // makeMeDrag
setTooltipMarker(gId(colorMarker.id));
updateGradient();
setTip(gId(cMark.id)); // setTooltipMarker
updGrad();
}
///////// Update Gradient
function updateGradient() {
const colorMarkers = gradientBox.querySelectorAll('.color-marker');
function updGrad() { // updateGradient
const cMarks = gBox.querySelectorAll('.cMark');
pxCol = {};
tCol = {}
colorMarkers.forEach((colorMarker, index) => {
const thisColorPicker = gId(colorMarkers[index].id.replace('colorMarker', 'colorPicker'));
const colorToSet = thisColorPicker.value;
gId(colorMarkers[index].id.replace('colorMarker', 'colorPickerMarker')).style.backgroundColor = colorToSet;
colorMarkers[index].style.backgroundColor = colorToSet;
colorMarkers[index].setAttribute("data-truecol", colorToSet);
const tPos = colorMarkers[index].getAttribute("data-truepos");
const gradientPos = Math.round((gradientLength / 256)*tPos);
pxCol[gradientPos] = colorToSet;
tCol[tPos] = colorToSet;
tCol = {};
cMarks.forEach((cm) => {
const cp = gId(cm.id.replace('cMark','cPick'));
const col = cp.value;
gId(cm.id.replace('cMark','cPM')).style.backgroundColor = col;
cm.style.backgroundColor = col;
cm.setAttribute("data-tcol", col);
const tPos = cm.getAttribute("data-tpos");
const gPos = Math.round((gLen/256)*tPos);
pxCol[gPos] = col;
tCol[tPos] = col;
});
gradientString = 'linear-gradient(to right';
Object.entries(pxCol).forEach(([p, c]) => {
gradientString += `, ${c} ${p}px`;
let gStr = 'linear-gradient(to right';
Object.entries(pxCol).forEach(([p,c]) => {
gStr += `, ${c} ${p}px`;
});
gradientString += ')';
gradientBox.style.background = gradientString;
//gId("jsonstring").innerHTML = calcJSON();
gStr += ')';
gBox.style.background = gStr;
}
function stopFurtherProp(e) {
e.stopPropagation();
}
function stopProp(e) { e.stopPropagation(); }
function colClk(e){
removeTrashcan(e)
function colClk(e) {
rmTrash(e);
e.stopPropagation();
let cp = gId(e.srcElement.id.replace("Marker",""));
const src = e.target || e.srcElement;
let cp = gId(src.id.replace("M","")); // marker → picker
cp.click();
}
function cpClk(e) {
removeTrashcan(event)
rmTrash(e);
e.stopPropagation();
}
//This neat little function makes any element draggable on the X-axis.
//Just call: makeMeDrag(myElement); And you are good to go.
function makeMeDrag(elmnt) {
var posNew = 0, mousePos = 0, mouseOffset = 0
// make element draggable
function mkDrag(el) { // makeMeDrag
var posNew=0, mPos=0;
var rect=gBox.getBoundingClientRect();
var maxX=rect.right, minX=rect.left, gLen=maxX-minX+1;
//Set these to whatever you want to limit your movement to
var rect = gradientBox.getBoundingClientRect();
var maxX = rect.right; // maximum X coordinate
var minX = rect.left; // minimum X coordinate i.e. also offset from left of screen
var gradientLength = maxX - minX + 1;
el.onmousedown=dragStart;
el.ontouchstart=dragStart;
elmnt.onmousedown = dragMouseDown;
elmnt.ontouchstart = dragMouseDown;
function dragMouseDown(e) {
removeTrashcan(event)
e = e || window.event;
var isTouch = e.type.startsWith('touch');
if (!isTouch) e.preventDefault();
// get the mouse cursor position at startup:
mousePos = isTouch ? e.touches[0].clientX : e.clientX;
d.onmouseup = closeDragElement;
d.ontouchcancel = closeDragElement;
d.ontouchend = closeDragElement;
// call a function whenever the cursor moves:
d.onmousemove = elementDrag;
d.ontouchmove = elementDrag;
function dragStart(e) {
rmTrash(e);
var isT=e.type.startsWith('touch');
if (!isT) e.preventDefault();
mPos=isT?e.touches[0].clientX:e.clientX;
d.onmouseup=dragEnd; d.ontouchend=dragEnd; d.ontouchcancel=dragEnd;
d.onmousemove=dragMove; d.ontouchmove=dragMove;
}
function elementDrag(e) {
e = e || window.event;
var isTouch = e.type.startsWith('touch');
if (!isTouch) e.preventDefault();
// calculate the new cursor position:
var clientX = isTouch ? e.touches[0].clientX : e.clientX;
posNew = mousePos - clientX;
mousePos = clientX;
mousePosInGradient = mousePos - (minX + 1)
truePos = Math.round((mousePosInGradient/gradientLength)*256);
oldTruePos = elmnt.getAttribute("data-truepos");
// set the element's new position if new position within min/max limits:
if (truePos > 0 && truePos < 255 && oldTruePos != truePos) {
if (truePos < 64) {
thisOffset = 0;
} else if (truePos > 192) {
thisOffset = 7;
} else {
thisOffset=3;
}
elmnt.style.left = (Math.round((gradientLength/256)*truePos)+mOffs) + "px";
gId(elmnt.id.replace('colorMarker', 'colorPickerMarker')).style.left = elmnt.style.left;
gId(elmnt.id.replace('colorMarker', 'deleteMarker')).style.left = elmnt.style.left;
gId(elmnt.id.replace('colorMarker', 'colorPicker')).style.left = elmnt.style.left;
elmnt.setAttribute("data-truepos", truePos);
setTooltipMarker(elmnt);
updateGradient();
function dragMove(e) {
var isT=e.type.startsWith('touch');
if (!isT) e.preventDefault();
var cX=isT?e.touches[0].clientX:e.clientX;
posNew=mPos-cX; mPos=cX;
var mInG=mPos-(minX+1);
var tPos=Math.round((mInG/gLen)*256);
var old=el.getAttribute("data-tpos");
if (tPos>0 && tPos<255 && old!=tPos) {
el.style.left=(Math.round((gLen/256)*tPos)+mOffs)+"px";
gId(el.id.replace('cMark','cPM')).style.left=el.style.left;
gId(el.id.replace('cMark','dMark')).style.left=el.style.left;
gId(el.id.replace('cMark','cPick')).style.left=el.style.left;
el.setAttribute("data-tpos",tPos);
setTip(el);
updGrad();
}
}
function closeDragElement() {
/* stop moving when mouse button is released:*/
d.onmouseup = null;
d.ontouchcancel = null;
d.ontouchend = null;
d.onmousemove = null;
d.ontouchmove = null;
function dragEnd() {
d.onmouseup=null; d.ontouchend=null; d.ontouchcancel=null;
d.onmousemove=null; d.ontouchmove=null;
}
}
function setTooltipMarker(elmnt) {
elmnt.setAttribute('title', `${elmnt.getAttribute("data-truepos")} : ${elmnt.getAttribute("data-truecol")}`)
function setTip(el) { // setTooltipMarker
el.setAttribute('title', `${el.getAttribute("data-tpos")} : ${el.getAttribute("data-tcol")}`);
}
function deleteColor(e) {
var trash = cE("div");
thisDeleteMarker = e.srcElement;
thisColorMarker = gId(thisDeleteMarker.id.replace("delete", "color"));
thisColorPickerMarker = gId(thisDeleteMarker.id.replace("delete", "colorPicker"));
thisColorPicker = gId(thisDeleteMarker.id.replace("deleteMarker", "colorPicker"));
renderOffsetX = 15 - 5;
renderX = e.srcElement.getBoundingClientRect().x - renderOffsetX;
renderY = e.srcElement.getBoundingClientRect().y + 13;
function delCol(e) { // deleteColor
var trash=cE("div");
var dM=e.target || e.srcElement;
var cM=gId(dM.id.replace("d","c"));
var cPM=gId(dM.id.replace("dMark","cPM"));
var cP=gId(dM.id.replace("dMark","cPick"));
var rX=dM.getBoundingClientRect().x-10;
var rY=dM.getBoundingClientRect().y+13;
trash.id = "trash";
trash.innerHTML = svgTrash;
trash.style.position = "absolute";
trash.style.left = (renderX) + "px";
trash.style.top = (renderY) + "px";
trash.id="trash";
trash.innerHTML=svgTrash;
trash.style.position="absolute";
trash.style.left=rX+"px";
trash.style.top=rY+"px";
d.body.appendChild(trash);
trash.addEventListener("click", (e)=>{
trash.parentNode.removeChild(trash);
thisDeleteMarker.parentNode.removeChild(thisDeleteMarker);
thisColorPickerMarker.parentNode.removeChild(thisColorPickerMarker);
thisColorMarker.parentNode.removeChild(thisColorMarker);
thisColorPicker.parentNode.removeChild(thisColorPicker);
updateGradient();
trash.addEventListener("click",()=>{
trash.remove(); cM.remove(); cPM.remove(); cP.remove(); dM.remove();
updGrad();
});
e.stopPropagation();
// Add event listener to close the trashcan on outside click
d.addEventListener("click", removeTrashcan);
e.stopPropagation();
d.addEventListener("click", rmTrash);
}
function removeTrashcan(event) {
trash = gId("trash");
if (event.target != trash && trash) {
trash.parentNode.removeChild(trash);
d.removeEventListener("click", removeTrashcan);
}
function rmTrash(e) { // removeTrashcan
var t=gId("trash");
if (t && e.target!=t) { t.remove(); d.removeEventListener("click", rmTrash);}
}
function chkW() {
//Possibly add more code that recalculates the gradient... Massive job ;)
const wrap = gId('wrap');
const head = gId('head');
if (wrap.offsetWidth < 600) {
head.style.display = 'none';
} else {
head.style.display = 'inline';
}
const wrap=gId('wrap'); const head=gId('head');
head.style.display=(wrap.offsetWidth<600)?'none':'inline';
}
function calcJSON() {
let rStr = '{"palette":['
Object.entries(tCol).forEach(([p, c]) => {
if (p > 0) rStr += ',';
rStr += `${p},"${c.slice(1)}"`; // store in hex notation
//rStr += `${p},${parseInt(c.slice(1, 3), 16)},${parseInt(c.slice(3, 5), 16)},${parseInt(c.slice(5, 7), 16)}`;
let rStr='{"palette":[';
Object.entries(tCol).forEach(([p,c],i)=>{
if (i>0) rStr+=',';
rStr+=`${p},"${c.slice(1)}"`;
});
rStr += ']}';
rStr+=']}';
return rStr;
}
function initiateUpload(idx) {
const data = calcJSON();
const fileName = `/palette${idx}.json`;
uploadJSON(data, fileName);
function initUpload(i) {
uploadJSON(calcJSON(), `/palette${i}.json`);
}
function uploadJSON(jsonString, fileName) {
@@ -525,41 +425,38 @@
}
async function getInfo() {
hst = location.host;
if (hst.length > 0 ) {
try {
var arr = [];
const responseInfo = await fetch('http://'+hst+'/json/info');
const responsePalettes = await fetch('http://'+hst+'/json/palettes');
const json = await responseInfo.json();
paletteName = await responsePalettes.json();
cpalc = json.cpalcount;
fetchPalettes(cpalc-1);
} catch (error) {
console.error(error);
}
} else {
console.error('cannot identify host');
getLoc();
try {
var arr = [];
const resInfo = await fetch(getURL('/json/info')); // fetch info (includes cpalcount and cpalmax)
const resPals = await fetch(getURL('/json/pal')); // fetch palette names
const json = await resInfo.json();
palNm = await resPals.json();
cpalc = json.cpalcount;
cpalm = json.cpalmax;
fetchPals(cpalc-1);
} catch (error) {
console.error(error);
}
}
async function fetchPalettes(lastPal) {
paletteArray.length = 0;
async function fetchPals(lastPal) {
palArr.length = 0;
for (let i = 0; i <= lastPal; i++) {
const url = `http://${hst}/palette${i}.json`;
const url = getURL(`/palette${i}.json`);
try {
const response = await fetch(url);
const json = await response.json();
paletteArray.push(json);
palArr.push(json);
} catch (error) {
cpalc--; //remove audio/dynamically generated palettes
console.error(`Error fetching JSON from ${url}: `, error);
}
}
//If there is room for more custom palettes, add an empty, gray slot
if (paletteArray.length < 10) {
if (palArr.length < cpalm) {
//Room for one more :)
paletteArray.push({"palette":[0,70,70,70,255,70,70,70]});
palArr.push({"palette":[0,70,70,70,255,70,70,70]});
}
//Get static palettes from localStorage and do some magic to reformat them into the same format as the palette JSONs
@@ -569,12 +466,12 @@
const wledPalx = JSON.parse(localStorage.getItem('wledPalx'));
if (!wledPalx) {
alert("The cache of palettes are missig from your browser. You should probably return to the main page and let it load properly for the palettes cache to regenerate before returning here.","Missing cached palettes!")
alert("Palette cache missing from browser. Return to main page first.","Missing cache!")
} else {
for (const key in wledPalx.p) {
wledPalx.p[key].name = paletteName[key];
if (key > 245) {
delete wledPalx.p[key];
wledPalx.p[key].name = palNm[key];
if (key > 255-cpalm) {
delete wledPalx.p[key]; // remove custom palettes
continue;
}
const arr = wledPalx.p[key];
@@ -610,130 +507,133 @@
// Sort pArray by name
pArray.sort((a, b) => a.name.localeCompare(b.name));
paletteArray.push( ...pArray);
palArr.push( ...pArray);
}
generatePaletteDivs();
genPalDivs();
}
function generatePaletteDivs() {
const palettesDiv = gId("palettes");
const staticPalettesDiv = gId("staticPalettes");
const paletteDivs = Array.from(palettesDiv.children).filter((child) => {
return child.id.match(/^palette\d$/); // match only elements with id starting with "palette" followed by a single digit
function genPalDivs() {
const palsDiv = gId("pals");
const sPalsDiv = gId("sPals");
const memWarn = gId("memWarn");
const palDivs = Array.from(palsDiv.children).filter((child) => {
return /^pal\d+$/.test(child.id); // match ids "pal" followed by one or more digits
});
for (const div of paletteDivs) {
palettesDiv.removeChild(div); // remove each div that matches the above selector
for (const div of palDivs) {
palsDiv.removeChild(div); // remove each div that matches the above selector
}
for (let i = 0; i < paletteArray.length; i++) {
const palette = paletteArray[i];
const paletteDiv = cE("div");
paletteDiv.id = `palette${i}`;
paletteDiv.classList.add("palette");
const thisKey = Object.keys(palette)[0];
paletteDiv.dataset.colarray = JSON.stringify(palette[thisKey]);
memWarn.style.display = (cpalc >= 10) ? 'block' : 'none'; // Show/hide memory warning based on custom palette count
const gradientDiv = cE("div");
gradientDiv.id = `paletteGradient${i}`
const buttonsDiv = cE("div");
buttonsDiv.id = `buttonsDiv${i}`;
buttonsDiv.classList.add("buttonsDiv")
for (let i = 0; i < palArr.length; i++) {
const pal = palArr[i];
const palDiv = cE("div");
palDiv.id = `pal${i}`;
palDiv.classList.add("pal");
const thisKey = Object.keys(pal)[0];
palDiv.dataset.colarray = JSON.stringify(pal[thisKey]);
const sendSpan = cE("span");
sendSpan.id = `sendSpan${i}`;
sendSpan.onclick = function() {initiateUpload(i)};
sendSpan.setAttribute('title', `Send current editor to slot ${i}`); // perhaps Save instead of Send?
sendSpan.innerHTML = svgSave;
sendSpan.classList.add("sendSpan")
const editSpan = cE("span");
editSpan.id = `editSpan${i}`;
editSpan.onclick = function() {loadForEdit(i)};
editSpan.setAttribute('title', `Copy slot ${i} palette to editor`);
if (paletteArray[i].name) {
editSpan.setAttribute('title', `Copy ${paletteArray[i].name} palette to editor`);
const gradDiv = cE("div");
gradDiv.id = `pGrad${i}`
const btnsDiv = cE("div");
btnsDiv.id = `btns${i}`;
btnsDiv.classList.add("btnsDiv")
const sSpan = cE("span");
sSpan.id = `s${i}`;
sSpan.onclick = function() {initUpload(i)};
sSpan.setAttribute('title', `Send current editor to slot ${i}`); // perhaps Save instead of Send?
sSpan.innerHTML = svgSave;
sSpan.classList.add("sSpan")
const eSpan = cE("span");
eSpan.id = `e${i}`;
eSpan.onclick = function() {loadEdit(i)};
eSpan.setAttribute('title', `Copy slot ${i} to editor`);
if (palArr[i].name) {
eSpan.setAttribute('title', `Copy ${palArr[i].name} to editor`);
}
editSpan.innerHTML = svgEdit;
editSpan.classList.add("editSpan")
eSpan.innerHTML = svgEdit;
eSpan.classList.add("eSpan")
gradientDiv.classList.add("paletteGradients");
let gradientColors = "";
gradDiv.classList.add("pGrads");
let gCols = "";
for (let j = 0; j < palette[thisKey].length; j += 2) {
const position = palette[thisKey][j];
if (typeof(palette[thisKey][j+1]) === "string") {
gradientColors += `#${palette[thisKey][j+1]} ${position/255*100}%, `;
for (let j = 0; j < pal[thisKey].length; j += 2) {
const pos = pal[thisKey][j];
if (typeof(pal[thisKey][j+1]) === "string") {
gCols += `#${pal[thisKey][j+1]} ${pos/255*100}%, `;
} else {
const red = palette[thisKey][j + 1];
const green = palette[thisKey][j + 2];
const blue = palette[thisKey][j + 3];
gradientColors += `rgba(${red}, ${green}, ${blue}, 1) ${position/255*100}%, `;
const r = pal[thisKey][j + 1];
const g = pal[thisKey][j + 2];
const b = pal[thisKey][j + 3];
gCols += `rgba(${r}, ${g}, ${b}, 1) ${pos/255*100}%, `;
j += 2;
}
}
gradientColors = gradientColors.slice(0, -2); // remove the last comma and space
gradientDiv.style.backgroundImage = `linear-gradient(to right, ${gradientColors})`;
paletteDiv.className = "palGradientParent";
gCols = gCols.slice(0, -2); // remove the last comma and space
gradDiv.style.backgroundImage = `linear-gradient(to right, ${gCols})`;
palDiv.className = "pGradPar";
if (thisKey == "palette") {
buttonsDiv.appendChild(sendSpan); //Only offer to send to custom palettes
btnsDiv.appendChild(sSpan); //Only offer to send to custom palettes
} else{
editSpan.style.marginLeft = "25px";
eSpan.style.marginLeft = "25px";
}
if (i!=cpalc) {
buttonsDiv.appendChild(editSpan); //Dont offer to edit the empty spot
btnsDiv.appendChild(eSpan); //Dont offer to edit the empty spot
}
paletteDiv.appendChild(gradientDiv);
paletteDiv.appendChild(buttonsDiv);
palDiv.appendChild(gradDiv);
palDiv.appendChild(btnsDiv);
if (thisKey == "palette") {
palettesDiv.appendChild(paletteDiv);
palsDiv.appendChild(palDiv);
} else {
staticPalettesDiv.appendChild(paletteDiv);
sPalsDiv.appendChild(palDiv);
}
}
}
function loadForEdit(i) {
d.querySelectorAll('input[id^="colorPicker"]').forEach((input) => {
function loadEdit(i) {
d.querySelectorAll('input[id^="cPick"]').forEach((input) => {
input.parentNode.removeChild(input);
});
d.querySelectorAll('span[id^="colorMarker"], span[id^="colorPickerMarker"], span[id^="deleteMarker"]').forEach((span) => {
d.querySelectorAll('span[id^="cMark"], span[id^="cPM"], span[id^="dMark"]').forEach((span) => {
span.parentNode.removeChild(span);
});
let colArray = JSON.parse(gId(`palette${i}`).getAttribute("data-colarray"));
let colArr = JSON.parse(gId(`pal${i}`).getAttribute("data-colarray"));
for (let j = 0; j < colArray.length; j += 2) {
const position = colArray[j];
for (let j = 0; j < colArr.length; j += 2) {
const pos = colArr[j];
let hex;
if (typeof(colArray[j+1]) === "string") {
hex = `#${colArray[j+1]}`;
if (typeof(colArr[j+1]) === "string") {
hex = `#${colArr[j+1]}`;
} else {
const red = colArray[j + 1];
const green = colArray[j + 2];
const blue = colArray[j + 3];
hex = rgbToHex(red, green, blue);
const r = colArr[j + 1];
const g = colArr[j + 2];
const b = colArr[j + 3];
hex = rgbToHex(r, g, b);
j += 2;
}
addC(position, hex);
addC(pos, hex);
window.scroll(0, 0);
}
}
function distribute() {
let colorMarkers = [...gradientBox.querySelectorAll('.color-marker')];
colorMarkers.sort((a, b) => a.getAttribute('data-truepos') - b.getAttribute('data-truepos'));
colorMarkers = colorMarkers.slice(1, -1);
const spacing = Math.round(256 / (colorMarkers.length + 1));
function distrib() {
let cMarks = [...gBox.querySelectorAll('.cMark')];
cMarks.sort((a, b) => a.getAttribute('data-tpos') - b.getAttribute('data-tpos'));
cMarks = cMarks.slice(1, -1);
const spacing = Math.round(256 / (cMarks.length + 1));
colorMarkers.forEach((e, i) => {
const markerId = e.id.match(/\d+/)[0];
const trueCol = e.getAttribute("data-truecol");
gradientBox.removeChild(e);
gradientBox.removeChild(gId(`colorPicker${markerId}`));
gradientBox.removeChild(gId(`colorPickerMarker${markerId}`));
gradientBox.removeChild(gId(`deleteMarker${markerId}`));
addC(spacing * (i + 1), trueCol);
cMarks.forEach((e, i) => {
const mId = e.id.match(/\d+/)[0];
const tCol = e.getAttribute("data-tcol");
gBox.removeChild(e);
gBox.removeChild(gId(`cPick${mId}`));
gBox.removeChild(gId(`cPM${mId}`));
gBox.removeChild(gId(`dMark${mId}`));
addC(spacing * (i + 1), tCol);
});
}

View File

@@ -1042,8 +1042,7 @@ function genPalPrevCss(id)
}
var gradient = [];
for (let j = 0; j < paletteData.length; j++) {
const e = paletteData[j];
paletteData.forEach((e,j) => {
let r, g, b;
let index = false;
if (Array.isArray(e)) {
@@ -1065,9 +1064,8 @@ function genPalPrevCss(id)
if (index === false) {
index = Math.round(j / paletteData.length * 100);
}
gradient.push(`rgb(${r},${g},${b}) ${index}%`);
}
});
return `background: linear-gradient(to right,${gradient.join()});`;
}
@@ -3088,11 +3086,9 @@ function unify(e) { return e.changedTouches ? e.changedTouches[0] : e; }
function hasIroClass(classList)
{
for (var i = 0; i < classList.length; i++) {
var element = classList[i];
if (element.startsWith('Iro')) return true;
}
return false;
let found = false;
classList.forEach((e)=>{ if (e.startsWith('Iro')) found = true; });
return found;
}
//required by rangetouch.js
function lock(e)

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>WLED Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
function S() {
getLoc();

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>2D Set-up</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
var maxPanels=64;
var ctx = null;

View File

@@ -4,7 +4,7 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="utf-8">
<title>DMX Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
function HW(){window.open("https://kno.wled.ge/interfaces/dmx-output/");}
function GCH(num) {

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>LED Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
var maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var customStarts=false,startsDirty=[];

View File

@@ -4,7 +4,7 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="utf-8">
<title>Misc Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
function U() { window.open(getURL("/update"),"_self"); }
function checkNum(o) {

View File

@@ -4,7 +4,7 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="utf-8">
<title>Sync Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} }

View File

@@ -4,7 +4,7 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="utf-8">
<title>Time Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
var el=false;
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>UI Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
var initial_ds, initial_st, initial_su, oldUrl;
var sett = null;

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>Usermod Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
var umCfg = {};
var pins = [], pinO = [], owner;

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<title>WiFi Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
var scanLoops = 0, preScanSSID = "";
var maxNetworks = 3;

View File

@@ -3,7 +3,7 @@
<head>
<meta content='width=device-width' name='viewport'>
<title>WLED Update</title>
<script src="common.js" async type="text/javascript"></script>
<script src="common.js" type="text/javascript"></script>
<script>
function B() { window.history.back(); }
var cnfr = false;

View File

@@ -1,7 +1,5 @@
#include "wled.h"
#include "palettes.h"
#define JSON_PATH_STATE 1
#define JSON_PATH_INFO 2
#define JSON_PATH_STATE_INFO 3
@@ -771,7 +769,8 @@ void serializeInfo(JsonObject root)
root[F("fxcount")] = strip.getModeCount();
root[F("palcount")] = getPaletteCount();
root[F("cpalcount")] = customPalettes.size(); //number of custom palettes
root[F("cpalcount")] = customPalettes.size(); // number of custom palettes
root[F("cpalmax")] = WLED_MAX_CUSTOM_PALETTES; // maximum number of custom palettes
JsonArray ledmaps = root.createNestedArray(F("maps"));
for (size_t i=0; i<WLED_MAX_LEDMAPS; i++) {
@@ -935,7 +934,7 @@ void serializePalettes(JsonObject root, int page)
#endif
int customPalettesCount = customPalettes.size();
int palettesCount = getPaletteCount() - customPalettesCount;
int palettesCount = getPaletteCount() - customPalettesCount; // palettesCount is number of palettes, not palette index
int maxPage = (palettesCount + customPalettesCount -1) / itemPerPage;
if (page > maxPage) page = maxPage;
@@ -947,8 +946,8 @@ void serializePalettes(JsonObject root, int page)
root[F("m")] = maxPage; // inform caller how many pages there are
JsonObject palettes = root.createNestedObject("p");
for (int i = start; i < end; i++) {
JsonArray curPalette = palettes.createNestedArray(String(i>=palettesCount ? 255 - i + palettesCount : i));
for (int i = start; i <= end; i++) {
JsonArray curPalette = palettes.createNestedArray(String(i<=palettesCount ? i : 255 - (i - (palettesCount + 1))));
switch (i) {
case 0: //default palette
setPaletteColors(curPalette, PartyColors_p);
@@ -977,8 +976,8 @@ void serializePalettes(JsonObject root, int page)
curPalette.add("c1");
break;
default:
if (i >= palettesCount)
setPaletteColors(curPalette, customPalettes[i - palettesCount]);
if (i > palettesCount)
setPaletteColors(curPalette, customPalettes[i - (palettesCount + 1)]);
else if (i < 13) // palette 6 - 12, fastled palettes
setPaletteColors(curPalette, *fastledPalettes[i-6]);
else {

5
wled00/palettes.h → wled00/palettes.cpp Executable file → Normal file
View File

@@ -1,5 +1,4 @@
#ifndef PalettesWLED_h
#define PalettesWLED_h
#include "wled.h"
/*
* WLED Color palettes
@@ -768,5 +767,3 @@ const uint8_t* const gGradientPalettes[] PROGMEM = {
candy2_gp, //70-57 Candy2
trafficlight_gp //71-58 Traffic Light
};
#endif

View File

@@ -230,7 +230,7 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
} else return 0;
}
if (src == JSON_palette_names && mode > (GRADIENT_PALETTE_COUNT + 13)) {
if (src == JSON_palette_names && mode > 255-customPalettes.size()) {
snprintf_P(dest, maxLen, PSTR("~ Custom %d ~"), 255-mode);
dest[maxLen-1] = '\0';
return strlen(dest);

View File

@@ -475,29 +475,31 @@ void initServer()
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
if (captivePortal(request)) return;
if (!showWelcomePage || request->hasArg(F("sliders"))) {
handleStaticContent(request, F("/index.htm"), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_index, PAGE_index_L);
handleStaticContent(request, F("/index.htm"), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_index, PAGE_index_length);
} else {
serveSettings(request);
}
});
#ifdef WLED_ENABLE_PIXART
#ifndef WLED_DISABLE_2D
#ifdef WLED_ENABLE_PIXART
static const char _pixart_htm[] PROGMEM = "/pixart.htm";
server.on(_pixart_htm, HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, FPSTR(_pixart_htm), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_pixart, PAGE_pixart_L);
handleStaticContent(request, FPSTR(_pixart_htm), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_pixart, PAGE_pixart_length);
});
#endif
#endif
#ifndef WLED_DISABLE_PXMAGIC
#ifndef WLED_DISABLE_PXMAGIC
static const char _pxmagic_htm[] PROGMEM = "/pxmagic.htm";
server.on(_pxmagic_htm, HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, FPSTR(_pxmagic_htm), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_pxmagic, PAGE_pxmagic_L);
handleStaticContent(request, FPSTR(_pxmagic_htm), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_pxmagic, PAGE_pxmagic_length);
});
#endif
#endif
static const char _cpal_htm[] PROGMEM = "/cpal.htm";
server.on(_cpal_htm, HTTP_GET, [](AsyncWebServerRequest *request) {
handleStaticContent(request, FPSTR(_cpal_htm), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_cpal, PAGE_cpal_L);
handleStaticContent(request, FPSTR(_cpal_htm), 200, FPSTR(CONTENT_TYPE_HTML), PAGE_cpal, PAGE_cpal_length);
});
#ifdef WLED_ENABLE_WEBSOCKETS