/**
 * @projectDescription	Controller object for Project Management.
 * @inherits 	i2b2
 * @namespace	i2b2.PM
 * @author		Nick Benik, Griffin Weber MD PhD
 * @version 	1.5
 * ----------------------------------------------------------------------------------------
 * updated 9-15-09: Refactor loading process to allow CELL loading timeouts and failures [Nick Benik] 
 * updated 11-9-09: Changes to announcement dialog functionality [Charles McGow]
 * updated 11-23-09: Bug Fix for Firefox's 4k XML node text limit [Nick Benik]
 */
console.group('Load & Execute component file: cells > PM > ctrlr');
console.time('execute time');

// ================================================================================================== //
i2b2.PM.doLogin = function() {
	i2b2.PM.model.shrine_domain = false;
	// change the cursor
	// show on GUI that work is being done
	i2b2.h.LoadingMask.show();
	
	// copy the selected domain info into our main data model
	var e = 'The following problems were encountered:';
	var val = i2b2.PM.udlogin.inputUser.value;
	if (!val.blank()) {
		var login_username = val;
	} else {
		e += "\n  Username is empty";
	}
	var val = i2b2.PM.udlogin.inputPass.value;
	if (!val.blank()) {
		var login_password = val;
	} else {
		e += "\n  Password is empty";
	}
	var p = i2b2.PM.udlogin.inputDomain;
	var val = p.options[p.selectedIndex].value;
	if (!val.blank()) {
		var p = i2b2.PM.model.Domains;
		if (p[val]) {
			// copy information from the domain record
			var login_domain = p[val].domain;
			var login_url = p[val].urlCellPM;
			i2b2.PM.model.url = login_url;
			var shrine_domain = Boolean.parseTo(p[val].isSHRINE);
			var login_project = p[val].project;
			if (p[val].debug != undefined) {
				i2b2.PM.model.login_debugging = Boolean.parseTo(p[val].debug);
			} else {
				i2b2.PM.model.login_debugging = false;
			}
			if (p[val].allowAnalysis != undefined) {
				i2b2.PM.model.allow_analysis = Boolean.parseTo(p[val].allowAnalysis);
			} else {
				i2b2.PM.model.allow_analysis = true;
			}
			if (p[val].adminOnly != undefined) {
				i2b2.PM.model.admin_only = Boolean.parseTo(p[val].adminOnly);
			} else {
				i2b2.PM.model.admin_only = false;
			}
			if (p[val].isDQ != undefined) {
				i2b2.PM.model.distributed_query = Boolean.parseTo(p[val].isDQ);
			} else {
				i2b2.PM.model.distributed_query = false;
			}
		}
	} else {
		e += "\n  No login channel was selected";
	}
	// call the PM Cell's communicator Object
	var callback = new i2b2_scopedCallback(i2b2.PM._processUserConfig, i2b2.PM);
	var parameters = {
		domain: login_domain, 
		is_shrine: shrine_domain,
		project: login_project,
		username: login_username,
		password_text: login_password
	};
	var transportOptions = {
		url: login_url,
		user: login_username,
		password: login_password,
		domain: login_domain,
		project: login_project
	};
	i2b2.PM.ajax.getUserAuth("PM:Login", parameters, callback, transportOptions);

}


