├── fetch.html.gz ├── icons ├── icon.png ├── icon_r.png ├── icon.svg └── icon_r.svg ├── .gitignore ├── LICENSE ├── Test ├── longpress custom.min.js ├── longpress.min.js ├── test.html ├── longpress custom.js ├── longpress.js ├── test.css ├── efc.min.js └── test.min.js ├── README.md ├── fetch.min.html └── fetch.html /fetch.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chromoxdor/easyfetch/HEAD/fetch.html.gz -------------------------------------------------------------------------------- /icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chromoxdor/easyfetch/HEAD/icons/icon.png -------------------------------------------------------------------------------- /icons/icon_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chromoxdor/easyfetch/HEAD/icons/icon_r.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## OS Specific Hidden Files #### 2 | 3 | # MacOSX Finder ***** 4 | .DS_Store 5 | 6 | 7 | /doku 8 | /sketch 9 | test/test_bkup.css 10 | test/test.min.css 11 | test/test 2.js 12 | .vscode 13 | fetch.min.html 14 | 15 | -------------------------------------------------------------------------------- /icons/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icons/icon_r.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 chromoxdor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Test/longpress custom.min.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"use strict";let t=null;const o=10,a=10;let i={x:0,y:0},s=1e3;const c="ontouchstart"in e||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0,r="PointerEvent"in e||e.navigator&&"msPointerEnabled"in e.navigator?{down:"pointerdown",up:"pointerup",move:"pointermove",leave:"pointerleave"}:c?{down:"touchstart",up:"touchend",move:"touchmove",leave:"touchleave"}:{down:"mousedown",up:"mouseup",move:"mousemove",leave:"mouseleave"};function u(e){v();const t=l(e),o=new CustomEvent("long-press",{bubbles:!0,cancelable:!0,detail:(a=t,{clientX:a.clientX,clientY:a.clientY,offsetX:a.offsetX,offsetY:a.offsetY,pageX:a.pageX,pageY:a.pageY,screenX:a.screenX,screenY:a.screenY})});var a;this.dispatchEvent(o)||n.addEventListener("click",m,!0)}function l(e){return e.changedTouches?e.changedTouches[0]:e}function d(e,n=s){v();const o=e.target;t=setTimeout((()=>u.call(o,e)),n)}function v(){t&&(clearTimeout(t),t=null)}function m(e){n.removeEventListener("click",m,!0),e.preventDefault(),e.stopImmediatePropagation()}n.addEventListener(r.down,(function(e){const n=l(e);i={x:n.clientX,y:n.clientY},d(e)}),!0),n.addEventListener(r.move,(function(e){const n=l(e);(Math.abs(i.x-n.clientX)>o||Math.abs(i.y-n.clientY)>a)&&v()}),!0),n.addEventListener(r.up,v,!0),n.addEventListener(r.leave,v,!0),n.addEventListener("wheel",v,!0),n.addEventListener("scroll",v,!0),navigator.userAgent.toLowerCase().includes("android")||n.addEventListener("contextmenu",v,!0),e.setLongPressDelay=function(e){s=e}}(window,document); -------------------------------------------------------------------------------- /Test/longpress.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"use strict";let n=null;const o=10,a=10;let i={x:0,y:0};const s="ontouchstart"in e||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0,u="PointerEvent"in e||e.navigator&&"msPointerEnabled"in e.navigator?{down:"pointerdown",up:"pointerup",move:"pointermove",leave:"pointerleave"}:s?{down:"touchstart",up:"touchend",move:"touchmove",leave:"touchleave"}:{down:"mousedown",up:"mouseup",move:"mousemove",leave:"mouseleave"};"function"!=typeof e.CustomEvent&&(e.CustomEvent=function(e,n={bubbles:!1,cancelable:!1,detail:void 0}){const o=t.createEvent("CustomEvent");return o.initCustomEvent(e,n.bubbles,n.cancelable,n.detail),o},e.CustomEvent.prototype=e.Event.prototype);const c=e.requestAnimationFrame||e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||(t=>e.setTimeout(t,1e3/60));function r(e){v();const n=l(e),o=new CustomEvent("long-press",{bubbles:!0,cancelable:!0,detail:(a=n,{clientX:a.clientX,clientY:a.clientY,offsetX:a.offsetX,offsetY:a.offsetY,pageX:a.pageX,pageY:a.pageY,screenX:a.screenX,screenY:a.screenY})});var a;this.dispatchEvent(o)||t.addEventListener("click",d,!0)}function l(e){return e.changedTouches?e.changedTouches[0]:e}function m(o){v();const a=o.target,i=parseInt(function(e,n,o){for(;e&&e!==t.documentElement;){const t=e.getAttribute(n);if(t)return t;e=e.parentNode}return o}(a,"data-long-press-delay","1000"),10);n=function(t,n){if(!c)return e.setTimeout(t,n);const o=(new Date).getTime(),a={},i=()=>{(new Date).getTime()-o>=n?t():a.value=c(i)};return a.value=c(i),a}(r.bind(a,o),i)}function v(){var t;(t=n)&&(e.cancelAnimationFrame||e.clearTimeout)(t.value),n=null}function d(e){t.removeEventListener("click",d,!0),e.preventDefault(),e.stopImmediatePropagation()}t.addEventListener(u.down,(function(e){const t=l(e);i={x:t.clientX,y:t.clientY},m(e)}),!0),t.addEventListener(u.move,(function(e){const t=l(e);(Math.abs(i.x-t.clientX)>o||Math.abs(i.y-t.clientY)>a)&&v()}),!0),t.addEventListener(u.up,v,!0),t.addEventListener(u.leave,v,!0),t.addEventListener("wheel",v,!0),t.addEventListener("scroll",v,!0),navigator.userAgent.toLowerCase().includes("android")||t.addEventListener("contextmenu",v,!0)}(window,document); -------------------------------------------------------------------------------- /Test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
39 |
40 | ## Installing Fetch:
41 |
42 | 1. Download the [fetch.html.gz](https://github.com/chromoxdor/EasyFetch/raw/refs/heads/main/fetch.html.gz) file to your computer. Alternatively, you can rename it to index.htm.gz to replace the main page.
43 |
44 | 2. Open your browser and navigate to http://
79 |
80 |
81 | ***
82 |
83 | **2. Buttons:**
84 | - Buttons are basically ordinary tiles where the valuenames/values are not rendered
85 | - They have the ability to change their color depending on their state
86 | 
87 | (the device here is a sonoff s20 and since we do not need the first button, since its the hardwarebutton, on our dahboard we can hide it with the "XX" option. the second button is the actual relay so the state changes depending on the gpio state)
88 |
89 | The rule for it:
90 |
91 | On buttonevent do
92 | gpiotoggle,12
93 | endon
94 |
95 |
96 | 2. Name a dummy device something that consists "dButtons" and every value becomes a button.
97 | - You can add an option for colorbuttons with "?C"(see picture below)
98 | - put "&\
102 |
103 |
104 | - Alert: whenever the value of “btnState” / “btnStateC” or any value in a "dButtons" task is 2 the tile becomes red.
105 |
106 |
107 | ***
108 |
109 | **3. Slider**
110 |
111 | - Slider: there are two types of slider. The “ordinary” slider and the “time" slider
112 | - Every slider calls an event when finished sliding. (e.g. “sliderEvent”)
113 | - To create a slider name a dummy device either "vSlider", "nvSlider" or "tSlider"
114 |
115 | 1. The ordinary slider: There are two versions too:
116 |
117 | 1. The slider with values displayed: name a task something consisting of “vSlider” and every item will become a Slider with values shown while sliding
118 |
119 | 2. The slider with values hidden: name a task something consisting of “nvSlider”
120 |
121 | - For both kinds of slider you can set a minimum, a maximum and the steps.
122 | - To achieve this add ?
128 |
129 | - If you add "Sw" to the Taskname (e.g. “vSliderSw” ), a "switch" function is added to the slider. If you click on the left 1/10th of the slider, the value becomes the set minimum (default=0) and if you click on the right 1/10th, it becomes the maximum (default=1023)
130 |
131 | 2. The "time" slider: Name a task something consisting of “tSlider” and every item will become a "time" slider. (Important! To make this work you need to set the number of decimals to 4)
132 | - The "time" slider stores the values of both times in one number. This makes it easier to store these values with the regulator - level
133 | control plugin since only one plugin for both values is needed
134 | - This slider has two thumbs for two time values (e.g. on and off time). Both times are stored in the corresponding taskvalue. The code
135 | example shows how to make use of it (for persistant storage of the values add the "level control" plugin):
136 |
137 | On System#Boot Do //retrieve the values after a power loss back from the "level control" plugin
138 | TaskValueSet,tslider,1,[timekeepXX#getlevel]/10000
139 | Let,1,[tSlider#Time]
140 | Let,2,[var#1]*10000-[var#1#F]*10000
141 | Endon
142 |
143 | On tSliderEvent do
144 | TimerSet,2,10 // after 10secs store the value in the "level control" plugin
145 | Let,1,[tSlider#Time]
146 | Let,2,[var#1]*10000-[var#1#F]*10000
147 | endon
148 |
149 | On Rules#Timer=2 Do
150 | config,task,timekeepXX,SetLevel,[tSlider#Time]*10000 //level stores only two digits so we make an integer
151 | Endon
152 |
153 | On Clock#Time=All,**:** Do
154 | If %syssec_d%/60>=[var#1#F] And %syssec_d%/60<[var#2]
155 | GPIO,2,1
156 | Else
157 | GPIO,2,0
158 | Endif
159 | Endon
160 |
161 |
162 |
163 |
164 |
165 |
166 | ***
167 |
168 | **4. Big values**
169 |
170 | - Big values are displayed when the taskname consist of “bigVal” or if colored “bigValC”
171 | - You can call an item of a “bigVal” Task something that consists “clock”/”time”, “date”, “year” to get this displayed independent of the value. (german speaking persons can also use “uhr” / "zeit", “datum” -> for date format with dots and "jahr")
172 |
173 |
174 |
175 |
176 |
177 | ***
178 |
179 | **5. Additional things**
180 |
181 | - Thingspeak: add a thingspeak value to display the last value of a field of a public thingspeak channel, use this scheme for a valuename:
182 | "name&
185 |
186 |
187 | 
188 |
189 | - Grid layout (desktop view):
190 | - the amount of colums (1,2,3 or maximum 4) is determined by the ammount of tiles.
191 | - 1 tile = one colum
192 | - 2 - 4 tiles = 2 colums
193 | - 5 - 9 tiles = 3 colums
194 | - \> 9 tiles = 4 colums
195 | - The amount of "big values" however is prioritized for rendering the grid layout and constraints it.
196 | - e.g. if a 4 colum grid is preferred just create a dummy-device with 4 values and call it "bigVal"
197 | but if you only have 3 values to display you can add an empty "big value" tile by calling an valuename "noVal" or hide it with "XX"
198 | 
199 | 
200 | 
201 |
202 | - with this "trick" you can have a 2 colum grid layout even wihout displaying any "big values":
203 | 
204 |
205 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/Test/test.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | font-family: sans-serif;
4 | font-size: 12pt;
5 | margin: 0;
6 | padding: 0;
7 | }
8 |
9 | body {
10 | user-select: none;
11 | -webkit-user-select: none;
12 | margin: 0;
13 | background: #e8e8e8;
14 | color: #4e4a4a;
15 | }
16 |
17 | #dateV {
18 | opacity: 0;
19 | top: 10px;
20 | writing-mode: vertical-lr;
21 | position: fixed;
22 | pointer-events: none;
23 | }
24 |
25 | #openSys:hover #dateV {
26 | opacity: 1;
27 | }
28 |
29 | #container {
30 | display: flex;
31 | height: 100vh;
32 | justify-content: center;
33 | }
34 |
35 | #framie {
36 | height: 100%;
37 | transition: .8s;
38 | position: relative;
39 | z-index: 2;
40 | padding-bottom: 50px;
41 | left: 1px;
42 | }
43 |
44 | iframe {
45 | width: 100%;
46 | height: 100%;
47 | border: 0;
48 | background: #fff;
49 | box-shadow: 4px 0 13px -7px #000;
50 | }
51 |
52 | #unitId {
53 | padding: 1vh;
54 | width: 110vw;
55 | z-index: 1;
56 | display: flex;
57 | position: fixed;
58 | transition: 0.3s ease-out;
59 | justify-content: center;
60 | }
61 |
62 | #allList {
63 | display: grid;
64 | transform: translate3d(0, 0, 0);
65 | /*safari blur issue fix*/
66 | grid-row-gap: 1vh;
67 | margin: auto;
68 | padding: 5px;
69 | transition: .8s ease-out;
70 | flex-direction: column;
71 | align-content: center;
72 | flex-wrap: wrap;
73 | padding-bottom: 50px;
74 | padding-top: 40px;
75 | color: #fff;
76 | }
77 |
78 | #allList>div>:not(.bigNum) {
79 | box-shadow: 0 0 10px 1px #00000026;
80 | }
81 |
82 | .allExtra {
83 | grid-row-gap: .5vh !important;
84 | }
85 |
86 | #bigNumber {
87 | display: grid;
88 | gap: 1vh;
89 | transition: .8s ease-out;
90 | }
91 |
92 | .bigNum {
93 | display: grid;
94 | gap: 1vh;
95 | justify-content: start;
96 | transition: .8s ease-out;
97 | border-radius: 5px
98 | }
99 |
100 | .bigNumOne {
101 | justify-content: center;
102 | }
103 |
104 | .bigNumWrap {
105 | width: -webkit-fill-available;
106 | display: flex;
107 | justify-items: stretch;
108 | cursor: default;
109 | padding: 4.2px;
110 | border-radius: 5px;
111 | width: 150px;
112 | height: 75px;
113 | flex-direction: column;
114 | background: #837c7c;
115 | flex-wrap: nowrap;
116 | justify-content: space-between;
117 | box-shadow: 0 0 10px 1px #00000026;
118 | }
119 |
120 | #sensorList {
121 | display: grid;
122 | gap: 1vh;
123 | justify-content: center;
124 | transition: .8s ease-out;
125 | }
126 |
127 | #sliderList {
128 | display: grid;
129 | gap: 1vh;
130 | transition: .8s ease-out;
131 | min-width: 305px
132 | }
133 |
134 | #sliderList .sensorset {
135 | width: inherit;
136 | height: 40px;
137 | background: #837c7c;
138 | }
139 |
140 | .clickables:active,
141 | .push:active {
142 | transform: scale(0.95);
143 | transition: .1s;
144 | }
145 |
146 | .slider,
147 | .npSl {
148 | -webkit-appearance: none;
149 | position: absolute;
150 | height: 40px;
151 | }
152 |
153 | .slider {
154 | background: #847d7d;
155 | width: calc(100% + 44.2px);
156 | left: -24.2px;
157 | top: 0;
158 | }
159 |
160 | .noI {
161 | pointer-events: none;
162 | }
163 |
164 | .npSl {
165 | width: 100%;
166 | left: -.2px;
167 | position: inherit;
168 | height: 40px;
169 | overflow: hidden;
170 | border-radius: 5px;
171 | }
172 |
173 | .slider::-webkit-slider-thumb {
174 | -webkit-appearance: none;
175 | width: 0;
176 | height: 50px;
177 | cursor: pointer;
178 | box-shadow: -100vw 0 0 100vw #458fd1;
179 | border-left: 20px solid #458fd1;
180 | border-right: 20px solid #837c7c;
181 | }
182 |
183 | /* .thT::-webkit-slider-thumb{
184 | border-right: 90 !important;
185 | } */
186 |
187 | .slider::-moz-range-thumb {
188 | width: 0;
189 | height: 70px;
190 | cursor: pointer;
191 | box-shadow: -100vw 0 0 100vw #458fd1;
192 | border-left: 20px solid #458fd1;
193 | border-right: 20px solid #837c7c;
194 | }
195 |
196 | .npSl::-webkit-slider-thumb {
197 | -webkit-appearance: none;
198 | width: 20px;
199 | height: 42px;
200 | cursor: pointer;
201 | box-shadow: 0 0 2px 0 #fff;
202 | background: #3e88cb;
203 | }
204 |
205 | .npSl::-moz-range-thumb {
206 | width: 20px;
207 | height: 42px;
208 | cursor: pointer;
209 | box-shadow: 0 0 2px 0 #fff;
210 | background: #3e88cb;
211 | border: 0;
212 | }
213 |
214 | .npH {
215 | background-image: linear-gradient(to right, red, yellow, lime, cyan, blue, magenta, red);
216 | }
217 |
218 | .npV {
219 | background-image: linear-gradient(to right, #333, white);
220 | }
221 |
222 | .npS {
223 | background-image: linear-gradient(to right, white, #333);
224 | }
225 |
226 | .slTS,
227 | .thermO {
228 | -webkit-appearance: none;
229 | position: absolute;
230 | height: 40px;
231 | z-index: 1;
232 | border-radius: 5px;
233 | width: 100%;
234 | top: 0;
235 | pointer-events: none;
236 | }
237 |
238 | .slTS {
239 | mix-blend-mode: overlay;
240 | background: #8a8787;
241 | }
242 |
243 | .slTS:hover {
244 | background: transparent;
245 | }
246 |
247 | .slTS:nth-child(1)::-webkit-slider-thumb {
248 | border-left: 10px solid #d25e42;
249 | border-right: 10px solid #458fd1;
250 | }
251 |
252 | .slTS:nth-child(2)::-webkit-slider-thumb {
253 | border-left: 10px solid #458fd1;
254 | border-right: 10px solid #d25e42;
255 | }
256 |
257 | .slTS:nth-child(1)::-moz-range-thumb {
258 | border-left: 10px solid #d25e42;
259 | border-right: 10px solid #458fd1;
260 | }
261 |
262 | .slTS:nth-child(2)::-moz-range-thumb {
263 | border-left: 10px solid #458fd1;
264 | border-right: 10px solid #d25e42;
265 | }
266 |
267 | .thermO::-webkit-slider-thumb {
268 | border: 4px solid #d25e42;
269 | }
270 |
271 | .thermO::-moz-range-thumb {
272 | border: 4px solid #d25e42;
273 | }
274 |
275 | .slTS::-webkit-slider-thumb {
276 | width: 0;
277 | height: 40px;
278 | box-shadow: 0 0 10px 10px #00000026;
279 | }
280 |
281 | .slTS::-moz-range-thumb {
282 | width: 0;
283 | height: 100vh;
284 | }
285 |
286 | .slTHU::-moz-range-thumb {
287 | cursor: pointer;
288 | appearance: none;
289 | pointer-events: all;
290 | }
291 |
292 | .slTHU::-webkit-slider-thumb {
293 | -webkit-appearance: none;
294 | background: none;
295 | cursor: pointer;
296 | appearance: none;
297 | pointer-events: all;
298 | }
299 |
300 | .thermO::-webkit-slider-thumb {
301 | width: 25px;
302 | height: 40px;
303 | border-radius: 5px;
304 | box-shadow: 0 0 10px 10px #00000026, inset 0 0 5px 2px #00000026;
305 | }
306 |
307 | .thermO::-moz-range-thumb {
308 | background: none;
309 | width: 20px;
310 | height: 32px;
311 | border-radius: 5px;
312 | box-shadow: 0 0 10px 10px #00000026, inset 0 0 5px 2px #00000026;
313 | }
314 |
315 | .slTimeSet {
316 | display: flex;
317 | align-items: center;
318 | justify-content: center;
319 | }
320 |
321 | .slTimeSetWrap {
322 | height: 40px;
323 | padding: 4.2px;
324 | border-radius: 5px;
325 | overflow: hidden;
326 | position: relative;
327 | }
328 |
329 | .slTimeText,
330 | .sliderAmount {
331 | position: absolute;
332 | right: 4.2px;
333 | bottom: 0;
334 | font-weight: 400;
335 | z-index: 2;
336 | pointer-events: none;
337 | }
338 |
339 | .thermO {
340 | background: none;
341 | }
342 |
343 | .sensorset:not(.btnTile) {
344 | background: #847d7d;
345 | }
346 |
347 | .sensorset {
348 | display: flex;
349 | justify-items: stretch;
350 | flex-direction: column;
351 | padding: 4.2px;
352 | flex-wrap: nowrap;
353 | border-radius: 5px;
354 | width: 150px;
355 | overflow: hidden;
356 | position: relative;
357 | align-items: stretch;
358 | cursor: pointer;
359 | }
360 |
361 | #sensorList > * {
362 | min-height: 60px
363 | }
364 |
365 | .valueBig {
366 | font-size: 27pt;
367 | text-align: right;
368 | }
369 |
370 | .valuesBig {
371 | overflow-wrap: anywhere;
372 | }
373 |
374 | .valWrap {
375 | background: #635d5d;
376 | border-radius: 5px;
377 | text-align: end;
378 | display: flex;
379 | align-items: center;
380 | justify-content: flex-end;
381 | width: 100%;
382 | }
383 |
384 | .vInputs {
385 | height: 30px;
386 | width: 84%;
387 | border: 0;
388 | background: none;
389 | text-align: end;
390 | color: inherit;
391 | }
392 |
393 | input:focus-visible {
394 | width: inherit;
395 | }
396 |
397 | input::placeholder {
398 | opacity: 1;
399 | color: #fff;
400 | }
401 |
402 | input:focus::placeholder {
403 | opacity: .2;
404 | }
405 |
406 | input::-webkit-outer-spin-button,
407 | input::-webkit-inner-spin-button {
408 | -webkit-appearance: none;
409 | margin: 0;
410 | }
411 |
412 | input[type=number] {
413 | -moz-appearance: textfield;
414 | }
415 |
416 | .kindInput {
417 | padding: 0 2px 0 1px;
418 | }
419 |
420 | .on,
421 | .push:active {
422 | background: #3e88cb !important;
423 | }
424 |
425 | .alert {
426 | background: #d8524f !important;
427 | }
428 |
429 | .btnTile {
430 | background: #635d5d
431 | }
432 |
433 | .sensors {
434 | padding-bottom: 4px;
435 | overflow-wrap: anywhere;
436 | z-index: 2;
437 | pointer-events: none;
438 | }
439 |
440 | .row {
441 | display: flex;
442 | justify-content: space-between;
443 | align-items: flex-end;
444 | }
445 |
446 | .odd {
447 | padding-right: 1px;
448 | overflow-wrap: anywhere;
449 | }
450 |
451 | .odd:hover {
452 | overflow: visible;
453 | }
454 |
455 | .even {
456 | display: flex;
457 | word-spacing: -2px;
458 | white-space: nowrap;
459 | height: 20px;
460 | align-items: center;
461 | justify-content: flex-end;
462 | }
463 |
464 | .cron {
465 | padding-bottom: 1pt;
466 | text-align: end
467 | }
468 |
469 | #opener {
470 | height: 50px;
471 | bottom: 0;
472 | z-index: 2;
473 | width: 100%;
474 | position: fixed
475 | }
476 |
477 | .open {
478 | background: none;
479 | display: flex;
480 | max-width: 275px;
481 | align-items: center;
482 | margin: auto;
483 | padding-bottom: 2vh;
484 | justify-content: space-between;
485 | }
486 |
487 | .sidenav {
488 | height: 100%;
489 | width: 280px;
490 | position: fixed;
491 | z-index: 20;
492 | top: 0;
493 | left: -280px;
494 | background: #111111d9;
495 | transition: .6s;
496 | overflow-y: hidden;
497 | display: flex;
498 | flex-direction: column;
499 | align-items: center;
500 | cursor: pointer;
501 | justify-content: center;
502 | color: #fff;
503 | }
504 |
505 | .menueItem {
506 | display: grid;
507 | align-items: center;
508 | justify-content: start;
509 | grid-template-columns: 25px auto
510 | }
511 |
512 | .numberUnit {
513 | padding-left: 2px;
514 | font-size: 12px;
515 | color: #818181
516 | }
517 |
518 | #menueList div:hover {
519 | color: #444
520 | }
521 |
522 | .nc {
523 | word-break: break-word
524 | }
525 |
526 | .sideNbtn {
527 | position: absolute;
528 | bottom: 2%;
529 | font-size: 35px;
530 | color: #b3b3b3
531 | }
532 |
533 | #openSys {
534 | left: 20px;
535 | width: 40px
536 | }
537 |
538 | #closeBtn {
539 | right: 20px
540 | }
541 |
542 | #menueList {
543 | display: flex;
544 | flex-direction: column;
545 | overflow: scroll;
546 | -ms-overflow-style: none;
547 | scrollbar-width: none;
548 | transition: .2s;
549 | padding: 6px;
550 | margin-bottom: 50px;
551 | }
552 |
553 | #menueList::-webkit-scrollbar {
554 | display: none;
555 | }
556 |
557 | #menueList div {
558 | padding: 3px;
559 | font-size: 25px
560 | }
561 |
562 | #sysInfo {
563 | background: #ffffff17;
564 | color: #fff;
565 | transition: .5s;
566 | border-radius: 5px;
567 | width: 200px;
568 | height: 0;
569 | overflow: hidden;
570 | position: absolute;
571 | top: 5px;
572 | display: flex;
573 | flex-direction: column;
574 | justify-content: space-evenly
575 | }
576 |
577 | .syspair {
578 | display: flex;
579 | justify-content: flex-start
580 | }
581 |
582 | .syspair>div {
583 | font-size: 8pt !important;
584 | }
585 |
586 | .syspair div:nth-child(1) {
587 | text-align: end;
588 | width: 35%;
589 | padding-right: 5px
590 | }
591 |
592 | .menueWrap {
593 | transition: .5s;
594 | height: 185px;
595 | flex-shrink: 999
596 | }
597 |
598 | .bigSpan:nth-child(1) {
599 | grid-row: 1 / span 2;
600 | height: 100%;
601 | }
602 | #opener,
603 | #unitId {
604 | background: #e8e8e874
605 | }
606 |
607 | @media screen and (max-height: 450px) {
608 | /* #framie {
609 | padding-bottom: 40px;
610 | } */
611 |
612 | /* #opener {
613 | height: 40px
614 | } */
615 | }
616 |
617 | @media screen and (max-width: 799px) {
618 | #framie {
619 | position: absolute;
620 | }
621 | }
622 |
623 | @media screen and (max-width: 450px) {
624 | #allList {
625 | grid-row-gap: 5px;
626 | min-width: unset;
627 | overflow-y: auto
628 | }
629 |
630 | /* #opener {
631 | height: 40px
632 | } */
633 |
634 | /* #framie {
635 | padding-bottom: 40px
636 | } */
637 |
638 | .allExtra {
639 | grid-row-gap: 2.5px !important
640 | }
641 |
642 | #bigNumber {
643 | grid-row-gap: 5px
644 | }
645 |
646 | #sliderList {
647 | gap: 5px 1vh
648 | }
649 |
650 | #sensorList {
651 | gap: 5px
652 | }
653 |
654 | .bigNum {
655 | gap: 5px;
656 | }
657 |
658 | .sensors {
659 | padding-bottom: 2%
660 | }
661 | }
662 |
663 | @media (prefers-color-scheme: dark) {
664 | body {
665 | background: #000;
666 | color: #b3b3b3
667 | }
668 |
669 | input::placeholder,
670 | #allList {
671 | color: #b3b3b3
672 | }
673 |
674 | iframe,
675 | .slTimeSetWrap {
676 | background: #000
677 | }
678 |
679 | #opener,
680 | #unitId {
681 | background: #000000a8
682 | }
683 |
684 | .bigNumWrap {
685 | background: #444
686 | }
687 |
688 | .slTS {
689 | mix-blend-mode: lighten;
690 | background: #444 !important
691 | }
692 |
693 | .slTS:nth-child(1)::-webkit-slider-thumb {
694 | border-left: 10px solid #6d1343;
695 | border-right: 10px solid #44607a;
696 | }
697 |
698 | .slTS:nth-child(2)::-webkit-slider-thumb {
699 | border-left: 10px solid #44607a;
700 | border-right: 10px solid #6d1343;
701 | }
702 |
703 | .slTS:nth-child(1)::-moz-range-thumb {
704 | border-left: 10px solid #6d1343;
705 | border-right: 10px solid #44607a;
706 | }
707 |
708 | .slTS:nth-child(2)::-moz-range-thumb {
709 | border-left: 10px solid #44607a;
710 | border-right: 10px solid #6d1343;
711 | }
712 |
713 | .thermO::-webkit-slider-thumb {
714 | border: 4px solid #882c29;
715 | }
716 |
717 | .thermO::-moz-range-thumb {
718 | border: 4px solid #882c29;
719 | }
720 |
721 | .slider {
722 | background: #444
723 | }
724 |
725 | .slider::-webkit-slider-thumb {
726 | box-shadow: -100vw 0 0 100vw #44607a;
727 | border-left: 20px solid #44607a;
728 | border-right: 20px solid #444
729 | }
730 |
731 | .slider::-moz-range-thumb {
732 | box-shadow: -100vw 0 0 100vw #44607a;
733 | border-left: 20px solid #44607a;
734 | border-right: 20px solid #444
735 | }
736 |
737 | /* .npSl::-webkit-slider-thumb {
738 | box-shadow: 0 0 4px 3px grey;
739 | background: #44607a;
740 | }
741 |
742 | .npSl::-moz-range-thumb {
743 | box-shadow: 0 0 4px 3px grey;
744 | background: #44607a;
745 | } */
746 |
747 | .npS,
748 | .npH,
749 | .npV {
750 | background-color: #9b9b9b;
751 | background-blend-mode: multiply;
752 | }
753 |
754 | .on,
755 | .push:active {
756 | background: #44607a !important
757 | }
758 |
759 | .alert {
760 | background: #553044 !important
761 | }
762 |
763 | .valueBig {
764 | background: none;
765 | }
766 |
767 | .btnTile,
768 | .valWrap {
769 | background: #333;
770 | }
771 |
772 | .sensorset:not(.btnTile),
773 | #sliderList .sensorset {
774 | background: #444;
775 | }
776 | }
--------------------------------------------------------------------------------
/Test/efc.min.js:
--------------------------------------------------------------------------------
1 | var currentDivId,menu;window.efc=!0,window.configMode=!1;var saveButton=null,resetButton=null,toggleButton=null;const pointerEventsStyle=document.createElement("style");var contextIsAlready=!1;let tempName="";var mOpen,nOpen,selectionDataOld,interactionHandled=!1;const efcVersion="20251016/1",expected="20251210/1";function addContext(){/(android)/i.test(navigator.userAgent)||document.addEventListener("contextmenu",handleRightClick,!0)}function handleRightClick(e){e.preventDefault(),removeHighlighting();let t,n=e.target.closest('div[id^="efc"]');if(addPointerEvents(),null===n){t=e.target;const o=Array.from(t.children);o.length>0&&o.forEach((o=>{if(o.id.includes("efc")){const o=document.elementFromPoint(e.clientX,e.clientY);o&&(n=o,n.classList.contains("sensors")&&(n=o?.parentElement),t.id.startsWith("sliderList")&&(n=o?.parentElement?.parentElement))}}))}if(n&&(n.id.startsWith("efc")||"unitId"===n.id)){const t=n.querySelector("span");isittime=!1,updateSaveButton("initial"),currentDivId=n.id,rebuildContextMenu(n.id,n.className,t),e.detail.clientX?(xCoord=e.detail.clientX,yCoord=e.detail.clientY):(xCoord=e.clientX,yCoord=e.clientY),menu.style.display="block";const o=document.getElementById("custom-menu");if(o&&o.offsetWidth>0){const e=o.offsetWidth,t=o.offsetHeight,n=window.scrollX,i=window.scrollY;let l=xCoord+n+5;l+e>window.innerWidth+n&&(l=window.innerWidth+n-e-10),l=Math.max(10,l);let a=yCoord+i;const r=i+40,d=i+window.innerHeight-t-50;a=Math.min(Math.max(a,r),d),o.style.position="absolute",o.style.left=`${l}px`,o.style.top=`${a}px`}}else(t.id.startsWith("allList")||t.id.startsWith("container"))&&updateSaveButton("initial")}function highlightMatchingDivs(e,t){let n=e.substring(0,e.lastIndexOf(","));if(!n)return;document.querySelectorAll(`div[id*="${n.split("=")[1]}"]`).forEach((e=>{e.style.outline="2px solid #ffcc00"}));let o=document.querySelectorAll(`[id="${e}"]`);o.length>0&&o.forEach((e=>{!t.includes("bigNumWrap")&&!t.includes("bigSingles")||contextIsAlready?e.style.outline="2px solid red":e.parentElement&&(e.parentElement.style.setProperty("transition","none","important"),e.parentElement.style.outline="2px solid red")}))}function removeHighlighting(){const e=document.getElementById("allList");e.style.setProperty("transition-property","none");e.querySelectorAll("div").forEach((e=>e.style.outline="")),setTimeout((()=>{e.style.removeProperty("transition")}),50)}function parseDivId(e){if(e.startsWith("efc")){let[t,n]=e.split("="),o=n.match(/(\d+),(\d+),(\d+)([A-Z]?)$/);return o?{deviceName:t.split(":")[1],deviceType:parseInt(o[1]),deviceIndex:o[2],valueIndex:o[4]||o[3]}:null}return null}function rebuildContextMenu(e,t,n){let o=parseDivId(currentDivId);if(!o)return;let{deviceName:i,deviceType:l,deviceIndex:a,valueIndex:r}=o;const d=[43,1,81].includes(l);let s=selectionData[a]?selectionData[a][r]:{},c=selectionData[a]?.A?.val;menu.innerHTML="";let u=document.createElement("select");u.id="menu-main-select",u.dataset.key="val";let p="";"bigVal"===c||"bigVS"===s?.val?tempName!==i?(contextIsAlready=!1,tempName=i):contextIsAlready=!0:(contextIsAlready=!1,tempName=""),highlightMatchingDivs(e,t),"A"===r&&1!==l||"bigVal"===c&&!contextIsAlready?(p='\n \n \n ',l="A"):"bigSingle"!==i||contextIsAlready?33===l?(p='\n \n \n \n \n \n \n \n \n \n ',(checkBigSinglesLength()<4||4==checkBigSinglesLength()&&"bigVS"===s?.val)&&(p+='')):(p='\n \n \n ',(checkBigSinglesLength()<4||4==checkBigSinglesLength()&&"bigVS"===s?.val)&&(p+='')):l="S",u.innerHTML=p,(d||contextIsAlready&&"bigVal"===c||"S"===l)&&(u.style.display="none"),menu.appendChild(u),menu.appendChild(document.createElement("br")),u.addEventListener("change",(e=>{updateMenuFields(l,e.target.value,i,a),saveSelections(!1)}));let m=u.dataset.key;"bigVal"===c&&"A"===l&&(s=selectionData[a]?.A),void 0!==s&&void 0!==s[m]&&(u.value=s[m],d&&(u.value="")),updateMenuFields(l,u.value,i,a,r,d,t,n)}function updateMenuFields(e,t,n,o,i,l,a,r){let d=document.getElementById("dynamic-fields");d&&menu.removeChild(d),d=document.createElement("div"),d.id="dynamic-fields",d.style.display="grid","S"===e?addColorPicker(d,"color: ","SBC"):("A"===e&&["dButtons","pButtons"].includes(t)&&addCheckbox(d," no input","noI"),"C"===e&&addNumberInput(d," columns","cols"),["vSlider","nvSlider","thSlider"].includes(t)&&addTextInput(d,"range: ","range",t),["dButtons","vSlider"].includes(t)&&33===e&&addCheckbox(d," no input","noI"),["dButtons","pButtons"].includes(t)&&addCheckbox(d," invert","inv"),["tSlider"].includes(t)&&33===e&&(addTextInput(d,"taskname: ","timeName"),addDropdown(d,"day"),addDropdown(d,"fields")),(!["thSlider","tSlider","dButtons","pButtons","bigVal"].includes(t)&&"A"!==e&&1!==e||contextIsAlready&&"A"!==e)&&(r?.title||addTextInput(d,"UoM: ","unit"),addCheckbox(d," hide name","noV")),["dButtons"].includes(t)&&33===e&&addTextInput(d,"sendTo: ","sendTo"),"A"!==e||l||(addCheckbox(d," chart","chart"),selectionData[o]?.A?.chart&&addCheckbox(d," always Y","Y"),a?.includes("chart")?addColorPicker(d,"color: ","color",!0):addColorPicker(d,"color: ","color")),("A"===e&&!["bigVal"].includes(t)||l||contextIsAlready||"bigVS"===t||"vSlider"===t||33===e&&!["none"].includes(t))&&addNumberInput(d,"order: ","order"),"none"===t&&!hasNonEmptyValues(selectionData,o)||contextIsAlready||addResetButton(d,o,n),addCheckbox(d," hide entry","hide")),menu.appendChild(d);let s=parseDivId(currentDivId);if(s){let t,{deviceIndex:n,valueIndex:o}=s;"A"===e&&(o="A"),t="S"!==e?selectionData[n]?selectionData[n][o]:{}:selectionData?.S,d.querySelectorAll("input, select, button").forEach((e=>{let n=e.dataset.key;void 0!==t&&void 0!==t[n]&&("checkbox"===e.type?e.checked=1===t[n]:("number"===e.type||e.tagName.toLowerCase(),e.value=t[n]))}))}d.addEventListener("input",(()=>saveSelections(!1)))}function addTextInput(e,t,n,o){let i=document.createElement("label");i.innerText=t;let l=document.createElement("input");l.type="text","unit"===n&&(l.maxLength=6,l.style.width="6ch"),"range"===n&&(["vSlider","nvSlider"].includes(o)?l.placeholder="0,1024,1":["thSlider"].includes(o)&&(l.placeholder="5,35,1"),l.addEventListener("input",(e=>{l.value=l.value.replace(/[^0-9.,-]/g,"")})),l.addEventListener("blur",(()=>{if(""===l.value.trim())return;/^-?\d+(\.\d+)?,-?\d+(\.\d+)?,-?\d+(\.\d+)?$/.test(l.value)||(alert("Invalid format! Please enter three values separated by two commas (e.g., 0,1024,0.5)"),l.value="")}))),"sendTo"===n&&(l.placeholder="nodeNR(,GPIO)",l.style.width="15ch",l.addEventListener("input",(()=>{l.value=l.value.replace(/[^0-9,]/g,"")})),l.addEventListener("blur",(()=>{if(""===l.value.trim())return;/^\d+$|^\d+,?\d+$/.test(l.value)||(alert("Invalid format! Please enter either a nodenumber (e.g., 12) or a nodenumber + comma + the GPIO to toggle (e.g., 2,12)."),l.value="")}))),l.dataset.key=n,e.appendChild(i),e.appendChild(l)}function addNumberInput(e,t,n){let o=document.createElement("label");o.innerText=t,o.style.marginRight="5px";let i=document.createElement("input");if(i.type="number",i.dataset.key=n,i.style.width="4ch",i.style.textAlign="center","order"===n){i.min="0",i.max="255",i.step="1",i.value="0";let t=document.createElement("div");t.style.display="inline-flex",t.style.alignItems="center",t.style.gap="5px";let n=document.createElement("button");n.innerText="−",n.type="button",n.style.width="25px";let a=document.createElement("button");function l(e){let t=parseInt(i.value)+e;i.value=t,saveSelections(!1)}a.innerText="+",a.type="button",a.style.width="25px",a.addEventListener("click",(()=>l(1))),n.addEventListener("click",(()=>l(-1))),t.appendChild(n),t.appendChild(i),t.appendChild(a),e.appendChild(o),e.appendChild(t)}else e.appendChild(o),e.appendChild(i)}function addCheckbox(e,t,n){let o=document.createElement("label"),i=document.createElement("input");i.type="checkbox",i.dataset.key=n,o.style.padding="5px 0",o.appendChild(i),o.appendChild(document.createTextNode(t)),e.appendChild(o)}function addDropdown(e,t){const n={day:{label:"Day:",options:["All","Mon","Tue","Wed","Thu","Fri","Sat","Sun","Wrk","Wkd"]},fields:{label:"Fields:",options:["1-2","3-4","5-6","7-8"]}};if(!n[t])return;const{label:o,options:i}=n[t],l=document.createElement("div");l.style.padding="5px 0";const a=document.createElement("span");a.textContent=o,a.style.marginRight="5px";const r=document.createElement("select");r.dataset.key=t,r.style.padding="2px 5px",i.forEach((e=>{const t=document.createElement("option");t.value=e,t.textContent=e,r.appendChild(t)})),l.appendChild(a),l.appendChild(r),e.appendChild(l)}function addColorPicker(e,t,n,o){let i=document.createElement("label");i.innerText=t;let l=document.createElement("input");l.type="color",l.dataset.key=n,o&&(i.style.display="none",l.style.display="none"),e.appendChild(i),e.appendChild(l)}function addResetButton(e,t,n){let o=document.createElement("button");e.appendChild(document.createElement("br")),o.innerText=`reset ${n}`,o.style.backgroundColor="#7d1414",o.style.color="white",o.style.padding="2px",o.addEventListener("click",(()=>deleteDevice(t))),e.appendChild(o)}function hasNonEmptyValues(e,t){return!(!e[t]||"object"!=typeof e[t])&&Object.values(e[t]).some((e=>Object.values(e).some((e=>0!==e&&""!==e&&void 0!==e))))}function deleteDevice(e){delete selectionData[e],saveSelections(!0),closeContextMenu()}function saveSelections(e){let t=parseDivId(currentDivId);if(!t)return;let{deviceName:n,deviceIndex:o,valueIndex:i}=t;selectionData[o]||(selectionData[o]={}),selectionData[o][i]||(selectionData[o][i]={});let l=document.getElementById("dynamic-fields"),a={};selectionData.unit=unitName,selectionData.nr=unitNr;let r=document.getElementById("menu-main-select");if(r){let e=r.dataset.key;e&&(a[e]=r.value)}l&&l.querySelectorAll("input, select").forEach((e=>{let t=e.dataset.key;"hide"===t&&(html2=""),"checkbox"===e.type?a[t]=e.checked?1:0:"number"===e.type?a[t]=parseFloat(e.value)||0:"text"===e.type?a[t]=e.value:"color"===t&&"#000000"===e.value?a[t]="":a[t]=e.value})),"bigVal"!==selectionData[o]?.A?.val||contextIsAlready||(i="A"),e||("bigSingle"!==n||contextIsAlready?selectionData[o][i]=a:selectionData.S=a),removeEmptyKeys(selectionData),updateSaveButton(),extraConfig()}function keepOnlyA(e){if(e&&"object"==typeof e)for(const t in e)"A"!==t&&delete e[t]}function updateSaveButton(e){"initial"!==e||selectionDataOld||(closeNav(),selectionDataOld=JSON.stringify(selectionData));const t="hide"===e,n={true:["☰︎","⌂︎"],false:["⚙︎","×︎"]};nOpen&&(nOpen.innerHTML=n[t][0]),mOpen&&(mOpen.innerHTML=n[t][1]),saveButton.style.display=!t&&selectionDataOld&&selectionDataOld!==JSON.stringify(selectionData)?"block":"none",resetButton.style.display=t?"none":"block",toggleButton.style.display=!t&&hasHiddenProperty(selectionData)?"block":"none",t&&closeContextMenu(),window.configMode=!t}function createSaveButton(){(saveButton=document.createElement("button")).innerText="Save Settings",saveButton.style.position="fixed",saveButton.style.top="10px",saveButton.style.left="calc(50% - 40px)",saveButton.style.transform="translateX(-120px)",saveButton.style.padding="10px 15px",saveButton.style.background="#28a745",saveButton.style.color="white",saveButton.style.border="none",saveButton.style.borderRadius="5px",saveButton.style.cursor="pointer",saveButton.style.zIndex="3",saveButton.style.display="none",saveButton.addEventListener("click",saveToFile),document.body.appendChild(saveButton),(resetButton=document.createElement("button")).innerText="Reset File",resetButton.style.position="fixed",resetButton.style.top="10px",resetButton.style.left="calc(50% + 30px)",resetButton.style.transform="translateX(-50%)",resetButton.style.padding="10px 15px",resetButton.style.background="red",resetButton.style.color="white",resetButton.style.border="none",resetButton.style.borderRadius="5px",resetButton.style.cursor="pointer",resetButton.style.zIndex="3",resetButton.style.display="none",resetButton.addEventListener("click",(()=>saveToFile("del"))),document.body.appendChild(resetButton),(toggleButton=document.createElement("button")).innerText=hiddenOverride?"Hide":"Show",toggleButton.style.position="fixed",toggleButton.style.top="10px",toggleButton.style.left="calc(50% + 128px)",toggleButton.style.transform="translateX(-50%)",toggleButton.style.padding="10px 15px",toggleButton.style.background="#007bff",toggleButton.style.color="white",toggleButton.style.border="none",toggleButton.style.borderRadius="5px",toggleButton.style.cursor="pointer",toggleButton.style.zIndex="3",toggleButton.style.display="none",document.body.appendChild(toggleButton),toggleButton.addEventListener("click",(()=>{hiddenOverride=!hiddenOverride,toggleButton.innerText=hiddenOverride?"Hide":"Show"}))}function saveToFile(e){"use strict";updateJsonArray(selectionData);let t=JSON.stringify(selectionData,null,2);if("del"===e){const e=`Are you sure you want to delete ALL the settings of ${selectionData.unit}?`;if(!confirm(e))return;t=" ",selectionData.unit&&(efcArray=efcArray.filter((e=>e.unit!==selectionData.unit)))}let n=JSON.stringify(efcArray,null,2);loadScript("https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako.min.js",(function(){const o=pako.gzip(t),i=new File([o],"efc.json.gz",{type:"application/gzip"}),l=new FormData;l.append("file",i);const a=`${baseUrl}/upload`;if((unitNr!==unitNr1&&"N"!=e||!isMain)&&fetchFile(a,l),isMain){const e=pako.gzip(n),t=new File([e],"main_efc.json.gz",{type:"application/gzip"}),o=new FormData;o.append("file",t),fetchFile("/upload",o)}})),"M"!==e&&exitConfig()}function fetchFile(e,t){const n=new AbortController,o=setTimeout((()=>n.abort()),5500);fetch(e,{method:"POST",body:t,mode:"cors",signal:n.signal}).then((e=>{if(clearTimeout(o),!e.ok)throw new Error(`HTTP error! Status: ${e.status}`);runonce2=!0})).catch((e=>{clearTimeout(o)}))}function saveUrlToServer(e,t){fetch(e).then((e=>{if(!e.ok)throw new Error(`Failed to fetch: ${e.statusText}`);return e.blob()})).then((e=>{const n=new File([e],t,{type:e.type}),o=new FormData;o.append("file",n),fetchFile("/upload",o),alert("Upgrade Successful"),document.cookie="efcVersion=; path=/; max-age=0",location.reload()})).catch((e=>{}))}function createMenu(){if(nOpen=document.getElementById("nOpen"),mOpen=document.getElementById("mOpen"),document.getElementById("areaChart")?.addEventListener("resize",updateYAxisVisibility),mOpen.onclick=function(e){configMode?(e.preventDefault(),e.stopPropagation(),exitConfig()):(splitOn(),topF())},nOpen.addEventListener("long-press",(()=>{"none"===saveButton.style.display?(updateSaveButton("initial"),setLongPressDelay(200),"ontouchstart"in window&&document.addEventListener("long-press",handleRightClick,!0)):exitConfig()})),"ontouchstart"in window){let e=0;const t=200;document.addEventListener("touchstart",(t=>{e=Date.now()}),{passive:!0}),document.addEventListener("touchend",(n=>{Date.now()-e