// View.js
// 2006-12-31
/*
(C)2008 Julio Di Egidio
http://julio.diegidio.name
mailto:julio@diegidio.name

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

The Software shall be used for Good, not Evil.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
////////////////////////////////////////////////////////////////
// jde_3t_view (View.js) ////////////////////////////////////

// Requires page context
//
/*extern
	window
*/


////////////////////////////////////////////////////////////////
	/////////////////////////////
	// jde_3t_view ///////////
	
// <<class>>GX_View {singleton} //
var jde_3t_view = new (function () {
	
////////////////////////////////////////////////////////////////
	/////////////////////////////
	// Board ////////////////////
	
	this._boards = [];
	
	// <<class>>GX_View.Board //
	this.Board = function () {
		
		/////////////////////
		// Private ////////////
		
		var cellClassNames = [];
		cellClassNames[0] = [];
		cellClassNames[1] = [];
		
		var squareScanDelay,
			squareRotDelayRatio,
			squareBlkDelayRatio,
			squareBlkCount;
		
		var squareOverEventRef,
			squareOutEventRef,
			squareClickEventRef;
		
		// Note: squareKey's values
		//  must match next member names!
		var squareKey = { e : "e", x : "x", o : "o" };
		var squareState = {
			hidle  : "HIDLE",
			empty  : "EMPTY",
			set    : { x : "SET_X",    o : "SET_O" },
			hilite : { x : "HILITE_X", o : "HILITE_O" },
			start  : "START",
			hit    : { x : "HIT_X",    o : "HIT_O" },
			hold   : { x : "HOLD_X",   o : "HOLD_O" },
			win    : { x : "WIN_X",    o : "WIN_O" },
			hover  : { x : "HOVER_X",  o : "HOVER_O" }
		};
		var squareLabel = { e : "&nbsp;", x : "X", o : "O" };
		//
		
		
////////////////////////////////////////////////////////////////
		/////////////////////////////
		// Board.Square ///////////////
		
		// <<class>>GX_View.Board.Square //
		function Square(row, col) {
			
			/////////////////////
			// Private ////////////
			
			var aCell;
			
			var newState,
				oldState,
				bagObj;
			
			var that = this;
			
			
			var currKey = null;
			function updateCell(key, B, F) {
				if(key !== currKey) {
					aCell.innerHTML = squareLabel[key];
					currKey = key;
				}
				
				aCell.className = cellClassNames[B][F];
				
				if(newState !== oldState) {
					if(newState === squareState.empty ||
							newState === squareState.start ||
							newState === squareState.hit.x ||
							newState === squareState.hit.o ||
							newState === squareState.hover.x ||
							newState === squareState.hover.o) {
						if(newState === squareState.hover.x ||
								newState === squareState.hover.o) {
							aCell.style.cursor = "pointer";
						}
						else {
							aCell.style.cursor = "default";
						}
						
						aCell.onmouseover = function() {
							window.status = "";
							squareOverEventRef(row, col);
							return true;
						};
						aCell.onmouseout = function() {
							squareOutEventRef(row, col);
						};
						aCell.onclick = function() {
							window.status = "";
							squareClickEventRef(row, col);
							return false;
						};
					}
					else {
						aCell.style.cursor = "default";
						
						aCell.onmouseover = function() {
							window.status = "";
							return true;
						};
						aCell.onmouseout = function() {
						};
						aCell.onclick = function() {
							window.status = "";
							return false;
						};
					}
				}
			}
			
			
			/* HIDLE */
			function doHidle() {
				if(bagObj.iter % squareRotDelayRatio === 0) {
					var state = Math.floor(Math.random() * 3);
					
					if(state !== bagObj.state) {
						if(state === 0) {
							updateCell(squareKey.e, 1, 1);
						}
						else if(state === 1) {
							updateCell(squareKey.x, 1, 1);
						}
						else {
							updateCell(squareKey.o, 1, 1);
						}
						
						bagObj.state = state;
					}
				}
				
				bagObj.iter++;
			}
			
			/* EMPTY */
			function doEmpty() {
				updateCell(squareKey.e, 0, 0);
			}
			
			/* SET_X/O */
			function doSet(key) {
				updateCell(key, 0, 0);
			}
			
			/* HILITE_X/O */
			function doHilite(key) {
				updateCell(key, 0, 1);
			}
			
			/* START */
			function doStart() {
				if(bagObj.iter % squareBlkDelayRatio === 0) {
					var state;
					
					// start -> 0
					if(bagObj.state === -1) {
						state = 0;
					}
					// 0 -> (2*c < blkC ? 1 : stop)
					else if(bagObj.state === 0) {
						var c = (bagObj.iter / squareBlkDelayRatio) + 1; // 2,4,6,...
						state = c < 2 * squareBlkCount ? 1 : null;
					}
					// 1 -> 0
					else if(bagObj.state === 1) {
						state = 0;
					}
					
					if(state === 0) {
						updateCell(squareKey.e, 1, 1);
					}
					else if(state === 1) {
						updateCell(squareKey.e, 0, 1);
					}
					else {
						// stop: EMPTY
						newState = squareState.empty;
						that.update();
						return;
					}
					
					bagObj.state = state;
				}
				
				bagObj.iter++;
			}
			
			/* HIT_X/O */
			function doHit(key) {
				if(bagObj.iter % squareBlkDelayRatio === 0) {
					var state;
					
					// start -> 0
					if(bagObj.state === -1) {
						state = 0;
					}
					// 0 -> (2*c < blkC ? 1 : stop)
					else if(bagObj.state === 0) {
						var c = (bagObj.iter / squareBlkDelayRatio) + 1; // 2,4,6,...
						state = c < 2 * squareBlkCount ? 1 : null;
					}
					// 1 -> 0
					else if(bagObj.state === 1) {
						state = 0;
					}
					
					if(state === 0) {
						updateCell(key, 0, 0);
					}
					else if(state === 1) {
						updateCell(squareKey.e, 0, 0);
					}
					else {
						// stop: EMPTY
						newState = squareState.empty;
						that.update();
						return;
					}
					
					bagObj.state = state;
				}
				
				bagObj.iter++;
			}
			
			/* HOLD_X/O */
			function doHold(key) {
				if(bagObj.iter % squareBlkDelayRatio === 0) {
					var state;
					
					// start -> 0
					if(bagObj.state === -1) {
						state = 0;
					}
					// 0 -> (2*c < blkC ? 1 : stop)
					else if(bagObj.state === 0) {
						var c = (bagObj.iter / squareBlkDelayRatio) + 1; // 2,4,6,...
						state = c < 2 * squareBlkCount ? 1 : null;
					}
					// 1 -> 0
					else if(bagObj.state === 1) {
						state = 0;
					}
					
					if(state === 0) {
						updateCell(key, 0, 1);
					}
					else if(state === 1) {
						updateCell(key, 0, 0);
					}
					else {
						// stop: SET_X/O
						newState = squareState.set[key];
						that.update();
						return;
					}
					
					bagObj.state = state;
				}
				
				bagObj.iter++;
			}
			
			/* WIN_X/O */
			function doWin(key) {
				if(bagObj.iter % squareBlkDelayRatio === 0) {
					var state;
					
					// start -> 0
					if(bagObj.state === -1) {
						state = 0;
					}
					// 0 -> (2*c < blkC ? 1 : stop)
					else if(bagObj.state === 0) {
						var c = (bagObj.iter / squareBlkDelayRatio) + 1; // 2,4,6,...
						state = c < 2 * squareBlkCount ? 1 : null;
					}
					// 1 -> 0
					else if(bagObj.state === 1) {
						state = 0;
					}
					
					if(state === 0) {
						updateCell(key, 0, 1);
					}
					else if(state === 1) {
						updateCell(key, 0, 0);
					}
					else {
						// stop: HILITE_X/O
						newState = squareState.hilite[key];
						that.update();
						return;
					}
					
					bagObj.state = state;
				}
				
				bagObj.iter++;
			}
			
			/* HOVER_X/O */
			function doHover(key) {
				if(bagObj.iter % squareBlkDelayRatio === 0) {
					var state = -bagObj.state;
					
					if(state > 0) {
						updateCell(key, 0, 1);
					}
					else {
						updateCell(squareKey.e, 0, 0);
					}
					
					bagObj.state = state;
				}
				
				bagObj.iter++;
			}
			
			
			function BagObj() {
				this.iter = 0;
				this.state = -1;
			}
			
			
			/////////////////////
			// Public ////////////
			
			this.init = function (aCellEl) {
				aCell = aCellEl;
				
				// Trigger -> hidle!
				newState = squareState.hidle;
				oldState = squareState.empty;
				//
			};
			
			this.set = function (state) {
				newState = state;
			};
			
			this.update = function () {
				// Is-newState
				if(newState !== oldState) {
					bagObj = new BagObj();
					
					/* HIDLE */
					if(newState === squareState.hidle) {
						doHidle();
					}
					/* EMPTY */
					else if(newState === squareState.empty) {
						doEmpty();
					}
					/* SET_X/O */
					else if(newState === squareState.set.x) {
						doSet(squareKey.x);
					}
					else if(newState === squareState.set.o) {
						doSet(squareKey.o);
					}
					/* HILITE_X/O */
					else if(newState === squareState.hilite.x) {
						doHilite(squareKey.x);
					}
					else if(newState === squareState.hilite.o) {
						doHilite(squareKey.o);
					}
					/* START */
					if(newState === squareState.start) {
						doStart();
					}
					/* HIT_X/O */
					else if(newState === squareState.hit.x) {
						doHit(squareKey.x);
					}
					else if(newState === squareState.hit.o) {
						doHit(squareKey.o);
					}
					/* HOLD_X/O */
					else if(newState === squareState.hold.x) {
						doHold(squareKey.x);
					}
					else if(newState === squareState.hold.o) {
						doHold(squareKey.o);
					}
					/* WIN_X/O */
					else if(newState === squareState.win.x) {
						doWin(squareKey.x);
					}
					else if(newState === squareState.win.o) {
						doWin(squareKey.o);
					}
					/* HOVER_X/O */
					else if(newState === squareState.hover.x) {
						doHover(squareKey.x);
					}
					else if(newState === squareState.hover.o) {
						doHover(squareKey.o);
					}
					
					oldState = newState;
				}
				
				// Is-not-newState
				else {
					
					/* HIDLE */
					if(newState === squareState.hidle) {
						doHidle();
					}
					/* START */
					else if(newState === squareState.start) {
						doStart();
					}
					/* HIT_X/O */
					else if(newState === squareState.hit.x) {
						doHit(squareKey.x);
					}
					else if(newState === squareState.hit.o) {
						doHit(squareKey.o);
					}
					/* HOLD_X/O */
					else if(newState === squareState.hold.x) {
						doHold(squareKey.x);
					}
					else if(newState === squareState.hold.o) {
						doHold(squareKey.o);
					}
					/* WIN_X/O */
					else if(newState === squareState.win.x) {
						doWin(squareKey.x);
					}
					else if(newState === squareState.win.o) {
						doWin(squareKey.o);
					}
					/* HOVER_X/O */
					else if(newState === squareState.hover.x) {
						doHover(squareKey.x);
					}
					else if(newState === squareState.hover.o) {
						doHover(squareKey.o);
					}
					
				}
			};
			
		}
		
		
		/////////////////////
		// Private ////////////
		
		var squares = [];
		for(var r = 0; r < 3; r++) {
			squares[r] = [];
			for(var c = 0; c < 3; c++) {
				squares[r][c] = new Square(r, c);
			}
		}
		
		var boardIdx = jde_3t_view._boards.length;
		jde_3t_view._boards[boardIdx] = this;
		
		var thatBoard = this;
		
		
		this._refreshSquares = function () {
			for(var r = 0; r < 3; r++) {
				for(var c = 0; c < 3; c++) {
					squares[r][c].update();
				}
			}
			
			window.setTimeout(
				"jde_3t_view._boards[" + boardIdx +
					"]._refreshSquares()", squareScanDelay);
		};
		
		function initSquares(params, overEventRef, outEventRef, clickEventRef) {
			var tblDisp = window.document.getElementById(params.dom.tableId);
			
			cellClassNames[0][0] = params.css.tdClassName_B0_F0;
			cellClassNames[0][1] = params.css.tdClassName_B0_F1;
			cellClassNames[1][0] = params.css.tdClassName_B1_F0;
			cellClassNames[1][1] = params.css.tdClassName_B1_F1;
			
			squareScanDelay = params.proc.scanDelay;
			squareRotDelayRatio = params.proc.rotDelayRatio;
			squareBlkDelayRatio = params.proc.blkDelayRatio;
			squareBlkCount = params.proc.blkCount;
			
			squareOverEventRef = overEventRef;
			squareOutEventRef = outEventRef;
			squareClickEventRef = clickEventRef;
			
			for(var r = 0; r < 3; r++) {
				for(var c = 0; c < 3; c++) {
					squares[r][c].init(
						tblDisp.rows[r].cells[c].childNodes[0]);
				}
			}
			
			thatBoard._refreshSquares();
		}
		
		function setSquare(r, c, state) {
			squares[r][c].set(state);
		}
		
		
		/////////////////////
		// Public ////////////
		
		this.init = function (params, overEventRef, outEventRef, clickEventRef) {
			initSquares(params, overEventRef, outEventRef, clickEventRef);
		};
		
		/* HIDLE */
		this.hidle = function (r, c) {
			setSquare(r, c, squareState.hidle);
		};
		
		/* EMPTY */
		this.empty = function (r, c) {
			setSquare(r, c, squareState.empty);
		};
		
		/* SET_X/O */
		this.setX = function (r, c) {
			setSquare(r, c, squareState.set.x);
		};
		this.setO = function (r, c) {
			setSquare(r, c, squareState.set.o);
		};
		
		/* HILITE_X/O */
		this.hiliteX = function (r, c) {
			setSquare(r, c, squareState.hilite.x);
		};
		this.hiliteO = function (r, c) {
			setSquare(r, c, squareState.hilite.o);
		};
		
		/* START */
		this.start = function (r, c) {
			setSquare(r, c, squareState.start);
		};
		
		/* HIT_X/O */
		this.hitX = function (r, c) {
			setSquare(r, c, squareState.hit.x);
		};
		this.hitO = function (r, c) {
			setSquare(r, c, squareState.hit.o);
		};
		
		/* HOLD_X/O */
		this.holdX = function (r, c) {
			setSquare(r, c, squareState.hold.x);
		};
		this.holdO = function (r, c) {
			setSquare(r, c, squareState.hold.o);
		};
		
		/* WIN_X/O */
		this.winX = function (r, c) {
			setSquare(r, c, squareState.win.x);
		};
		this.winO = function (r, c) {
			setSquare(r, c, squareState.win.o);
		};
		
		/* HOVER_X/O */
		this.hoverX = function (r, c) {
			setSquare(r, c, squareState.hover.x);
		};
		this.hoverO = function (r, c) {
			setSquare(r, c, squareState.hover.o);
		};
		
	};
	
	
////////////////////////////////////////////////////////////////
	/////////////////////////////
	// Score ////////////////////
	
	// <<class>>GX_View.Score //
	this.Score = function () {
		
		/////////////////////
		// Private ////////////
		
		var spnMeLbl,
			spnMeVal,
			spnYouLbl,
			spnYouVal,
			spnDrawLbl,
			spnDrawVal,
			spnAllLbl,
			spnAllVal;
		
		var spnDefClassName,
			spnHilClassName;
		
		var score = {};
		
		var that = this;
		
		
		function resetScore() {
			score.me = 0;
			score.you = 0;
			score.draw = 0;
			score.all = 0;
			score.high = 0;
		}
		
		function recalcScore() {
			score.all = score.me + score.you + score.draw;
			score.high = 0;
			if(score.high < score.me) {
				score.high = score.me;
			}
			if(score.high < score.you) {
				score.high = score.you;
			}
			if(score.high < score.draw) {
				score.high = score.draw;
			}
		}
		
		
		function updateSpnMYD(scor, spnLbl, spnVal) {
			spnLbl.className = scor === score.high ?
				spnHilClassName : spnDefClassName;
			spnVal.innerHTML = scor;
		}
		
		function updateSpnAll(scor, spnLbl, spnVal) {
			spnLbl.className = spnDefClassName;
			spnVal.innerHTML = scor;
		}
		
		
		function init(params) {
			spnMeLbl = window.document.getElementById(
				params.dom.spanIdPfx + "_Me_Lbl");
			spnMeVal = window.document.getElementById(
				params.dom.spanIdPfx + "_Me_Val");
			spnYouLbl = window.document.getElementById(
				params.dom.spanIdPfx + "_You_Lbl");
			spnYouVal = window.document.getElementById(
				params.dom.spanIdPfx + "_You_Val");
			spnDrawLbl = window.document.getElementById(
				params.dom.spanIdPfx + "_Draw_Lbl");
			spnDrawVal = window.document.getElementById(
				params.dom.spanIdPfx + "_Draw_Val");
			spnAllLbl = window.document.getElementById(
				params.dom.spanIdPfx + "_All_Lbl");
			spnAllVal = window.document.getElementById(
				params.dom.spanIdPfx + "_All_Val");
			
			spnDefClassName = params.css.spanDefClassName;
			spnHilClassName = params.css.spanHilClassName;
		}
		
		function update() {
			updateSpnMYD(score.me, spnMeLbl, spnMeVal);
			updateSpnMYD(score.you, spnYouLbl, spnYouVal);
			updateSpnMYD(score.draw, spnDrawLbl, spnDrawVal);
			updateSpnAll(score.all, spnAllLbl, spnAllVal);
		}
		
		
		/////////////////////
		// Public ////////////
		
		this.init = function (params) {
			init(params);
			resetScore();
			update();
		};
		
		this.reset = function () {
			resetScore();
			update();
		};
		
		this.setMe = function (val) {
			if(val !== score.me) {
				score.me = val;
				recalcScore();
				update();
			}
		};
		
		this.setYou = function (val) {
			if(val !== score.you) {
				score.you = val;
				recalcScore();
				update();
			}
		};
		
		this.setDraw = function (val) {
			if(val !== score.draw) {
				score.draw = val;
				recalcScore();
				update();
			}
		};
		
	};
	
	
////////////////////////////////////////////////////////////////
	/////////////////////////////
	// Info /////////////////////
	
	// IE patch!
	this._infos = [];
	//
	
	// <<class>>GX_View.Info //
	this.Info = function () {
		
		/////////////////////
		// Private ////////////
		
		var divInfo,
			messages;
		
		var currKey = null;
		
		// IE patch!
		var infoIdx = jde_3t_view._infos.length;
		jde_3t_view._infos[infoIdx] = this;
		//
		
		var that = this;
		
		
		function init(params) {
			divInfo = window.document.getElementById(params.dom.divInfoId);
			messages = params.proc.msg;
		}
		
		function update() {
			divInfo.innerHTML = messages[currKey];
		}
		
		
		/////////////////////
		// Public ////////////
		
		this.key = {
			newGame      : "newGame",
			yourTurn     : "yourTurn",
			myTurn       : "myTurn",
			yourAutoTurn : "yourAutoTurn",
			youWin       : "youWin",
			iWin         : "iWin",
			draw         : "draw"
		};
		
		
		this.init = function (params) {
			currKey = that.key.newGame;
			init(params);
			// IE patch!
			//update();
			window.setTimeout(
				"jde_3t_view._infos[" + infoIdx + "]" +
					"._updateInfo()", 1);
			//
		};
		
		this.update = function (key) {
			if(key !== currKey) {
				currKey = key;
				// IE patch!
				//update();
				window.setTimeout(
					"jde_3t_view._infos[" + infoIdx + "]" +
						"._updateInfo()", 1);
				//
			}
		};
		
		// IE patch!
		this._updateInfo = function () {
			update();
		};
		//
		
	};
	
	
////////////////////////////////////////////////////////////////
	/////////////////////////////
	// Ctrl /////////////////////
	
	// <<class>>GX_View.Ctrl //
	this.Ctrl = function () {
		
		/////////////////////
		// Private ////////////
		
		var btnDisClassName,
			btnEnaClassName;
		
		var buttons = {};
		
		var that = this;
		
		
		function init(params, handlers) {
			buttons[that.key.newGame] =
				window.document.getElementById(params.dom.btnNewGameId);
			buttons[that.key.restart] =
				window.document.getElementById(params.dom.btnRestartId);
			buttons[that.key.undo] =
				window.document.getElementById(params.dom.btnUndoId);
			buttons[that.key.suggest] =
				window.document.getElementById(params.dom.btnSuggestId);
			buttons[that.key.startAutoplay] =
				window.document.getElementById(params.dom.btnStartAutoplayId);
			buttons[that.key.stopAutoplay] =
				window.document.getElementById(params.dom.btnStopAutoplayId);
			
			buttons[that.key.newGame].handler = handlers[that.key.newGame];
			buttons[that.key.restart].handler = handlers[that.key.restart];
			buttons[that.key.undo].handler = handlers[that.key.undo];
			buttons[that.key.suggest].handler = handlers[that.key.suggest];
			buttons[that.key.startAutoplay].handler = handlers[that.key.startAutoplay];
			buttons[that.key.stopAutoplay].handler = handlers[that.key.stopAutoplay];
			
			btnDisClassName = params.css.btnDisClassName;
			btnEnaClassName = params.css.btnEnaClassName;
		}
		
		
		function update(key, disabled) {
			var btn = buttons[key];
			
			if(disabled) {
				btn.className = btnDisClassName;
				btn.onclick = function() {
					window.status = "";
					return false;
				};
			}
			else {
				btn.className = btnEnaClassName;
				btn.onclick = function() {
					window.status = "";
					btn.handler();
					return false;
				};
			}
			btn.onmouseover = function() {
				window.status = "";
				return true;
			};
		}
		
		
		/////////////////////
		// Public ////////////
		
		this.key = {
			newGame       : "newGame",
			restart       : "restart",
			undo          : "undo",
			suggest       : "suggest",
			startAutoplay : "startAutoplay",
			stopAutoplay  : "stopAutoplay"
		};
		
		
		this.init = function (params, handlers) {
			init(params, handlers);
			
			update(that.key.newGame, true);
			update(that.key.restart, true);
			update(that.key.undo, true);
			update(that.key.suggest, true);
			update(that.key.startAutoplay, true);
			update(that.key.stopAutoplay, true);
		};
		
		this.update = function (key, disabled) {
			update(key, disabled);
		};
		
	};
	
	
////////////////////////////////////////////////////////////////
	/////////////////////////////
	// CtrlExt //////////////////
	
	// <<class>>GX_View.CtrlExt //
	this.CtrlExt = function () {
		
		/////////////////////
		// Private ////////////
		
		var btnExtClassName;
		
		var chkFastAuto,
			cmbAiStrategy,
			cmbAutoStrategy,
			cmbFuzzyThreshold,
			btnResetAll;
		
		var that = this;
		
		
		function updateChk(chk) {
			chk.checked = chk.opt;
			chk.onclick = function() {
				chk.handler(chk.checked);
			};
			chk.disabled = false;
		}
		
		function updateCmb(cmb) {
			cmb.options.length = 0;
			for(var i=0; i<cmb.opts.length; i++) {
				var opt = window.document.createElement("option");
				opt.value = cmb.opts[i].value;
				opt.text = cmb.opts[i].text;
				opt.selected = cmb.opts[i].selected;
				cmb.options.add(opt);
			}
			cmb.onchange = function() {
				cmb.handler(cmb.value);
			};
			cmb.disabled = false;
		}
		
		function updateBtn(btn) {
			btn.className = btnExtClassName;
			btn.onclick = function() {
				window.status = "";
				btn.handler();
				return false;
			};
			btn.onmouseover = function() {
				window.status = "";
				return true;
			};
		}
		
		
		function init(params, handlers, options) {
			chkFastAuto =
				window.document.getElementById(params.dom.chkFastAutoId);
			cmbAiStrategy =
				window.document.getElementById(params.dom.cmbAiStrategyId);
			cmbAutoStrategy =
				window.document.getElementById(params.dom.cmbAutoStrategyId);
			cmbFuzzyThreshold =
				window.document.getElementById(params.dom.cmbFuzzyThresholdId);
			btnResetAll =
				window.document.getElementById(params.dom.btnResetAllId);
			
			chkFastAuto.handler = handlers[that.key.fastAuto];
			cmbAiStrategy.handler = handlers[that.key.aiStrategy];
			cmbAutoStrategy.handler = handlers[that.key.autoStrategy];
			cmbFuzzyThreshold.handler = handlers[that.key.fuzzyThreshold];
			btnResetAll.handler = handlers[that.key.resetAll];
			
			chkFastAuto.opt = options[that.key.fastAuto];
			cmbAiStrategy.opts = options[that.key.aiStrategy];
			cmbAutoStrategy.opts = options[that.key.autoStrategy];
			cmbFuzzyThreshold.opts = options[that.key.fuzzyThreshold];
			
			btnExtClassName = params.css.btnExtClassName;
			
			updateChk(chkFastAuto);
			updateCmb(cmbAiStrategy);
			updateCmb(cmbAutoStrategy);
			updateCmb(cmbFuzzyThreshold);
			updateBtn(btnResetAll);
		}
		
		function update(options) {
			chkFastAuto.opt = options[that.key.fastAuto];
			cmbAiStrategy.opts = options[that.key.aiStrategy];
			cmbAutoStrategy.opts = options[that.key.autoStrategy];
			cmbFuzzyThreshold.opts = options[that.key.fuzzyThreshold];
			
			updateChk(chkFastAuto);
			updateCmb(cmbAiStrategy);
			updateCmb(cmbAutoStrategy);
			updateCmb(cmbFuzzyThreshold);
		}
		
		
		/////////////////////
		// Public ////////////
		
		this.key = {
			fastAuto       : "fastAuto",
			aiStrategy     : "aiStrategy",
			autoStrategy   : "autoStrategy",
			fuzzyThreshold : "fuzzyThreshold",
			resetAll       : "resetAll"
		};
		
		
		this.init = function (params, handlers, options) {
			init(params, handlers, options);
		};
		
// NEEDS REVISION! ******************************************************
		this.update = function (options) {
			update(options);
		};
		
	};
	
})();

