IncdecApp2: IncdecApp2.html

<!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>
[download file]