// ================================================================================================== //
i2b2.PM._processUserConfig = function (data) {
	console.group("PROCESS Login XML");
	console.debug(" === run the following command in Firebug to view message sniffer: i2b2.hive.MsgSniffer.show() ===");



	// save the valid data that was passed into the PM cell's data model
	i2b2.PM.model.login_username = data.msgParams.sec_user;
	try {
		var t = i2b2.h.XPath(data.refXML, '//user/password')[0]; //[@token_ms_timeout]
		i2b2.PM.model.login_password = i2b2.h.Xml2String(t);
		
		var timeout = t.getAttribute('token_ms_timeout');
		if (timeout == undefined ||  timeout < 300001)
		{
		 i2b2.PM.model.IdleTimer.start(1800000-300000); //timeout); //timeout-60000);		
			
		} else {
		
		 i2b2.PM.model.IdleTimer.start(timeout-300000); //timeout); //timeout-60000);		
		}
	} catch (e) {
		//console.error("Could not find returned password node in login XML");
		i2b2.PM.model.login_password = "<password>"+data.msgParams.sec_pass+"</password>\n";
	}	
	// clear the password
	i2b2.PM.udlogin.inputPass.value = "";
	
	if (i2b2.PM.model.reLogin) {
		i2b2.h.LoadingMask.hide();
		try { i2b2.PM.view.modal.login.hide(); } catch(e) {}
		i2b2.PM.model.reLogin = false;
		
		return;
	}	
	
		i2b2.PM.model.isAdmin = false;
	try { 
		var t = i2b2.h.XPath(data.refXML, '//user/full_name')[0];
		i2b2.PM.model.login_fullname = i2b2.h.Xml2String(t);
	} catch(e) {}	
	try { 
		var t = i2b2.h.XPath(data.refXML, '//user/is_admin')[0];
		if (Boolean.parseTo(i2b2.h.getXNodeVal(t, 'is_admin')) ) {
			i2b2.PM.model.isAdmin = true;
		}		
	} catch(e) {}		
	i2b2.PM.model.login_domain = data.msgParams.sec_domain;
	i2b2.PM.model.shrine_domain = Boolean.parseTo(data.msgParams.is_shrine);
	i2b2.PM.model.login_project = data.msgParams.sec_project;
	i2b2.PM.model.loginXML = data.refXML; 
	console.info("AJAX Login Successful! Updated: i2b2.PM.model");

	// hide the modal form if needed
	try { i2b2.PM.view.modal.login.hide(); } catch(e) {}

	i2b2.PM.cfg.cellURL = i2b2.PM.model.url;  // remember the url
	// if user has more than one project display a modal dialog box to have them select one
	var xml = data.refXML;

	var projs = i2b2.h.XPath(xml, 'descendant::user/project[@id]');	
	console.debug(projs.length+' project(s) discovered for user');
	// populate the Project data into the data model
	i2b2.PM.model.projects = {};
	for (var i=0; i<projs.length; i++) {
		// save data into model
		var code = projs[i].getAttribute('id');
		i2b2.PM.model.projects[code] = {};
		i2b2.PM.model.projects[code].name = i2b2.h.getXNodeVal(projs[i], 'name');
		var roledetails = i2b2.h.XPath(projs[i], 'descendant-or-self::role');
		i2b2.PM.model.projects[code].roles = {};
		i2b2.PM.model.projects[code].path = i2b2.h.getXNodeVal(projs[i],'path');
		/*
		for (var d=0; d<roledetails.length; d++) {
			//alert(roledetails[d].textContent);
			// BUG FIX - Firefox splits large values into multiple 4k text nodes... use Firefox-specific function to read concatenated value
			if ((roledetails[d].textContent) && (roledetails[d].textContent  == "ADMIN")) {
				i2b2.PM.model.isAdmin = true
			} else if ((roledetails[d].firstChild) && (roledetails[d].firstChild.nodeValue.unescapeHTML() == "ADMIN")) {
				i2b2.PM.model.isAdmin = true		
			}
		}
		*/
		// details`
		var projdetails = i2b2.h.XPath(projs[i], 'descendant-or-self::param[@name]');
		i2b2.PM.model.projects[code].details = {};
		for (var d=0; d<projdetails.length; d++) {
			var paramName = projdetails[d].getAttribute('name');
			// BUG FIX - Firefox splits large values into multiple 4k text nodes... use Firefox-specific function to read concatenated value
			if (projdetails[d].textContent) {
				i2b2.PM.model.projects[code].details[paramName] = projdetails[d].textContent;
			} else if (projdetails[d].firstChild) {
				i2b2.PM.model.projects[code].details[paramName] = projdetails[d].firstChild.nodeValue.unescapeHTML();				
			}
		}
	}

	 if (!i2b2.PM.model.isAdmin && i2b2.PM.model.admin_only)
	{
		alert("Requires ADMIN role, please contact your system administrator");
		try { i2b2.PM.view.modal.login.show(); } catch(e) {}
		return true;
	} else if (i2b2.PM.model.admin_only) {	
		// default to the first project
		$('crcQueryToolBox').hide(); 
		i2b2.PM.model.login_project = ""; //i2b2.h.XPath(projs[0], 'attribute::id')[0].nodeValue;
		i2b2.PM._processLaunchFramework();
	} else 	if (projs.length == 0) {
		// show project selection dialog if needed	
		try { i2b2.h.LoadingMask.hide(); } catch(e) {}
		// better error messages
		var s = i2b2.h.XPath(xml, 'descendant::result_status/status[@type="ERROR"]');
		if (s.length > 0) {
			// we have a proper error msg
			try {
					alert("ERROR: "+s[0].firstChild.nodeValue);				
			} catch (e) {
				alert("An unknown error has occured during your login attempt!");
			}
		} else if (i2b2.PM.model.login_fullname != "") {
			alert("Your account does not have access to any i2b2 projects.");		
		//} else if (s == null || s == "") {
		//	alert("The PM Cell is down or the address in the properties file is incorrect.");	
		} else {
			alert("The PM Cell is down or the address in the properties file is incorrect.");	
			//alert("Your account does not have access to any i2b2 projects.");		
		}
		try { i2b2.PM.view.modal.login.show(); } catch(e) {}
		return true;
	} else if (projs.length == 1) {
		// default to the only project the user has access to
		i2b2.PM.model.login_project = i2b2.h.XPath(projs[0], 'attribute::id')[0].nodeValue;
		i2b2.PM.model.login_projectname = i2b2.h.getXNodeVal(projs[0], "name");
		try {
			var announcement = i2b2.PM.model.projects[i2b2.PM.model.login_project].details.announcement;
			if (announcement) {
				i2b2.PM.view.modal.announcementDialog.showAnnouncement(announcement);
				return;
			}
		} catch(e) {}
		i2b2.PM._processLaunchFramework();
	} else {
		// display list of possible projects for the user to select
		i2b2.PM.view.modal.projectDialog.showProjects();
	}

}


