Browse Source

Adding CRF portal

Eager Beaver 5 years ago
parent
commit
fb1e3a4eda
3 changed files with 889 additions and 0 deletions
  1. 181 0
      views/crfPortal.html
  2. 5 0
      views/crfPortal.view.xml
  3. 703 0
      web/tecant/crfPortal.js

+ 181 - 0
views/crfPortal.html

@@ -0,0 +1,181 @@
+<style>
+p.hidden{
+	display:none;
+}
+table {margin-bottom:20px;table-layout:fixed; border-collapse:collapse; border-spacing:10px}
+table.t1 {width:400px; border:1px solid black}
+table.t1 th {border:1px solid black;padding:4px;background-color:#e0e0e0}
+table.t1 td {text-align:center}
+table.t2 {width:800px; border:1px solid black;}
+table.t2 th {border:1px solid black;padding:4px;background-color:#e0e0e0}
+table.t2 td {border:1px solid black; text-align:center}
+
+table.headTable {width:800px}
+table.headTable th {font-size:20px,text-align:center,background-color:#e0e0e0}
+table.headTable td {font-size:20px,text-align:center}
+
+div.d1 {text-align:center; width=400px; background-color:#e0e0e0;
+        font-size:      20px; margin-bottom:20px}
+
+</style>
+
+<div class="d1">Clinical Report Form Portal</div>
+
+<div id="generalDataHead"></div>
+<div id="generalDataDiv" style="display:none">
+<table id="generalDataTable" cellspacing="2" cellpadding="5" border="0">
+<tr><td>Eudra CT Number: </td><td><strong id="eudraCTNumber">Loading</strong></td></tr>
+<tr><td>Study Sponsor: </td><td><strong id="studySponsor">Loading</strong></td></tr>
+<tr><td>Study Coordinator: </td><td><strong id="studyCoordinator">Loading</strong></td></tr>
+<tr><td>Regulatory authority Entry Number:</td><td><strong id="regulatoryNumber">Loading</strong></td></tr>
+</table>
+</div>
+
+<div id="pickFormHead"></div>
+<div id="pickFormDiv" style="display:none">
+	<div id="formDiv"></div>
+	<div id="selectFormDiv" style="display:block"></div>
+	<div id="listDiv"></div>
+</div>
+
+<div id="startDiv"></div>
+
+<div id="debugAreaHead"></div>
+<div id="debugAreaDiv" style="display:none">
+<textarea cols="95" rows="5" name="formStatus" id="formStatus">Entering data</textarea>
+</div>
+
+<script type="text/javascript">
+
+    document.getElementById("formStatus").value+="\nStarting";
+    window.onload = init();
+
+
+
+// Initialize the form by populating the Reagent drop-down list and
+// entering data associated with the current user.
+function init() {
+
+	generateHead("debugAreaHead","debugAreaDiv","Debug info");
+	generateHead("generalDataHead","generalDataDiv","General data");
+	generateHead("pickFormHead","pickFormDiv","Select CRF");
+	
+	//parameters should include
+	//divName - place to render select entry table
+	//masterQuery - the master query that will collect al the data
+	//masterSelectVarName - variable that is going to fill the content of select. 
+	//		  Typically, the variable is a lookup in the master query
+	//masterUserVarName	- variable that stores the user id in the master query 
+	//		only entries matching current user in the master query will be selected
+	//authorization - additional data to check:
+	//	* if the current user is authorized to perform action and/or 
+	// 	* limit number of different queryVariable values user is entitled to use
+	//authorization should contain:
+	//	* queryName - query of user/queryVariable pairs to check for authorized access
+	//	* authUserVarName - name of the variable in authorization.queryName that contain usesId
+	//	* authSelectVarName - name of the selectVar in authorization query
+	//callback - function that executes when value of the select changes (onchange).
+	//		argument to callback are the parameters
+	//selectId - id of the DOM element where select is rendered - to check for value in callback
+	//dataDiv - div element to render potential output of the callback
+
+	//rework to enable addNew
+	let par=new Object();
+	par.masterQuery="crfEntry";
+	par.dataDiv="listDiv";
+	par.masterUserVarName="UserId";
+	par.formStatusName="FormStatus";
+	par.formStatus=1;
+	par.addDiv="selectFormDiv";
+	
+	par.source=new Object();
+	par.source.queryName="StudyProperties";
+	par.source.schemaName="study";
+	par.source.vars=["eudraCTNumber","studyCoordinator","regulatoryNumber","studySponsor"];
+
+	par.vars=new Object();
+
+	//User
+	let parUser=new Object();
+	parUser.masterSelectVarName="UserId";
+	parUser.callback=function(){};
+	parUser.selectId="UserSelect";
+	parUser.inputType="select";
+	
+	//only display users in crfEditors list
+	parUser.filter=new Object();
+	parUser.filter.queryName="crfEditors";
+	parUser.filter.filterVarName="User";
+	parUser.filter.filters=new Object();
+	parUser.filter.filters["User"]=LABKEY.Security.currentUser.id;
+	par.vars["User"]=parUser;
+	
+
+
+	//Crf		
+	let parCrf=new Object();
+	parCrf.masterSelectVarName="entryId";
+	parCrf.callback=generateList;
+	parCrf.selectId="crfSelect";
+	parCrf.addSelect=true;
+	parCrf.addNewFlag=-1;
+	parCrf.inputType="select";
+	par.vars["Crf"]=parCrf;
+
+	//Site
+	let parSite=new Object();
+	parSite.masterSelectVarName="Site";
+	
+	parSite.callback=generateListAndPopulateDaughterSelect;
+	parSite.selectId="SiteSelect";
+	parSite.daughterSelect="Crf";
+	parSite.inputType="select";
+	
+	//apply additional filter to variable - specify
+	//- queryName, 
+	//- name of variable in queryName 
+	// - list of filters with name of the variable associated targeted value
+
+	parSite.filter=new Object();
+	parSite.filter.queryName="crfEditors";
+	parSite.filter.filterVarName="Site";
+	parSite.filter.filters=new Object();
+	parSite.filter.filters["User"]=LABKEY.Security.currentUser.id;
+	par.vars["Site"]=parSite;
+
+	//Form
+	par.vars["Form"]=new Object();
+	let parForm=par.vars["Form"];
+	parForm.masterSelectVarName="Form";
+	parForm.callback=function(){};
+	parForm.selectId="FormSelect";
+	parForm.selectAll=true;
+	parForm.inputType="select";
+	parForm.urlName="formUrl";
+
+	//FormStatus;
+	par.vars["FormStatus"]=new Object();
+	let parFormStatus=par.vars["FormStatus"];
+	parFormStatus.masterSelectVarName="FormStatus";
+	parFormStatus.callback=generateListAndPopulateDaughterSelect;
+	parFormStatus.selectId="FormStatusSelect";
+	parFormStatus.daughterSelect="Crf";
+	parFormStatus.inputType="select";
+
+	parFormStatus.filter=new Object();
+	parFormStatus.filter.queryName="formStatusVisibility";	
+	parFormStatus.filter.filterVarName="formStatus";
+	parFormStatus.filter.filters=new Object();
+	parFormStatus.filter.filters["visibilityLevel"]="crfEditor"; 
+
+		
+	par.vars["eudraCTNumber"]=sourceVar("EudraCTNumber","eudraCTNumber","EudraCTNumber");
+	par.vars["studyCoordinator"]=sourceVar("StudyCoordinator","studyCoordinator","StudyCoordinator");
+	par.vars["studySponsor"]=sourceVar("StudySponsor","studySponsor","StudySponsor");
+	par.vars["regulatoryNumber"]=sourceVar("RegulatoryNumber","regulatoryNumber","RegulatoryNumber");	
+
+	par.filters=["User","Site","FormStatus"];
+	//par.filters=["User","Site"];
+    generateForm(par)
+	
+</script>

+ 5 - 0
views/crfPortal.view.xml

@@ -0,0 +1,5 @@
+<view xmlns="http://labkey.org/data/xml/view" title="CRF Portal">
+	<dependencies>
+			<dependency path="tecant/crfPortal.js"/>
+	</dependencies>
+</view>

+ 703 - 0
web/tecant/crfPortal.js

@@ -0,0 +1,703 @@
+function drawForm(par){
+    
+	populateSourceTable(par);
+
+	let tableId="entryTable"; 
+	generateTable("formDiv",tableId);
+	generateRow(tableId, par,"User");
+
+	populateSelectTableEntry(par,"User");
+	
+	generateRow(tableId, par,"Site");
+	generateRow(tableId, par, "FormStatus");
+	generateRow(tableId, par,"Crf");
+
+	populateSelectTableEntry(par,"Site");
+	populateSelectTableEntry(par,"FormStatus");
+
+	let formTableId="selectFormTable";
+	generateTable("selectFormDiv",formTableId);
+	generateRow(formTableId,par,"Form");
+
+	generateButtonRow(formTableId,"Add new CRF","Add", par, addNewEntry);
+
+	populateSelectTableEntry(par,"Form");
+
+	generateTable("startDiv","startTable");
+	generateButtonRow("startTable","Start filling the selected form","Start", par, startForm);
+	
+		
+}
+
+function sourceVar(crfEntryName,elementId,sourceName){
+	let f=new Object();
+	f.masterSelectVarName=crfEntryName;
+	f.selectId=elementId;
+	f.inputType="innerHTML";
+	f.sourceSelectVarName=sourceName;
+	return f;
+}
+
+function generateHead(headDivName,divName,title){
+	document.getElementById("formStatus").value+="\ngenerateHead";
+	let tb=document.createElement('table');
+	tb.className='t2';
+	let row=tb.insertRow();
+	let cell=document.createElement('th');
+	row.appendChild(cell);	
+	cell.setAttribute("colspan","4");
+	cell.style.fontSize="20px";
+	cell.style.textAlign="center";
+	let cellData=document.createTextNode(title);
+	cell.appendChild(cellData);
+	cell=row.insertCell();
+	cell.style.fontSize="20px";
+	let input=document.createElement("input");	
+	input.type="button";
+	input.value="Show";
+	input.id="toggle"+divName+"VisbilityButton";
+	input.onclick=function(){toggleVisibility(divName,input.id)};
+	cell.appendChild(input);
+	document.getElementById(headDivName).appendChild(tb);
+	document.getElementById("formStatus").value+="\ngenerateHead: Done";
+}
+
+function toggleVisibility(divName,buttonName){
+
+	let x = document.getElementById(divName);
+	if (x.style.display === "none") {
+    		x.style.display = "block";
+		document.getElementById(buttonName).value="Hide";
+
+  	} else {
+    		x.style.display = "none";
+		document.getElementById(buttonName).value="Show";
+
+  	}
+} 
+
+function populateSourceTable(par){
+	let debug=true;
+	if (debug){
+		document.getElementById('formStatus').value +=
+			"\npopulateSourceTable: Starting";
+	}
+	let config=new Object();
+	if (!("source" in par)) return;
+	if (debug){
+		print("populateSourceTable ["+par.source.queryName+"]");
+	}
+
+	config.schemaName=par.source.schemaName; 
+	config.queryName=par.source.queryName;
+	config.success=function(data){populateSourceTableData(data,par)};
+	config.failure=function(errorTxt){print("populateSourceData:fail")};
+	LABKEY.Query.selectRows(config);
+}
+
+function print(msg){
+	document.getElementById('formStatus').value +="\n"+msg;
+}
+
+
+function populateSourceTableData(data,par){
+	let debug=true;
+	if (debug){
+		document.getElementById('formStatus').value +=
+			"\npopulateSourceTableData: nrow: "+data.rows.length;
+	}
+	let entry=data.rows[0];
+	for (let i=0;i < par.source.vars.length;i++){
+		let srcVarName=par.source.vars[i];
+		if (debug){
+			document.getElementById('formStatus').value +=
+				"\npopulateSourceTable ["+srcVarName+"]";
+		}
+		let row=par.vars[srcVarName];
+		let el=document.getElementById(row.selectId);
+		if (debug){
+			document.getElementById('formStatus').value +=
+				"\nElement: "+el;
+		}
+		el.innerHTML=entry[row.sourceSelectVarName];
+	}
+}
+
+function populateSelectNotLookup(data,parameters,rowId, entry){
+	//selectId
+	//masterSelectVarName
+
+	let debug=true;
+
+	if (debug)
+		print("populateSelectNonLookup on "+data.queryName);
+	
+	let row=parameters.vars[rowId];
+	let varName=row.masterSelectVarName;
+	if (debug)	print("var "+varName+" rows "+data.rows.length);
+
+	if (debug) print("Getting element "+row.selectId);
+
+	let el=document.getElementById(row.selectId);
+
+	if (!el) {
+		print("Element not found");
+		return;
+	}
+
+	if (debug) print("Element "+el);
+	if (debug) print("Clearing entries");
+
+	//remove previous options
+	for(i = el.options.length; i >= 0; i--) { 
+		el.remove(i);
+   	}
+	
+	if (debug)
+		print("Adding entries");
+
+	if ("addSelect" in row){
+		if (debug)
+			print("adding <Select>");
+
+		let opt = document.createElement("option"); 
+		opt.text = "<Select>";
+		opt.value = -2;
+		el.options[0] = opt;
+	}
+
+	if ("addNewFlag" in row){
+		if (debug) print("adding Add new");
+		let opt = document.createElement("option");
+		opt.text = "Add New";
+		opt.value = row.addNewFlag;
+		el.options[el.options.length] = opt;
+	}
+
+	for (let i=0;i< data.rows.length;i++){
+		let valEntry=data.rows[i];
+		if (debug)
+			print("adding "+valEntry[varName]);
+		let opt = document.createElement("option"); 
+		opt.text = valEntry[varName];
+		opt.value = valEntry[varName];
+		if (entry){
+			if (opt.value==entry[varName])
+			el.selectedIndex=el.options.length-1;
+		}
+		el.options[el.options.length] = opt;
+	}
+	if (entry) 
+		row.callback(parameters,rowId);
+}
+
+
+function populateSelect(data,selectedData, parameters, rowId){
+	//data is the set of lookup entries, selectedData is a subset of entries valued at lookup.keyColumn 
+	let debug=true;
+	if (debug)
+		print("populateSelect Data: "+data.queryName+" selectedData:"+selectedData.queryName);
+	let row=parameters.vars[rowId];
+	let selectId=row.selectId; 
+	
+	let varName=row.masterSelectVarName;
+	if ("filter" in row){
+		varName=row.filter.filterVarName;
+	}
+	let field=getField(selectedData,varName);
+
+	let displayColumn=field.lookup.displayColumn;
+	let keyColumn=field.lookup.keyColumn;
+
+	if (debug){
+		document.getElementById('formStatus').value+="\n Query: "+data.queryName;
+		document.getElementById('formStatus').value+="\n ElementId: "+selectId;
+		document.getElementById('formStatus').value+="\n keyColumn: "+keyColumn;
+		document.getElementById('formStatus').value+="\n displayColumn: "+displayColumn;
+	
+	}
+	let el = document.getElementById(selectId);
+	
+	if (debug)
+		document.getElementById('formStatus').value+="\n Element: "+el;
+   	
+	for(i = el.options.length; i >= 0; i--) {
+		el.remove(i);
+   	}
+	if ("addSelect" in row){
+		let opt = document.createElement("option");
+		opt.text = "<Select>";
+		opt.value = -1;
+		el.options[0] = opt;
+	}
+
+	if ("addNewFlag" in row){
+		let opt = document.createElement("option");
+		opt.text = "Add New" 
+		opt.value = row.addNewFlag;
+		el.options[el.options.length] = opt;
+	}
+
+	for (var i = 0; i < data.rows.length; i++) {
+		let key=data.rows[i][keyColumn];
+		let skip=true;
+		if (row.selectAll){
+			skip=false;
+		}
+		else{
+			if (debug)
+				print("Selecting from: "+selectedData.rows.length);
+			for (let j=0; j< selectedData.rows.length; j++){
+				let entry=selectedData.rows[j];
+				if (debug)
+					print("Comparing: "+entry[varName]+"/"+key);
+				if (key!=entry[varName]) continue;
+				skip=false;
+				break;
+			}
+		}
+		if (skip) continue;
+		let opt = document.createElement("option");
+		opt.text = data.rows[i][displayColumn];
+		opt.value = data.rows[i][keyColumn];
+		if (debug)
+			print("Adding: "+opt.value+" : "+opt.text);
+	
+		el.options[el.options.length] = opt;
+	
+
+		if ("selectedKey" in row){ 
+			if (debug)
+				print("Comparing: " + opt.value  + "/" + row["selectedKey"]);
+		
+        		if (opt.value==row["selectedKey"]){
+				el.selectedIndex=el.options.length-1;
+				if (debug)
+					print("Equal; "+el.selectedIndex);
+			}
+		}
+	}
+	if (debug)
+		print("Running callback");
+
+	row.callback(parameters,rowId);
+}	
+
+function saveData(queryName){
+	document.getElementById('formStatus').value+="\n saveData: "+queryName;
+	
+}
+
+function populateSelectTableEntry(parameters,rowId){
+	//parameters should include
+	//tableId - update this table (unused)
+	//masterQuery - the master query that will collect al the data
+	//masterSelectVarName - variable that is going to fill the content of select. 
+	//		  Typically, the variable is a lookup in the master query
+	//masterUserVarName	- variable that stores the user id in the master query 
+	//		only entries matching current user in the master query will be selected
+	//authorization - additional data to check:
+	//	* if the current user is authorized to perform action and/or 
+	// 	* limit number of different queryVariable values user is entitled to use
+	//authorization should contain:
+	//	* queryName - query of user/queryVariable pairs to check for authorized access
+	//	* authUserVarName - name of the variable in authorization.queryName that contain usesId
+	//	* authSelectVarName - name of the selectVar in authorization query
+	//callback - function that executes when value of the select changes (onchange).
+	//		argument to callback are the parameters
+	//selectId - id of the DOM element where select is rendered - to check for value in callback
+	//dataDiv - div element to render potential output of the callback
+
+	let debug=true;	
+	let row=parameters.vars[rowId];
+	if (debug)
+		document.getElementById('formStatus').value+="\n populateSelectTableEntry:"
+			+parameters.masterQuery+"/"+row.masterSelectVarName;
+		
+	
+
+	let config=new Object();
+	config.schemaName='lists';
+	if ("filter" in row){
+		//populateSelect on authorizationQuery with authSelectVarName
+		let filter=row.filter;
+		if (debug){
+			print("Filter:"+filter.queryName);
+			print("FilterVar "+filter.filterVarName);
+		}
+		config.queryName=filter.queryName;
+		config.filterArray=[];
+		for (f in filter.filters){
+			if (debug) print("Adding filter: "+f+" val "+filter.filters[f]);
+			config.filterArray.push(LABKEY.Filter.create(f,filter.filters[f]));
+		}
+	}
+	else{
+		config.queryName=parameters.masterQuery;
+	}
+	
+	config.success=function(data){populateTableRow(data,parameters,rowId)};
+	LABKEY.Query.selectRows(config);
+	if (debug)
+		print("generateSelect: End");
+	return;
+	
+}
+
+function getField(data, varName){
+	let debug=false;
+	if (debug) print("getField");
+	let fields=data.metaData.fields;
+	for (f in fields){
+		if (debug) print("Checking "+f+": name "+fields[f].name+"/"+varName);
+		if (fields[f].name!=varName) continue;
+		return fields[f];
+	}
+	return null;
+}
+
+
+function getCaption(data, varName){
+	let field=getField(data,varName);
+	if (field) return field.shortCaption;
+	return "NONE";
+}
+
+
+function generateTable(divName,elementId){
+	let debug=true;
+	if (debug)
+		document.getElementById('formStatus').value+="\n generateTable";
+	let tb=document.createElement('table');
+	tb.className="t2";
+	tb.id=elementId;
+	document.getElementById(divName).appendChild(tb);
+	if (debug)
+		document.getElementById('formStatus').value+="\n generateTable: Done";
+}
+
+function generateRow(tableId, parameters,rowId){
+	let debug=true;
+	if (debug)
+		document.getElementById('formStatus').value+="\n generateRow: Start";
+	let config=new Object();
+	config.schemaName='lists';
+	config.queryName=parameters.masterQuery;
+	config.success=function(data){generateTableRow(data,tableId, parameters,rowId)};
+	LABKEY.Query.selectRows(config);
+	if (debug)
+		document.getElementById('formStatus').value+="\n generateRow: End";
+	return;
+
+}
+
+function generateTableRow(data,tableId, parameters,rowId){
+	let tb=document.getElementById(tableId);
+	let row=parameters.vars[rowId];
+	let field=getField(data,row.masterSelectVarName);
+	let trow=tb.insertRow();
+	let cell=document.createElement('th');
+	trow.appendChild(cell);
+	let text = document.createTextNode(field.shortCaption);
+	cell.appendChild(text);
+	cell=trow.insertCell();
+	let input = document.createElement("select");
+	input.id = row.selectId;
+	input.onchange=function(){row.callback(parameters,rowId)};
+	cell.appendChild(input); 
+}
+
+function populateTableRow(data,parameters,rowId){
+	//data is output of selectRows on either 
+	//	* masterQuery looking at masterSelectVarName or 
+	//	* authQuery looking at authSelectVarName
+
+	//in both cases, query[varName] is a lookup variable, so do populateSelect with lookupData,queryData and parameters
+	let debug=true;
+	let row=parameters.vars[rowId];
+	let varName=row.masterSelectVarName;
+	if ("filter" in row){
+		if (row.filter.queryName==data.queryName){
+			varName=row.filter.filterVarName;
+		}
+	}
+
+	if (debug)
+		print("generateSelectVar: "+data.queryName+"/"+varName+" size "+data.rows.length);
+
+	let field=getField(data,varName);
+	
+	if (!field) {
+		print("Field "+varName+" not found");
+		return;
+	}
+
+	if (!("lookup" in field)){
+		let entry=data.rows[0];
+		print("Field "+varName+" not a lookup");
+		populateSelectNotLookup(data,parameters,rowId, entry);	
+		return;
+	}
+	
+	let config=new Object();
+	config.schemaName=field.lookup.schemaName;
+	config.queryName=field.lookup.queryName;
+	config.success=function(lookupData){populateSelect(lookupData,data,parameters,rowId)};
+	LABKEY.Query.selectRows(config);	
+	
+	if (debug)
+		print("generateSelectVar: End");
+	
+}	
+
+function generateButtonRow(tableId,caption,label,parameters,callback){
+	let tb=document.getElementById(tableId);
+	let trow=tb.insertRow();
+	let cell=document.createElement('th');
+	trow.appendChild(cell);
+	let text = document.createTextNode(caption);
+	cell.appendChild(text);
+	cell=trow.insertCell();
+	let input = document.createElement("input");
+	input.type="button";
+	input.value=label;
+	input.onclick=function(){callback(parameters)};
+	cell.appendChild(input); 
+}
+
+
+function generateListAndPopulateDaughterSelect(parameters,rowId){
+	let debug=true;
+
+	if (debug){
+		print("generateListAndPopulateDaughter");
+	
+	}
+	
+	generateList(parameters,rowId);
+	let row=parameters.vars[rowId];
+	if ("daughterSelect" in row){
+		populateDaughterSelect(parameters,rowId,row["daughterSelect"],null);
+	}
+}
+
+function populateDaughterSelect(parameters,rowId,daughterRowId,entry){
+	let debug=true;	
+	if (debug)
+		print("populateDaughterSelect: "+rowId+" "+daughterRowId);
+
+	let row=parameters.vars[rowId];
+	if (debug)
+		print("row["+rowId+"]:"+row);
+
+	let daughterRow=parameters.vars[daughterRowId];
+
+	if (debug)
+		print("daughterRow["+daughterRowId+"]:"+daughterRow);
+
+	let el=document.getElementById(row.selectId);
+	if (debug)
+		print("\n Element:"+el);
+	let varValue=el.options[el.selectedIndex].value;
+
+	if (debug)
+		print("\nAdding filter ["+row.masterSelectVarName+"]:"+varValue);
+
+	let config=new Object();
+	config.schemaName='lists';
+	config.queryName=parameters.masterQuery;
+	config.filterArray=[];
+	for (let i=0;i < parameters.filters.length;i++){
+		let filterRowId=parameters.filters[i];
+		let filterRow=parameters.vars[filterRowId];
+		let filterValue=document.getElementById(filterRow.selectId).value;
+		config.filterArray.push(LABKEY.Filter.create(filterRow.masterSelectVarName,filterValue));
+	}
+	config.success=function(data){populateSelectNotLookup(data,parameters,daughterRowId,entry)}; 
+	LABKEY.Query.selectRows(config);
+
+}
+
+function generateList(parameters,rowId){
+	let row=parameters.vars[rowId];
+	let debug=true;
+	if (debug)
+		print("generateList: "+parameters.masterQuery);
+	
+		//ignore authorization, just select on select variable
+	let el=document.getElementById(row.selectId);
+	let varValue=el.options[el.selectedIndex].value;
+	if (debug)
+		print("Using value "+varValue+" from "+row.selectId);
+	
+	let iValue=parseInt(varValue); 
+
+	let div=document.getElementById(parameters.addDiv);
+	div.style.display="none";
+	//add new crf entry
+	if ("addNewFlag" in row){
+		if (debug)
+			print("Comparing " + iValue + "/" + row.addNewFlag);
+
+		if (iValue==row.addNewFlag) {
+			addNew(parameters);
+			return;
+		}
+	}
+	
+
+
+	//do filtering
+	let filterArray=[];
+	for (let i=0;i < parameters.filters.length;i++){
+		let filterRowId=parameters.filters[i];
+		let filterRow=parameters.vars[filterRowId];
+		let filterValue=document.getElementById(filterRow.selectId).value;
+		filterArray.push(LABKEY.Filter.create(filterRow.masterSelectVarName,filterValue));
+	}
+	
+
+	//show all for system entries (Select, Add New)
+	if (debug)
+		print("Using iValue "+iValue);
+	if (iValue<0) {
+		if (debug)
+			print("Ignoring ["+row.masterSelectVarName+ "]: "+varValue);
+	}
+	else{
+		if (debug)
+			print("Filtering ["+row.masterSelectVarName+"]: "+varValue);
+
+		if (rowId==="Crf"){
+			filterArray.push(LABKEY.Filter.create(row.masterSelectVarName,varValue));
+			if (debug)
+				print("Filtering ["+row.masterSelectVarName+"]: "+varValue);
+
+		}
+		
+	}
+
+	LABKEY.QueryWebPart({renderTo: parameters.dataDiv, schemaName: 'lists',
+		queryName: parameters.masterQuery, 
+		buttonBarPosition: 'top',filters: filterArray, viewName:"sparseView", 
+		success:updateSuccess, failure:updateFailure });
+	
+}
+
+function updateSuccess(){
+	document.getElementById('formStatus').value+="\n Update success";
+}
+
+function updateFailure(json){
+	document.getElementById('formStatus').value+="\n Update failed";
+
+}
+
+function addNewEntry(parameters){
+	print("Add new, npar ");
+	let entry=new Object();
+
+	for (vv in parameters.vars){
+		let f=parameters.vars[vv];
+		print("New: Adding "+f.masterSelectVarName);
+		setValue(entry,f);
+	}
+	for (f in entry){
+		print("entry ["+f+"]="+entry[f]);
+	}
+	entry.entryId=Date.now();
+	entry.Date=new Date();
+	entry.formStatus=1;//In Progress
+	let config=new Object();
+	config.schemaName='lists';
+	config.queryName=parameters.masterQuery;
+	config.rows=[entry];
+	config.success=function(data){
+		populateDaughterSelect(parameters,"Site","Crf",data.rows[0]);
+		selectEntry(data,parameters.vars["Crf"]);
+		generateList(parameters,"Crf");};
+	LABKEY.Query.insertRows(config);
+}
+
+function selectEntry(data,row){
+	let entry=data.rows[0];
+	let varName=row.masterSelectVarName;
+	let el=document.getElementById(row.selectId);
+	for (let i=0;i< el.options.length;i++){
+		print("selectEntry: "+el.options[i].value+"/"+entry[varName]);
+		if (el.options[i].value!=entry[varName]) continue;
+		el.selectedIndex=i;
+		return;
+	}
+}
+
+function setValue(entry,f){
+	let el=document.getElementById(f.selectId);
+	print("setValue: Element: "+el);
+	if (f.inputType=="select"){
+		entry[f.masterSelectVarName]=el.value;
+	}
+	if (f.inputType=="innerHTML"){
+		entry[f.masterSelectVarName]=el.innerHTML;
+	}
+}
+
+function addNew(parameters){
+	print("Show Add new");
+	let div=document.getElementById(parameters.addDiv);
+	div.style.display="block";
+}
+
+function startForm(parameters){
+	let crfVar=parameters.vars["Crf"];
+	let el=document.getElementById(crfVar.selectId);
+	let config=new Object();
+	config.schemaName='lists';
+	config.queryName=parameters.masterQuery;
+	config.filterArray=[LABKEY.Filter.create(crfVar.masterSelectVarName,el.value)]
+       	config.success=function(data){findURL(data,parameters)};
+	LABKEY.Query.selectRows(config);
+	// The set of URL parameters.
+}
+
+function findURL(data,parameters){
+	let entry=data.rows[0];
+	
+	let fields=data.metaData.fields;
+	let formVar=parameters.vars["Form"];
+	let formVarName=formVar.masterSelectVarName;
+	let lookup;
+	for (f in fields){
+		if (fields[f].name!=formVarName) continue;
+		lookup=fields[f].lookup;
+		break;
+	}
+	let config=new Object();
+	config.schemaName=lookup.schemaName;
+	config.queryName=lookup.queryName;
+	config.filterArray=[LABKEY.Filter.create(lookup.keyColumn,entry[formVarName])];
+	config.success=function(data){finalRedirect(data,entry,parameters)};
+	LABKEY.Query.selectRows(config);
+}
+
+function finalRedirect(data,entry,parameters){
+	let formEntry=data.rows[0];
+	let formVar=parameters.vars["Form"];
+	let formUrl=formEntry[formVar.urlName];
+	var params = {
+		"name": formUrl, // The destination wiki page. The name of this parameter is not arbitrary.
+		"userid": entry[parameters.vars["User"].masterSelectVarName], 
+		"entryId": entry[parameters.vars["Crf"].masterSelectVarName]
+	};
+
+        // This changes the page after building the URL. 
+	//Note that the wiki page destination name is set in params.
+        var wikiURL = LABKEY.ActionURL.buildURL("wiki", "page", LABKEY.ActionURL.getContainer(), params);
+        print("Redirecting to "+wikiURL);
+		 
+	window.location = wikiURL;
+}
+
+
+
+</script>