瀏覽代碼

www: implement "smart checkboxes"

Add a custom variant of the <input> tag which can specify
dependencies (needs="foo;bar" and conflicts="baz;quux").
H. Peter Anvin 1 年之前
父節點
當前提交
59f204fe04
共有 10 個文件被更改,包括 114 次插入32 次删除
  1. 二進制
      esp32/output/max80.ino.bin
  2. 9 9
      esp32/www/abcbus.html
  3. 7 7
      esp32/www/abcmem.html
  4. 3 3
      esp32/www/config.html
  5. 90 8
      esp32/www/max80.js
  6. 4 4
      esp32/www/status.html
  7. 1 1
      esp32/www/update.html
  8. 二進制
      fpga/output/max80.fw
  9. 二進制
      fpga/output/v1.fw
  10. 二進制
      fpga/output/v2.fw

二進制
esp32/output/max80.ino.bin


+ 9 - 9
esp32/www/abcbus.html

@@ -47,7 +47,7 @@
 	  <div class="iodev">
 	    <label class="enabled">
 	      <span>Enabled</span>:&nbsp;
-	      <input type="checkbox" name="abc.io.mo.enable" value="1" />
+	      <input is="x-box" name="abc.io.mo.enable" />
 	    </label>
 	    <label class="devsel">
 	      <span>Device select:</span>
@@ -64,7 +64,7 @@
 	  <div class="iodev">
 	    <label class="enabled">
 	      <span>Enabled</span>:&nbsp;
-	      <input type="checkbox" name="abc.io.mf.enable" value="1" />
+	      <input is="x-box" name="abc.io.mf.enable" />
 	    </label>
 	    <label class="devsel">
 	      <span>Device select:</span>
@@ -80,7 +80,7 @@
 	  <div class="iodev">
 	    <label class="enabled">
 	      <span>Enabled</span>:&nbsp;
-	      <input type="checkbox" name="abc.io.sf.enable" value="1" />
+	      <input is="x-box" name="abc.io.sf.enable" />
 	    </label>
 	    <label class="devsel">
 	      <span>Device select:</span>
@@ -97,7 +97,7 @@
 	  <div class="iodev">
 	    <label class="enabled">
 	      <span>Enabled</span>:&nbsp;
-	      <input type="checkbox" name="abc.io.hd.enable" value="1" />
+	      <input is="x-box" name="abc.io.hd.enable" />
 	    </label>
 	    <label class="devsel">
 	      <span>Device select:</span>
@@ -113,7 +113,7 @@
 	  <div class="iodev">
 	    <label class="enabled">
 	      <span>Enabled</span>:&nbsp;
-	      <input type="checkbox" name="abc.io.xd.enable" value="1" />
+	      <input is="x-box" name="abc.io.xd.enable" />
 	    </label>
 	    <label class="devsel">
 	      <span>Device select:</span>
@@ -132,7 +132,7 @@
 	  <div class="iodev">
 	    <label class="enabled">
 	      <span>Enabled</span>:&nbsp;
-	      <input type="checkbox" name="abc.io.pun80.enable" value="1" />
+	      <input is="x-box" name="abc.io.pun80.enable" />
 	    </label>
 	    <label class="devsel">
 	      <span>Device select:</span>
@@ -158,7 +158,7 @@
 	  <div class="iodev">
 	    <label class="enabled">
 	      <span>Enabled</span>:&nbsp;
-	      <input type="checkbox" name="abc.io.rtc.enable" value="1" />
+	      <input is="x-box" name="abc.io.rtc.enable" />
 	    </label>
 	    <label class="devsel">
 	      <span>Device select:</span>
@@ -172,11 +172,11 @@
 	<legend>Reset when updating configuration</legend>
 	<label class="fpga">
 	  <b>Reset MAX80</b>
-	  <input type="checkbox" name="fpga.reset" value="1" />
+	  <input is="x-box" name="fpga.reset" />
 	</label>
 	<label class="abc">
 	  <b>Reset ABC</b>
-	  <input type="checkbox" name="abc.reset" value="1" />
+	  <input is="x-box" name="abc.reset" />
 	</label>
       </fieldset>
 

+ 7 - 7
esp32/www/abcmem.html

@@ -14,22 +14,22 @@
 	<legend>Emulated memories for ABC80</legend>
 	<label class="nvram">
 	  <b>NVRAM</b>
-	  <input type="checkbox" name="abc.mem.abc80.nvram" value="1" />
+	  <input is="x-box" name="abc.mem.abc80.nvram" />
 	  <span class="help">Fake NVRAM (20-22K)</span>
 	</label>
 	<label class="ufddos">
 	  <b>UFD-DOS</b>