// ================================================================================================== //
i2b2.PM.doLogout = function() {
	// bug fix - must reload page to avoid dirty data from lingering
	window.location.reload();
}


i2b2.PM.view.modal.projectDialog = {
	loginXML: false,
	showProjects: function() {
		var dataXML = i2b2.PM.model.loginXML;
		var thisRef = i2b2.PM.view.modal.projectDialog;
		if (!$("i2b2_projects_modal_dialog")) {
			var htmlFrag = i2b2.PM.model.html.projDialog;
			Element.insert(document.body,htmlFrag);
		
			if (!thisRef.yuiDialog) {
				thisRef.yuiDialog = new YAHOO.widget.SimpleDialog("i2b2_projects_modal_dialog", {
					zindex: 700,
					width: "400px",
					fixedcenter: true,
					constraintoviewport: true,
					close: false
				});
				var kl = new YAHOO.util.KeyListener("i2b2_projects_modal_dialog", { keys:13 },  							
																  { fn:i2b2.PM.view.modal.projectDialog.loadProject,
																	scope:i2b2.PM.view.modal.projectDialog,
																	correctScope:true }, "keydown" );
				thisRef.yuiDialog.cfg.queueProperty("keylisteners", kl);
				thisRef.yuiDialog.render(document.body);
				// show the form
				thisRef.yuiDialog.show();
			}
		}
		// show the form
		thisRef.yuiDialog.show();
		$('loginProjs').focus();
		// load the project data
		var pli = $('loginProjs');
		while( pli.hasChildNodes() ) { pli.removeChild( pli.lastChild ); }
		// populate the Project data into the form
		for (var code in i2b2.PM.model.projects) {
			// dropdown
			pno = document.createElement('OPTION');
			pno.setAttribute('value', code);
			var pnt = document.createTextNode(i2b2.PM.model.projects[code].name);
			pno.appendChild(pnt);
			pli.appendChild(pno);			
		}
		// select first project
		$('loginProjs').selectedIndex = 0;

		// display the details for the currently selected project
		i2b2.PM.view.modal.projectDialog.renderDetails();
	},
	renderDetails: function() {
		// clear the details display
		var pli = $('projectAttribs');
		while( pli.hasChildNodes() ) { pli.removeChild( pli.lastChild ); }
		
		// get the currently selected project
		var p = $('loginProjs');
		var projectCode = p.options[p.selectedIndex].value;
		
		// show details
		for (var i in i2b2.PM.model.projects[projectCode].details) {
			// ignore "announcement" param
			if (i != "announcement") {
				// clone the record DIV and add it to the display list
				var rec = $('projDetailRec-CLONE').cloneNode(true);
				// change the entry id
				rec.id = "";
				rec.style.display = "";
				try {
					var part = rec.select('.name')[0];
					part.innerHTML = i;
					part = rec.select('.value')[0];
					part.innerHTML = i2b2.PM.model.projects[projectCode].details[i];
				} catch(e) {}
				pli.appendChild(rec);
			}
		}
		if (!i) {
			Element.insert(pli,'<DIV class="NoDetails">No additional information is available.</DIV>');
		} else {
			Element.insert(pli,'<DIV style="clear:both;"></DIV>');
		}
	},
	loadProject: function(ProjId) {
		if (!ProjId) {
			// get the ID of the currently selected project in the dropdown
			var p = $('loginProjs');
			ProjId = p.options[p.selectedIndex].value;
			ProjName = p.options[p.selectedIndex].text;
		}
		i2b2.PM.model.login_project = ProjId;
		i2b2.PM.model.login_projectname = ProjName;
		i2b2.PM.view.modal.projectDialog.yuiDialog.destroy();
		try {
			var announcement = i2b2.PM.model.projects[ProjId].details.announcement;
			if (announcement) {
				i2b2.PM.view.modal.announcementDialog.showAnnouncement(announcement);
				return;
			}
		} catch(e) {}
		i2b2.PM._processLaunchFramework();
	}
}

