Bläddra i källkod

(1) Adding crfStaticVariables that are displayed at the top of the form as labkey list, (2) adding variableDetails list to provide long descriptions of variables and (3) adding first approximation of a form generalized participantId entry, which is not used in upload of the data

Andrej Studen 2 år sedan
förälder
incheckning
953b80f3ee
3 ändrade filer med 430 tillägg och 113 borttagningar
  1. 1 0
      views/visit.view.xml
  2. 182 113
      web/crf/crfVisit.js
  3. 247 0
      web/crf/participantIdManager.js

+ 1 - 0
views/visit.view.xml

@@ -1,6 +1,7 @@
 <view xmlns="http://labkey.org/data/xml/view" title="CRF Form">
 	<dependencies>
       <dependency path="crf/runQuery.js"/>
+      <dependency path="crf/participantIdManager.js"/>
       <dependency path="crf/crfVisit.js"/>
 		<dependency path="crf/crfReview.js"/>
 		<!--local copy of pdfkit, version 0.10.0-->

+ 182 - 113
web/crf/crfVisit.js

@@ -18,78 +18,6 @@ function print(msg){
 	el.value+="\n"+msg;
 }
 
-function makeQuery(containerName,queryName,fieldName,filterArray){
-	let e=new Object();
-	e.containerName=containerName;
-	e.queryName=queryName;
-	e.fieldName=fieldName;
-	e.filterArray=filterArray;
-	return e;
-}
-
-function getDataFromQueries(queryArray,cb){
-	//queryArray should contain elements with
-	//- fieldName to set the data variable
-	//- containerName to select container (data,config,CRF)
-	//- queryName to select query
-	//- filterArray to perform filtering, empty array works
-	//- callback cb to be called with no arguments
-	//
-	afterQuery(new Object(),-1,queryArray,cb);
-}
-
-function afterQuery(data,id,queryArray,cb){
-	//queryArray should contain elements with
-	//- fieldName to set the data variable
-	//- containerName to select container (data,config,CRF)
-	//- queryName to select query
-	//- filterArray to perform filtering, empty array works
-	//- callback cb to be called with no arguments
-	//
-	//it should be called with id -1.
-	//
-	print('afterQuery['+id+']: ');
-
-	if (id>-1){
-		let fieldName=queryArray[id].fieldName;
-		print('afterQuery['+fieldName+']: '+data.rows.length);
-		config.formConfig[fieldName]=data;
-	}
-	id+=1;
-	if (id==queryArray.length) {
-		cb();
-		return;
-	}
-
-
-	let e=queryArray[id];
-	let qconfig=new Object();
-	qconfig.containerPath=getContainer(e.containerName);
-	qconfig.schemaName="lists";
-	if ("schemaName" in e){
-		print('afterQuery: schemaName='+e.schemaName);
-		qconfig.schemaName=e.schemaName;
-	}
-
-	if ("columns" in e){
-		print('afterQuery: columns='+e.columns);
-		qconfig.columns=e.columns;
-	}
-	qconfig.queryName=e.queryName;
-	//this should point to configuration container
-	//don't filter -> so we can pick up other forms (say registration) later on
-	//qconfig.filterArray=[LABKEY.Filter.create('Key',config.formId)];
-	if ("filterArray" in e)
-		qconfig.filterArray=e.filterArray;
-	
-	//qconfig.filterArray=[LABKEY.Filter.create('formStatus',1)]
-	qconfig.success=function(data){afterQuery(data,id,queryArray,cb);};
-	qconfig.failure=doNothing;
-	LABKEY.Query.selectRows(qconfig);
-
-}
-
-
 function getCRFrefFirst(){
 	//crfRef is part of html call and gets stored in the page
 	return config.document.getElementById(config.crfRefId).innerHTML;
@@ -173,40 +101,60 @@ function generateDebugSection(){
 
 
 function getAdditionalData(formSetupEntry){
+   //return information on additional data associated with the form
+   //additionalData is a sub-list with multiple entries per patient/visit
+   
 
+   //argument is the row of the formSetup setup list
 	let queryName=config.formConfig.queryMap[formSetupEntry['queryName']];
 	let fName='[getAdditionalData/'+queryName+']';
 	print(fName);
 
+   //additionalData holds a reference to all queries already parsed
+   //this helps in reducing number of calls to the database (I assume)
 	if (queryName in config.formConfig.additionalData){
 		print(fName+': Returning preset value');
 		return config.formConfig.additionalData[queryName];
 	}
+
+   //first time we see this query, so we have to do the setup
 	print(fName+': generating');
 	config.formConfig.additionalData[queryName]=new Object();
-	//takes address, so further changes will be to the newly created object
+	
+   //takes address, so further changes will be to the newly created object
+   //in fact, ad is just a short alias of the long variable name on the right
 	let ad=config.formConfig.additionalData[queryName];
 
+   //no additional data
 	if (formSetupEntry["showFlag"]==="NONE") {
 		print(fName+": empty");
 		return ad;
 	}
+
+   //use showFlag to setup report section of the CRF list
 	if (formSetupEntry["showFlag"]==="REVIEW") {
 		//abuse additionalData to signal different segment
 		print(fName+": generateReport");
 		ad.isReview=true;
 		return ad;
 	}
+
+   //setup the additionalData memory object
 	print(fName+': setting values');
 	ad.showFlag=formSetupEntry["showFlag"];
 	ad.showFlagValue=formSetupEntry["showFlagValue"];
 	ad.queryName=formSetupEntry["showQuery"];
+
+   //for data queries, limit to present CRF only
 	ad.filters=new Object();
 	ad.filters['crfRef']=getCRFref();
+
+   //compose a long debug message
 	let msg=fName+": flag "+ad.showFlag;
 	msg+=" value "+ad.showFlagValue;
 	msg+=" query "+ad.queryName;
 	print(msg);
+
 	return ad;	
 }
 
@@ -260,12 +208,7 @@ function getSetup(listName,writeAccess=true){
 	//	return readonlySetup(listName);
 	return fullAccessSetup(listName);
 }
-	
-//afterFormConfig replaced by afterFormSetup
-
-//afterFormSetupLookup replaced by afterFormDatasets
 
-//function generateSection(listName, sectionTitle, accessMode, additionalData){
 function generateSection(formSetupEntry){
 
 	let listName=config.formConfig.queryMap[formSetupEntry['queryName']];
@@ -1055,19 +998,36 @@ function addFieldRow(tb,field,setup,additionalData){
 	let vType=field.type;
 	let isLookup=("lookup" in field);
 	print(fName+": ["+vName+"/"+vType+'/'+isLookup+"]");
+
+   let detailsRow=config.formConfig['variableDetails'].rows;
+   let details=undefined;
+   for (let i=0;i<detailsRow.length;i++){
+      if (detailsRow[i]['variableName']==field.name){
+         print(fName+": ["+vName+"]: details "+detailsRow[i].description);
+         details=detailsRow[i].description;
+         break;
+      }
+   }
 	
 	let row=tb.insertRow();
 	let cell=config.document.createElement('th');
+   cell.style.width='300px';
 	row.appendChild(cell);
 	
 	let text = config.document.createTextNode(field.shortCaption);
 	cell.appendChild(text);
-	let cell1=row.insertCell();
 		
 	
 	let input=null;
-	cell1.colSpan="3";
-
+   let colSpan="3";
+   if (details!=undefined){
+      let cell2=row.insertCell();
+      cell2.colSpan="2";
+      cell2.innerText=details;
+      colSpan="1";
+   }
+	let cell1=row.insertCell();
+	cell1.colSpan=colSpan;
 	let readonlyFlag=setup.readonlyFlag(vName);
 
 
@@ -2421,22 +2381,54 @@ function generateMasterForm(){
 //(fields defined in html file)
 function populateBasicData(){
 
-	config.document.getElementById('version').innerText=config.formConfig.softwareVersion;	
-	config.document.getElementById('eudraCTNumber').innerText=
-		config.formConfig.crfEntry.EudraCTNumber;
-	config.document.getElementById('studyCoordinator').innerText=
-		config.formConfig.crfEntry.StudyCoordinator;
-	config.document.getElementById('studySponsor').innerText=
-		config.formConfig.crfEntry.StudySponsor;
-	config.document.getElementById('siteName').innerText=
-		config.formConfig.currentSite['siteName'];
-	config.document.getElementById('sitePhone').innerText=
-		config.formConfig.currentSite['sitePhone'];
-	config.document.getElementById('investigatorName').innerText=
-		config.formConfig.user['DisplayName'];
+   let staticData=new Object();
+   let titles=new Object();
+   staticData['version']=config.formConfig.softwareVersion;	
+   titles['version']='Software version';
+	let varRows=config.formConfig['crfStaticVariables'].rows;
+   for (let i=0;i<varRows.length;i++){
+      let vName=varRows[i].staticVariable;
+      let val=config.formConfig.crfEntry[vName];
+      if (val==undefined) continue;
+      staticData[vName]=val;
+      titles[vName]=varRows[i].Title;
+   }
+	staticData['investigatorName']=config.formConfig.user['DisplayName'];
+   titles['investigatorName']='Investigator';
+   staticData['email']=config.formConfig.user['Email'];
+   titles['email']='Email';
+   staticData['siteName']=config.formConfig.currentSite['siteName'];
+   titles['siteName']='Site';
+   staticData['sitePhone']=config.formConfig.currentSite['sitePhone'];
+   titles['sitePhone']='Telephone(site)';
+
+   for (f in staticData){
+      addStaticData(f,titles[f],staticData[f]);
+   }
+}
+
+function addStaticData(f,title,value){
+   let el=config.document.getElementById(f);
+
+   //populate only
+   if (el!=undefined){
+      el.innerText=value;
+      return;
+   }
+   
+   //add row to table if element cannot be found
+   let table=config.document.getElementById('staticTable');
+   let row=table.insertRow();
+   let cell=row.insertCell();
+   cell.innerText=title;
+   let cell1=row.insertCell();
+   cell1.id=f;
+   cell1.style.fontWeight='bold';
+
+   //populate
+   cell1.innerText=value;
 }
 
-
 //come here after the layout is read from labkey page
 //
 function generateErrorMsg(msg){
@@ -2581,6 +2573,24 @@ function afterData(){
 	//operatorBasedAccessMode	
 	let accessMode=config.formConfig.operator+'Mode';
 	let rowsSetup=config.formConfig.formSetupRows;
+
+   print(fName+': '+'generatePariticpantEntry');
+   //generateParticipantEntryField();
+   //add print to config so participantManager can use it
+   config.print=print;
+   let pM=getParticipantManagerObject(config);
+
+   print(fName+': pariticpantManager: '+pM);
+   //check if ParticipantId is set in crfEntry
+   let pId=config.formConfig.crfEntry['ParticipantId'];
+   print(fName+' pId '+pId);
+   if (!pId){ 
+      pM.setParticipantManagerToEditMode();
+   }
+   else{
+      pM.setParticipantManagerToLabelMode(pId,'Record');
+   }
+
 	for (let i=0;i<rowsSetup.length;i++){
 		let entry=rowsSetup[i];
 		let queryName=config.formConfig.queryMap[entry['queryName']];
@@ -2602,6 +2612,7 @@ function afterData(){
 		//let additionalData=new Object();
 		//setAdditionalData(additionalData,entry);
 		//section fits one dataset/list
+      
 		generateSection(entry);
 		//generateSection(queryName,entry["title"],entry[accessMode], 
 		//		additionalData);
@@ -2928,7 +2939,7 @@ function setFormConfig(){
 	//add object to store form related data
 	config.formConfig=new Object();
 
-	config.formConfig.softwareVersion='0.14.18';
+	config.formConfig.softwareVersion='0.15.08';
 	let debug=true;
 
 	if (debug)
@@ -3004,50 +3015,98 @@ function afterCRFEntry(data){
 }
 
 function collectData(){
-
-	let varLabel='sourceFormStatus';
-	let formStatus=config.formConfig.crfEntry['FormStatus'];
-
+	
+   let targetObject=config.formConfig;
+   targetObject.print=print;
+   targetObject.getContainer=getContainer;
 	let queryArray=new Array();
+   //k
 	//site
-	queryArray.push(makeQuery('config','site','siteData',[]));
+	queryArray.push(makeQuery(targetObject,'config','site','siteData',[]));
 	//users
-	queryArray.push(makeQuery('CRF','users','userData',[]));
+	queryArray.push(makeQuery(targetObject,'CRF','users','userData',[]));
 	queryArray[queryArray.length-1].schemaName='core';
 	//crfEditors
-	queryArray.push(makeQuery('config','crfEditors','crfEditorsData',[]));
+	queryArray.push(makeQuery(targetObject,'config','crfEditors','crfEditorsData',[]));
 	//crfMonitors
-	queryArray.push(makeQuery('config','crfMonitors','crfMonitorsData',[]));
+	queryArray.push(makeQuery(targetObject,'config','crfMonitors','crfMonitorsData',[]));
 	//crfSponsors
-	queryArray.push(makeQuery('config','crfSponsors','crfSponsorsData',[]));
+	queryArray.push(makeQuery(targetObject,'config','crfSponsors','crfSponsorsData',[]));
+
+   //study static data
+   queryArray.push(
+      makeQuery(targetObject,'data','crfStaticVariables','crfStaticVariables',[]));
+
+   queryArray.push(makeQuery(targetObject,'data','variableDetails','variableDetails',[]));
 	//study
-	queryArray.push(makeQuery('data','Study','studyDataAll',[]));
+	queryArray.push(makeQuery(targetObject,'data','Study','studyDataAll1',[]));
 	let e=queryArray[queryArray.length-1];
+	//overload schema name
 	e.schemaName='study';
+	//make sure variables not part of default view are loaded
+	//here we should already have read crfStaticVariables table
 	e.columns="SubjectColumnName,EudraCTNumber,StudySponsor";
 	e.columns+=",StudyCoordinator,RegulatoryNumber";
 	
 	//formStatus
+	let varLabel='sourceFormStatus';
+	let formStatus=config.formConfig.crfEntry['FormStatus'];
 	let formFilter=LABKEY.Filter.create('Key',formStatus);
-	queryArray.push(makeQuery('config','FormStatus','formStatusData',[formFilter]));
+	queryArray.push(
+		makeQuery(targetObject,'config','FormStatus','formStatusData',[formFilter]));
 	//crfButtons
 	let statusFilter=LABKEY.Filter.create(varLabel,formStatus);
-	queryArray.push(makeQuery('config','crfButtons','crfButtons',[statusFilter]));
+	queryArray.push(
+		makeQuery(targetObject,'config','crfButtons','crfButtons',[statusFilter]));
 	//Forms	
-	queryArray.push(makeQuery('config','Forms','formData',[]));
+	queryArray.push(makeQuery(targetObject,'config','Forms','formData',[]));
 	//FormSetup	
-	queryArray.push(makeQuery('config','FormSetup','formSetup',[]));
+	queryArray.push(makeQuery(targetObject,'config','FormSetup','formSetup',[]));
 	//generateConfig
-	queryArray.push(makeQuery('config','generateConfig','generateConfigData',[]));	
+	queryArray.push(
+		makeQuery(targetObject,'config','generateConfig','generateConfigData',[]));	
 	//parentCrf
 	let parentCrf=config.formConfig.crfEntry['parentCrf'];
 	if (parentCrf!=undefined){
 		let crfFilter=LABKEY.Filter.create('entryId',parentCrf);
-		queryArray.push(makeQuery('data','crfEntry','parentCrfData',[crfFilter]));	
+		queryArray.push(makeQuery(targetObject,'data','crfEntry','parentCrfData',[crfFilter]));	
 	}	
 
 	print('running getDataFromQueries');
+	getDataFromQueries(queryArray,addStudyData);
+}
+
+function addStudyData(){
+	let queryArray=new Array();
+	
+   let targetObject=config.formConfig;
+   targetObject.print=print;
+   targetObject.getContainer=getContainer;
+	//study
+	queryArray.push(makeQuery(targetObject,'data','Study','studyDataAll',[]));
+	//queryArray.push(makeQuery('data','Study','studyDataAll',[]));
+	let e=queryArray[queryArray.length-1];
+	//overload schema name
+	e.schemaName='study';
+	//make sure variables not part of default view are loaded
+	//here we should already have read crfStaticVariables table
+   let staticVarRows=config.formConfig['crfStaticVariables'].rows;
+   let columnModel=""
+   for (let i=0;i<staticVarRows.length;i++){
+      if (i>0) columnModel+=',';
+      columnModel+=staticVarRows[i]['staticVariable'];
+   }
+	e.columns=columnModel;
+
+   //also collect ids already in study
+   let demoQuery=config.formConfig.settings['demographicQuery'];
+
+   queryArray.push(makeQuery(targetObject,'data',demoQuery,'demographicData',[]));
+   queryArray[queryArray.length-1].schemaName='study';
+      
+	
 	getDataFromQueries(queryArray,fcontinue);
+
 }
 
 function selectFormSetupRows(formId){
@@ -3062,6 +3121,16 @@ function selectFormSetupRows(formId){
 }
 
 function fcontinue(){
+
+   //debug
+   let fName='[fcontinue]';
+   let varRows=config.formConfig['crfStaticVariables'].rows;
+   let studyVars=config.formConfig['studyDataAll'].rows[0];
+   for (let i=0;i<varRows.length;i++){
+      let vName=varRows[i].staticVariable;
+      print(fName+' '+vName+': '+studyVars[vName]);
+   }
+
 	//parse site
 	config.formConfig.siteRows=config.formConfig.siteData.rows;
 	let sRows=config.formConfig.siteRows;
@@ -3363,7 +3432,7 @@ function dataLayoutSet(){
 
 function setData(cb){
 	fName='[setData]';
-	print(fName+': cb '+cb);	
+	//print(fName+': cb '+cb);	
 	let crfMatch=getCRFref();
 	let parentCrf=config.formConfig.crfEntry['parentCrf'];
 	if (parentCrf!=undefined) crfMatch=parentCrf;

+ 247 - 0
web/crf/participantIdManager.js

@@ -0,0 +1,247 @@
+//all functions are based off of participantManager (print, config, etc.)
+
+function addSelectOptions(){
+   let pM=this;
+   let input=pM.inputSelector;
+   let config=pM.config;
+
+   let fName='addParticipantSelectOptions';
+   pM.print(fName);
+   let opt=config.document.createElement("option");
+   opt.value=-1;
+
+   opt.text="<Select>";
+   input.options[input.options.length]=opt;
+
+   let opt1=config.document.createElement("option");
+   opt1.value=0;
+   opt1.text="New";
+   input.options[input.options.length]=opt1;
+
+   let demoRows=config.formConfig['demographicData'].rows;
+   pM.print(fName+" demoRows: "+demoRows.length);
+   for (let i=0;i<demoRows.length;i++){
+      let opt2=config.document.createElement("option");
+      opt2.value=i+1;
+      opt2.text=demoRows[i][pM.participantField];
+      input.options[input.options.length]=opt2;
+      pM.print(fName+' '+pM.participantField+' '+demoRows[i][pM.participantField]);
+   }
+
+   input.selectedIndex=0;
+}
+
+function updateElements(){
+   let pM=this;
+   //reset all values (some might be different depending on the call timing)
+   pM.cellSelector=config.document.getElementById(pM.cellSelectorId);
+   pM.inputSelector=config.document.getElementById(pM.inputSelectorId);
+   pM.textSelector=config.document.getElementById(pM.textSelectorId);
+   pM.cellValue=config.document.getElementById(pM.cellValueId);
+   pM.inputValue=config.document.getElementById(pM.inputValueId);
+   pM.textValue=config.document.getElementById(pM.textValueId);
+   
+   pM.inputManage=config.document.getElementById(pM.inputManageId);
+}
+
+function generateEntryField(){
+   let pM=this;
+   let fName='[generateParticipantEntryField]:';
+   pM.print(fName);
+
+   //this is HTML designator of area on page
+   let config=pM.config;
+	let formName=config.masterForm;
+
+    
+   pM.tb=config.document.createElement('table');
+   let tb=pM.tb;
+	tb.className='t2';
+	let row=tb.insertRow();
+	
+   //header
+   let cell=config.document.createElement('th');
+	row.appendChild(cell);	
+	cell.setAttribute("colspan","1");
+	cell.style.fontSize="20px";
+	cell.style.textAlign="left";
+   //Use study coding for participant field
+	cell.innerText=pM.participantField;
+
+
+   //selector
+   let cellSelector=row.insertCell();
+   cellSelector.id=pM.cellSelectorId;
+
+
+   //value
+   let cellValue=row.insertCell();
+   cellValue.id=pM.cellValueId;
+
+   //manage
+   let cellManage=row.insertCell();
+   let inputManage=config.document.createElement("input");
+   inputManage.type="button";
+   //initially in set mode
+   inputManage.onclick=function(){pM.manageParticipantId();};
+   inputManage.id=pM.inputManageId;
+   cellManage.appendChild(inputManage);
+
+   config.document.getElementById(formName).appendChild(tb);
+
+}
+
+//callback
+function manageParticipantId(){
+   let pM=this;
+   let fName='[manageParticipantId]';
+   pM.print(fName);
+   //this can happen after object was created, so make sure current
+   //elements are used
+   pM.updateElements();
+   if (pM.inputManage.value=="Set"){
+      pM.setParticipantId();
+      return;
+   }
+   if (pM.inputManage.value=="Edit"){
+      pM.editParticipantId();
+      return;
+   }
+}
+
+//set mode
+function setParticipantId(){
+   let pM=this;
+   let fName='[setParticipantId]';
+   pM.print(fName);
+   //update to current elements
+   pM.print(fName+" select value: "+pM.inputSelector.value+" value: "+pM.inputValue.value);
+   if (pM.inputSelector.value<0) return;
+   let pId=pM.inputValue.value;
+   let opt=pM.inputSelector.options[pM.inputSelector.selectedIndex];
+   if (pM.inputSelector.value>0)
+      pId=opt.text;
+
+   pM.print(fName+" new value "+pId);
+   pM.setParticipantManagerToLabelMode(pId,opt.text);
+}
+
+function setParticipantManagerToLabelMode(pId,optText){
+   let pM=this;
+   let config=pM.config;
+   //set crfEntry and upload it to labkey
+   let textValue=config.document.createElement("p");
+   textValue.innerText=pId;
+   textValue.id=pM.textValueId;
+   if (pM.inputValue!=undefined)
+      pM.cellValue.replaceChild(textValue,pM.inputValue);
+   else
+      pM.cellValue.appendChild(textValue);
+
+   
+   let textSelector=config.document.createElement("p");
+   textSelector.innerText=optText;
+   textSelector.id=pM.textSelectorId;
+   if (pM.inputSelector!=undefined)
+      pM.cellSelector.replaceChild(textSelector,pM.inputSelector);
+   else
+      pM.cellSelector.appendChild(textSelector);
+
+   //addStaticData(participantField,participantField,pid);
+
+   pM.inputManage.value="Edit";
+}
+
+function editParticipantId(){
+   let pM=this;
+   pM.setParticipantManagerToEditMode();
+}
+
+function setParticipantManagerToEditMode(){
+   let pM=this;
+   let config=pM.config;
+
+   let fName='[setParticipantManagerToEditMode]';
+   pM.print(fName);
+   //input
+   let inputValue=config.document.createElement("input");
+   inputValue.id=pM.inputValueId;
+   pM.print(fName+" value old "+pM.textValue+" new "+inputValue);
+   if (pM.textValue!=undefined)
+      pM.cellValue.replaceChild(inputValue,pM.textValue);
+   else
+      pM.cellValue.appendChild(inputValue);
+
+   //select
+   let inputSelector = config.document.createElement("select");
+   inputSelector.id=pM.inputSelectorId;
+   pM.inputSelector=inputSelector;
+   pM.addSelectOptions();
+   pM.print(fName+" select old "+pM.textSelector+" new "+inputSelector);
+   if (pM.textSelector!=undefined)
+      pM.cellSelector.replaceChild(inputSelector,pM.textSelector);
+   else
+      pM.cellSelector.appendChild(inputSelector);
+
+   pM.inputManage.value="Set";
+
+}
+
+function getParticipantField(config){
+   return config.formConfig['studyDataAll'].rows[0]['SubjectColumnName'];
+}
+
+function getParticipantId(){
+   let pM=this;
+   //let participantField=config.formConfig['studyDataAll'].rows[0]['SubjectColumnName'];
+   return pM.textValue.innerText;
+}
+
+
+
+//main interface. Use this to generate object and to refer to it later on
+function getParticipantManagerObject(config){
+
+   let fName='[getParticipantManagerObject]';
+
+   if ("participantManager" in config) {
+      let pM=config.participantManager;
+      pM.updateElements();
+      return pM;
+   }
+
+   let pM=new Object();
+   //circular reference to traverse pM up and down
+   config.participantManager=pM;
+   pM.config=config;
+   //config should have a print routine
+   pM.print=config.print;
+
+   //this never change
+   pM.participantField=getParticipantField(config);
+
+
+   pM.cellSelectorId=pM.participantField+"_cellSelect";
+   pM.inputSelectorId=pM.participantField+"_Select";
+   pM.textSelectorId=pM.participantField+"_textSelect";
+
+   pM.cellValueId=pM.participantField+"_cellValue";
+   pM.inputValueId=pM.participantField+"_Value";
+   pM.textValueId=pM.participantField+"_textValue";
+
+   pM.inputManageId=pM.participantField+"_Manage";
+   //add methods
+   pM.updateElements=updateElements;
+   pM.addSelectOptions=addSelectOptions;
+   pM.generateEntryField=generateEntryField;
+   pM.manageParticipantId=manageParticipantId;
+   pM.setParticipantId=setParticipantId;
+   pM.editParticipantId=editParticipantId;
+   pM.setParticipantManagerToLabelMode=setParticipantManagerToLabelMode;
+   pM.setParticipantManagerToEditMode=setParticipantManagerToEditMode;
+   pM.getParticipantId=getParticipantId;
+
+   pM.generateEntryField();
+   pM.updateElements();
+   return pM;
+}