-	  <input type="checkbox" name="abc.mem.abc80.ufddos" value="1" />
+	  <input is="x-box" name="abc.mem.abc80.ufddos" />
 	  <span class="help">UFD-DOS ROM for ABC80 (24-28K)</span>
 	</label>
 	<label class="pun80">
 	  <b>PUN80</b>
-	  <input type="checkbox" name="abc.mem.abc80.pun80" value="1" />
+	  <input is="x-box" name="abc.mem.abc80.pun80" />
 	  <span class="help">PUN80 network ROM for ABC80 (29-30K)</span>
 	</label>
 	<label class="ram">
 	  <b>Expansion RAM</b>
-	  <input type="checkbox" name="abc.mem.abc80.ram" value="1" />
+	  <input is="x-box" name="abc.mem.abc80.ram" />
 	  <span class="help">Expand RAM to 32K (32-48K)</span>
 	</label>
       </fieldset>
@@ -37,7 +37,7 @@
 	<legend>Emulated memories for ABC800</legend>
 	<label class="ufddos">
 	  <b>UFD-DOS</b>
-	  <input type="checkbox" name="abc.mem.abc800.ufddos" value="1" />
+	  <input is="x-box" name="abc.mem.abc800.ufddos" />
 	  <span class="help">UFD-DOS ROM for ABC800 (32-48K)</span>
 	</label>
 	<p class="help">ABC800 needs jumper configuration to access external ROM.</p>
@@ -47,11 +47,11 @@
 	<legend>Reset when updating configuration</legend>
 	<label class="fpga">
 	  <b>Reset MAX80</b>
-	  <input type="checkbox" name="fpga.reset" value="1" />
+	  <input is="x-box" name="fpga.reset" />
 	</label>
 	<label class="abc">
 	  <b>Reset ABC</b>
-	  <input type="checkbox" name="abc.reset" value="1" />
+	  <input is="x-box" name="abc.reset" />
 	</label>
       </fieldset>
 

+ 3 - 3
esp32/www/config.html

@@ -18,7 +18,7 @@
 	</label>
 	<label class="mdns">
 	  <b>mDNS</b>
-	  <input type="checkbox" name="mdns.enabled" />
+	  <input is="x-box" name="mdns.enabled" />
 	</label>
 	<label class="wifi-ssid">
 	  <b>Network name (SSID)</b>
@@ -40,7 +40,7 @@
 	</label>
 	<label class="sntp-enabled">
 	  <b>Synchronize time from network</b>
-	  <input type="checkbox" name="sntp.enabled" />
+	  <input is="x-box" name="sntp.enabled" />
 	</label>
 	<label class="sntp-server">
 	  <b>NTP server</b>
@@ -48,7 +48,7 @@
 	</label>
 	<label class="ip4-dhcp-sntp">
 	  <b>Use DHCP-provided NTP server</b>
-	  <input type="checkbox" name="ip4.dhcp.nosntp" value="0" />
+	  <input is="x-box" name="ip4.dhcp.nosntp" negative />
 	</label>
       </fieldset>
       <button class="submit" type="submit" disabled>Update configuration</button>

+ 90 - 8
esp32/www/max80.js

@@ -72,13 +72,10 @@ function initform(form,map,ro = false) {
 	    const val = map.get(e.name);
 	    if (val == null)
 		continue;
-	    if (e.type == 'checkbox') {
-		e.checked = cfgbool(val) == cfgbool(e.value);
-	    } else if (e.type == 'radio') {
+	    if (e.type == 'radio')
 		e.checked = (val == e.value);
-	    } else {
+	    else
 		e.value = val;
-	    }
 	} else if (e instanceof HTMLButtonElement) {
 	    e.disabled = ro;
 	}
@@ -174,9 +171,7 @@ function textformdata(form) {
 	if (val == undefined || !e.name || e instanceof HTMLButtonElement) {
 	    continue;
 	} else if (e instanceof HTMLInputElement) {
-	    if (e.type == 'checkbox')
-		val = e.checked == cfgbool(val) ? '1' : '0';
-	    else if (e.type == 'radio' && !e.checked)
+	    if (e.type == 'radio' && !e.checked)
 		continue;
 	}
 	data += e.name + '=' + val + "\r\n";
@@ -250,3 +245,90 @@ class IncHTML extends HTMLElement {
     }
 }
 customElements.define('x-inc', IncHTML);