i2b2.PM.view.modal.announcementDialog = {
	showAnnouncement: function(msg) {

		var thisRef = i2b2.PM.view.modal.announcementDialog;
		if (!thisRef.yuiDialog) {
			thisRef.yuiDialog = new YAHOO.widget.SimpleDialog("PM-announcement-panel", {
				zindex: 700,
				width: "400px",
				fixedcenter: true,
				constraintoviewport: true,
				close: false
			});
			thisRef.yuiDialog.cfg.queueProperty("buttons",
				[{text: "Yes, I agree", handler:i2b2.PM.view.modal.announcementDialog.clickOK, isDefault:true},
				 {text: "No, I disagree", handler:i2b2.PM.view.modal.announcementDialog.clickCancel}
				]);
			thisRef.yuiDialog.render(document.body);
			// show the form
			thisRef.yuiDialog.show();
		}
		
		// display the announcement text
		$('PM-announcement-title').innerHTML = i2b2.PM.model.login_project + " Announcements";

		$('PM-announcement-body').innerHTML =  msg.replace(/&lt;/g, '<', 'gm').replace(/&gt;/g, '>');

		// show the form
		$('PM-announcement-panel').show();
		thisRef.yuiDialog.show();
		thisRef.yuiDialog.center();
	},
	clickOK: function() {
		this.hide();
		if (!i2b2.hive.isLoaded) {
			i2b2.PM._processLaunchFramework();
		}
	},
	clickCancel: function(){
		this.hide();
		i2b2.PM.doLogout();
	}
}



