From 40f544fffc194361a87bdaf59711e7a2d1229ade Mon Sep 17 00:00:00 2001 From: Woody Date: Thu, 9 Nov 2023 17:05:59 +0100 Subject: [PATCH] Reimplemented simplified UI --- wled00/data/index.css | 1 + wled00/data/index.js | 5 +- wled00/data/simple.css | 933 ------------------------ wled00/data/simple.htm | 261 +------ wled00/data/simple.js | 1540 +++------------------------------------- wled00/wled_server.cpp | 9 + 6 files changed, 118 insertions(+), 2631 deletions(-) delete mode 100644 wled00/data/simple.css diff --git a/wled00/data/index.css b/wled00/data/index.css index 7faaeb47e..058dc6c26 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -413,6 +413,7 @@ button { position: sticky; bottom: 0; max-width: 300px; + z-index: 2; } #sliders .labels { diff --git a/wled00/data/index.js b/wled00/data/index.js index 10ebfaa41..1efd5332e 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -12,6 +12,7 @@ var currentPreset = -1; var lastUpdate = 0; var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0; var pcMode = false, pcModeA = false, lastw = 0, wW; +var simpleUI = false; var tr = 7; var d = document; var palettesData; @@ -2902,7 +2903,7 @@ function hasIroClass(classList) //required by rangetouch.js function lock(e) { - if (pcMode) return; + if (pcMode || simpleUI) return; var l = e.target.classList; var pl = e.target.parentElement.classList; @@ -2916,7 +2917,7 @@ function lock(e) //required by rangetouch.js function move(e) { - if(!locked || pcMode) return; + if(!locked || pcMode || simpleUI) return; var clientX = unify(e).clientX; var dx = clientX - x0; var s = Math.sign(dx); diff --git a/wled00/data/simple.css b/wled00/data/simple.css deleted file mode 100644 index b685a97f9..000000000 --- a/wled00/data/simple.css +++ /dev/null @@ -1,933 +0,0 @@ -@font-face { - font-family: "WIcons"; - src: url(data:font/woff2;charset=utf-8;base64,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) format('woff'); -} - -:root { - --c-1: #111; - --c-f: #fff; - --c-2: #222; - --c-3: #333; - --c-4: #444; - --c-5: #555; - --c-6: #666; - --c-8: #888; - --c-b: #bbb; - --c-c: #ccc; - --c-e: #eee; - --c-d: #ddd; - --c-r: #e42; - --c-g: #4e2; - --c-l: #48a; - --t-b: .5; - --c-o: rgba(34, 34, 34, .9); - --c-tb : rgba(34, 34, 34, var(--t-b)); - --c-tba: rgba(102, 102, 102, var(--t-b)); - --c-tbh: rgba(51, 51, 51, var(--t-b)); - /*following are internal*/ - --th: 70px; - --tp: 70px; - --bh: 63px; - --tbp: 14px 8px 10px; - --bbp: 9px 0 7px 0; - --bhd: none; - --bmt: 0; -} - -html { - touch-action: manipulation; -} - -body { - margin: 0; - background-color: var(--c-1); - font-family: Helvetica, Verdana, sans-serif; - font-size: 17px; - color: var(--c-f); - text-align: center; - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-tap-highlight-color: transparent; - scrollbar-width: 6px; - scrollbar-color: var(--c-sb) transparent; -} - -html, -body { - height: 100%; - width: 100%; - position: fixed; - overscroll-behavior: none; -} - -#bg { - height: 100vh; - width: 100vw; - position: fixed; - z-index: -10; - background-position: center; - background-repeat: no-repeat; - background-size: cover; - opacity: 0; - transition: opacity 2s; -} - -p { - margin: 10px 0 2px 0; -} -a, p, a:visited { - color: var(--c-d); -} -a, a:visited { - text-decoration: none; -} - -button { - outline: 0; - cursor: pointer; - background-color: transparent; - border: 0; - transition: color .3s, background-color .3s; - font-size: 19px; - color: var(--c-c); - min-width: 40px; - min-height: 40px; -} -button:hover { - background: var(--c-4); -} - -.label { - margin: 0; - padding: 6px 0 0; -} - -#namelabel { - position: fixed; - bottom: calc(var(--bh) + 6px); - right: 4px; - color: var(--c-6); - cursor: pointer; - writing-mode: vertical-rl; -} - -.wrapper { - position: fixed; - top: 0; - left: 0; - right: 0; - background: var(--c-tb); - z-index: 1; -} - -.center { - margin: 0 auto; - width: 320px; -} - -.icons { - font-family: 'WIcons'; - font-style: normal; - font-size: 24px; - line-height: 1; - display: inline-block; - margin: -2px 0 4px 0; - text-shadow: -1px -1px 0 var(--c-3), 1px -1px 0 var(--c-3), -1px 1px 0 var(--c-3), 1px 1px 0 var(--c-3); -} - -.huge { - font-size: 42px; -} - -.infot { - table-layout: fixed; - width: 100%; -} - -.keytd { - text-align: left; - padding-bottom: 8px; -} - -.valtd { - text-align: right; - padding-bottom: 8px; -} - -.valtd i { - font-size: small; -} - -.slider-icon -{ - transform: translate(4px,3px); - color: var(--c-d); -} - -.il { - display: inline-block; - vertical-align: middle; -} - -.tab { - background-color: transparent; - color: var(--c-d); -} - -.tab button { - background-color: transparent; - float: left; - border: 0; - transition: color .3s, background-color .3s; - font-size: 17px; - color: var(--c-c); - min-width: 44px; -} - -.top button { - padding: var(--tbp); - margin: 0; -} - -.tab button:hover { - background-color: var(--c-tbh); - color: var(--c-e); -} - -.tab button.active { - background-color: var(--c-tba) !important; - color: var(--c-f); -} - -.active { - background-color: var(--c-6) !important; - color: var(--c-f); -} - -.container { - width: 100%; - height: calc(100% - var(--tp) - var(--bh)); - margin-top: var(--tp); - overscroll-behavior: none; -} - -.tabcontent { - position: relative; - width: 100%; - box-sizing: border-box; - border: 0; - overflow: auto; - height: 100%; - overscroll-behavior: none; -} - -.smooth { transition: transform calc(var(--f, 1)*.5s) ease-out } - -.tab-label { - margin: 0 0 -5px 0; - padding-bottom: 4px; -} - -.overlay { - position: fixed; - height: 100%; - width: 100%; - top: 0; - left: 0; - background-color: var(--c-3); - font-size: 24px; - display: flex; - align-items: center; - justify-content: center; - z-index: 11; - opacity: .95; - transition: .7s; - pointer-events: none; -} - -#toast { - opacity: 0; - background-color: var(--c-5); - max-width: 90%; - color: var(--c-f); - text-align: center; - border-radius: 5px; - padding: 16px; - position: fixed; - z-index: 5; - left: 50%; - transform: translateX(-50%); - bottom: calc(var(--bh) + 22px); - font-size: 17px; - pointer-events: none; -} - -#toast.show { - opacity: 1; - animation: fadein .5s, fadein .5s 2.5s reverse; -} - -#toast.error { - opacity: 1; - background-color: #b21; - animation: fadein .5s; -} - -.modal { - position: fixed; - left: 0; - bottom: 0; - right: 0; - top: calc(var(--th) - 1px); - background-color: var(--c-o); - transform: translateY(100%); - transition: transform .4s; - padding: 8px; - font-size: 20px; - overflow: auto; -} - -#info, #nodes { - z-index: 3; -} - -#rover { - z-index: 2; -} - -#ndlt { - margin: 12px 0; -} - -#roverstar { - position: fixed; - top: calc(var(--th) + 5px); - left: 1px; - display: none; - cursor: pointer; -} - -#connind { - position: fixed; - bottom: calc(var(--bh) + 5px); - left: 4px; - padding: 5px; - border-radius: 5px; - background-color: #a90; - z-index: -2; -} - -#imgw { - display: inline-block; - margin: 8px; -} - -#kv, #kn { - /*max-width: 490px;*/ - display: inline-block; -} - -#info table, #nodes table { - table-layout: fixed; - width: 100%; -} - -#info td, #nodes td { - padding-bottom: 8px; -} - -#info .btn { - margin: 5px; -} -#info table .btn, #nodes table .btn { - margin: 0; - width: 180px; -} -#info div, #nodes div { - width: 490px; - margin: 0 auto; -} - -#kn td { - padding-bottom: 12px; -} - -#heart { - transition: color .9s; - font-size: 16px; - color: #f00; -} - -img { - max-width: 100%; - max-height: 100%; -} - -.wi { - image-rendering: pixelated; - image-rendering: crisp-edges; - width: 210px; -} - -@keyframes fadein { - from {bottom: 0; opacity: 0;} - to {bottom: calc(var(--bh) + 22px); opacity: 1;} -} - -.sliderwrap { - height: 30px; - width: 250px; - position: relative; - margin: 4px 0; -} -#Colors .sliderwrap { - width: 260px; - margin: 10px 0 0; -} - -.sliderdisplay { - content:''; - position: absolute; - top: 10px; left: 8px; right: 8px; - height: 8px; - background: var(--c-4); - border-radius: 16px; - pointer-events: none; - z-index: -1; -} -#Colors .sliderdisplay { - height: 28px; - top: 0; bottom: 0; - left: 0; right: 0; - /*border: 1px solid var(--c-b);*/ -} -#rwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #f00); } -#gwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #0f0); } -#bwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #00f); } -#wwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #fff); } -#kwrap .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); } -#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #d4e0ff); } - -.sliderbubble { - width: 24px; - position: relative; - display: inline-block; - border-radius: 10px; - background: var(--c-3); - color: var(--c-f); - padding: 4px 4px 2px; - font-size: 14px; - right: 3px; - transition: visibility .25s ease, opacity .25s ease; - opacity: 0; - visibility: hidden; -} - -output.sliderbubbleshow { - visibility: visible; - opacity: 1; -} - -.hidden { - display: none; -} - -input[type=range] { - -webkit-appearance: none; - width: 100%; - padding: 0; - margin: 0; - background-color: transparent; - cursor: pointer; -} -#Colors input[type=range] { - width: 252px; - margin: 0; -} -input[type=range]::-webkit-slider-runnable-track { - width: 100%; - height: 30px; - cursor: pointer; - background: transparent; -} -input[type=range]::-webkit-slider-thumb { - border: 2px solid #000; - height: 20px; - width: 20px; - border-radius: 50%; - background: var(--c-f); - cursor: pointer; - -webkit-appearance: none; - margin-top: 4px; -} -input[type=range]::-moz-range-track { - width: 100%; - height: 30px; - background-color: var(--c-0); -} -input[type=range]::-moz-range-thumb { - border: 2px solid var(--c-3); - height: 20px; - width: 20px; - border-radius: 50%; - background: var(--c-f); - transform: translateY(5px); -} -#Colors input[type=range]::-webkit-slider-thumb { - border: 2px solid #000; -} -#Colors input[type=range]::-moz-range-thumb { - border: 2px solid var(--c-1); -} - -#Presets .list { - max-height: 215px; - overflow-y: scroll; - overflow-x: hidden; - width: 280px; - margin: 0 0 0 20px; - -ms-overflow-style: none; - scrollbar-width: none; /* Firefox */ -} -/* Hide scrollbar for Chrome, Safari and Opera */ -#Presets .list::-webkit-scrollbar { - display: none; -} - -#Segments .sliderwrap{ - width: 225px; -} - -#picker, #rgbwrap, #kwrap, #vwrap, #wwrap, #wbal { - display: none; -} - -.hd { - display: var(--bhd); -} - -#briwrap { - float: right; - margin-top: var(--bmt); -} - -#picker { - width: 260px; -} - -#picker, #csl, #segcont { - margin: 10px auto 0; -} - -.btn { - margin: 10px auto 0; - width: 280px; - font-size: 19px; - background-color: var(--c-3); - color: var(--c-d); - cursor: pointer; - border: 1px solid var(--c-3); - border-radius: 25px; - transition-duration: .3s; - -webkit-backface-visibility: hidden; - -webkit-transform: translate3d(0,0,0); - overflow: clip; - text-overflow: clip; - min-height: 40px; - line-height: 40px; -} -.btn:hover { - background-color: var(--c-4); - border: 1px solid var(--c-4); -} - -.btn-xs { - width: 42px; - height: 42px; - margin: 4px; - padding: 0; -} - -#fxBtn, #palBtn { - background-color: var(--c-2); - border: 1px solid var(--c-2); -} -#fxBtn:hover, #palBtn:hover { - background-color: var(--c-3); - border: 1px solid var(--c-3); -} - -.btn-icon { - margin-right: 8px; - vertical-align: middle; - display: inline-block; -} - -.qcs { - margin: 2px; - border-radius: 14px; - display: inline-block; - width: 28px; - height: 28px; - line-height: 28px;} -.qcsb { - width: 26px; - height: 26px; - line-height: 26px; - border: 1px solid var(--c-f); -} -option { - background-color: var(--c-3); - color: var(--c-f); -} -input[type=number], input[type=text] { - background: var(--c-3); - color: var(--c-f); - border: 0 solid var(--c-f); - border-radius: 5px; - padding: 8px; - margin: 6px 6px 6px 0; - font-size: 19px; - transition: background-color .2s; - outline: 0; - width: 50px; - -webkit-appearance: textfield; - -moz-appearance: textfield; - appearance: textfield; -} - -::selection { - background: var(--c-b); -} - -input[type=number]:focus, input[type=text]:focus { - background: var(--c-6); -} - -input[type=number]::-webkit-inner-spin-button, -input[type=number]::-webkit-outer-spin-button { - -webkit-appearance: none; -} - -.pid { - position: absolute; - top: 0; - left: 0; - padding: 12px 0 0 12px; - font-size: 16px; - width: 20px; - text-align: center; - color: var(--c-b); -} - -.xxs { - border: 2px solid var(--c-e) !important; - width: 44px; - height: 44px; - margin: 5px; - padding: 0; -} - -.xxs-w { - border-width: 4px !important; - margin: 2px; - width: 50px; - height: 50px; - padding: 0; -} - -.qcs, .xxs { - text-shadow: -1px -1px 0 var(--c-6), 1px -1px 0 var(--c-6), -1px 1px 0 var(--c-6), 1px 1px 0 var(--c-6); -} - -.psts { - color: var(--c-f); - margin: 6px; -} - -.pwr { - color: var(--c-6); - cursor: pointer; -} - -.act { - color: var(--c-f); -} - -.check, .radio { - display: inline-block; - position: relative; - cursor: pointer; - text-align: center; -} - -.schkl { - width: 24px; - top: -2px; -} - -.check input, .radio input { - position: absolute; - opacity: 0; - cursor: pointer; - height: 0; - width: 0; -} - -.checkmark, .radiomark { - position: absolute; - top: 0; - bottom: 0; - left: 0; - background-color: var(--c-3); - border: 1px solid var(--c-2); -} - -.radiomark { - height: 24px; - width: 24px; - border-radius: 50%; -} - -.checkmark { - height: 25px; - width: 25px; - border-radius: 10px; -} - -.check:hover input ~ .checkmark { - background-color: var(--c-4); -} - -.check input:checked ~ .checkmark { - background-color: var(--c-6); -} - -.checkmark:after, .radiomark:after { - content: ""; - position: absolute; - display: none; -} - -.check input:checked ~ .checkmark:after, .radio input:checked ~ .radiomark:after { - display: block; -} - -.check .checkmark:after { - left: 9px; - top: 5px; - width: 5px; - height: 10px; - border: solid var(--c-f); - border-width: 0 3px 3px 0; - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); -} - -.radio .radiomark:after { - width: 12px; - height: 12px; - top: 50%; - left: 50%; - margin: -6px; - border-radius: 50%; - background: var(--c-f); -} - -.h { - font-size: 13px; - color: var(--c-b); -} - -.list { - position: relative; - width: 280px; - transition: background-color .5s; - margin: auto auto 20px; - font-size: 19px; - line-height: 24px; -} - -.lstI { - cursor: pointer; - background-color: var(--c-2); - overflow: hidden; - border-radius: 20px; - display: block; - position: relative; - border: 1px solid var(--c-2); - padding: 8px 10px; - margin: 10px 0; - min-height: 24px; -} - -.selected { /* has to be after .lstI */ - background: var(--c-5); -} - -.lstI:hover { - background: var(--c-4); -} -/* -.lstI:last-child { - border: 0; - border-radius: 0 0 20px 20px; - padding-bottom: 10px; -} -*/ -.lstIcontent { - width: 100%; - vertical-align: middle; - padding: 0 20px 0 5px; - text-align: left; -} - -.lstIname { - white-space: nowrap; - cursor: pointer; -} - -.lstIprev { - width: 100%; - height: 8px; - position: absolute; - bottom: 0; - left: 0; - } - -/* Dropdown Content (Hidden by Default) */ -.dd-content { - display: none; - position: absolute; - width: 284px; - z-index: 1; - height: 260px; - overflow-y: scroll; - overflow-x: hidden; - padding: 0 18px; - margin-top: 10px; - -ms-overflow-style: none; - scrollbar-width: none; /* Firefox */ -} -/* Hide scrollbar for Chrome, Safari and Opera */ -.dd-content::-webkit-scrollbar { - display: none; -} - -.fnd { - position: sticky; - top: 0; - z-index: 1; - width: 280px; - margin: 0 auto; -} - -.search-icon { - position: absolute; - top: 10px; - left: 13px; - pointer-events: none; - width: 24px; - height: 24px; - margin-top: -1px; - z-index: 1; -} - -.clear-icon { - position: absolute; - display: none; - top: 10px; - right: 13px; - cursor: pointer; - margin-top: -1px; - z-index: 1; -} - -input[type=text].fnd { - display: block; - width: 100%; - box-sizing: border-box; - padding: 8px 48px 8px 48px; - margin: 5px auto 0; - text-align: left; - border-radius: 25px; - background-color: var(--c-2); - border: 1px solid var(--c-4); -} - -input[type=text].fnd:focus { - background-color: var(--c-4); -} - -input[type=text].fnd:not(:placeholder-shown), input[type=text].fnd:hover { - background-color: var(--c-3); -} - -.h, .c { - text-align: center; -} - -::-webkit-scrollbar { - width: 6px; -} -::-webkit-scrollbar-track { - background: transparent; -} -::-webkit-scrollbar-thumb { - background: var(--c-sb); - opacity: .2; - border-radius: 5px; -} -::-webkit-scrollbar-thumb:hover { - background: var(--c-sbh); -} - -@media not all and (hover: none) { - .sliderwrap:hover + output.sliderbubble { - visibility: visible; - opacity: 1; - } -} - -@media all and (max-width: 335px) { - .sliderbubble { - display: none; - } -} - -@media all and (max-width: 550px) and (min-width: 374px) { - #info .btn, #nodes .btn { - width: 150px; - } - #info div, #nodes div { - width: 320px; - } -} - -@media all and (max-width: 540px) { - .top button { - width: 16.6%; - padding: 8px 0 4px 0; - } -} - -@media all and (min-width: 541px) and (max-width: 719px) { - .top button { - width: 14.2%; - padding: 8px 0 4px 0; - } -} - -@media all and (max-width: 719px) { - .hd { - display: none !important; - } - #briwrap { - margin-top: 0 !important; - float: none; - } -} diff --git a/wled00/data/simple.htm b/wled00/data/simple.htm index 955bd65d8..eca95ddd8 100644 --- a/wled00/data/simple.htm +++ b/wled00/data/simple.htm @@ -1,263 +1,12 @@ + - - - - - - WLED - - + + -
Loading WLED UI...
- -
- -
-
-
- - -
- - - -
-
-

