Incdec2

Incdec2.html  [download]

<!DOCTYPE html>

<html>
<head>

<title>Comp 86 Incdec2 + Incdec3 examples combined</title>

<script type="text/javascript">

/*
 **************************************************
 * 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");
		this._upButton.innerHTML = "+"
		this._panel.appendChild (this._upButton);
	}

	_makeDown () { //*1
		this._downButton = document.createElement("button");
		this._downButton.innerHTML = "-"
		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 () {
		// "this" in callback is normally the HTML element,
		// but now "self" will be in scope when callback is called
		var self = this
		this._upButton.onclick = function (e) { self._actionPerformed(e); }
		this._downButton.onclick = function (e) { self._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.innerHTML = 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()
	}
}

/*
 **************************************************
 * 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 overload this one.
	// This is not a genuine repaint callback,
	// you only need to call this when you change something
	_refreshShow () { //*2
		// Temp vars just to make code cleaner
		var width = this._show.width
		var height = this._show.height
		var rad = width/2

		var 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(); //*2
		gc.arc (width/2, height/2, rad, 0, 2*Math.PI); //*2
		gc.stroke(); //*2

		// Filled in segment
		gc.beginPath(); //*2
		gc.moveTo (width/2, height/2) //*2
		gc.arc (width/2, height/2, rad,  //*2
			0, -2*Math.PI * (this._value-this._min) / (this._max-this._min), //*2
			true); //*2
		gc.fill(); //*2
	}
}

/*
 **************************************************
 * Initialization
 **************************************************
 */
window.onload = function () {
	var maindiv = document.getElementById("maindiv")

	// Create some Incdec's for testing
	var id1 = new IncdecVert()
	maindiv.appendChild (id1.getPanel())

	var id2 = new IncdecHor()
	maindiv.appendChild (id2.getPanel())

	var id3 = new IncdecPie()
	maindiv.appendChild (id3.getPanel())

	// And a button to report the data from the Incdec's, for testing
	var b = document.createElement ("button")
	b.innerHTML = "Get data"
	maindiv.appendChild (b)
	b.onclick = function (e) {
		alert ("Values = " + id1.value + 
			", " +  id2.value +
			", " +  id3.value)
	}
}

</script> 
</head>

<body>

<div id="maindiv" height="500px" width="100%">
</div>

</body>
</html>