// ================================================================================================================================
// NEW FRAMEWORK LAUNCH CODE (cells can timeout on failure instead of hanging the entire load process)
// ================================================================================================================================
i2b2.PM._processLaunchFramework = function() {
	i2b2.hive.isLoaded = false;
	var oXML = i2b2.PM.model.loginXML;

	// create signal sender for afterLogin event
	i2b2.events.afterCellInit.subscribe((function(type,args) {
		if (i2b2.hive.isLoaded) { 
			// turn off our watchdog timer
			if (i2b2.PM.WDT) { clearTimeout(i2b2.PM.WDT); }
			return;
		}

		// keep track of cells loading and fire "afterAllCellsLoaded"
		// event after all cells are confirmed as loaded
		var loadedCells = [];
		for (var cellKey in i2b2.hive.cfg.LoadedCells) {
			if ((i2b2.hive.cfg.LoadedCells[cellKey] && i2b2[cellKey]) && !i2b2[cellKey].isLoaded) {
				return true;
			}
			loadedCells.push(cellKey);
		}

		// all cells are loaded, fire the "all go" signal if all cells are loaded
		console.info("EVENT FIRE i2b2.events.afterLogin");
		i2b2.events.afterLogin.fire(loadedCells);
		delete i2b2.hive.tempCellsList;
		i2b2.hive.isLoaded = true;
		// turn off our watchdog timer
		if (i2b2.PM.WDT) { clearTimeout(i2b2.PM.WDT); }
		// hide the "loading" mask
		i2b2.h.LoadingMask.hide();
		// clear our cached copy of the xml message
		delete i2b2.PM.model.loginXML;
	}));

	// extract additional user/project information
	i2b2.PM.model.userRoles = [];
	i2b2.PM.model.isObfuscated = true;
	var roles = i2b2.h.XPath(oXML, "//user/project[@id='"+i2b2.PM.model.login_project+"']/role/text()");
	var l = roles.length;
	for (var i=0; i<l; i++) {
		if (i2b2.PM.model.userRoles.indexOf(roles[i].nodeValue) == -1)
			i2b2.PM.model.userRoles.push(roles[i].nodeValue);
			if (roles[i].nodeValue == "DATA_AGG")
			{
				i2b2.PM.model.isObfuscated = false;	
			}
	}

	// process cell listing
	var cellIDs = {};
	var c = i2b2.h.XPath(oXML, "//cell_data/@id");
	var l = c.length;
	for (var i=0; i<l; i++) {
		try {
			cellIDs[c[i].nodeValue] = true;
		} catch(e) {
			console.error("Invalid Node Info!");
		}
	}
	// add additional provided by the server or flag for deletion;
	var deleteKeys = {};
	for (var cellKey in i2b2.hive.cfg.lstCells) {
		if (cellIDs[cellKey]) {
			try {
				// server requested loading of cell
				var cellRef = i2b2.hive.cfg.lstCells[cellKey];
				cellRef.serverLoaded = true;
				// load the rest of the info provided by the server
				//var x = i2b2.h.XPath(oXML, "//cell_data[@id='"+cellKey+"']")[0];
				//cellRef.name = i2b2.h.getXNodeVal(x, "name");
				//cellRef.url = i2b2.h.getXNodeVal(x, "url");
				//cellRef.xmlStr = i2b2.h.Xml2String(x);
				var  y = i2b2.h.XPath(oXML,"//cell_data[@id='"+cellKey+"']");

				for(var i=y.length; i>=0; i--)
				{

					var  x = i2b2.h.XPath(oXML,"//cell_data[@id='"+cellKey+"']")[i-1];

					if( i2b2.h.getXNodeVal(x,"project_path")== i2b2.PM.model.projects[i2b2.PM.model.login_project].path )
					{
						cellRef.name = i2b2.h.getXNodeVal(x,"name");
						cellRef.project_path = i2b2.h.getXNodeVal(x,"project_path");
						cellRef.url = i2b2.h.getXNodeVal(x,"url");
						cellRef.xmlStr = i2b2.h.Xml2String(x);
					}
				}

				if(!cellRef.name)
				{
					var  x = i2b2.h.XPath(oXML,"//cell_data[@id='"+cellKey+"']")[0];
					cellRef.name = i2b2.h.getXNodeVal(x,"name");
					cellRef.project_path = i2b2.h.getXNodeVal(x,"project_path");
					cellRef.url = i2b2.h.getXNodeVal(x,"url");
					cellRef.xmlStr = i2b2.h.Xml2String(x);
				}

				// params
				var x = i2b2.h.XPath(oXML, "//cell_data[@id='"+cellKey+"']/param[@name]");
				var l = x.length;
				for (var i=0; i<l; i++) {
					var n = i2b2.h.XPath(x[i], "attribute::name")[0].nodeValue;
					cellRef.params[n] = x[i].firstChild.nodeValue;
				}
				// do not save cell info unless the URL attribute has been set (exception is PM cell)
				if (cellRef.url == "" && cellKey != "PM") {
					deleteKeys[cellKey] = true;
				} else {
					i2b2.hive.cfg.lstCells[cellKey] = cellRef;
				}
			} catch (e) {
				console.error("Error occurred while processing PM cell config msg about cell:"+cellKey);
				deleteKeys[cellKey] = true;
			}
		} else {
			//Remove cells.plugins that dont have right access to
			 //if (i2b2.PM.model.userRoles.indexOf(i2b2.hive.cfg.lstCells[cellKey].roles) == -1) {
			//	deleteKeys[cellKey] = true;				 
			 //}
			 if (!i2b2.PM.model.admin_only) {
			 var roleFound = -1;
			for (var i=0; i<i2b2.hive.cfg.lstCells[cellKey].roles.length; i++) {
				roleFound = 0;
				 if (i2b2.PM.model.userRoles.indexOf(i2b2.hive.cfg.lstCells[cellKey].roles[i]) != -1)
				 {
					roleFound = 1;
					break; 
				 }
			 }
			 if (roleFound == 0)
			 {
				 deleteKeys[cellKey] = true;				 
			 }
			 }
			// no need to load the cell unless forced
			if (cellKey != "PM" && !i2b2.hive.cfg.lstCells[cellKey].forceLoading) {
				// add to the delete list
				deleteKeys[cellKey] = true;
			}
		}
	}

    // purge all non-used cells from the cells listing and i2b2 namespace
	for (var cellKey in deleteKeys) {
		delete i2b2.hive.cfg.lstCells[cellKey];
		if (i2b2[cellKey] && i2b2[cellKey].cellCode) {
			// made sure it's a "cell" we are about to delete
			delete i2b2[cellKey];
		}
	}

	// see if Shrine was loaded by the server
	var t = i2b2.hive.cfg.lstCells["SHRINE"];
	if (!Object.isUndefined(t) && t.serverLoaded) {
		i2b2.PM.model.shrine_domain = true;
	}
	delete t;


	// create a list of valid Cells that are loaded for this session
	var t = {};
	for (var cellKey in i2b2) {
		// is it a cell
		if (i2b2[cellKey].cellCode) {
			// valid config file?
			if (i2b2.hive.cfg.lstCells[cellKey]) {
				t[cellKey] = true;
			} else {
				console.error("CELL CONFIGURATION ERROR! ["+cellKey+"]");
				delete i2b2[cellKey];
			}
		}
	}
	i2b2.hive.cfg.LoadedCells = t;
	delete t;

	// start our watchdog time (WDT) 
	if (i2b2.hive.cfg.loginTimeout) { 
		var t = i2b2.hive.cfg.loginTimeout;
	} else {
		var t = 120;
	}
	t = t * 1000;
	i2b2.PM.WDT = setTimeout("i2b2.PM.trigger_WDT()", t);
	
	// Initialize the Cell Stubs
	for (var cellKey in i2b2.hive.cfg.LoadedCells) {
		// is it a cell
		if (cellKey != "PM" && !i2b2[cellKey].isLoaded) {
			try {
				var cfg = i2b2.hive.cfg.lstCells[cellKey];
				i2b2[cellKey].Init(cfg.url, cfg.params);
			} catch(e) {
				console.error("CELL INITIALIZATION FAILURE! ["+cellKey+"]");
				delete i2b2[cellKey];
				i2b2.hive.cfg.LoadedCells[cellKey] = false;
			}
		}
	}
	console.groupEnd("PROCESSED Login XML");
};