+
+// Smart checkbox INPUT element
+class XBox extends HTMLInputElement {
+    set negative(x) { return this.toggleAttribute('negative', x); }
+    get negative() { return this.hasAttribute('negative'); }
+    #truth;
+    get truth() { return this.#truth; }
+    update_truth() {
+	const old_truth = this.#truth;
+	this.#truth = this.checked != this.negative;
+	if (this.#truth !== old_truth)
+	    this.resolve_conflicts();
+    }
+    set truth(x) {
+	const new_truth = !!x;
+	if (new_truth !== this.#truth) {
+	    this.checked = new_truth != this.negative;
+	    this.update_truth();
+	}
+    }
+    set_list(n,x) { this.setAttribute(n, x ? x.join(',') : ''); }
+    get_list(n) { var a = this.getAttribute(n); return a ? a.split(/\s*;\s*/) : []; }
+    get needs() { return this.get_list('needs'); }
+    set conflicts(x) { this.set_list('conflicts',x); }
+    get conflicts() { return this.get_list('conflicts'); }
+    set value(x) { this.truth = cfgbool(x); }
+    get value() { return this.truth ? '1' : '0'; }
+
+    constructor() {
+	super();
+	this.setAttribute('type', 'checkbox');
+	this.checked = cfgbool(this.getAttribute('value')) != this.negative;
+	this.addEventListener('change', this.update_truth);
+	this.update_truth();
+    }
+
+    connectedCallback() { this.update_truth(); }
+    adoptedCallback() { this.update_truth(); }
+
+    static get observedAttributes() {
+	return ['value', 'negative', 'conflicts', 'needs'];
+    }
+    attributeChangedCallback(name, oldval, newval) {
+	if (oldval === newval)
+	    return;
+
+	if (name == 'value') {
+	    this.truth = cfgbool(newval);
+	} else if (name == 'negative') {
+	    this.checked = this.#truth != this.negative;
+	} else if (name == 'conflicts' || name == 'needs') {
+	    this.resolve_conflicts();
+	}
+    }
+
+    form_objects(list) {
+	if (!this.form)
+	    return [];
+	return list.map((n) => this.form.elements[n]);
+    }
+    do_resolve(which, sense) {
+	for (const e of this.form_objects(which)) {
+	    if (e instanceof XBox)
+		e.truth = sense;
+	}
+    }
+    #resolving;
+    resolve_conflicts() {
+	const t = this.#truth;
+	if (this.#resolving || !this.form)
+	    return;
+	this.#resolving = true;
+	if (t) {
+	    this.do_resolve(this.needs, true);
+	    this.do_resolve(this.conflicts, false);
+	} else {
+	    for (const e of this.form.elements) {
+		if (e instanceof XBox && e.truth) {
+		    if (e.form_objects(e.needs).some((x) => x === this))
+			e.truth = false;
+		}
+	    }
+	}
+	this.#resolving = false;
+    }
+}
+customElements.define('x-box', XBox, { extends: 'input' });

+ 4 - 4
esp32/www/status.html

@@ -26,7 +26,7 @@
 	</label>
 	<label class="fpgaok">
 	  <b>FPGA online</b>
-	  <input type="checkbox" name="max80.fpga" />
+	  <input is="x-box" name="max80.fpga" />
 	</label>
       </fieldset>
       <fieldset class="fw">
@@ -48,7 +48,7 @@
 	<legend>Wifi Client</legend>
 	<label class="net-connected">
 	  <b>Connected</b>
-	  <input type="checkbox" name="net.sta.conn" />
+	  <input is="x-box" name="net.sta.conn" />
 	</label>
 	<label class="wifi-ssid">
 	  <b>Network name (SSID)</b>
@@ -75,7 +75,7 @@
 	<legend>Wifi Access Point</legend>
 	<label class="net-connected">
 	  <b>Active</b>
-	  <input type="checkbox" name="net.ap.conn" />
+	  <input is="x-box" name="net.ap.conn" />
 	</label>
 	<label class="wifi-ssid">
 	  <b>Network name (SSID)</b>
@@ -110,7 +110,7 @@
 	</label>
 	<label class="sntp-sync">
 	  <b>Time synchronized</b>
-	  <input type="checkbox" name="net.sntp.sync" />
+	  <input is="x-box" name="net.sntp.sync" />
 	</label>
       </fieldset>
       <fieldset class="refresh">

+ 1 - 1
esp32/www/update.html

@@ -14,7 +14,7 @@
 	<legend class="ver">Hardware Version</legend>
 	<label class="unlock">
 	  <b>Unlock Programming</b>
-	  <input type="checkbox" name="unlock" id="unlock" onchange="bropen()">
+	  <input is="x-box" name="unlock" id="unlock" onchange="bropen()">
 	</label>
 	<label class="ver">
 	  <b>Hardware Version</b>

二進制
fpga/output/max80.fw


二進制
fpga/output/v1.fw


二進制
fpga/output/v2.fw