Example of using closure to access 'this' object in a different context, e.g. in a global context when using setTimeout or setInterval.
//Example of using closures to access 'this' object in a different context, //e.g. in a global context when using setTimeout() function Person(name, count) { this.name = name; this.count = count; } Person.prototype.countDown = function() { var self = this; var closureFunc = function() { console.log(self.name + ": " + self.count); self.count–; if (self.count > 0) { self.countDown(); } } setTimeout(closureFunc, 500); }The usage is pretty simple:
- just copy the code to a file or run the code
- then instantiate a timer by hooking it to an existing div tag on the page
- then invoke two functions on the timer object: timer.createHTML(); timer.start();
Example Usage:
Now here's the javascript for the CustomTimer.
/* Usage: var myCt = new com.clt.CustomTimer('myCt', 20, 'div1'); myCt.createHTML(); //creates tabular div progress bar [and/or] myCt.createHTML2(); //creates continuous div progress bar myCt.addTickEventHandler(function(txt, timerObj) { alert(txt+'\n'+timerObj.id); }); myCt.addTickEventHandler(function(txt, timerObj) { fireAjaxWebRequest(....); }) //optionally add extra event handlers to the timer div //$addHandler($get('div1'), 'click', com.clt.createDelegate(myCt, myCt.timerDiv_onclick)); myCt.start(); myCt.pause(); myCt.resume(); or myCt.tickTock(); myCt.reset(); myCt.start(); To reInitialize with a different time and widthFactor: --------------------------------------------------------------------- myCt.reInitializeTimer(15000, 2); OR reInitializeTimer = function(queryIntervalInMS, widthFactor) { var numTicks = queryIntervalInMS / 1000; var timerId = _timerObject.id; _timerObject.dispose(false); _timerObject = new com.clt.CustomTimer(timerId, numTicks, 'timerDivId'); if (_timerObject) { _timerObject.widthFactor = widthFactor; _timerObject.createHTML2(); _timerObject.addTickEventHandler(_timerEventHandler); _timerObject.start(); } } */ var com; if (typeof com == 'undefined' || com == null) { com = {}; } if (typeof com.clt == 'undefined') { com.clt = {}; } //public static variable com.clt.numCustomTimerObj = 0; //Utility function com.clt.createDelegate = function(instance, method){ return function() { return method.apply(instance, arguments); } } //function in global namespace, usage: var divEl = dget('div1'); function dget(id){ return document.getElementById(id); } /********************************************************************************* Implements: Observer design pattern: listeners/eventHandlers are registered, events fired and corresponding listeners/eventHandlers are invoked. TODO: use a logger - if IE use logging in a txtbox, if firefox & firebug use console.log(), if ie & 'asp.net ajax' use sys.debug.trace() Dependencies: - asp.net ajax library: - $clearHandlers - $addHandler - $removeHandler - homegrown: - log("message","optional_log_level=debug") *********************************************************************************/ /* valid values for CustomTimer.state = started, paused, stopped, created, initialized, disposed cycle: - created (when constructed) - initialized (when everything is ready, including html) - started - paused (optional) - stopped (when reset) - disposed */ com.clt.CustomTimer = function(id, numTicks, divId, tableId, tableRowId) { com.clt.numCustomTimerObj++; this.id = id; this.currentTick = 0; this.numTicks = numTicks; this.divId = divId; this.divEl = dget(this.divId); this.divElEventHandlers = new Array(); this.backgroundColor = 'gray'; this.height = '10px'; this.activeColor = 'red'; this.tableCellWidth = '1px'; this.tableId = tableId; this.tableRowId = tableRowId; this.tableEl = null; this.tableRowEl = null; this.timerProg; this.updateIntervalInMs = 1000; this.progDivContainerEl = null; this.progDivContainerId = null; this.progressDivId = null; this.progressDivEl = null; //multiplier for progress bar when using div instead of table this.widthFactor = 2; this.tickEventHandlers = new Array(); this.startTime = null; this.state = 'created'; this.prevTickEventTime = null; this.totalTickEvents = 0; } com.clt.CustomTimer.prototype.update = function() { } com.clt.CustomTimer.prototype.addTickEventHandler = function(handler) { var index = this.tickEventHandlers.length; this.tickEventHandlers[index] = handler; return index; } // removes by index or the function eventHandler Object com.clt.CustomTimer.prototype.removeTickEventHandler = function(indexOrFunction) { if (typeof indexOrFunction == 'number') { if (indexOrFunction >= this.tickEventHandlers.length) { //error return null; } var f = this.tickEventHandlers[indexOrFunction]; this.tickEventHandlers[indexOrFunction] = null; return f; } else { for (var i = 0; i < this.tickEventHandlers.length; i++) { if (this.tickEventHandlers[i] == indexOrFunction) { var f = this.tickEventHandlers[i]; this.tickEventHandlers[i] = null; return f; } } } return null; } /* @param data:Object = {'startTime':this.startTime, 'prevTickTime':prevTickTime, 'endTime':new Date()} */ com.clt.CustomTimer.prototype.raiseTickEvent = function() { this.totalTickEvents++; var endTime = new Date(); this.currentTick = 0; this.resetUI(); this.state = 'started'; //var dataObj = {'startTime':this.startTime.getTime(), 'prevTickTime':prevTickTime.getTime(), 'endTime':endTime.getTime()} var txt = "start time: " + this.startTime + "\nprev tick time:"+this.prevTickEventTime +"\nend time:" + endTime + "\nTotalTickEvents: " + this.totalTickEvents; this.prevTickEventTime = endTime; for (var i = 0; i < this.tickEventHandlers.length; i++) { var f = this.tickEventHandlers[i]; if (f) { if (f != null && typeof f == 'function') { var self = this; f(txt, self); } } } } com.clt.CustomTimer.prototype.setNumTicks = function(totalTicks) { this.numTicks = totalTicks; this.reset(); } com.clt.CustomTimer.prototype.setUpdateInterval = function(inMilliseconds){ this.updateIntervalInMs = inMilliseconds; } //this creates a div progress bar as opposed to a tabular prog bar com.clt.CustomTimer.prototype.createHTML2 = function() { if (this.divEl) { if (this.progDivContainerEl) { //do nothing } else { if (this.progDivContainerId) { //do nothing, use specified divId } else { //define a new ID for this div tag this.progDivContainerId = this.id + "_Container"; } this.progDivContainerEl = dget(this.progDivContainerId); if (this.progDivContainerEl) { // do nothing, use existing div } else { // create a new div, append to parent this.progDivContainerEl = document.createElement("div"); this.progDivContainerEl.id = this.progDivContainerId; this.divEl.appendChild(this.progDivContainerEl); } this.progDivContainerEl.style.backgroundColor = this.backgroundColor; this.progDivContainerEl.style.width = "" + (this.numTicks * this.widthFactor) + "px"; this.progDivContainerEl.style.height = this.height; this.progDivContainerEl.style.border = "1px solid black"; } //..... if (this.progressDivEl) { //do nothing } else { if (this.progressDivId) { //do nothing } else { this.progressDivId = this.id + "_ProgressBar"; } this.progressDivEl = dget(this.progressDivId); if (this.progressDivEl) { //do nothing, use existing div } else { //create a new div, append to parent this.progressDivEl = document.createElement("div"); this.progressDivEl.id = this.progressDivId; this.progDivContainerEl.appendChild(this.progressDivEl); } this.progressDivEl.style.backgroundColor = this.activeColor; this.progressDivEl.style.width = "0px"; this.progressDivEl.style.height = "10px"; } this.reset(); this.state = 'initialized'; } else { //log error if(typeof log != 'undefined') { log('Unable to createHTML2(), DIV element is null','warn'); } } } //creates a tabular progres bar with discrete visible timer steps/ticks com.clt.CustomTimer.prototype.createHTML = function() { if (this.divEl) { this.tableEl = dget(this.tableId); if (this.tableEl) { //do noting } else { if (this.tableId) { //do nothing } else { this.tableId = this.id + "_Table"; } this.tableEl = dget(this.tableId); if (this.tableEl) { //do nothing } else { this.tableEl = document.createElement("table"); this.tableId = this.id + "_Table"; this.divEl.appendChild(this.tableEl); } this.tableEl.id = this.tableId; this.tableEl.style.backgroundColor = this.backgroundColor; this.tableEl.style.height = this.height; } //...... this.tableRowEl = dget(this.tableRowId); if (this.tableRowEl) { //do nothing } else { if (this.tableRowId) { //do nothing } else { this.tableRowId = this.id + "_TableRow"; } this.tableRowEl = dget(this.tableRowId); if (this.tableRowEl) { //do nothing } else { this.tableRowEl = document.createElement("tr"); this.tableRowEl.id = this.tableRowId; this.tableEl.appendChild(this.tableRowEl); } this.tableRowEl.style.backgroundColor = this.backgroundColor; } if (this.tableEl && this.tableRowEl) { this.reset(); this.state = 'initialized'; //this.createRows(); } else { //serious error - log message } } else { //error if(typeof log != 'undefined') { log('Unable to createHTML2(), DIV element is null','warn'); } } } //this updates the progress bar after a 1 sec, i.e. if numTicks=10, then timer will stop at exactly the 10th second mark com.clt.CustomTimer.prototype.start = function() { if (this.state == 'paused') { this.resume(); return; } this.startTime = new Date(); var f = com.clt.createDelegate(this, this.tickTock); //this.timerProg = setTimeout(f, this.updateIntervalInMs); f(); //execute a tick immediately this.timerProg = setInterval(f, this.updateIntervalInMs); this.state = 'started'; } //this updates the progress bar immediately, i.e. if numTicks=10, then timer will stop at 11th second mark com.clt.CustomTimer.prototype.tickTock = function() { if (this.numTicks) { if (this.currentTick <= this.numTicks) { if (this.startTime == null) { this.startTime = new Date(); } if (this.tableRowEl) { var el1 = this.tableRowEl.getElementsByTagName("td")[this.currentTick]; if (el1) el1.style.backgroundColor = this.activeColor; } if (this.progressDivEl) { this.progressDivEl.style.width = "" + (this.widthFactor * (this.currentTick)) + "px"; } if (this.currentTick == this.numTicks) { this.raiseTickEvent(); return; //clearInterval(this.timerProg); } this.currentTick++; //var f = com.clt.createDelegate(this, this.tickTock); //this.timerProg = setTimeout(f, this.updateIntervalInMs); } else { //clearInterval(this.timerProg); } } } com.clt.CustomTimer.prototype.resume = function() { var f = com.clt.createDelegate(this, this.tickTock); this.tickTock(); this.timerProg = setInterval(f, this.updateIntervalInMs); this.state = 'started'; } com.clt.CustomTimer.prototype.pause = function() { if (this.numTicks) { //clearTimeout(this.timerProg); clearInterval(this.timerProg); this.state = 'paused'; } else { } } com.clt.CustomTimer.prototype.reInitializeTimer = function(tickIntervalInMS, widthFactor) { var numTicks = tickIntervalInMS / 1000; this.reset(); this.numTicks = numTicks; this.widthFactor = widthFactor; this.createHTML2(); this.start(); } com.clt.CustomTimer.prototype.resetUI = function() { if (this.tableRowEl) { this.tableRowEl.innerHTML = ""; this.createRows(); } if (this.progressDivEl) { this.progressDivEl.style.width = "0px"; } } com.clt.CustomTimer.prototype.reset = function() { if (this.numTicks) { this.currentTick = 0; //this.startTime = null; clearTimeout(this.timerProg); this.resetUI(); this.state = 'stopped'; } else { //log error msg } } com.clt.CustomTimer.prototype.createRows = function() { if (this.tableRowEl && this.numTicks) { for (var i = 0; i < this.numTicks; i++) { var tdElem = document.createElement("td"); tdElem.style.backgroundColor = this.backgroundColor; tdElem.style.width = this.tableCellWidth; tdElem.innerHTML = ''; //' ' this.tableRowEl.appendChild(tdElem); } } else { //log error msg } } com.clt.CustomTimer.prototype.dispose = function(removeParentDiv) { //remove any eventHandlers for DOM nodes //set reference to event handlers to null if (this.tickEventHandlers) { this.tickEventHandlers.splice(0, this.tickEventHandlers.length); this.tickEventHandlers = null; } //remove html DOM elements & set reference to DOM nodes to null if (this.progDivContainerId) { var el = dget(this.progDivContainerId); if (el) el.parentNode.removeChild(el); el = null; } if (this.tableId) { var el = dget(this.tableId); if (el) el.parentNode.removeChild(el); el = null; } this.tableEl = null; this.progDivContainerEl = null; this.progressDivEl = null; this.tableEl = null; this.tableRowEl = null; if (removeParentDiv) { if (this.divEl) this.divEl.parentNode.removeChild(this.divEl); var domEl1 = dget(this.divId); if (domEl1) { domEl1.parentNode.removeChild(domEl1); domEl1 = null; } } this.divEl = null; /// if (this.timerProg) { clearInterval(this.timerProg); this.timerProg = null; } //set everything else to null, not necessary this.id = null; this.currentTick = null; this.numTicks = null; this.updateIntervalInMs = null; this.widthFactor = null; this.startTime = null; this.divId = null; this.tableId = null; this.tableRowId = null; this.progDivContainerId = null; this.progressDivId = null; this.state = 'disposed'; return true; } com.clt.CustomTimer.prototype.timerDiv_onclick = function(event) { if (!this) { alert('Timer is not available'); return; } var timerState = this.state; if (timerState == null) { alert('Timer state is NULL'); return; } var userConfirm = false; if (timerState == 'stopped') { userConfirm = confirm('Are you sure you want to START the timer?'); if (userConfirm) { this.reset(); this.start(); } } else if (timerState == 'started') { userConfirm = confirm('Are you sure you want to PAUSE the timer?'); if (userConfirm) { this.pause(); } } else if (timerState == 'paused') { userConfirm = confirm('Are you sure you want the timer to CONTINUE?'); if (userConfirm) { this.start(); } } else if (timerState == 'disposed') { alert('Timer is not available'); } else if (timerState == 'created') { this.createHTML2(); //add event handlers this.start(); } else if (timerState == 'initialized') { this.start(); } else { alert('Invalid timer state[' + timerState + '], doing nothing.'); } } //============================== END CLASS ==========================
No comments:
Post a Comment