i2b2.PM.trigger_WDT = function() {
	console.warn('CHECKING FOR STUCK CELLS');
	var foundStuckCells = false
	for (var cellKey in i2b2.hive.cfg.LoadedCells) {
		if (i2b2.hive.cfg.LoadedCells[cellKey] && !i2b2[cellKey].isLoaded) { 
			// clear stuck module
			console.error("FOUND STUCK CELL: "+cellKey);
			foundStuckCells = true;
		}
	}
	if (!foundStuckCells) { return true; }
	if (confirm("Some modules are still attempted to load.\nDo you want to continue waiting?")) {
		// reset WDT
		if (i2b2.hive.cfg.loginTimeout) { 
			var t = i2b2.hive.cfg.loginTimeout;
		} else {
			var t = 120;
		}
		t = t * 1000;
		i2b2.PM.WDT = setTimeout("i2b2.PM.trigger_WDT()", t);
		// recheck loading status
		i2b2.events.afterCellInit.fire();
	} else {
		// clear any unloaded modules
		console.warn("CLEARING STUCK CELLS...");
		for (var cellKey in i2b2.hive.cfg.LoadedCells) {
			if (i2b2.hive.cfg.LoadedCells[cellKey] && !i2b2[cellKey].isLoaded) { 
				// clear stuck module
				console.error("CELL FORCEFULLY UNLOADED: "+cellKey);
				i2b2.hive.cfg.LoadedCells[cellKey] = false;
				delete i2b2[cellKey];
			}
		}				
		// recheck loading status (which will now pass)
		i2b2.events.afterCellInit.fire();
	}
};


console.timeEnd('execute time');
console.groupEnd();
