647 lines
21 KiB
HTML
647 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
<meta http-equiv="Pragma" content="no-cache">
|
|
<meta http-equiv="Expires" content="0">
|
|
<title>WLED Custom Palette Editor</title>
|
|
<script type="text/javascript">
|
|
var d = document;
|
|
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 {
|
|
font-family: Arial, sans-serif;
|
|
background-color: #111;
|
|
font-size: 16px;
|
|
color: #ddd;
|
|
margin: 0 10px;
|
|
line-height: 0.5;
|
|
}
|
|
#pCont {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 20px;
|
|
}
|
|
#bCont {
|
|
position: absolute;
|
|
margin-top: 50px;
|
|
}
|
|
#gBox {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.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;
|
|
}
|
|
.cMark {
|
|
height: 30px;
|
|
width: 7px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
touch-action: none;
|
|
}
|
|
.cPickMark {
|
|
height: 7px;
|
|
width: 7px;
|
|
top: 150%;
|
|
}
|
|
.dMark {
|
|
position: absolute;
|
|
height: 5px;
|
|
width: 5px;
|
|
border-radius: 3px;
|
|
background-color: rgb(255, 255, 255);
|
|
border: 3px solid rgb(155, 40, 40);
|
|
top: 220%;
|
|
z-index: 2;
|
|
}
|
|
.cPick {
|
|
position: absolute;
|
|
height: 1px;
|
|
width: 1px;
|
|
border: 1px;
|
|
top: 150%;
|
|
z-index: 1;
|
|
border-color: #111;
|
|
background-color: #111;
|
|
}
|
|
.btnCls {
|
|
padding: 0;
|
|
margin: 0;
|
|
vertical-align: bottom;
|
|
background-color: #111;
|
|
}
|
|
#bCont span {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
color: #fff;
|
|
font-size: 12px;
|
|
vertical-align: middle;
|
|
}
|
|
#info {
|
|
text-align: center;
|
|
color: #fff;
|
|
font-size: 12px;
|
|
position: relative;
|
|
margin-top: 10px;
|
|
line-height: 1;
|
|
}
|
|
.wrap {
|
|
width: 100%;
|
|
margin: 0 auto;
|
|
}
|
|
@media (min-width: 800px) {
|
|
.wrap {
|
|
width: 800px;
|
|
}
|
|
}
|
|
.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: #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 Palette Editor</span>
|
|
</h1>
|
|
</div>
|
|
|
|
<div id="pCont"><div id="gBox"></div></div>
|
|
<div style="display: flex; justify-content: center;">
|
|
<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 gradient to add. Box = color. Red = delete. Arrow = upload. Pencil = edit.</div>
|
|
</div>
|
|
<div style="display: flex; justify-content: center;">
|
|
<div id="sPals" class="pMain">
|
|
<div id="spTop" class="pTop">Static palettes</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
|
|
<script type="text/javascript">
|
|
// 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', distrib);
|
|
distDiv.setAttribute('title', 'Distribute equally');
|
|
distDiv.innerHTML = svgDist;
|
|
|
|
function recOf() {
|
|
rect = gBox.getBoundingClientRect();
|
|
gLen = rect.width;
|
|
mOffs = Math.round((gLen / 256) / 2) - 5;
|
|
}
|
|
|
|
//Initiation
|
|
getInfo();
|
|
window.addEventListener('load', chkW);
|
|
window.addEventListener('resize', chkW);
|
|
|
|
gBox.addEventListener("click", clikGrad);
|
|
|
|
//Sets start and stop, mandatory
|
|
addC(0);
|
|
addC(255);
|
|
|
|
updGrad(); // updateGradient at startup
|
|
|
|
function clikGrad(e) { // clickOnGradient
|
|
rmTrash(e); // removeTrashcan
|
|
addC(Math.round((e.offsetX/gLen)*256));
|
|
}
|
|
|
|
///////// Add a new color marker
|
|
function addC(tPos, thisCol = '') {
|
|
let pos = -1;
|
|
let exist = false;
|
|
const cMarks = gBox.querySelectorAll('.cMark'); // color markers
|
|
|
|
cMarks.forEach((cm) => {
|
|
if (cm.getAttribute("data-tpos") == tPos) exist = true;
|
|
});
|
|
|
|
if (cMarks.length > 17) exist = true;
|
|
if (exist) return;
|
|
|
|
if (tPos > 0 && tPos < 255) {
|
|
for (var i=1; i<=16 && pos<1; i++) {
|
|
if (!gId("cMark"+i)) pos = i;
|
|
}
|
|
} else {
|
|
pos = tPos;
|
|
}
|
|
if (thisCol == '') {
|
|
thisCol = `#${(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,'0')}`;
|
|
}
|
|
|
|
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 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 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;
|
|
|
|
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);
|
|
}
|
|
|
|
cMark.style.backgroundColor = cPick.value;
|
|
cPM.style.backgroundColor = cPick.value;
|
|
|
|
gBox.appendChild(cPick);
|
|
gBox.appendChild(cMark);
|
|
gBox.appendChild(cPM);
|
|
if (pos > 0 && pos < 255) mkDrag(gId(cMark.id)); // makeMeDrag
|
|
|
|
setTip(gId(cMark.id)); // setTooltipMarker
|
|
updGrad();
|
|
}
|
|
|
|
///////// Update Gradient
|
|
function updGrad() { // updateGradient
|
|
const cMarks = gBox.querySelectorAll('.cMark');
|
|
pxCol = {};
|
|
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;
|
|
});
|
|
let gStr = 'linear-gradient(to right';
|
|
Object.entries(pxCol).forEach(([p,c]) => {
|
|
gStr += `, ${c} ${p}px`;
|
|
});
|
|
gStr += ')';
|
|
gBox.style.background = gStr;
|
|
}
|
|
|
|
function stopProp(e) { e.stopPropagation(); }
|
|
|
|
function colClk(e) {
|
|
rmTrash(e);
|
|
e.stopPropagation();
|
|
const src = e.target || e.srcElement;
|
|
let cp = gId(src.id.replace("cPM","cPick"));
|
|
cp.click();
|
|
}
|
|
|
|
function cpClk(e) {
|
|
rmTrash(e);
|
|
e.stopPropagation();
|
|
}
|
|
|
|
// 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;
|
|
|
|
el.onmousedown=dragStart;
|
|
el.ontouchstart=dragStart;
|
|
|
|
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 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 dragEnd() {
|
|
d.onmouseup=null; d.ontouchend=null; d.ontouchcancel=null;
|
|
d.onmousemove=null; d.ontouchmove=null;
|
|
}
|
|
}
|
|
|
|
function setTip(el) { // setTooltipMarker
|
|
el.setAttribute('title', `${el.getAttribute("data-tpos")} : ${el.getAttribute("data-tcol")}`);
|
|
}
|
|
|
|
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=rX+"px";
|
|
trash.style.top=rY+"px";
|
|
d.body.appendChild(trash);
|
|
|
|
trash.addEventListener("click",()=>{
|
|
trash.remove(); cM.remove(); cPM.remove(); cP.remove(); dM.remove();
|
|
updGrad();
|
|
});
|
|
e.stopPropagation();
|
|
d.addEventListener("click", rmTrash);
|
|
}
|
|
|
|
function rmTrash(e) { // removeTrashcan
|
|
var t=gId("trash");
|
|
if (t && e.target!=t) { t.remove(); d.removeEventListener("click", rmTrash);}
|
|
}
|
|
|
|
function chkW() {
|
|
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],i)=>{
|
|
if (i>0) rStr+=',';
|
|
rStr+=`${p},"${c.slice(1)}"`;
|
|
});
|
|
rStr+=']}';
|
|
return rStr;
|
|
}
|
|
|
|
function initUpload(i) {
|
|
uploadJSON(calcJSON(), `/palette${i}.json`);
|
|
}
|
|
|
|
function uploadJSON(jsonString, fileName) {
|
|
//Some indication on "I'm working"
|
|
var req = new XMLHttpRequest();
|
|
var blob = new Blob([jsonString], {type: "application/json"});
|
|
req.addEventListener('load', ()=>{
|
|
console.log(this.responseText, ' - ', this.status)
|
|
localStorage.removeItem('wledPalx');
|
|
//setTimeout(()=>{
|
|
// ss.setAttribute('fill', '#fff');
|
|
//}, 1000);
|
|
//setTimeout(()=>{window.location.href='/';},2000);
|
|
window.location.href = '/'; //Guessing we want to return ASAP when we get confirmation save is done
|
|
});
|
|
req.addEventListener('error', (e)=>{
|
|
console.log('Error: ', e); console.log(' Status: ', this.status);
|
|
//Show some error notification for some time
|
|
setTimeout(()=>{
|
|
//Remove it when time has passed
|
|
}, 1000);
|
|
});
|
|
req.open("POST", "/upload");
|
|
var formData = new FormData();
|
|
formData.append("data", blob, fileName);
|
|
req.send(formData);
|
|
return false;
|
|
}
|
|
|
|
async function getInfo() {
|
|
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 fetchPals(lastPal) {
|
|
palArr.length = 0;
|
|
for (let i = 0; i <= lastPal; i++) {
|
|
const url = getURL(`/palette${i}.json`);
|
|
try {
|
|
const response = await fetch(url);
|
|
const json = await response.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 (palArr.length < cpalm) {
|
|
//Room for one more :)
|
|
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
|
|
//This code excludes any objects with "non valid integer colors", i.e. r, c1, c2, c3 and such
|
|
//This code also fixes potentially broken palettes which doesn't end on 255
|
|
//The code finally also removes any representations of the custom palettes, since we read them from file
|
|
|
|
const wledPalx = JSON.parse(localStorage.getItem('wledPalx'));
|
|
if (!wledPalx) {
|
|
alert("Palette cache missing from browser. Return to main page first.","Missing cache!")
|
|
} else {
|
|
for (const key in wledPalx.p) {
|
|
wledPalx.p[key].name = palNm[key];
|
|
if (key > 255-cpalm) {
|
|
delete wledPalx.p[key]; // remove custom palettes
|
|
continue;
|
|
}
|
|
const arr = wledPalx.p[key];
|
|
let valid = true;
|
|
for (const subArr of arr) {
|
|
if (!Array.isArray(subArr) || subArr.length !== 4) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
for (const val of subArr) {
|
|
if (typeof val !== 'number' || val < 0 || val > 255 || !Number.isInteger(val)) {
|
|
valid = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!valid) {
|
|
delete wledPalx.p[key];
|
|
continue;
|
|
}
|
|
const lastArr = arr[arr.length - 1];
|
|
if (lastArr[0] !== 255) {
|
|
const copyArr = [...lastArr];
|
|
copyArr[0] = 255;
|
|
arr.push(copyArr);
|
|
}
|
|
}
|
|
|
|
const pArray = Object.entries(wledPalx.p).map(([key, value]) => ({
|
|
[key]: value.flat(),
|
|
name: value.name
|
|
}));
|
|
// Sort pArray by name
|
|
pArray.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
palArr.push( ...pArray);
|
|
}
|
|
genPalDivs();
|
|
}
|
|
|
|
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 palDivs) {
|
|
palsDiv.removeChild(div); // remove each div that matches the above selector
|
|
}
|
|
|
|
memWarn.style.display = (cpalc >= 10) ? 'block' : 'none'; // Show/hide memory warning based on custom palette count
|
|
|
|
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 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`);
|
|
}
|
|
eSpan.innerHTML = svgEdit;
|
|
eSpan.classList.add("eSpan")
|
|
|
|
gradDiv.classList.add("pGrads");
|
|
let gCols = "";
|
|
|
|
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 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;
|
|
}
|
|
}
|
|
|
|
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") {
|
|
btnsDiv.appendChild(sSpan); //Only offer to send to custom palettes
|
|
} else{
|
|
eSpan.style.marginLeft = "25px";
|
|
}
|
|
if (i!=cpalc) {
|
|
btnsDiv.appendChild(eSpan); //Dont offer to edit the empty spot
|
|
}
|
|
palDiv.appendChild(gradDiv);
|
|
palDiv.appendChild(btnsDiv);
|
|
if (thisKey == "palette") {
|
|
palsDiv.appendChild(palDiv);
|
|
} else {
|
|
sPalsDiv.appendChild(palDiv);
|
|
}
|
|
}
|
|
}
|
|
|
|
function loadEdit(i) {
|
|
d.querySelectorAll('input[id^="cPick"]').forEach((input) => {
|
|
input.parentNode.removeChild(input);
|
|
});
|
|
d.querySelectorAll('span[id^="cMark"], span[id^="cPM"], span[id^="dMark"]').forEach((span) => {
|
|
span.parentNode.removeChild(span);
|
|
});
|
|
|
|
let colArr = JSON.parse(gId(`pal${i}`).getAttribute("data-colarray"));
|
|
|
|
for (let j = 0; j < colArr.length; j += 2) {
|
|
const pos = colArr[j];
|
|
let hex;
|
|
if (typeof(colArr[j+1]) === "string") {
|
|
hex = `#${colArr[j+1]}`;
|
|
} else {
|
|
const r = colArr[j + 1];
|
|
const g = colArr[j + 2];
|
|
const b = colArr[j + 3];
|
|
hex = rgbToHex(r, g, b);
|
|
j += 2;
|
|
}
|
|
addC(pos, hex);
|
|
window.scroll(0, 0);
|
|
}
|
|
}
|
|
|
|
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));
|
|
|
|
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);
|
|
});
|
|
}
|
|
|
|
function rgbToHex(r, g, b) {
|
|
const hex = ((r << 16) | (g << 8) | b).toString(16);
|
|
return "#" + "0".repeat(6 - hex.length) + hex;
|
|
}
|
|
|
|
</script>
|
|
</html>
|