/*************************
** GUI-Hilfsfunktionen **
*************************/
"use strict"
function clearRows(tbody) {
while (tbody.firstChild) tbody.firstChild.remove();
}
// dv = DataView, stets Little Endian (USB)
function getInt16(dv,ofs,fak) {
const v=dv.getInt16(ofs,true);
switch (v) {
case 32767: return Number.POSITIVE_INFINITY;
case -32768: return Number.NaN;
case -32767: return Number.NEGATIVE_INFINITY;
}
if (!fak) return v;
return v*fak;
}
function getInt8(dv,ofs,fak) {
const v=dv.getInt8(ofs);
switch (v) {
case 127: return Number.POSITIVE_INFINITY;
case -128: return Number.NaN;
case -127: return Number.NEGATIVE_INFINITY;
}
if (!fak) return v;
return v*fak;
}
function numberToPrec(v,n=12) {
return v.toPrecision(n).replace(/\.?0+$/,"");
}
// <input> zu einem Int16-Eingabefeld machen; <value> ist bereits mit <step> skaliert
function make_Int16_input(inp,value,step,userRange) {
inp.type="number";
inp.step=numberToPrec(step,3); // nicht mit Kommastellen überfluten
let min=-32766*step;
if (userRange && min<userRange.a) min=userRange.a;
inp.min=numberToPrec(min,3);
let max=+32766*step;
if (userRange && max>userRange.e) max=userRange.e;
inp.max=numberToPrec(max,3);
if (value!==undefined) inp.value=emptyNaN(value);
inp.placeholder="NaN";
}
// Hilfsklasse für 4-Status-Checkboxen (die als "Button" realisiert werden)
class Fourstate{
constructor(div,k) { // k = Anzahl Zustände, die der Anwender durchklicken kann; 0,2,3,4 erlaubt
const inp=div.insertBefore(document.createElement("input"),div.firstChild);
this.inp=inp;
this.chars="01 ↕"; // so vorbelegt und ggf. individuell veränderbar
this.k=k;
inp.type="button";
if (k) inp.addEventListener("click",e => {
let i=this.state+1; // this.state ruft Getter
if (i>=k) i=0;
this.state = i; // this.state ruft Setter
}); else inp.disabled=true; // nur Anzeige, kein EventListener
}
get state() {return this.chars.indexOf(this.inp.value);}
set state(i) {
this.inp.value=this.chars.charAt(i);
this.inp.title=["Aus","Ein","unverändert","Wechsel"][i];
}
}
class FourstateGroup{
constructor(div,n,k,bit0,bit1) {
clearRows(div);
div.style="white-space:nowrap;"
this.a=new Array(n);
if (bit0===undefined) {bit0=0; bit1=~0;}
for(let i=0; i<n; i++) {
const f=new Fourstate(div,k);
f.state = (bit1&1)<<1 | bit0&1;
bit0>>=1; // nächstes Bit
bit1>>=1;
this.a[i]=f;
}
}
setState(bit0,bit1,focus) {
this.a.forEach(f => {
if (f!=focus) f.state = (bit1&1)<<1 | bit0&1;
bit0>>=1; bit1>>=1;
});
}
getState(mask=1) { // erlaubte Masken: 1 (Bit 0) und 2 (Bit 1)
let r=0, m=1;
this.a.forEach(f => {
if (f.state&mask) r|=m;
m<<=1;
});
return r;
}
get state() {return this.getState();}
}
class CheckboxList{
constructor(div,ena,titles) {
clearRows(div);
this.a=[];
titles.forEach(t => {
const inp=div.appendChild(document.createElement("input"));
inp.type="checkbox";
inp.id=div.id+this.a.length;
if (!(ena&1)) inp.disabled=true;
inp.indeterminate=true; // Zustand zunächst unbekannt
ena>>=1;
const lbl=div.appendChild(document.createElement("label"));
lbl.setAttribute("for",inp.id); // lbl.for= geht nicht
lbl.innerHTML=t;
div.appendChild(document.createElement("br"));
this.a.push(inp);
});
}
setState(bit0,bit1,focus) {
this.a.forEach(inp => {
if (inp!=focus) {
inp.checked = bit0&1;
inp.indeterminate = bit1&1;
bit0>>=1; bit1>>=1;
}
});
}
getState(mask=1) {
let r=0, m=1;
this.a.forEach(inp => {
if (mask==1 && inp.checked
|| mask==2 && inp.indeterminate) r|=m;
m<<=1;
});
return r;
}
get state() {return this.getState();}
}
// Das <input>-Element macht Komma-Punkt-Konvertierung
function fromTime(s) { // "hh:mm:ss.xxx" nach Sekunden
const a=s.split(':');
return (+a[0]*60+ +a[1])*60+(+a[2]||0);
}
// Das <input>-Element macht Punkt-Komma-Konvertierung
function toTime(t) { // Sekunden nach "hh:mm:ss.xxx"
const date=new Date(t*1000);
function digi(v,l=2) {return v.toString().padStart(l,'0');}
return digi(date.getUTCHours())
+':'+digi(date.getUTCMinutes())
+':'+digi(date.getSeconds())
+'.'+digi(date.getMilliseconds(),3);
}
function toTimeShort(t) { // Sekunden nach [hh:]mm:ss[,x[x[x]]]
return toTime(t).replace(/^00:/,"").replace(/.?0+$/,"").replace(".",",");
}
function emptyNaN(v) {
return isFinite(v)?numberToPrec(v,5):"";
}
class Color{
constructor(r,g,b,a=1,t) {this.init(r,g,b,a,t); Object.seal(this);}
init(r,g,b,a=1,t) {
if (typeof r == "string") return this.fromString(r);
if (r instanceof Color) return this.init(r.r,r.g,r.b,r.a);
switch (t) {
case true:
case "hsv":
case "HSV": {
while (r<0) r+=360;
while (r>=360) r-=360;
let e=r/60>>>0; // ganzzahliger Sextant
let f=r/60-e; // 0..1
let p=b*(1-g);
let q=b*(1-g*f);
let t=b*(1-g*(1-f))
switch (e) {
case 1: return this.init(b,t,p,a);
case 2: return this.init(q,b,p,a);
case 3: return this.init(p,b,t,a);
case 4: return this.init(t,p,b,a);
case 5: return this.init(b,p,q,a);
}
return this.init(b,t,p,a);
}break;
case "hsl": return this.init(r,g,b,a,"hsv");
}
this.r=r; this.g=g; this.b=b; this.a=a; return this;
}
static base255(b) {return b*255.5>>>0;}
static hexByte(b) {return Color.base255(b).toString(16).padStart(1,'0');}
static hexChr2(s) {return s.parseInt(16)/255;}
static hexChr1(s) {return s.parseInt(16)/15;}
toString(t) {
if (!t && this.a!==1) t="rgba";
switch (t) {
case "rgba": return t+"("+Color.base255(this.r)+","+Color.base255(this.g)+","+Color.base255(this.b)+","+this.a+")";
}
return "#"+Color.hexByte(this.r)+Color.hexByte(this.g)+Color.hexByte(this.b);
}
fromString(s) {
s=s.trim();
if (s.startsWith("#") && s.length==7) return this.init(Color.hexChr2(s.substr(1,2)),Color.hexChr2(s.substr(3,2)),Color.hexChr2(s.substr(5,2)));
if (s.startsWith("#") && s.length==4) return this.init(Color.hexChr1(s.substr(1,1)),Color.hexChr1(s.substr(2,1)),Color.hexChr1(s.substr(3,1)));
if (s.startsWith("rgba(") && s.endsWith(")")) {
let a=s.substr(5,s.length-6).split(",");
if (a.length==4) return this.init(a[0]/255,a[1]/255,a[2]/255,a[3]);
}
if (s.startsWith("rgb(") && s.endsWith(")")) {
let a=s.substr(4,s.length-5).split(",");
if (a.length==3) return this.init(a[0]/255,a[1]/255,a[2]/255);
}
return this.init();
}
get hsv() {
const min = Math.min(this.r,this.g,this.b);
const max = Math.max(this.r,this.g,this.b);
const d = max-min;
let h = d && (max==this.r ? (this.g-this.b)/d : max==this.g ? 2+(this.b-this.r)/d : 4+(this.r-this.g)/d);
if (h<0) h+=6;
return {
h: h*60,
s: d && d/max,
v: max};
}
}
| Detected encoding: UTF-8 | 0
|