Global brightness

-
- -
- -
-
- -
-
-
- - -
-
-
-

Quick Load

-
-
- -
-

Solid color

-
-
-
-
-
-
-

-
-
-
-
-
R
-
-
- -
- -
-
- -
- -
-

-
-
- -
- -
-
-
-
-

RGB channels

-
-
- -
-
-

-
-
- -
-
-

-
-
- -
-
-

-
-
-

White channel

-
- -
-
-
-
-

White balance

-
- -
-
-
-
- -
-

Color slots

- -
- -
-
-
- -
-

Presets

-
- - - -
-
-
- -
-

Effect

-
- -
- -
-
- -
-
- -
- -
-
- -
-
-
Solid
-
Default
-
-
- - - -
-
- -
-
-
-
- - - -
-
- -
-
-
-
-
-
-
- -
-
-
- - - - - -
- - + + \ No newline at end of file diff --git a/wled00/data/simple.js b/wled00/data/simple.js index b35d1b624..417626b21 100644 --- a/wled00/data/simple.js +++ b/wled00/data/simple.js @@ -1,1459 +1,119 @@ -//page js +/* +This file creates the simple UI by fetching the default UI and modifying it. +*/ + var loc = false, locip, locproto = "http:"; -var noNewSegs = false; -var isOn = false, isInfo = false, isNodes = false, isRgbw = false, cct = false; -var whites = [0,0,0]; -var selColors; -var powered = [true]; -var selectedFx = 0; -var selectedPal = 0; -var csel = 0; -var currentPreset = -1; -var lastUpdate = 0; -var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0; -var tr = 7; -var d = document; -var palettesData; -var fxdata = []; -var pJson = {}, eJson = {}, lJson = {}; -var pN = "", pI = 0, pNum = 0; -var pmt = 1, pmtLS = 0, pmtLast = 0; -var lastinfo = {}; -var ws, cpick, ranges; -var cfg = { - theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, - comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, labels:true, pcmbot:false, pid:true, seglen:false} -}; -var hol = [ - [0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas - [0,2,17,1,"https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day - [2022,3,17,2,"https://aircoookie.github.io/easter.png"], - [2023,3,9,2,"https://aircoookie.github.io/easter.png"], - [2024,2,31,2,"https://aircoookie.github.io/easter.png"] -]; - -function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();} -function sCol(na, col) {d.documentElement.style.setProperty(na, col);} -function gId(c) {return d.getElementById(c);} -function gEBCN(c) {return d.getElementsByClassName(c);} -function isEmpty(o) {return Object.keys(o).length === 0;} -function isObj(i) { return (i && typeof i === 'object' && !Array.isArray(i)); } - -function applyCfg() -{ - cTheme(cfg.theme.base === "light"); - var bg = cfg.theme.color.bg; - if (bg) sCol('--c-1', bg); - var ccfg = cfg.comp.colors; - //gId('picker').style.display = "none"; // ccfg.picker ? "block":"none"; - //gId('vwrap').style.display = "none"; // ccfg.picker ? "block":"none"; - //gId('rgbwrap').style.display = ccfg.rgb ? "block":"none"; - gId('qcs-w').style.display = ccfg.quick ? "block":"none"; - var l = cfg.comp.labels; //l = false; - var e = d.querySelectorAll('.tab-label'); - for (var i=0; i { - var a = parseFloat(cfg.theme.alpha.bg); - if (isNaN(a)) a = 0.6; - bg.style.opacity = a; - bg.style.backgroundImage = `url(${img.src})`; - img = null; - }); -} - -function loadSkinCSS(cId) -{ - if (!gId(cId)) // check if element exists - { - var h = document.getElementsByTagName('head')[0]; - var l = document.createElement('link'); - l.id = cId; - l.rel = 'stylesheet'; - l.type = 'text/css'; - l.href = getURL('/skin.css'); - l.media = 'all'; - h.appendChild(l); +} else { + // detect reverse proxy + let paths = window.location.pathname.slice(1, window.location.pathname.endsWith('/') ? -1 : undefined).split("/"); + if (paths.length > 2) { + locproto = window.location.protocol; + loc = true; + locip = window.location.hostname + (window.location.port ? ":" + window.location.port : "") + "/" + paths[0]; } } function getURL(path) { return (loc ? locproto + "//" + locip : "") + path; } -async function onLoad() -{ - let l = window.location; - if (l.protocol == "file:") { - loc = true; - locip = localStorage.getItem('locIp'); - if (!locip) { - locip = prompt("File Mode. Please enter WLED IP!"); - localStorage.setItem('locIp', locip); - } - } else { - // detect reverse proxy and/or HTTPS - let pathn = l.pathname; - let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/"); - if (paths[0]==="sliders") paths.shift(); - //while (paths[0]==="") paths.shift(); - locproto = l.protocol; - locip = l.hostname + (l.port ? ":" + l.port : ""); - if (paths.length > 0 && paths[0]!=="") { - loc = true; - locip += "/" + paths.join('/'); - } else if (locproto==="https:") { - loc = true; - } - } - var sett = localStorage.getItem('wledUiCfg'); - if (sett) cfg = mergeDeep(cfg, JSON.parse(sett)); - makeWS(); +// fetch default UI and modify it +fetch(getURL("/index.htm")) + .then(response => response.text()) + .then(data => { + let parser = new DOMParser(); + let doc = parser.parseFromString(data, 'text/html'); + // patch simple ui + simplifyUI(doc); - applyCfg(); - if (cfg.theme.bg.url=="" || cfg.theme.bg.url === "https://picsum.photos/1920/1080") { - var iUrl = cfg.theme.bg.url; - fetch(getURL("/holidays.json"), { - method: 'get' - }) - .then((res)=>{ - return res.json(); - }) - .then((json)=>{ - if (Array.isArray(json)) hol = json; - //TODO: do some parsing first - }) - .catch((e)=>{ - console.log("holidays.json does not contain array of holidays. Defaults loaded."); - }) - .finally(()=>{ - var today = new Date(); - for (var i=0; i=hs && today { + // create new script element + let newScript = document.createElement('script'); + // copy attributes + for (let i = 0; i < script.attributes.length; i++) { + newScript.setAttribute(script.attributes[i].name, script.attributes[i].value); } - if (iUrl !== "") loadBg(iUrl); + // copy content + newScript.innerHTML = script.innerHTML; + // replace script + script.parentNode.replaceChild(newScript, script); }); - } else - loadBg(cfg.theme.bg.url); - loadSkinCSS('skinCss'); + finalizeSimpleUI(); - var cd = gId('csl').children; - for (var i = 0; i < cd.length; i++) cd[i].style.backgroundColor = "rgb(0, 0, 0)"; - selectSlot(0); - cpick.on("input:end", ()=>{ - setColor(1); - }); - pmtLS = localStorage.getItem('wledPmt'); + }) + .catch(error => console.error('Error:', error)); - // Load initial data - loadPalettes(()=>{ - loadPalettesData(redrawPalPrev); - loadFX(()=>{ - loadFXData(); - loadPresets(()=>{ - requestJson(); - }); +// Transforms the default UI into the simple UI +function simplifyUI(doc) { + function gId(id) { + return doc.getElementById(id); + } + + // Disable PC Mode as it does not exist in simple UI + localStorage.setItem("pcm", "false"); + + // Put effects below palett list + gId("Colors").innerHTML += gId("Effects").innerHTML; + // Hide buttons in top bar + gId("buttonNl").style.display = "none"; + gId("buttonSync").style.display = "none"; + gId("buttonSr").style.display = "none"; + gId("buttonPcm").style.display = "none"; + + // Hide bottom bar + gId("bot").style.display = "none"; + doc.documentElement.style.setProperty('--bh', '0px'); + + // Hide other tabs + gId("Effects").style.display = "none"; + gId("Segments").style.display = "none"; + gId("Presets").style.display = "none"; + + // Chage height of palette list + gId("pallist").style.height = "300px"; + gId("pallist").style.overflow = "scroll"; + + // set brightness to 255 and hide slider + gId("sliderBri").value = 255; + gId("briwrap").style.display = "none"; + + // Hide filter options + gId("filters").style.display = "none"; + + /* var observer = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + if (mutation.attributeName === "class") { + let element = mutation.target; + if (element.matches("#pallist .lstI.selected")) { + element.style.top = "0px"; + } + } }); }); - - d.addEventListener("visibilitychange", handleVisibilityChange, false); - size(); - gId("cv").style.opacity=0; - var sls = d.querySelectorAll('input[type="range"]'); - for (var sl of sls) { - sl.addEventListener('touchstart', toggleBubble); - sl.addEventListener('touchend', toggleBubble); - } -} - -var timeout; -function showToast(text, error = false) -{ - if (error) gId('connind').style.backgroundColor = "var(--c-r)"; - var x = gId("toast"); - x.innerHTML = text; - x.className = error ? "error":"show"; - clearTimeout(timeout); - x.style.animation = 'none'; - timeout = setTimeout(()=>{ x.classList.remove("show"); }, 2900); - if (error) console.log(text); -} - -function showErrorToast() -{ - if (ws && ws.readyState === WebSocket.OPEN) { - // if we received a timeout force WS reconnect - ws.close(); - ws = null; - if (lastinfo.ws > -1) setTimeout(makeWS,500); - } - showToast('Connection to light failed!', true); -} - -function clearErrorToast() {gId("toast").className = gId("toast").className.replace("error", "");} - -function getRuntimeStr(rt) -{ - var t = parseInt(rt); - var days = Math.floor(t/86400); - var hrs = Math.floor((t - days*86400)/3600); - var mins = Math.floor((t - days*86400 - hrs*3600)/60); - var str = days ? (days + " " + (days == 1 ? "day" : "days") + ", ") : ""; - str += (hrs || days) ? (hrs + " " + (hrs == 1 ? "hour" : "hours")) : ""; - if (!days && hrs) str += ", "; - if (t > 59 && !days) str += mins + " min"; - if (t < 3600 && t > 59) str += ", "; - if (t < 3600) str += (t - mins*60) + " sec"; - return str; -} - -function inforow(key, val, unit = "") -{ - return `${key}${val}${unit}`; -} - -function pName(i) -{ - var n = "Preset " + i; - if (pJson && pJson[i] && pJson[i].n) n = pJson[i].n; - return n; -} - -function isPlaylist(i) -{ - return pJson[i].playlist && pJson[i].playlist.ps; -} - -function papiVal(i) -{ - if (!pJson || !pJson[i]) return ""; - var o = Object.assign({},pJson[i]); - if (o.win) return o.win; - delete o.n; delete o.p; delete o.ql; - return JSON.stringify(o); -} - -function qlName(i) -{ - if (!pJson || !pJson[i] || !pJson[i].ql) return ""; - return pJson[i].ql; -} - -function cpBck() -{ - var copyText = gId("bck"); - - copyText.select(); - copyText.setSelectionRange(0, 999999); - d.execCommand("copy"); - showToast("Copied to clipboard!"); -} - -function loadPresets(callback = null) -{ - //1st boot (because there is a callback) - if (callback && pmt == pmtLS && pmt > 0) { - //we have a copy of the presets in local storage and don't need to fetch another one - pJson = JSON.parse(localStorage.getItem("wledP")); - populatePresets(); - pmtLast = pmt; - callback(); - return; - } - - //afterwards - if (!callback && pmt == pmtLast) return; - - pmtLast = pmt; - - fetch(getURL('/presets.json'), { - method: 'get' - }) - .then(res => { - if (!res.ok) showErrorToast(); - return res.json(); - }) - .then(json => { - clearErrorToast(); - pJson = json; - populatePresets(); - }) - .catch(function (error) { - showToast(error, true); - console.log(error); - }) - .finally(()=>{ - if (callback) setTimeout(callback,99); - }); -} - -function loadPalettes(callback = null) -{ - fetch(getURL('/json/palettes'), { - method: 'get' - }) - .then(res => { - if (!res.ok) showErrorToast(); - return res.json(); - }) - .then(json => { - clearErrorToast(); - lJson = Object.entries(json); - populatePalettes(); - }) - .catch(function (error) { - showToast(error, true); - }) - .finally(()=>{ - if (callback) callback(); - }); -} - -function loadFX(callback = null) -{ - fetch(getURL('/json/effects'), { - method: 'get' - }) - .then(res => { - if (!res.ok) showErrorToast(); - return res.json(); - }) - .then(json => { - clearErrorToast(); - eJson = Object.entries(json); - populateEffects(); - }) - .catch(function (error) { - showToast(error, true); - }) - .finally(()=>{ - if (callback) callback(); - }); -} - -function loadFXData(callback = null) -{ - fetch(getURL('/json/fxdata'), { - method: 'get' - }) - .then(res => { - if (!res.ok) showErrorToast(); - return res.json(); - }) - .then(json => { - clearErrorToast(); - fxdata = json||[]; - // add default value for Solid - fxdata.shift() - fxdata.unshift("@;!;"); - }) - .catch(function (error) { - fxdata = []; - showToast(error, true); - }) - .finally(()=>{ - if (callback) callback(); - updateUI(); - }); -} - -var pQL = []; -function populateQL() -{ - var cn = ""; - if (pQL.length > 0) { - pQL.sort((a,b) => (a[0]>b[0])); - for (var key of (pQL||[])) { - cn += ``; - } - } - gId('pql').innerHTML = cn; -} - -function populatePresets() -{ - if (!pJson) {pJson={};return}; - delete pJson["0"]; - var cn = ""; //`

