<!DOCTYPE html> <html> <head> <title>Comp 86 Incdec2 + Incdec3 examples combined</title> <script> /* ************************************************** * Main class ************************************************** */ class Main { constructor () { // Create some Incdec's for testing this._id1 = new IncdecVert() maindiv.appendChild (this._id1.getPanel()) this._id2 = new IncdecHor() maindiv.appendChild (this._id2.getPanel()) this._id3 = new IncdecPie() maindiv.appendChild (this._id3.getPanel()) // And a button to report the data from the Incdec's, for testing let b = document.createElement ("button") b.textContent = "Get data" maindiv.appendChild (b) b.addEventListener("click", () => alert ("Values = " + this._id1.value + ", " + this._id2.value + ", " + this._id3.value)) } } /* ************************************************** * Abstract base class for all Incdec's ************************************************** */ class Incdec { constructor () { /* * We will *have* a captive HTML panel (div), not *be* a panel * also provide public access function to the panel */ this._panel = document.createElement ("div") /* * Our data, initialized inline here */ this._value = 0 this._min = 0 this._max = 100 this._incr = 1 // Put a border on our panel // default color, user can modify later this._panel.style.borderStyle = "solid" this._panel.style.borderColor = "red" this._panel.style.borderWidth = "1px" // And some spacing this._panel.style.margin = "4px" this._panel.style.padding = "2px" } /* * Pieces of old constructor, factored out here for subclasses to use */ _makeUp () { //*1 Factor out pieces of old constructor this._upButton = document.createElement("button"); //*1 this._upButton.textContent = "+" //*1 this._panel.appendChild (this._upButton); //*1 } _makeDown () { //*1 this._downButton = document.createElement("button"); this._downButton.textContent = "-" this._panel.appendChild (this._downButton); } // Arg = tell us what kind of HTML element you want, // which also affects our layout _makeShow (element) { //*1 this._show = document.createElement(element); this._refreshShow (); this._panel.appendChild (this._show); } getPanel () {return this._panel} /* * Common callback for both buttons, * e = event, e.target = widget, this = incdec because we rigged it below */ _actionPerformed (e) { if (e.target==this._upButton) this._value += this._incr; else if (e.target==this._downButton) this._value -= this._incr; // Clamp at bounds if (this._value > this._max) this._value = this._max; if (this._value < this._min) this._value = this._min; // Update our widget to match this._refreshShow (); } // Install the above callback for both buttons _installCallbacks () { //*3 Install callbacks in subclass ie AFTER buttons exist this._upButton.addEventListener("click", //*3 (e) => this._actionPerformed (e)) //*3 this._downButton.addEventListener("click", (e) => this._actionPerformed (e)) } /* * Common code fragment, extracted to here, * subclasses that don't use text will override this */ _refreshShow () { //*2 Covers standard case, but IncdecPie will override this._show.textContent = this._value //*2 } // New special "getter" function get value () { return this._value; } } /* ************************************************** * Subclass IncdecVert * * Superclass constructor does most of the work, we finish the job here, * just provide the order of the 3 widgets, and tweak size ************************************************** */ class IncdecVert extends Incdec { constructor () { // Call the parent constructor super () this._panel.style.width = "30px" this._makeUp() this._makeShow ("div") this._makeDown() // Parent constructor couldn't do this cause the buttons don't exist yet this._installCallbacks() //*3 } } /* ************************************************** * Subclass IncdecHor, similarly ************************************************** */ class IncdecHor extends Incdec { constructor () { // Call the parent constructor super () this._panel.style.width = "80px" this._makeDown() this._makeShow("span") this._makeUp() this._installCallbacks() } } /* ************************************************** * Subclass IncdecPie ************************************************** */ class IncdecPie extends Incdec { constructor () { // Call the parent constructor super () // Similar to the other Incdec subclasses this._panel.style.width = "50px" this._makeUp() this._makeShow("canvas") this._makeDown() // Tweak our canvas, then redraw it this._show.width = 50 this._show.height = 50 this._refreshShow() this._installCallbacks() } // We override this one. // This is not a genuine repaint callback, // you only need to call this when you change something _refreshShow () { //*4 Override superclass refreshShow() // Temp vars just to make code cleaner let width = this._show.width let height = this._show.height let rad = width/2 let gc = this._show.getContext("2d"); // Erase the background gc.fillStyle = "white" gc.fillRect (0, 0, width, height); gc.fillStyle = "black" gc.strokeStyle = "black" // Outline gc.beginPath(); //*4 gc.arc (width/2, height/2, rad, 0, 2*Math.PI); //*4 gc.stroke(); //*4 // Filled in segment gc.beginPath(); //*4 gc.moveTo (width/2, height/2) //*4 gc.arc (width/2, height/2, rad, //*4 0, -2*Math.PI * (this._value-this._min) / (this._max-this._min), //*4 true); //*4 gc.fill(); //*4 } } /* ************************************************** * Initialization ************************************************** */ window.onload = function () { new Main (); } </script> </head> <body> <div id="maindiv" height="500px" width="100%"> </div> </body> </html>