const config=new Object(); function clear(){ let el=config.document.getElementById(config.debugId); if (el===null) { //alert("Debug section not initialized"); return; } config.document.getElementById(config.debugId).value=""; } function print(msg){ let el=config.document.getElementById(config.debugId); if (el===null) { //alert("Debug section not initialized. Message: "+msg); return; } el.value+="\n"+msg; } function getCRFrefFirst(){ //crfRef is part of html call and gets stored in the page return config.document.getElementById(config.crfRefId).innerHTML; } function getCRFref(){ //'crfRefId' return config.formConfig.crfEntry['entryId']; } function onFailure(errorInfo, options, responseObj){ if (errorInfo && errorInfo.exception) alert("Failure: " + errorInfo.exception); else alert("Failure: " + responseObj.statusText); } function doNothing(){ print('doNothing called'); } function doFailure(errorInfo){ print('Error '+errorInfo.exception); print('Trace '+errorInfo.stackTrace); alert('Failed with '+errorInfo.exception); } function generateDebugSection(){ //let debug=true; //if (debug) print("generateDebugSection "+sectionName); let formName=config.debugDiv; let sectionName="debugSection"; let sectionTitle="Debug Messages"; let tb=config.document.createElement('table'); tb.className='t2'; let row=tb.insertRow(); let cell=config.document.createElement('th'); row.appendChild(cell); cell.setAttribute("colspan","4"); cell.style.fontSize="20px"; cell.style.textAlign="center"; let cellData=config.document.createTextNode(sectionTitle); cell.appendChild(cellData); cell=row.insertCell(); let input=config.document.createElement("input"); input.type="button"; input.value="Show"; input.id="toggle"+sectionName+"VisbilityButton"; input.onclick=function(){toggleVisibility(sectionName,input.id)}; cell.appendChild(input); config.document.getElementById(formName).appendChild(tb); let div=config.document.createElement('div'); div.id=sectionName; div.style.display="none"; config.document.getElementById(formName).appendChild(div); let debugArea=config.document.createElement('textarea'); debugArea.rows=10; debugArea.cols=95; debugArea.id=config.debugId; div.appendChild(debugArea); //print('ver: 0.10'); } function setAdditionalData(additionalData,entry){ let debug=true; if (debug) print("setAdditionalData"); if (entry["showFlag"]==="NONE") { if (debug) print("Returning empty additionalData"); return additionalData; } if (entry["showFlag"]==="REVIEW") { //abuse additionalData to signal different segment if (debug) print("Returning additionalData that signal review form"); additionalData.isReview=true; return additionalData; } additionalData.showFlag=entry["showFlag"]; additionalData.showFlagValue=entry["showFlagValue"]; additionalData.queryName=entry["showQuery"]; additionalData.filters=new Object(); additionalData.filters['crfRef']=getCRFref(); return additionalData; } function fullAccessSetup(listName){ //generate setup object whcih should contain fields: //readonlyFlag - whether the dataset is writeable //filters - selection fields that allow creation of LABKEY.Filter.create() //getInputId - formating of unique ids for html elements //addApply - whether a submit/Save button is generated //unique - whether entries in list are unique let debug=true; if (debug) print("fullAccessSetup"); let setup=new Object(); setup.readonlyFlag=function(vName){return false}; setup.filters=new Object(); setup.filters['crfRef']=getCRFref(); setup.getInputId=function(vName){return listName+"_"+vName;} setup.addApply="Save"; return setup; } function readonlySetup(listName){ //see definition of setup object above let debug=true; if (debug) print("readonlySetup"); let setup=new Object(); setup.readonlyFlag=function(vName){return true}; setup.filters=new Object(); setup.filters['crfRef']=getCRFref(); setup.getInputId=function(vName){return listName+'_'+vName;} return setup; } function getSetup(listName){ let formStatus=config.formConfig.formStatus; if (formStatus=="Submitted") return readonlySetup(listName); if (formStatus=="Approved") return readonlySetup(listName); return fullAccessSetup(listName); } //afterFormConfig replaced by afterFormSetup //afterFormSetupLookup replaced by afterFormDatasets function generateSection(sectionName, sectionTitle, listName, additionalData){ let formName=config.masterForm;//this is HTML designator of area on page let debug=true; if (debug) print("generateSection "+sectionName); let tb=config.document.createElement('table'); tb.className='t2'; let row=tb.insertRow(); let cell=config.document.createElement('th'); row.appendChild(cell); cell.setAttribute("colspan","4"); cell.style.fontSize="20px"; cell.style.textAlign="center"; let cellData=config.document.createTextNode(sectionTitle); cell.appendChild(cellData); cell=row.insertCell(); let input=config.document.createElement("input"); input.type="button"; input.value="Show"; input.id="toggle"+sectionName+"VisbilityButton"; input.onclick=function(){toggleVisibility(sectionName,input.id)}; cell.appendChild(input); config.document.getElementById(formName).appendChild(tb); let div=config.document.createElement('div'); div.id=sectionName; div.style.display="none"; config.document.getElementById(formName).appendChild(div); let divTable=config.document.createElement('div'); divTable.id=sectionName+"Table"; div.appendChild(divTable); if ("showFlag" in additionalData) { additionalData.divName=sectionName+"SubDiv"; additionalData.divQueryName=sectionName+"SubDivList"; let div1=config.document.createElement('div'); div1.id=additionalData.divName; div1.style.display="none"; div.appendChild(div1); let div2=config.document.createElement('div'); div2.id=additionalData.divQueryName; div1.appendChild(div2); } if (debug) print("generate master table"); let setup=getSetup(listName); if ("isReview" in additionalData){ generateReviewSection(listName,div.id,generateReviewSectionCB); return; } //master table is unique per visit setup.unique=true; generateTable(listName,divTable.id,additionalData,setup); if (debug) print("generate master table: done"); let generateSubTable=true; if (formStatus=="Submitted") generateSubTable=false; if (formStatus=="Approved") generateSubTable=false; if (! ("showFlag" in additionalData) ) generateSubTable=false; if (generateSubTable){ let qName=additionalData.queryName; let dName=additionalData.divName; let setup=fullAccessSetup(qName); //if (readonly) setup=readonlySetup(config); generateTable(qName,dName,additionalData,setup); } print("generate review"); let divReviewList=config.document.createElement('div'); divReviewList.id=sectionName+"ReviewList"; div.appendChild(divReviewList); let divReview=config.document.createElement('div'); divReview.id=sectionName+"Review"; div.appendChild(divReview); //assume we already have listId (content of config.setupQueryName is listId) //we need listName also //qconfig.queryName=config.setupQueryName; generateReview(divReview.id,divReviewList.id,listName); } //>>>>reviewSection associated routines function parseResponseXML(){ //print(config.config,'Status:' +this.status); print('Status:'+this.status); if (this.status!=200) return; config.loadFileConfig.json=JSON.parse(this.responseText); config.loadFileConfig.cb(); } function loadFile(){ print('YY: '+config.loadFileConfig.url); let connRequest=new XMLHttpRequest(); connRequest.addEventListener("loadend",parseResponseXML); //function(e){parseResponseXML(e,config);}); connRequest.open("GET", config.loadFileConfig.url); connRequest.send(); } function getBasePath(){ let server=LABKEY.ActionURL.getBaseURL(); let basePath=server+"_webdav"; basePath+=LABKEY.ActionURL.getContainer(); return basePath; } function generateReviewSection(listName,id,callback){ //callback should be generateReviewSectionCB and it takes no arguments print("generateReviewSection"); //need base path config.loadFileConfig=new Object(); config.loadFileConfig.cb=callback; config.loadFileConfig.id=id; config.loadFileConfig.url=getBasePath()+'/@files/reportSetup/'+listName+'.json'; loadFile(); //load file and continue in the next function } function generateErrorMessage(id,listName,msg){ print('generateErrorMessage:'); let eid=listName+"_errorMsg"; let el=config.document.getElementById(eid); if (el===null){ el=config.document.createElement("p"); config.document.getElementById(id).appendChild(el); } el.innerHTML=msg; } function clearErrorMessage(listName){ let eid=listName+"_errorMsg"; let el=config.document.getElementById(eid); if (el===null) return; el.remove(); } function getParticipantCode(pid){ let selectRows=new Object(); selectRows.schemaName='lists'; //we should now which form we are in let mfId=config.formConfig.form['masterQuery']; selectRows.queryName=config.formConfig.queryMap[mfId]; //point to data container selectRows.containerPath=getContainer('data'); //selectRows.queryName='PET'; pid.afterId=setParticipantCode; selectRows.success=function(data){afterRegistration(pid,data);} selectRows.filterArray=[LABKEY.Filter.create("crfRef",getCRFref())]; LABKEY.Query.selectRows(selectRows); } function visitCodeFromVisitId(visitId){ if (visitId<0) return "NONE"; let project=LABKEY.ActionURL.getContainer(); print('visitCodeFromVisitId: '+project.search('retro')); if (project.search('retro')>-1) visitId-=1; return 'VISIT_'+visitId.toString(); } function replaceSlash(x){ return x.replace(/\//,'_'); } function setParticipantCode(pid){ let rows=pid.registration.rows; if (rows.length==1){ print('setParticipantCode: '+rows[0].participantCode+'/'+rows[0].visitId); let visitCode=visitCodeFromVisitId(rows[0].visitId); print('setParticipantCode: '+pid.participantId+'/'+visitCode); pid.participantCode=replaceSlash(pid.participantId); pid.visitCode=visitCode; } generateReviewSection2(pid); } function generateReviewSectionCB(){ let listName=config.loadFileConfig.listName; let id=config.loadFileConfig.id; clearErrorMessage(listName); let pid=new Object(); pid.participantCode="NONE"; pid.visitCode="NONE"; getParticipantCode(pid); print('Get participant code sent'); //involves database search, continue after callback } function getValueFromElement(id,defaultValue){ let e=config.document.getElementById(id); if (e!=null){ defaultValue=e.innerHTML; } return defaultValue; } function pickParticipantCodeFromPage(){ let pid=new Object(); pid.participantCode=getValueFromElement("participantCode","NIX-LJU-D2002-IRAE-A000"); pid.visitCode=getValueFromElement("visitCode","VISIT_1"); generateReviewSection2(pid); } function patternReplace(src,replacements,values){ for (rep in replacements){ let txt1=src.replace(new RegExp(rep),values[replacements[rep]]); src=txt1; } return src; } function plotImage(cell,k,row,rowVariable,obj,pid){ let baseDir=patternReplace(obj.imageDir,obj.replacements,pid); print('Base dir: '+pid.basePath); pid[obj.variable]=obj.values[k]; cell.id=pid[obj.variable]+"_"+rowVariable+pid[rowVariable]; let img=null; let imgId=cell.id+'_img_'; img=config.document.getElementById(imgId); if (img===null){ img=config.document.createElement('img'); img.id=imgId; cell.appendChild(img); } let imgSrc=patternReplace(obj.file,obj.replacements,pid); print('Image: '+imgSrc); let imagePath=pid.basePath+'/'+baseDir+'/'+imgSrc; img.src=imagePath; img.width="300"; } function showReport(cell,k,row,rowVariable,obj,pid){ cell.width="300px"; cell.id='report_'+obj.values[k]+"_"+rowVariable+pid[rowVariable]; let reportConfig=new Object(); reportConfig.partName="Report"; reportConfig.renderTo=cell.id; //reportConfig.showFrame=false; //reportConfig.width="300"; reportConfig.frame="none"; reportConfig.partConfig=new Object(); reportConfig.partConfig.width="300"; reportConfig.partConfig.title="R Report"; reportConfig.partConfig.reportName=obj.values[k]; for (f in obj.parameters){ reportConfig.partConfig[f]=pid[f]; } reportConfig.partConfig.showSection="myscatterplot"; let reportWebPartRenderer = new LABKEY.WebPart(reportConfig); print('Render to: '+reportConfig.renderTo); reportWebPartRenderer.render(); } function showProbability(cell,k,row,rowSetup,j,obj,pid){ print('showProbability: '+rowSetup); let rowVariable=rowSetup.variable; cell.id='prob_'+obj.values[k]+"_"+rowVariable+pid[rowVariable]; let probDensity=new Object(); probDensity.mean=rowSetup.mean[j]; probDensity.sigma=rowSetup.sigma[j]; print('showProbability: mean '+probDensity.mean+' sigma '+probDensity.sigma); probDensity.func=obj.values[k]; probDensity.organCode=pid.organCode; pid[obj.variable]=rowSetup[obj.variable][j]; probDensity.percentile=pid.percentile; let selectRows=new Object(); selectRows.queryName=obj.queryName; selectRows.schemaName="study"; selectRows.filterArray=[]; selectRows.containerPath=getContainer('data'); for (let f in obj.filters){ selectRows.filterArray.push( LABKEY.Filter.create(f,pid[obj.filters[f]])); print('Filter ['+f+']: '+pid[obj.filters[f]]); } selectRows.success=function(data){ drawProbability(data,cell,obj,pid,probDensity);} LABKEY.Query.selectRows(selectRows); } function erf(x){ let fx=[0,0.02,0.04,0.06,0.08,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9, 1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2, 2.1,2.2,2.3,2.4,2.5,3,3.5]; let fy=[0,0.222702589,0.328626759,0.428392355,0.520499878, 0.603856091,0.677801194,0.742100965,0.796908212, 0.842700793,0.880205070,0.910313978,0.934007945, 0.952285120,0.966105146,0.976348383, 0.983790459,0.989090502,0.992790429,0.995322265, 0.997020533,0.998137154,0.998856823,0.999311486, 0.999593048,0.999977910,0.999999257]; let n=32; let i0=n-1; for (let i=1;ifx[i]) continue; i0=i-1; break; } let fval=1; if (i0obj.intervals.zlimits[i]) continue; color=obj.intervals.colors[i-1]; break; } let fboxId=cell.id+'_fbox_'; let fbox=config.document.getElementById(fboxId); if (fbox===null){ fbox=config.document.createElement("div"); fbox.id=fboxId; cell.appendChild(fbox); } fbox.style.backgroundColor=color; fbox.style.width="180px"; fbox.style.height="180px"; print('organCode '+probDensity.organCode); let organName="Lung"; if (probDensity.organCode==4){ organName="Thyroid"; } if (probDensity.organCode==5){ organName="Bowel"; } setLine(fbox,'_fp4_',organName,"16px"); setLine(fbox,'_fp_',val.toPrecision(3),"25px"); setLine(fbox,'_fp1_',"SUV("+probDensity.percentile+"%)","16px"); setLine(fbox,'_fp2_',fzx.toPrecision(3),"25px"); setLine(fbox,'_fp3_',"z-value","16px"); } function generateReviewSection2(pid){ let listName=config.loadFileConfig.listName; let id=config.loadFileConfig.id; print('generateReviewSection2: '+pid.participantCode+'/'+ pid.visitCode); if (pid.participantCode=="NONE" || pid.visitCode=="NONE"){ generateErrorMessage(id,listName, "ParticipantId/visitId not set"); return; } print('JSON: '+config.loadFileConfig.json); let json=config.loadFileConfig.json; let nrows=json.rows.values.length; let ncol=json.columns.length; pid.basePath=getBasePath()+"/@files"; let el=config.document.getElementById(id); let tableId=id+'_Table'; let table=config.document.getElementById(tableId); if (table==null){ table=config.document.createElement('table'); table.id=tableId; el.appendChild(table); } table.style.tableLayout="fixed"; table.style.columnWidth="300px"; for (let i=0;i>>>>>>>>>>>>>end of reviewSection(REPORT) function generateReview(divReviewId,divReviewListId, listName){ let listId=config.formConfig.fields[listName].queryId; let debug=true; if (debug) print("Generate review for: "+listId); let reviewSetup=new Object(); reviewSetup.readonlyFlag=function(vName){ if (vName=="queryName") return true; if (vName=="queryname") return true; if (vName=="ModifiedBy") return true; return false;}; reviewSetup.addApply="Add Review"; let generateTableFlag=true; let formStatus=config.formConfig.formStatus; if (formStatus == "Approved" ){ delete reviewSetup.addApply; reviewSetup.readonlyFlag=function(vName){return false;} generateTableFlag=false; } reviewSetup.filters=new Object(); reviewSetup.filters["crfRef"]=getCRFref(); reviewSetup.filters["queryName"]=listId;//entry in reviewComments list is queryname, all in small caps //needs listName, in argument reviewSetup.getInputId=function(vName){return listName+"_add"+vName}; reviewSetup.divReviewListId=divReviewListId; if (debug) { let msg="Review: divId: "+divReviewId; msg+=" inputId: "+reviewSetup.getInputId; print(msg); } updateListDisplay(divReviewListId,"reviewComments",reviewSetup.filters,true); if (! generateTableFlag) return; generateTable("reviewComments",divReviewId,new Object(),reviewSetup); } //>>>>>>>>>>trigger visibility of additional lists function setListVisibility(setup,additionalData,readonlyFlag){ let debug=true; if (debug){ print("Set list visibility "); for (f in additionalData){ print("AdditionalData["+f+"]: "+additionalData[f]); } } let x = config.document.getElementById(additionalData.divName); if (debug) print("\n Div: "+x); x.style.display="none"; let eId=setup.getInputId(additionalData.showFlag); let e = config.document.getElementById(eId); let sText; if (readonlyFlag) sText=e.innerHTML; else sText=e.options[e.selectedIndex].text; if (debug) print("\n Selected option text: "+sText); if (sText == additionalData.showFlagValue){ let filters=new Object(); if ("filters" in additionalData) filters=additionalData.filters; x.style.display = "block"; updateListDisplay(additionalData.divQueryName, additionalData.queryName,filters,readonlyFlag); } } //>>have list refresh when data is added (not optimal yet) function updateListDisplay(divName,queryName,filters,readonlyFlag){ let debug=true; if (debug) print("UpdateListDisplay: Query - "+queryName +" div - "+divName); if (divName=="NONE") return; let crfRef=getCRFref(); let div=config.document.getElementById(divName); if (debug) print("Enabling display to queryName: "+queryName); var qconfig=new Object(); qconfig.renderTo=divName; //point to data container qconfig.containerPath=getContainer('data'); qconfig.schemaName='lists'; qconfig.queryName=queryName; qconfig.buttonBarPosition='top'; qconfig.filters=[]; for (f in filters){ qconfig.filters.push(LABKEY.Filter.create(f, filters[f])); } qconfig.success=updateSuccess; qconfig.failure=updateFailure; //show only print button if (readonlyFlag){ qconfig.buttonBar=new Object(); qconfig.buttonBar.items=["print"]; } LABKEY.QueryWebPart(qconfig); } function updateSuccess(data){ print("Update success"); } function updateFailure(data){ print("Update failed"); } //TODO: this should trigger a data refresh on section, ie populateData(field) function toggleVisibility(divName,buttonName){ let x = config.document.getElementById(divName); if (x.style.display === "none") { //exclude non data sections (like debug)... populateSection(divName); x.style.display = "block"; config.document.getElementById(buttonName).value="Hide"; } else { x.style.display = "none"; config.document.getElementById(buttonName).value="Show"; } } function generateButtonBU(divName,title,buttonName,callback, callbackParameters){ let debug=true; if (debug) print("generateButtonBU"); let tb=config.document.createElement('table'); tb.className="t2"; let r1=tb.insertRow(); th=config.document.createElement('th'); r1.appendChild(th); th.innerHTML=title; //*!* let c2=r1.insertCell(); let i1=config.document.createElement("input"); i1.type="button"; i1.value=buttonName; i1.style.fontSize="20px"; i1.onclick=function(){callback(callbackParameters);} c2.appendChild(i1); let c1=r1.insertCell(); c1.setAttribute("colspan","1"); c1.id=callbackParameters.submitReportId; let el=config.document.getElementById(divName); if (debug) print("generateButton: element["+divName+"]: "+el); el.appendChild(tb); } function generateButton(divName,title,buttonName,callback){ let debug=true; if (debug) print("generateButtonX"); let tb=config.document.createElement('table'); tb.className="t2"; let r1=tb.insertRow(); th=config.document.createElement('th'); r1.appendChild(th); th.innerHTML=title; //*!* let c2=r1.insertCell(); let i1=config.document.createElement("input"); i1.type="button"; i1.value=buttonName; i1.style.fontSize="20px"; i1.onclick=callback; c2.appendChild(i1); let c1=r1.insertCell(); c1.setAttribute("colspan","1"); c1.id=config.submitReportId; let el=config.document.getElementById(divName); if (debug) print("generateButton: element["+divName+"]: "+el); el.appendChild(tb); } //>>populate fields // // //split to field generation and field population // function addFieldRow(tb,field,setup,additionalData){ let vName=field.name; let vType=field.type; let isLookup=("lookup" in field); print("addFieldRow ["+vName+"/"+vType+'/'+isLookup+"]"); let row=tb.insertRow(); let cell=config.document.createElement('th'); row.appendChild(cell); let text = config.document.createTextNode(field.shortCaption); cell.appendChild(text); let cell1=row.insertCell(); let input=null; cell1.colSpan="3"; let readonlyFlag=setup.readonlyFlag(vName); //set the html input object while (1){ if (readonlyFlag){ input=config.document.createElement('label'); input.innerText='Loading'; break; } //lookup if (isLookup){ input = config.document.createElement("select"); break; } //date if (vType=="date"){ input = config.document.createElement("input"); input.type="date"; break; } //string if (vType=="string"){ //we have to make sure UNDEF is carried to below //since we are adapting file to either show //current file or allow user to select a file // //TODO change this so one can always select file //but also show the selected file if(vName.search("reviewComment")>-1){ input = config.document.createElement("textarea"); input.cols="65"; input.rows="5"; break; } input=config.document.createElement('input'); input.type="text"; if (vName.search('_file_')<0) break; cell1.setAttribute('colspan',"1"); let cell2=row.insertCell(); cell2.setAttribute('colspan',"2"); let input1=config.document.createElement('input'); input1.type="file"; input1.id=setup.getInputId(vName)+'_file_'; cell2.appendChild(input1); break; } if (vType=="float"){ input = config.document.createElement("input"); input.type="text"; break; } if (vType=="boolean"){ input = config.document.createElement("input"); input.type="checkbox"; print("Creating checkbox"); break; } break; } input.id=setup.getInputId(vName); cell1.appendChild(input); print('Adding element '+input.id); print('Listing element '+config.document.getElementById(input.id)); if (readonlyFlag) return; if (!isLookup) return; let lookup=field["lookup"]; //get all values from config.formConfig.lookup[X] let lObject=config.formConfig.lookup[lookup.queryName]; //debug print("Query: "+lookup.queryName); print("ElementId: "+input.id); print("No of options: " +lObject.LUT.length); print("Element: "+input); //set the lut value (input is text label) for readonly //clear existing fields from input for(let i = input.options.length; i >= 0; i--) { input.remove(i); } //create option -1 let opt = config.document.createElement("option"); opt.text = ""); //add other, label them with LUT for (let v in lObject.LUT) { print('populating '+v+': '+lObject.LUT[v]); let opt = config.document.createElement("option"); opt.text = lObject.LUT[v]; opt.value = v; input.options[input.options.length] = opt; } input.selectedIndex=0; //add watcher for showFlag field of additionalData if (!("showFlag" in additionalData)) return; print("\n Parsing additional data "); let expId=setup.getInputId(additionalData.showFlag); if (expId!=input.id) { print("Skipping fields not flagged as showFlag:"+expId+"/"+input.id); return; } print("Setting onChange to "+input.id); input.onchange=function(){setListVisibility(setup,additionalData,readonlyFlag)}; setListVisibility(setup,additionalData,readonlyFlag); } function populateFieldRow(entry,field,setup){ print('populateFieldRow ['+field.name+']: '+entry[field.name]+' ['+ setup.getInputId(field.name)+']'); let vName=field.name; let vType=field.type; let id=setup.getInputId(vName); let input=config.document.getElementById(id); let isLookup=("lookup" in field); let varValue="UNDEF"; //if (vName in setup.filters) varValue=setup.filters[vName]; if (vName in entry) varValue=entry[vName]; //if part of the filter, set it to value if (vName in setup.filters) varValue=setup.filters[vName]; //date if (vType=="date"){ if (varValue==="UNDEF") varValue=new Date(); else varValue=new Date(varValue); } //lookup for readonly if (isLookup && varValue!="UNDEF"){ let lookup=field["lookup"]; //get all values from config.formConfig.lookup[X] let lObject=config.formConfig.lookup[lookup.queryName]; varValue=lObject.LUT[varValue]; } print('Element: '+input); //figure out the element type let eType=input.nodeName.toLowerCase(); print('Element type: '+eType); //HTMLTextArea, createElement(textArea) if (eType==="textarea"){ input.value=varValue; return; } //Text, createTextNode if (eType==="#text"){ input.nodeValue=varValue; return; } //HTMLLabelElement, createElement('label') if (eType==="label"){ input.innerText=varValue; return; } //HTMLSelectElement, createElement('select') if (eType==="select"){ input.selectedIndex=0; for (let i=0;i0) entry=fQuery.rows[0]; let fields=fQuery.fields; for (f in fields){ let field=fields[f]; //each field is a new row print("Adding field: "+f+'/'+field.name); if (field.hidden) continue; if (field.name=="crfRef") continue; populateFieldRow(entry,field,setup); } } function generateTable(listName,divName,additionalData,setup){ let debug=true; if (debug) print("generateTable: "+listName); //assume data is set in config.formConfig.dataQueries[data.queryName].rows; let entry=new Object(); //data snapshot let fQuery=config.formConfig.dataQueries[listName]; //here I assume that listName was parsed during setDataLayout and setData //so that rows was set (even if they are empty) print("Nrows "+fQuery.rows.length); if (fQuery.rows.length>0) entry=fQuery.rows[0]; let tb=config.document.createElement('table'); tb.className="t2"; config.document.getElementById(divName).appendChild(tb); //this are the fields (probably constant) let fields=fQuery.fields; for (f in fields){ let field=fields[f]; //each field is a new row print("Adding field: "+f+'/'+field.name); if (field.hidden) continue; if (field.name=="crfRef") continue; addFieldRow(tb,field,setup,additionalData); populateFieldRow(entry,field,setup); } //add comment field if (!("addApply" in setup)) { print("populateTable: done"); return; } let row=tb.insertRow(); let th=config.document.createElement('th'); row.appendChild(th); th.innerHTML=setup.addApply; let cell=row.insertCell(); //cell.setAttribute("colspan","2"); let input=config.document.createElement("input"); input.type="button"; input.value=setup.addApply; cell.appendChild(input); let cell1=row.insertCell(); cell1.setAttribute("colspan","2"); cell1.id=setup.getInputId("rerviewLastSave"); cell1.innerHTML="No recent update"; //saveReview is a generic name for saving content of the html page to a list entry input.onclick=function(){saveReview(listName,cell1.id,setup)}; } function saveReview(queryName,elementId,setup){ //loads any queryName let debug=true; if (debug) print("saveReview: elementId "+elementId+" queryName "+queryName); let useInsert=false; if (!("unique" in setup)) useInsert=true; let fQuery=config.formConfig.dataQueries[queryName]; if (fQuery.rows.length==0) useInsert=true; let entry=new Object(); if (!useInsert){ entry=fQuery.rows[0]; } entry.crfRef=getCRFref(); if (debug) print("Set crfRef="+entry.crfRef); //if ("queryName" in setup.filters) { // entry.queryName=setup.filters["queryName"]; // if (debug) print("Setting queryName: "+entry.queryName); //} let fields=fQuery.fields; for (f in fields){ let field=fields[f]; if (debug) print("saveReview field: "+field.name); if (field.hidden) continue; let vName=field.name; let vType=field.type; if (debug) print("vType: "+vType); if (vName=="crfRef") continue; //need to save queryName for reviewComments let eId=setup.getInputId(vName); let el=config.document.getElementById(eId); if (!el) { if (debug) print("saveReview element: "+eId+" not found"); continue; } if (debug) print("saveReview element: "+eId); let eType=el.nodeName.toLowerCase(); if (eType==="select"){ entry[vName]=el.options[el.selectedIndex].value; continue; } if (eType==="td"){ entry[vName]=el.innerText; continue; } if (vType=="date"){ let date=el.valueAsDate; if (date==="null") continue; date.setUTCHours(12); entry[vName]=date.toString(); print("Setting date to "+entry[vName]); continue; } if (vType=="string"){ entry[vName]=el.value; if (vName.search('_file_')<0) continue; //upload file let id1=eId+'_file_'; let input1=config.document.getElementById(id1); print('Attachment field: '+input1.value); //entry[vName]=el.files[0].stream(); let ctx=new Object(); ctx['dirName']='consent'; ctx['ID']=entry['crfRef']; //should point to data container ctx['project']=getContainer('data'); //need ID->crf! //assume crfRef will get set before this //element is encountered uploadFile(input1,ctx); let fv=el.value; let suf=fv.split('.').pop(); entry[vName]=entry['crfRef']+'.'+suf; continue; } if (vType=="float" || vType=="int"){ entry[vName]=el.value; if (vName=="queryName") { print('Parsing queryName: '+el.innerText); entry[vName]=config.formConfig.fields[el.innerText].queryId; //use queryMap lookup } continue; } if (vType=="boolean"){ entry[vName]=el.checked; continue; } } let qconfig=new Object(); qconfig.rows=[entry]; //should point to data container qconfig.containerPath=getContainer('data'); qconfig.schemaName='lists'; qconfig.queryName=queryName; //only update comments print("modifyRows: useInsert "+useInsert); qconfig.success=function(data){updateLastSavedFlag(data,setup,elementId)}; if (!useInsert){ LABKEY.Query.updateRows(qconfig); } else{ LABKEY.Query.insertRows(qconfig); } } function updateLastSavedFlag(data,setup,elementId){ let debug=true; if (debug) print("Update last saved flag to "+elementId); let el=config.document.getElementById(elementId); let dt=new Date(); el.innerHTML="Last saved "+dt.toString(); if (data.queryName=="reviewComments"){ updateListDisplay(setup.divReviewListId,"reviewComments",setup.filters,true); } //refresh stored data! if ("unique" in setup) setData(function (){populateTable(data.queryName);}); } //******************************************upload to database ********************* function onDatabaseUpload(){ print("Database upload"); configUpload=new Object(); //figure out the participantId let qconfig=new Object(); qconfig.schemaName="lists"; //redirect to configuration container qconfig.containerPath=getContainer('config'); qconfig.queryName="Forms"; qconfig.filterArray=[LABKEY.Filter.create('Key',config.formId)]; //qconfig.filterArray=[LABKEY.Filter.create('formStatus',1)] qconfig.success=function(data){afterForms(configUpload,data)}; LABKEY.Query.selectRows(qconfig); } function afterForms(configUpload,data){ print('afterForms'); let formEntry=data.rows[0]; configUpload.registrationQueryId=formEntry["masterQuery"]; configUpload.afterId=afterParticipantId; let qconfig=new Object(); qconfig.queryName=config.formConfig.queryMap[configUpload.registrationQueryId]; //queryMap holds mapping for queries in visit; //masterQuery should be one of them, so this is safe. qconfig.schemaName='lists'; qconfig.containerPath=getContainer('data'); qconfig.filterArray=[LABKEY.Filter.create('crfRef',getCRFref())]; qconfig.success=function(data){afterRegistration(configUpload,data);} LABKEY.Query.selectRows(qconfig); //waitForCompleteUpload(config);// } function afterRegistration(configUpload,data){ print("afterRegistration: rows:"+data.rows.length); configUpload.registration=data; let registrationData=configUpload.registration; clearErr(); if (registrationData.rows.length!=1){ let msg="ERROR: Found "+registrationData.rows.length; msg+=" registration entries for crfrefid "+getCRFref()+" in "+data.queryName; print(msg); configUpload.afterId(configUpload); return; } print('registration participant field: '+config.registrationParticipantIdField); configUpload.participantId= registrationData.rows[0][config.registrationParticipantIdField]; //could be a lookup field print('ID: '+configUpload.participantId); let fields=registrationData.metaData.fields; let field="NONE"; for (f in fields){ if (fields[f]["name"]==config.registrationParticipantIdField) field=fields[f]; } if ("lookup" in field){ let pid=configUpload.participantId; print("Using lookup for participantId: "+pid); let lookup=field["lookup"]; print("Lookup: ["+lookup.schemaName+','+lookup.queryName+']'); let qconfig=new Object(); //should point to data container qconfig.containerPath=getContainer('data'); qconfig.schemaName=lookup.schemaName; qconfig.queryName=lookup.queryName; qconfig.filterArray= [LABKEY.Filter.create(lookup.keyColumn,pid)]; qconfig.success=function(data){ afterRegistrationLookup(configUpload,data,lookup.displayColumn)}; LABKEY.Query.selectRows(qconfig); } else{ //afterParticipantId(configUpload); configUpload.afterId(configUpload); } } function afterRegistrationLookup(configUpload,data,displayColumn){ print("afterRegistrationLookup"); let entry=data.rows[0]; configUpload.participantId=entry[displayColumn]; print('Setting to '+configUpload.participantId); configUpload.afterId(configUpload); //afterParticipantId(configUpload); } function afterParticipantId(configUpload){ print("Setting participantId to "+configUpload.participantId); //another select rows to update all queries from setup //just use registration for test let qconfig=new Object(); qconfig.schemaName='lists'; //qconfig.queryName=config.setupQueryName; qconfig.queryName="FormSetup"; qconfig.containerPath=getContainer('config'); qconfig.filterArray=[LABKEY.Filter.create("formName",config.formId)]; qconfig.success=function(data){afterSetup(configUpload,data);} LABKEY.Query.selectRows(qconfig); } function afterSetup(configUpload,data){ configUpload.queries=new Array(); for (let i=0;i0) { let qconfig=new Object(); qconfig.queryName=queryName; qconfig.schemaName="study"; qconfig.rows=studyRows; qconfig.containerPath=getContainer('data'); qconfig.success=function(data){afterStudyUpload(configUpload,data);} LABKEY.Query.updateRows(qconfig) } else{ let data=new Object(); data.rows=new Array(); afterStudyUpload(configUpload,data); } } function afterStudyUpload(configUpload,data){ //let participantField=config.participantField; let participantField=config.formConfig.studyData["SubjectColumnName"]; let dataObject=configUpload.queries[configUpload.queryId]; let queryName=dataObject.queryName; printErr("Updated "+data.rows.length+" rows to "+queryName); let studyRows=dataObject.studyData.rows; let listRows=dataObject.listData.rows; let rows=new Array(); //also updating existing rows, if they exist for (let i=studyRows.length;i1){ entry.SequenceNum+=i/100; } delete entry.Key; print( "Adding sequence number "+entry.SequenceNum + " key " + entry.Key); for (q in entry){ print("\t["+q+"]: "+entry[q]); } //remove Key as it might interferre with preset values rows.push(entry); } if (rows.length>0){ let qconfig=new Object(); qconfig.queryName=queryName; qconfig.schemaName="study"; qconfig.containerPath=getContainer('data'); qconfig.success=function(data){ afterListUpload(configUpload,data)}; qconfig.failure=doFailure; qconfig.rows=rows; LABKEY.Query.insertRows(qconfig); } else{ let data=new Object(); data.rows=rows; afterListUpload(configUpload,data); } } function afterListUpload(configUpload,data){ let queryName=configUpload.queries[configUpload.queryId].queryName; printErr("Inserted "+data.rows.length+" rows to "+queryName); configUpload.queries[configUpload.queryId].queryStatus="DONE"; configUpload.queryId+=1; copyToDataset(configUpload); } //*************************update for further review ************************* function onUpdateForReview(){ updateFlag(4);//Pending review } function updateFlag(flag){ if (flag==4) print("Sent to further review"); let qconfig=new Object(); qconfig.schemaName='lists'; qconfig.queryName='crfEntry'; qconfig.containerPath=getContainer('data'); qconfig.success=function(data){setFlag(data,flag);} qconfig.filterArray=[LABKEY.Filter.create("entryId",getCRFref())]; LABKEY.Query.selectRows(qconfig); } function setFlag(data,flag){ let debug=true; if (flag==4) print("setFlagToReview"); if (data.rows.length!=1){ let msg="ERROR: Found "+data.rows.length; msg+=" entries for crfrefid "+getCRFref(); print(msg); return; } let entry=data.rows[0]; entry.FormStatus=flag;//Pending Review if (debug) print("Set form status to "+entry.FormStatus); let qconfig=new Object(); qconfig.schemaName='lists'; qconfig.queryName='crfEntry'; qconfig.containerPath=getContainer('data'); qconfig.rows=[entry]; qconfig.success=function(data){completeWithFlag(data,flag);} LABKEY.Query.updateRows(qconfig); } function completeWithFlag(data,flag){ let debug=true; if (debug){ if (flag==4) print("complete with review"); } redirect(); } //************************************************ submit ******************************************* function onSubmit(){ let debug=true; hideErr(); clearErr(); printErr("onSubmit"); //the idea of check form is to provide a methodology for form validation //it consists of two parts - a routine that initiates the check, and a //polling watchdog that waits for its execution. //Non-linear approach follows the natural javascript pathway. checkForm(); //watchdog with callback that gets executed when checkForm is done let cb=new Object(); cb.success=finalValidation; cb.failure=function(config){printErr("waitForCheckForm failed")}; waitForCheckForm(cb); } function finalValidation(){ let debug=true; if (debug) print("validate"); let completed=true; for (f in config.fields){ let field=config.fields[f]; if (field.status!="DONE") { printErr("Missing entry for "+field.title); completed=false; } } if (debug) print("valid: "+completed); if (completed){ modifyCRFEntry(); } else{ let el=document.getElementById(config.submitReportId); el.innerHTML="Form invalid"; } } function modifyCRFEntry(){ printErr("Form valid"); let c1=new Object(); c1.schemaName='lists'; c1.queryName='crfEntry'; c1.containerPath=getContainer('data'); c1.filterArray=[LABKEY.Filter.create('entryId',getCRFref())]; c1.success=changeCRFStatus; LABKEY.Query.selectRows(c1); } function changeCRFStatus(data){ let entry=data.rows[0]; entry.formStatus=2;//Submitted let el=document.getElementById(config.submitReportId); el.innerHTML="Submitting form"; let c1=new Object(); c1.schemaName=data.schemaName; c1.queryName=data.queryName; //should point to data container c1.containerPath=getContainer('data'); c1.rows=[entry]; //close window upon success c1.success=sendEmail; LABKEY.Query.updateRows(c1); } function sendEmail(data){ let st=config.formConfig.settings; let cvar='sendEmail'; if (cvar in st){ print(cvar+' set to '+st[cvar]); if (st[cvar]=='FALSE'){ print('Skipping sending emails'); redirect(); return; } } print('send email'+data.rows.length); let crf=data.rows[0]['entryId']; let formId=data.rows[0]['Form']; let link=LABKEY.ActionURL.getBaseURL(); link+=LABKEY.ActionURL.getContainer(); link+='/crf-visit.view?'; link+='entryId='+crf; link+='&formId='+formId; //debug let recipients=new Array(); let typeTo=LABKEY.Message.recipientType.to; //from crfManagers list let as=LABKEY.Message.createRecipient(typeTo,'andrej.studen@ijs.si'); recipients.push(as); let typeHtml=LABKEY.Message.msgType.html; let typePlain=LABKEY.Message.msgType.plain; let msg=LABKEY.Message.createMsgContent(typeHtml,'

Test

'); let msg1=LABKEY.Message.createMsgContent(typePlain,link); LABKEY.Message.sendMessage({ msgFrom:'labkey@fmf.uni-lj.si', msgSubject:'Form submitted', msgRecipients:recipients, msgContent:[msg1], success: redirect }); } function hideErr(){ let el=config.document.getElementById("errorDiv"); el.style.display="none"; } function clearErr(){ let el=config.document.getElementById("errorTxt"); el.value=""; } function showErr(){ let el=config.document.getElementById("errorDiv"); el.style.display="block"; } function printErr(msg){ showErr(); el=config.document.getElementById("errorTxt"); el.style.color="red"; el.value+="\n"+msg; } //validation worker function checkForm(){ let debug=true; let crfRef=getCRFref() config.status="UNKNOWN"; //fields are queries that are part of the form //this allows validation to be performed on all //queries that are part of the form for (f in config.formConfig.fields){ let field=config.formConfig.fields[f]; field.status="UNKNOWN"; if (debug) print("Setting status for "+f+" to "+ field.status); let selectRows=new Object(); //points to data container selectRows.containerPath=getContainer('data'); selectRows.schemaName="lists"; selectRows.queryName=f; //select only entry related to present form selectRows.filterArray=[LABKEY.Filter.create('crfRef',crfRef)]; selectRows.success=checkData; selectRows.failure=function(errorObj){print("checkData failed.")}; LABKEY.Query.selectRows(selectRows); } } function waitForCheckForm(cb){ //watchdog - a timeout function //if control reaches the end of the function, it gets re-executed after the timeout //the body provides two alternative routes that end with return, //one for failure (if timeout gets executed too often) //one for success, which looks at the config object, which is in parallel //modified by the checkForm and as a daughter process, checkData functions let debug=true; //initalize counter if (!("i" in config)) config.i=0; //report execution depth if (debug) print("["+config.i+"] checkForm status "+config.status); //failure route if (config.i>100) { if (debug) print("executing failure"); cb.failure(config); return; } //success route; count on other functions to modify config.status //this is very non-obviuos for linear programing, but //checkData and waitForCheckForm share the same memory space if (config.status=="DONE") { if (debug) print("executing success"); cb.success(config); if (debug) print("success executed"); return; } //this is the repeat route, neither pathway was indicated, stay in loop config.i+=1; setTimeout(function(){waitForCheckForm(cb);},1000); } function checkData(data){ //insulated worker. Data corresponds to entry of a particular query let debug=true; if (debug) print("checkData "); let field=config.formConfig.fields[data.queryName]; field.status="NONE"; if (debug) print("Setting status for "+data.queryName+" to "+ field.status); //this is the only test implemented - we should have at least one row in each of the queries //we could also have a more targeted functions, but their form escapes me at the moment if (data.rows.length>0) field.status="DONE"; if (debug) print("checkData set status for "+data.queryName+" to "+field.status); //here we generate the flag/bell for the watchdog for (f in config.fields){ let subField=config.fields[f]; if (debug) print("checkData status["+f+"]: "+subField.status); //UNKNOW means we haven't visited it yet if (subField.status=="UNKNOWN") { if (debug) print("\t Status for "+f+" not set ["+ subField.status+"]"); //we opt out - clearly, unchecked fields are still present return; } } //since we didn't exit for any field, we are done, and can set watchdog flag config.status="DONE"; } //************************************************** // function onRemoveCRF(){ let debug=true; if (debug){ print("Removing CRF"); } let selectRows=new Object(); //points to data container selectRows.containerPath=getContainer('data'); selectRows.schemaName="lists"; selectRows.queryName="inputLists"; selectRows.success=afterInputLists; LABKEY.Query.selectRows(selectRows); } function afterInputLists(data){ let debug=true; if (debug) print("After input lists"); config.inputLists=data; config.inputListsIterator=0; removeCRFLoop(); } function removeCRFLoop(){ let debug=true; let i=config.inputListsIterator; if (debug) print("removeCRFLoop ["+i+"/"+config.inputLists.rows.length+"]"); if (i>config.inputLists.rows.length){ if (0) return; redirect(); } let queryName="crfEntry"; let idVar="entryId"; if (i100){ clearInterval(config.blobInterval); } } function printForm(){ config.doc=new PDFDocument(); //config.doc.end(); let stream = config.doc.pipe(blobStream()).on("finish",function(){ config.blob=stream.toBlob("application/pdf");}); print("BLob: "+config.blob); config.a = config.document.createElement("a"); config.document.body.appendChild(config.a); config.a.innerHTML="Download PDF"; config.a.style = "display: none"; config.count=0; //run until blob is set config.blobInterval=setInterval(checkBlob,1000); //pick data from crfForm list print("Printing form"); printHeader(); setData(formatPrintData); } function printHeader(){ config.doc.fontSize(25).text(config.formConfig.form['formName']); config.doc.moveDown(); let crfEntry=config.formConfig.crfEntry; let site=config.formConfig.site; let val=new Object(); let user=config.formConfig.user; val['A']={o:crfEntry,f:'EudraCTNumber',t:'Eudra CT Number'}; val['B']={o:crfEntry,f:'StudyCoordinator',t:'Study Coordinator'}; val['C']={o:crfEntry,f:'StudySponsor',t:'Study Sponsor'}; val['D']={o:site,f:'siteName',t:'Site'}; val['E']={o:site,f:'sitePhone',t:'Phone'}; val['F']={o:user,f:'DisplayName',t:'Investigator'}; for (let f in val){ print('Printing for '+f); let e=val[f]; let entry=new Object(); entry[f]=e.o[e.f]; printPDF(entry, {name:f,caption:e.t,type:'string'},null); } config.doc.moveDown(); } function formatPrintData(){ qS=config.formConfig.dataQueries; for (let q in qS){ print('Setting up '+q); let qData=qS[q]; print('Number of rows: '+qData.rows.length); if (qData.rows.length>0){ config.doc.fontSize(20).text(qData.title); } for (let i=0;i we must first establish link to the rigth crf entry selectRows.filterArray=[LABKEY.Filter.create('entryId',getCRFrefFirst())]; //store form related data to this object selectRows.success=afterCRFEntry; LABKEY.Query.selectRows(selectRows); } function afterCRFEntry(data){ config.formConfig.crfEntry=data.rows[0]; print("Setting crfEntry (x) to "+config.formConfig.crfEntry["entryId"]); let selectRows=new Object(); //this should point to configuration container selectRows.containerPath=getContainer('config'); selectRows.schemaName='lists'; selectRows.queryName='site'; let selectedSite=config.formConfig.crfEntry.Site; selectRows.filterArray= [ LABKEY.Filter.create('siteNumber',selectedSite)]; selectRows.success=afterSite; LABKEY.Query.selectRows(selectRows); } function afterSite(data){ print("afterSite"); config.formConfig.site=data.rows[0]; print("Setting site name to "+ config.formConfig.site["siteName"] +" phone: "+config.formConfig.site["sitePhone"]); let selectRows=new Object(); //this looks at local container selectRows.containerPath=getContainer('CRF'); selectRows.schemaName='core'; selectRows.queryName='Users'; selectRows.filterArray=[ LABKEY.Filter.create('siteNumber', config.formConfig.crfEntry.UserId)]; selectRows.success=afterUser; LABKEY.Query.selectRows(selectRows); } function afterUser(data){ config.formConfig.user=data.rows[0]; print("Setting user to "+config.formConfig.user["DisplayName"]); let selectRows=new Object(); //This is part of data, don't rely on configuration container selectRows.containerPath=getContainer('config'); selectRows.schemaName='study'; selectRows.queryName='Study'; selectRows.columns="SubjectColumnName"; selectRows.success=afterStudy; LABKEY.Query.selectRows(selectRows); } function afterStudy(data){ config.formConfig.studyData=data.rows[0]; print("XSetting participantField to "+ config.formConfig.studyData["SubjectColumnName"]); let selectRows=new Object(); //should point to configuration container selectRows.containerPath=getContainer('config'); selectRows.schemaName='lists'; selectRows.queryName='FormStatus'; selectRows.filterArray=[]; selectRows.success=afterFormStatus; LABKEY.Query.selectRows(selectRows); } function afterFormStatus(data){ print("afterFormStatus: "); config.formConfig.formStatusData=data; let qconfig=new Object(); qconfig.schemaName="lists"; qconfig.queryName="Forms"; //this should point to configuration container qconfig.containerPath=getContainer('config'); qconfig.filterArray=[LABKEY.Filter.create('Key',config.formId)]; //qconfig.filterArray=[LABKEY.Filter.create('formStatus',1)] qconfig.success=afterForms1; LABKEY.Query.selectRows(qconfig); } function afterForms1(data){ print("afterForms1: "); config.formConfig.form=data.rows[0]; let selectRows=new Object(); selectRows.containerPath=getContainer('config'); selectRows.schemaName='lists'; selectRows.queryName='FormSetup'; selectRows.filterArray=[LABKEY.Filter.create('formName',config.formId)]; selectRows.success=afterFormSetup; LABKEY.Query.selectRows(selectRows); } function afterFormSetup(data){ config.formConfig.formSetup=data; print("Number of datasets for form ["+config.formId+"]: "+ config.formConfig.formSetup.rows.length); let fields=config.formConfig.formSetup.metaData.fields; //get the lookup for queryName column let formQueryName='queryName'; let field="NONE"; for (f in fields){ if (fields[f]['name']!=formQueryName) continue; field=fields[f]; break; } let lookup=field.lookup; print("Getting dataset names from "+lookup.queryName); let selectRows=new Object(); //inputLists should be in configuration container selectRows.containerPath=getContainer('config'); selectRows.schemaName=lookup.schemaName; selectRows.queryName=lookup.queryName; selectRows.success=afterFormDatasets; LABKEY.Query.selectRows(selectRows); } function afterFormDatasets(data){ print('afterFormDatasets: '+data.rows.length); config.formConfig.formDatasets=data;//inputLists config.formConfig.fields=new Object(); config.formConfig.queryMap=new Object(); let rows=config.formConfig.formSetup.rows; //should skip report only rows for (let i=0;i>>>>>>>>>>>>>>>>new>>>>>>>>>>>> function setDataLayout(cb){ let rowsSetup=config.formConfig.formSetup.rows; config.formConfig.dataQueries=new Object(); let dS=config.formConfig.dataQueries;//reference only let qMap=config.formConfig.queryMap; config.formConfig.lookup=new Object(); for (let i=0;i0){ let file=inputElement.files[0]; print('uploadFile: '+inputElement.value+'/'+file.size); } let url=LABKEY.ActionURL.getBaseURL(); url+='_webdav'; url+=LABKEY.ActionURL.getContainer(); url+='/@files'; url+='/'+context['dirName']; print('uploadFile url: '+url); let uploadConfig=new Object(); uploadConfig.inputElement=inputElement; uploadConfig.context=context; uploadConfig.url=url; uploadConfig.success=afterBaseDir; uploadConfig.failure=tryMakeDir; webdavCheck(uploadConfig); } function afterBaseDir(cfg){ print('afterBaseDir'); cfg.url+='/'+cfg.context['ID']; cfg.success=afterIDDir; cfg.failure=tryMakeDir; webdavCheck(cfg); } function afterIDDir(cfg){ print('afterIDDir'); if (cfg.inputElement.files.length==0){ print('No files found'); return; } let file=cfg.inputElement.files[0]; print('Uploading '+file.name); let suf=file.name.split('.').pop(); cfg.url+='/'+cfg.context['ID']+'.'+suf; cfg.success=afterUpload; cfg.failure=onFailure; cfg.data=file; webdavPut(cfg); } function afterUpload(cfg){ print('afterUpload'); } function tryMakeDir(cfg){ print('tryMakeDir '+cfg.url); cfg.failure=onFailure; webdavMakeDir(cfg); } function request(cfg,verb,data){ print('request['+verb+'] '+cfg.url); let connRequest=new XMLHttpRequest(); connRequest.addEventListener("loadend", function(){checkResponse(connRequest,cfg);}); connRequest.open(verb, cfg.url); connRequest.send(data); //print('request['+verb+'] sent'); } function checkResponse(xrq,cfg){ //print('checkResponse: readyState '+xrq.readyState); //print('checkResponse: status '+xrq.status); if (xrq.status<400) { //client errors 400-499 //server errors 500-599 cfg.success(cfg); return; } cfg.status=xrq.status; cfg.failure(cfg); } function webdavMakeDir(cfg){ request(cfg,'MKCOL',null);} function webdavCheck(cfg) { request(cfg,'GET',null);} function webdavPut(cfg) { request(cfg,'PUT',cfg.data);} function onFailure(cfg){ print('request failed with status='+cfg.status); }