All presets

`; - var arr = Object.entries(pJson); - arr.sort(cmpP); - pQL = []; - var is = []; - pNum = 0; - for (var key of (arr||[])) - { - if (!isObj(key[1])) continue; - let i = parseInt(key[0]); - var qll = key[1].ql; - if (qll) pQL.push([i, qll, pName(i)]); - is.push(i); - - cn += `
`; - //if (cfg.comp.pid) cn += `
${i}
`; - cn += `${isPlaylist(i)?"":""}${pName(i)}
`; - pNum++; - } - gId('pcont').innerHTML = cn; - updatePA(); - populateQL(); -} - -function parseInfo() { - var li = lastinfo; - var name = li.name; - gId('namelabel').innerHTML = name; -// if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; - if (li.live) name = "(Live) " + name; - if (loc) name = "(L) " + name; - d.title = name; - isRgbw = li.leds.wv; - ledCount = li.leds.count; -// syncTglRecv = li.str; - maxSeg = li.leds.maxseg; - pmt = li.fs.pmt; - cct = li.leds.cct; -} - -function populateInfo(i) -{ - var cn=""; - var heap = i.freeheap/1000; - heap = heap.toFixed(1); - var pwr = i.leds.pwr; - var pwru = "Not calculated"; - if (pwr > 1000) {pwr /= 1000; pwr = pwr.toFixed((pwr > 10) ? 0 : 1); pwru = pwr + " A";} - else if (pwr > 0) {pwr = 50 * Math.round(pwr/50); pwru = pwr + " mA";} - var urows=""; - if (i.u) { - for (const [k, val] of Object.entries(i.u)) { - if (val[1]) - urows += inforow(k,val[0],val[1]); - else - urows += inforow(k,val); - } - } - var vcn = "Kuuhaku"; - if (i.ver.startsWith("0.14.")) vcn = "Hoshi"; - if (i.ver.includes("-bl")) vcn = "Supāku"; - if (i.cn) vcn = i.cn; - - cn += `v${i.ver} "${vcn}"

-${urows} -${inforow("Build",i.vid)} -${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} -${inforow("Uptime",getRuntimeStr(i.uptime))} -${inforow("Time",i.time)} -${inforow("Free heap",heap," kB")} -${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""} -${inforow("Estimated current",pwru)} -${inforow("Average FPS",i.leds.fps)} -${inforow("MAC address",i.mac)} -${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")} -${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")} -
`; - gId('kv').innerHTML = cn; -} - -function populateSegments(s) -{ - var cn = ""; - segCount = (s.seg||[]).length; - lowestUnused = 0; lSeg = 0; - - if (segCount > 1) { - for (var y = 0; y < segCount && y<4; y++) - { - var inst=s.seg[y]; - let i = parseInt(inst.id); - powered[i] = inst.on; - if (i == lowestUnused) lowestUnused = i+1; - if (i > lSeg) lSeg = i; - - cn += -`
${(inst.n&&inst.n!=='')?inst.n:('Segment '+y)}
-
- -
- -
- -
-
- -
-
`; - } - //if (gId('buttonBri').className !== 'active') tglBri(true); - } else { - //tglBri(false); - } - //gId('buttonBri').style.display = (segCount > 1) ? "block" : "none"; - gId('segcont').innerHTML = cn; - for (var i = 0; i < segCount && i<4; i++) updateTrail(gId(`seg${i}bri`)); -} - -function btype(b) -{ - switch (b) { - case 2: - case 32: return "ESP32"; - case 1: - case 82: return "ESP8266"; - } - return "?"; + var config = { attributes: true, childList: true, subtree: true }; + observer.observe(doc.body, config); */ } -function bname(o) -{ - if (o.name=="WLED") return o.ip; - return o.name; -} - -function populateNodes(i,n) -{ - var cn=""; - var urows=""; - var nnodes = 0; - if (n.nodes) { - n.nodes.sort((a,b) => (a.name).localeCompare(b.name)); - for (var x=0;x${bname(o)}`; - urows += inforow(url,`${btype(o.type)}
${o.vid==0?"N/A":o.vid}`); - nnodes++; - } - } - } - if (i.ndc < 0) cn += `Instance List is disabled.`; - else if (nnodes == 0) cn += `No other instances found.`; - cn += ` - ${urows} - ${inforow("Current instance:",i.name)} -
`; - gId('kn').innerHTML = cn; -} - -function loadNodes() -{ - fetch(getURL('/json/nodes'), { - method: 'get' - }) - .then(res => { - if (!res.ok) showToast('Could not load Node list!', true); - return res.json(); - }) - .then(json => { - clearErrorToast(); - populateNodes(lastinfo, json); - }) - .catch(function (error) { - showToast(error, true); - console.log(error); - }); -} - -function populateEffects() -{ - var effects = eJson; - var html = ""; - - effects.shift(); //remove solid - for (let i = 0; i < effects.length; i++) effects[i] = {id: effects[i][0], name:effects[i][1]}; - effects.sort((a,b) => (a.name).localeCompare(b.name)); - effects.unshift({ - "id": 0, - "name": "Solid@;!;0" - }); - - for (let i = 0; i < effects.length; i++) { - // WLEDSR: add slider and color control to setEffect (used by requestjson) - if (effects[i].name.indexOf("RSVD") < 0) { - var posAt = effects[i].name.indexOf("@"); - var extra = ''; - if (posAt > 0) - extra = effects[i].name.substr(posAt); - else - posAt = 999; - html += generateListItemHtml( - 'fx', - effects[i].id, - effects[i].name.substr(0,posAt), - 'setEffect', - '','', - extra - ); - } - } - gId('fxlist').innerHTML=html; -} - -function populatePalettes() -{ - var palettes = lJson; - palettes.shift(); //remove default - for (let i = 0; i < palettes.length; i++) { - palettes[i] = { - "id": palettes[i][0], - "name": palettes[i][1] - }; - } - palettes.sort((a,b) => (a.name).localeCompare(b.name)); - palettes.unshift({ - "id": 0, - "name": "Default", - }); - var html = ""; - for (let i = 0; i < palettes.length; i++) { - html += generateListItemHtml( - 'palette', - palettes[i].id, - palettes[i].name, - 'setPalette', - `
` - ); - } - gId('pallist').innerHTML=html; -} - -function redrawPalPrev() -{ - let palettes = d.querySelectorAll('#pallist .lstI'); - for (let i = 0; i < palettes.length; i++) { - let id = palettes[i].dataset.id; - let lstPrev = palettes[i].querySelector('.lstIprev'); - if (lstPrev) { - lstPrev.style = genPalPrevCss(id); - } - } -} - -function genPalPrevCss(id) -{ - if (!palettesData) return; - - var paletteData = palettesData[id]; - var previewCss = ""; - - if (!paletteData) return 'display: none'; - - // We need at least two colors for a gradient - if (paletteData.length == 1) { - paletteData[1] = paletteData[0]; - if (Array.isArray(paletteData[1])) { - paletteData[1][0] = 255; - } - } - - var gradient = []; - for (let j = 0; j < paletteData.length; j++) { - const element = paletteData[j]; - let r; - let g; - let b; - let index = false; - if (Array.isArray(element)) { - index = element[0]/255*100; - r = element[1]; - g = element[2]; - b = element[3]; - } else if (element == 'r') { - r = Math.random() * 255; - g = Math.random() * 255; - b = Math.random() * 255; - } else { - if (selColors) { - let e = element[1] - 1; - r = selColors[e][0]; - g = selColors[e][1]; - b = selColors[e][2]; - } - } - if (index === false) { - index = j / paletteData.length * 100; - } - - gradient.push(`rgb(${r},${g},${b}) ${index}%`); - } - - return `background: linear-gradient(to right,${gradient.join()});`; -} - -function generateOptionItemHtml(id, name) -{ - return ``; -} - -function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', extraClass = '', extraPar = '') -{ - return `
-
- - ${name} - -
- ${extraHtml} -
`; -} - -//update the 'sliderdisplay' background div of a slider for a visual indication of slider position -function updateTrail(e) -{ - if (e==null) return; - var max = e.hasAttribute('max') ? e.attributes.max.value : 255; - var perc = e.value * 100 / max; - perc = parseInt(perc); - if (perc < 50) perc += 2; - var val = `linear-gradient(90deg, var(--c-f) ${perc}%, var(--c-4) ${perc}%)`; - e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val; - var b = e.parentNode.parentNode.getElementsByTagName('output')[0]; - if (b) b.innerHTML = e.value; -} - -//rangetouch slider function -function toggleBubble(e) -{ - var b = e.target.parentNode.parentNode.getElementsByTagName('output')[0]; - b.classList.toggle('sliderbubbleshow'); -} - -function updatePA() -{ - var ps = gEBCN("pres"); - for (let i = 0; i < ps.length; i++) { - ps[i].classList.remove('selected');; - } - ps = gEBCN("psts"); - for (let i = 0; i < ps.length; i++) { - ps[i].classList.remove('selected');; - } - if (currentPreset > 0) { - var acv = gId(`p${currentPreset}o`); - if (acv) acv.classList.add('selected'); - acv = gId(`p${currentPreset}qlb`); - if (acv) acv.classList.add('selected'); - } -} - -function updateUI() -{ - gId('buttonPower').className = (isOn) ? "active":""; - - var sel = 0; - if (lJson && lJson.length) { - for (var i=0; i b[0]); - // playlists follow presets - var name = (a[1].playlist ? '~' : ' ') + a[1].n; - return name.localeCompare((b[1].playlist ? '~' : ' ') + b[1].n, undefined, {numeric: true}); -} - -function makeWS() { - if (ws) return; - let url = loc ? getURL('/ws').replace("http","ws") : "ws://"+window.location.hostname+"/ws"; - ws = new WebSocket(url); - ws.onmessage = (e)=>{ - var json = JSON.parse(e.data); - if (json.leds) return; //liveview packet - clearTimeout(jsonTimeout); - jsonTimeout = null; - lastUpdate = new Date(); - clearErrorToast(); - gId('connind').style.backgroundColor = "var(--c-l)"; - // json object should contain json.info AND json.state (but may not) - var i = json.info; - if (i) { - lastinfo = i; - parseInfo(); - if (isInfo) populateInfo(i); - } else - i = lastinfo; - var s = json.state ? json.state : json; - readState(s); - }; - ws.onclose = (e)=>{ - gId('connind').style.backgroundColor = "var(--c-r)"; - ws = null; - if (lastinfo.ws > -1) setTimeout(makeWS,500); - } - ws.onopen = (e)=>{ - ws.send("{'v':true}"); - reqsLegal = true; - clearErrorToast(); - } -} - -function readState(s,command=false) -{ - if (!s) return false; - - isOn = s.on; - gId('sliderBri').value= s.bri; - nlA = s.nl.on; - nlDur = s.nl.dur; - nlTar = s.nl.tbri; - nlFade = s.nl.fade; - syncSend = s.udpn.send; - if (s.pl<0) currentPreset = s.ps; - else currentPreset = s.pl; - tr = s.transition/10; - - var selc=0; var ind=0; - populateSegments(s); - for (let i = 0; i < (s.seg||[]).length; i++) - { - if(s.seg[i].sel) {selc = ind; break;} ind++; - } - var i=s.seg[selc]; - if (!i) { - showToast('No Segments!', true); - updateUI(); - return; - } - - selColors = i.col; - var cd = gId('csl').children; - for (let e = cd.length-1; e >= 0; e--) - { - var r,g,b,w; - r = i.col[e][0]; - g = i.col[e][1]; - b = i.col[e][2]; - if (isRgbw) w = i.col[e][3]; - cd[e].style.backgroundColor = "rgb(" + r + "," + g + "," + b + ")"; - if (isRgbw) whites[e] = parseInt(w); - selectSlot(csel); - } - gId('sliderW').value = whites[csel]; - if (i.cct && i.cct>=0) gId("sliderA").value = i.cct; - - gId('sliderSpeed').value = i.sx; - gId('sliderIntensity').value = i.ix; -/* - gId('sliderC1').value = i.f1x ? i.f1x : 0; - gId('sliderC2').value = i.f2x ? i.f2x : 0; - gId('sliderC3').value = i.f3x ? i.f3x : 0; -*/ - if (s.error && s.error != 0) { - var errstr = ""; - switch (s.error) { - case 10: - errstr = "Could not mount filesystem!"; - break; - case 11: - errstr = "Not enough space to save preset!"; - break; - case 12: - errstr = "Preset not found."; - break; - case 13: - errstr = "Missing IR.json."; - break; - case 19: - errstr = "A filesystem error has occured."; - break; - } - showToast('Error ' + s.error + ": " + errstr, true); - } - - selectedPal = i.pal; - selectedFx = i.fx; - updateUI(); +// Called when simple UI is ready +function finalizeSimpleUI() { + // disable horizontal scrolling + simpleUI = true; + // set brightness + setBri(); + // set correct position of selected and sticky palette + Array.from(document.styleSheets[0].cssRules).find(rule => rule.selectorText == "#pallist .lstI.sticky").style.top = "0px"; + Array.from(document.styleSheets[0].cssRules).find(rule => rule.selectorText == "#pallist .lstI.selected").style.top = "42px"; + } - -var jsonTimeout; -var reqsLegal = false; - -function requestJson(command=null) -{ - gId('connind').style.backgroundColor = "var(--c-r)"; - if (command && !reqsLegal) return; //stop post requests from chrome onchange event on page restore - if (!jsonTimeout) jsonTimeout = setTimeout(showErrorToast, 3000); - var req = null; - var useWs = (ws && ws.readyState === WebSocket.OPEN); - var type = command ? 'post':'get'; - if (command) { - if (useWs || !command.ps) command.v = true; // force complete /json/si API response - command.time = Math.floor(Date.now() / 1000); - req = JSON.stringify(command); - if (req.length > 1000) useWs = false; //do not send very long requests over websocket - }; - - if (useWs) { - ws.send(req?req:'{"v":true}'); - return; - } else if (command && command.ps) { //refresh UI if we don't use WS (async loading of presets) - setTimeout(requestJson,200); - } - - fetch(getURL('/json/si'), { - method: type, - headers: { - "Content-type": "application/json; charset=UTF-8" - }, - body: req - }) - .then(res => { - if (!res.ok) showErrorToast(); - return res.json(); - }) - .then(json => { - clearTimeout(jsonTimeout); - jsonTimeout = null; - lastUpdate = new Date(); - clearErrorToast(); - gId('connind').style.backgroundColor = "var(--c-g)"; - if (!json) { showToast('Empty response', true); return; } - if (json.success) return; - if (json.info) { - lastinfo = json.info; - parseInfo(); - if (isInfo) populateInfo(lastinfo); - } - var s = json.state ? json.state : json; - readState(s); - reqsLegal = true; - }) - .catch(function (error) { - showToast(error, true); - console.log(error); - }); -} - -function togglePower() -{ - isOn = !isOn; - var obj = {"on": isOn}; - requestJson(obj); -} - -function toggleInfo() -{ - if (isNodes) toggleNodes(); - isInfo = !isInfo; - if (isInfo) requestJson(); - gId('info').style.transform = (isInfo) ? "translateY(0px)":"translateY(100%)"; - gId('buttonI').className = (isInfo) ? "active":""; -} - -function toggleNodes() -{ - if (isInfo) toggleInfo(); - isNodes = !isNodes; - if (isNodes) loadNodes(); - gId('nodes').style.transform = (isNodes) ? "translateY(0px)":"translateY(100%)"; - gId('buttonNodes').className = (isNodes) ? "active":""; -} -/* -function tglBri(b=null) -{ - if (b===null) b = gId(`briwrap`).style.display === "block"; - gId('briwrap').style.display = !b ? "block":"none"; - gId('buttonBri').className = !b ? "active":""; - size(); -} -*/ -function tglCP() -{ - var p = gId('buttonCP').className === "active"; - gId('buttonCP').className = !p ? "active":""; - gId('picker').style.display = !p ? "block":"none"; - gId('vwrap').style.display = !p ? "block":"none"; - gId('rgbwrap').style.display = !p ? "block":"none"; - var csl = gId('Slots').style.display === "block"; - gId('Slots').style.display = !csl ? "block":"none"; - //var ps = gId(`Presets`).style.display === "block"; - //gId('Presets').style.display = !ps ? "block":"none"; -} - -function tglCs(i) -{ - var pss = gId(`p${i}cstgl`).checked; - gId(`p${i}o1`).style.display = pss? "block" : "none"; - gId(`p${i}o2`).style.display = !pss? "block" : "none"; -} - -function selSeg(s) -{ - var sel = gId(`seg${s}sel`).checked; - var obj = {"seg": {"id": s, "sel": sel}}; - requestJson(obj); -} - -function tglPalDropdown() -{ - var p = gId('palDropdown').style; - p.display = (p.display==='block'?'none':'block'); - gId('fxDropdown').style.display = 'none'; - if (p.display==='block') - gId('palDropdown').scrollIntoView({ - behavior: 'smooth', - block: 'center', - }); -} - -function tglFxDropdown() -{ - var p = gId('fxDropdown').style; - p.display = (p.display==='block'?'none':'block'); - gId('palDropdown').style.display = 'none'; - if (p.display==='block') - gId('fxDropdown').scrollIntoView({ - behavior: 'smooth', - block: 'center', - }); -} - -function setSegPwr(s) -{ - var obj = {"seg": {"id": s, "on": !powered[s]}}; - requestJson(obj); -} - -function setSegBri(s) -{ - var obj = {"seg": {"id": s, "bri": parseInt(gId(`seg${s}bri`).value)}}; - requestJson(obj); -} - -function setEffect(ind = 0) -{ - tglFxDropdown(); - var obj = {"seg": {"fx": parseInt(ind), "fxdef":true}}; // fxdef sets effect parameters to default values, TODO add client setting - requestJson(obj); -} - -function setPalette(paletteId = null) -{ - tglPalDropdown(); - var obj = {"seg": {"pal": paletteId}}; - requestJson(obj); -} - -function setBri() -{ - var obj = {"bri": parseInt(gId('sliderBri').value)}; - requestJson(obj); -} - -function setSpeed() -{ - var obj = {"seg": {"sx": parseInt(gId('sliderSpeed').value)}}; - requestJson(obj); -} - -function setIntensity() -{ - var obj = {"seg": {"ix": parseInt(gId('sliderIntensity').value)}}; - requestJson(obj); -} - -function setLor(i) -{ - var obj = {"lor": i}; - requestJson(obj); -} - -function setPreset(i) -{ - var obj = {"ps": i}; - if (isPlaylist(i)) obj.on = true; - showToast("Loading preset " + pName(i) +" (" + i + ")"); - requestJson(obj); -} - -function selectSlot(b) -{ - csel = b; - var cd = gId('csl').children; - for (let i = 0; i < cd.length; i++) cd[i].classList.remove('xxs-w'); - cd[b].classList.add('xxs-w'); - setPicker(cd[b].style.backgroundColor); - gId('sliderW').value = whites[b]; - redrawPalPrev(); - updatePSliders(); -} - -var lasth = 0; -function pC(col) -{ - if (col == "rnd") { - col = {h: 0, s: 0, v: 100}; - col.s = Math.floor((Math.random() * 50) + 50); - do { - col.h = Math.floor(Math.random() * 360); - } while (Math.abs(col.h - lasth) < 50); - lasth = col.h; - } - setPicker(col); - setColor(0); -} - -function updatePSliders() { - //update RGB sliders - var col = cpick.color.rgb; - gId('sliderR').value = col.r; - gId('sliderG').value = col.g; - gId('sliderB').value = col.b; - - //update hex field - var str = cpick.color.hexString.substring(1); - var w = whites[csel]; - if (w > 0) str += w.toString(16); - - //update value slider - var v = gId('sliderV'); - v.value = cpick.color.value; - //background color as if color had full value - var hsv = {"h":cpick.color.hue,"s":cpick.color.saturation,"v":100}; - var c = iro.Color.hsvToRgb(hsv); - var cs = 'rgb('+c.r+','+c.g+','+c.b+')'; - v.nextElementSibling.style.backgroundImage = `linear-gradient(90deg, #000 0%, ${cs})`; - - //update Kelvin slider - gId('sliderK').value = cpick.color.kelvin; -} - -function setPicker(rgb) { - var c = new iro.Color(rgb); - if (c.value > 0) cpick.color.set(c); - else cpick.color.setChannel('hsv', 'v', 0); -} - -function fromV() -{ - cpick.color.setChannel('hsv', 'v', d.getElementById('sliderV').value); -} - -function fromK() -{ - cpick.color.set({ kelvin: d.getElementById('sliderK').value }); -} - -function fromRgb() -{ - var r = gId('sliderR').value; - var g = gId('sliderG').value; - var b = gId('sliderB').value; - setPicker(`rgb(${r},${g},${b})`); - setColor(0); -} - -// sets color from picker: 0=all, 1=leaving picker/HSV, 2=ignore white channel -function setColor(sr) -{ - var cd = gId('csl').children; // color slots - if (sr == 1 && cd[csel].style.backgroundColor == 'rgb(0, 0, 0)') cpick.color.setChannel('hsv', 'v', 100); - cd[csel].style.backgroundColor = cpick.color.rgbString; - if (sr != 2) whites[csel] = parseInt(gId('sliderW').value); - var col = cpick.color.rgb; - var obj = {"seg": {"col": [[col.r, col.g, col.b, whites[csel]],[],[]]}}; - if (sr==1 || gId(`picker`).style.display !== "block") obj.seg.fx = 0; - if (csel == 1) { - obj = {"seg": {"col": [[],[col.r, col.g, col.b, whites[csel]],[]]}}; - } else if (csel == 2) { - obj = {"seg": {"col": [[],[],[col.r, col.g, col.b, whites[csel]]]}}; - } - requestJson(obj); -} - -function setBalance(b) -{ - var obj = {"seg": {"cct": parseInt(b)}}; - requestJson(obj); -} - -var hc = 0; -setInterval(()=>{if (!isInfo) return; hc+=18; if (hc>300) hc=0; if (hc>200)hc=306; if (hc==144) hc+=36; if (hc==108) hc+=18; -gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;}, 910); - -function openGH() { window.open("https://github.com/Aircoookie/WLED/wiki"); } - -var cnfr = false; -function cnfReset() -{ - if (!cnfr) { - var bt = gId('resetbtn'); - bt.style.color = "#f00"; - bt.innerHTML = "Confirm Reboot"; - cnfr = true; return; - } - window.location.href = "/reset"; -} - -function loadPalettesData(callback = null) -{ - if (palettesData) return; - const lsKey = "wledPalx"; - var palettesDataJson = localStorage.getItem(lsKey); - if (palettesDataJson) { - try { - palettesDataJson = JSON.parse(palettesDataJson); - if (palettesDataJson && palettesDataJson.vid == lastinfo.vid) { - palettesData = palettesDataJson.p; - if (callback) callback(); //redrawPalPrev() - return; - } - } catch (e) {} - } - - palettesData = {}; - getPalettesData(0, ()=>{ - localStorage.setItem(lsKey, JSON.stringify({ - p: palettesData, - vid: lastinfo.vid - })); - if (callback) setTimeout(callback, 99); //redrawPalPrev() - }); -} - -function getPalettesData(page, callback) -{ - fetch(getURL(`/json/palx?page=${page}`), { - method: 'get', - headers: { - "Content-type": "application/json; charset=UTF-8" - } - }) - .then((res)=>{ - if (!res.ok) showErrorToast(); - return res.json(); - }) - .then((json)=>{ - palettesData = Object.assign({}, palettesData, json.p); - if (page < json.m) setTimeout(()=>{ getPalettesData(page + 1, callback); }, 50); - else callback(); - }) - .catch((e)=>{ - showToast(e, true); - }); -} - -function search(f,l=null) -{ - f.nextElementSibling.style.display=(f.value!=='')?'block':'none'; - if (!l) return; - var el = gId(l).querySelectorAll('.lstI'); - for (i = 0; i < el.length; i++) { - var it = el[i]; - var itT = it.querySelector('.lstIname').innerText.toUpperCase(); - it.style.display = itT.indexOf(f.value.toUpperCase())>-1?'':'none'; - } -} - -function clean(c) -{ - c.style.display='none'; - var i=c.previousElementSibling; - i.value=''; - i.focus(); - i.dispatchEvent(new Event('input')); -} - -function unfocusSliders() -{ - gId("sliderBri").blur(); - gId("sliderSpeed").blur(); - gId("sliderIntensity").blur(); -} - -//sliding UI -const _C = d.querySelector('.container'), N = 1; - -let iSlide = 0, x0 = null, scrollS = 0, locked = false, w; - -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; -} -//required by rangetouch.js -function lock(e) -{ - var l = e.target.classList; - var pl = e.target.parentElement.classList; - - if (l.contains('noslide') || hasIroClass(l) || hasIroClass(pl)) return; - - x0 = unify(e).clientX; - scrollS = gEBCN("tabcontent")[iSlide].scrollTop; - - _C.classList.toggle('smooth', !(locked = true)); -} -//required by rangetouch.js -function move(e) -{ - if(!locked) return; - var clientX = unify(e).clientX; - var dx = clientX - x0; - var s = Math.sign(dx); - var f = +(s*dx/w).toFixed(2); - - if((clientX != 0) && - (iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) && - f > 0.12 && - gEBCN("tabcontent")[iSlide].scrollTop == scrollS) - { - _C.style.setProperty('--i', iSlide -= s); - f = 1 - f; - updateTablinks(iSlide); - } - _C.style.setProperty('--f', f); - _C.classList.toggle('smooth', !(locked = false)); - x0 = null; -} - -function size() -{ - var h = gId('top').clientHeight; - sCol('--th', h + "px"); - sCol("--tp", h - (gId(`briwrap`).style.display === "block" ? 0 : gId(`briwrap`).clientTop) + "px"); - sCol("--bh", "0px"); -} - -function mergeDeep(target, ...sources) -{ - if (!sources.length) return target; - const source = sources.shift(); - - if (isObj(target) && isObj(source)) { - for (const key in source) { - if (isObj(source[key])) { - if (!target[key]) Object.assign(target, { [key]: {} }); - mergeDeep(target[key], source[key]); - } else { - Object.assign(target, { [key]: source[key] }); - } - } - } - return mergeDeep(target, ...sources); -} - -size(); -window.addEventListener('resize', size, false); - -_C.addEventListener('mousedown', lock, false); -_C.addEventListener('touchstart', lock, false); - -_C.addEventListener('mouseout', move, false); -_C.addEventListener('mouseup', move, false); -_C.addEventListener('touchend', move, false); diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index 4dbfa1909..56e112a0c 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -253,6 +253,15 @@ void initServer() size_t len, bool final) {handleUpload(request, filename, index, data, len, final);} ); + server.on("/index.htm", HTTP_GET, [](AsyncWebServerRequest *request){ + if (handleFileRead(request, "/index.htm")) return; + if (handleIfNoneMatchCacheHeader(request)) return; + AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L); + response->addHeader(FPSTR(s_content_enc),"gzip"); + setStaticContentCacheHeaders(response); + request->send(response); + }); + #ifdef WLED_ENABLE_SIMPLE_UI server.on("/simple.htm", HTTP_GET, [](AsyncWebServerRequest *request){ if (handleFileRead(request, "/simple.htm")) return;