diff --git a/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_QryTool.js b/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_QryTool.js index c50bc9266..fac996c08 100644 --- a/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_QryTool.js +++ b/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_QryTool.js @@ -1,775 +1,784 @@ i2b2.MedCo.QT = {}; var pieResultParam = { "header": { "title": { "font": "cinzel", "text": "#patients", "color": "#000000", "fontSize": 24 }, "location": "pie-center", }, "size": { "canvasHeight": 400, "canvasWidth": 600, "pieInnerRadius": "59%", "pieOuterRadius": "63%" }, "data": { "sortOrder": "label-desc", "content": {} }, "labels": { "outer": { "format": "label-value2", "pieDistance": 20 }, "inner": { "format": "none" }, "mainLabel": { "font": "cinzel", "fontSize": 15 }, "percentage": { "font": "cinzel", "color": "#999999", "fontSize": 8, "decimalPlaces": 0 }, "value": { "font": "cinzel", "color": "#000000", "fontSize": 15, "fontWeight": 600 }, "lines": { "enabled": true, }, "truncation": { "enabled": true } }, "tooltips": { "enabled": true, "type": "placeholder", "string": "{label}: {value}, {percentage}%", "styles": { "fontSize": 15, "backgroundOpacity": 0.70, "borderRadius": 15, "padding": 7 } }, "effects": { "load": { "speed": 500 }, "pullOutSegmentOnClick": { "effect": "none", }, "highlightLuminosity": 0.3 }, "misc": { "colors": { "segmentStroke": "#000000" }, "pieCenterOffset": { "y": -50 } } } var currId = 0; // used to give a unique id to the panels function getPanelIndex(panel){ while (panel.className != 'MedCo-QueryPanel'){ if (!panel.parentElement){ return -1 } panel = panel.parentElement } var index = Array.prototype.indexOf.call(panel.parentNode.children, panel); return index } function htmlToElement(html) { var template = document.createElement('template'); html = html.trim(); // Never return a text node of whitespace as the result template.innerHTML = html; return template.content.firstChild; } i2b2.MedCo.QT.ctrlr = {}; i2b2.MedCo.QT.ctrlr.init = function(){ // Initialization of the query tool. var num_panels = 3; // start with 3 panels i2b2.MedCo.QT.panels = []; for (var i=0; i < num_panels; i++) { i2b2.MedCo.QT.ctrlr.appendPanel() } // i2b2.MedCo.QT.query.init(); }; i2b2.MedCo.QT.ctrlr.clear = function (){ // todo and ignore a possible query result? // clear all the panels var panels = i2b2.MedCo.QT.panels; for(var i = 0; i < panels.length; i++){ panels[i].ctrlr.clear() } }; i2b2.MedCo.QT.timer = { // number of milliseconds between two updates step: 100, // store the start time (in milliseconds) startTime: 0, //store the pointer to the interval object so to stop it timeinterval: null, start: function () { // hide the gif and start document.getElementById("medcoTimer").style.display = 'inline-block'; document.getElementById("MedCo-loadingGif").style.display = 'none'; var context = i2b2.MedCo.QT.timer; context.clear(); context.startTime = new Date().getTime(); context.timeinterval = setInterval(context.update, context.step); }, update: function () { var now = new Date().getTime(); document.querySelector("#medcoTimer").innerHTML = ((now-i2b2.MedCo.QT.timer.startTime)/1000).toFixed(2); }, stop: function () { clearInterval(i2b2.MedCo.QT.timer.timeinterval); i2b2.MedCo.QT.timer.timeinterval = null; }, clear: function(){ i2b2.MedCo.QT.timer.stop(); document.querySelector("#medcoTimer").innerHTML = 0; }, wait: function(){ // show the gif document.getElementById("medcoTimer").style.display = 'none'; document.getElementById("MedCo-loadingGif").style.display = 'inline'; } }; i2b2.MedCo.QT.query = { // timeout used to wait until we received all the variant ids timeout: null, // You should call this function to run a query so to make sure you received and encrypted all // the annotation ids you queried waitThenRun: function(qryName, qryType){ // - qryName: name of the query // - qryType: query type. Must be a string in {"PATIENT_COUNT_XML", "PATIENTSET"} // - budget: selected budget // todo delete (not used), or add obfuscated query count // alert(qryName + " " + qryType); // return; // todo delete // clear any previous timeout (in case the user keeps pressing "run query" we want to send only one query) clearTimeout(i2b2.MedCo.QT.query.timeout); // clear the timer in any case i2b2.MedCo.QT.timer.clear(); // show that we are preparing to send the query i2b2.MedCo.QT.timer.wait(); // wait until you receive all the variants you requested and all of them have been encrypted if (i2b2.MedCo.ctrlr.pendingVariantsQueries != 0 || !i2b2.MedCo.ctrlr.background.encryptionDone()) { // todo change names // try again later to run the query i2b2.MedCo.QT.query.timeout = setTimeout(i2b2.MedCo.QT.query.waitThenRun, 500); return } // the query can be build and sent i2b2.MedCo.QT.query.run(qryName, qryType); }, // build and send the query run: function(qryName, qryType){ // start the timer i2b2.MedCo.QT.timer.start(); // callback processor to run the query from definition this.MedCocallback = new i2b2_scopedCallback(); this.MedCocallback.scope = this; this.MedCocallback.callback = function(results) { // // "results" object contains the following attributes: // // refXML: xmlDomObject <--- for data processing // // msgRequest: xml (string) // // msgResponse: xml (string) // // error: boolean // // errorStatus: string [only with error=true] // // errorMsg: string [only with error=true] // stop the timer and the gif i2b2.MedCo.QT.timer.stop(); try{ if (results.error) { alert(results.errorMsg); return; } else { // extract and show the patient counts i2b2.MedCo.QT.query.showResponse(results, "MedCo-QueryResults"); } } catch(e){ alert("Error when extracting the results of the query: " + e.message) } }; // // // // // todo: delete. Shrine changes and many other field setting them with default values // for testing we decided to put the query type in the query name field and parse it on the server side qryName += "|" + qryType; this.MedCoparams = {}; this.MedCoparams.psm_query_definition = i2b2.MedCo.QT.query.buildQueryDefinition(qryName); this.MedCoparams.psm_result_output = "\n\t\t\n\t"; // this.MedCoparams.psm_result_output = "\n\n"; i2b2.CRC.ajax.runQueryInstance_fromQueryDefinition("PLUGIN:MedCo", this.MedCoparams, this.MedCocallback); }, showResponse: function(response, resultDiv){ // The response fo each Hospital is expected to contain: // - result_type: indicates the type of result, is either one of // "agg_result": each hospital returned the whole encrypted count // "total": each hospital returned its encrypted count // "patient_set": each hospital returned its set of patients // - result: the corresponding result // note: the result_type field is the same for every hospital response = response.refXML.getElementsByTagName('query_result_instance'); // first extract the results var institutions = new Array(response.length); var result_type; for (var i = 0; i < response.length; i++) { try { var currInstitution = JSON.parse(i2b2.h.getXNodeVal(response[i], 'description')); var instName = Object.keys(currInstitution)[0]; result_type = currInstitution[instName]["result_type"] institutions[i] = { label: instName, result: currInstitution[instName]["result"], }; if (result_type == "agg_result" || result_type == "total") { // agg_result and total indicates the the presence of an encrypted result i2b2.MedCo.ctrlr.background.toBeDecrypted([institutions[i].result]); } if (result_type == "total"){ // If the query type is "total" then the three hospitals returned three equal values // break; // todo } } catch (e) { alert("Error in extracting the response." + i2b2.h.getXNodeVal(response[i], 'summary')); return } } // todo show properly the different results var resultPanel = document.getElementById(resultDiv) while (resultPanel.firstChild) { resultPanel.removeChild(resultPanel.firstChild); } switch(result_type) { case "agg_result": // wait until all ciphertexts are decrypted and show the pie var checkThenShow = function(){ if (i2b2.MedCo.ctrlr.background.decryptionDone()) { var totPatientCount = 0; for (var i = 0; i < institutions.length; i++){ institutions[i].value = i2b2.MedCo.ctrlr.background.decryptionCache[institutions[i].result]; totPatientCount += institutions[i].value; } // if there are no patients that match the query then do not display the pie chart if (totPatientCount == 0) { resultPanel.innerHTML = "No patient matches the query"; return } pieResultParam.data.content = institutions; new d3pie(resultDiv, pieResultParam); } else{ setTimeout(checkThenShow, 100) } }; checkThenShow(); break; case "total": // wait fot all the ciphertext to get decrypted and show the total number of patients var checkThenShow = function(){ if (i2b2.MedCo.ctrlr.background.decryptionDone()) { var totPatientCount = i2b2.MedCo.ctrlr.background.decryptionCache[institutions[0].result]; document.getElementById(resultDiv).appendChild(htmlToElement( "" + "" + "" + ""+ "" + ""+ ""+ "" + "" + "" + "" + "" + "
Number of patients
" + totPatientCount + "
" + // "For Query mild dementia@10:21:25" + "
" ) ) } else{ setTimeout(checkThenShow, 100) } }; checkThenShow(); break; case "patient_set": var res = document.getElementById(resultDiv); res.innerHTML = ""; for (var i = 0; i < institutions.length; i++){ institutions[i].value = i2b2.MedCo.ctrlr.background.decryptionCache[institutions[i].result]; res.innerHTML += institutions[i].label + ": " + institutions[i].result + "
" } break; } }, defaultQryName: function(){ var d = new Date(); var time = d.getHours().toString() + ":" + d.getMinutes().toString() + ":" + d.getSeconds().toString(); // + ":" + d.getMilliseconds().toString(); return "MedCo_query" + "@" + time; }, // build the xml of the query from i2b2.MedCo.QT.panels buildQueryDefinition: function(qryName){ // name: name for the query // get the time of the query (we concatenate the time to the name of the query) // var d = new Date(); // var time = // d.getHours().toString() + ":" + // d.getMinutes().toString() + ":" + // d.getSeconds().toString() + ":" + // d.getMilliseconds().toString(); // var rand = Math.random()+"" var queryxml = "\n\t" + qryName + "\n\tANY\n\t0"; var panels = i2b2.MedCo.QT.panels; for (var i = 0 ; i < panels.length; i++){ // there are 3 panels if (panels[i].ctrlr.isEmpty()) {continue} // if in the panel there are no parameters the go to the next panel // open a panel object in which we put all its concepts queryxml += "\n\t\n\t\t" + "" + i + "\n\t\t" + "100\n\t\t" + "" + (panels[i].model.exclude ? 1 : 0) + "\n\t\t" + "ANY\n\t\t" + "1\n\t\t"; // put the all the concepts in the query for (var j = 0; j < panels[i].model.content.length; j++){ // take one "row" at a time and take encrypted values if sensitive var item_keys = []; var row = panels[i].model.content[j]; var dateConstraint = panels[i].model.date.model; if (row.sensitive){ // the row contains sensitive values for (var z=0; z" + item_keys[z] + "\n\t\t" if (dateConstraint.dateFrom || dateConstraint.dateTo){ queryxml += "\t" + (dateConstraint.dateFrom ? "\n\t\t\t\t" + dateConstraint.dateFrom.Year + "-" + dateConstraint.dateFrom.Month + "-" + dateConstraint.dateFrom.Day + "T00:00:00.000-05:00'\n\t\t\t" : "") + (dateConstraint.dateTo ? "\n\t\t\t\t" + dateConstraint.dateTo.Year + "-" + dateConstraint.dateTo.Month + "-" + dateConstraint.dateTo.Day + "T00:00:00.000-05:00'\n\t\t\t\t" : "") + "\n\t\t" } queryxml += "\n"; } } queryxml +="\t\n" } queryxml += "\t"; return queryxml }, }; i2b2.MedCo.QT.ctrlr.appendPanel = function(){ // Each element (row) visualized on a panel is bound either to a concept (which can // be either sensitive or non-sensitive) or to a list of variant ids (always sensitive). // create a new panel, add it to the list of panels and bind to it model, view and controller var panel = {}; panel.view = {}; panel.ctrlr = {}; panel.model = {}; i2b2.MedCo.QT.panels.push(panel); // ------ initialize the model of the panel ------ panel.model = { // indicates whether the panel is excluded or not "exclude": false, // contains a list of {"sensitive": bool, "values"=[ list of concepts/variant ids bound to i-th row of the panel ]} "content": [], // date constraint model and controller (to show the popup) "date": {}, // id of the whole html panel "panelId": 'MedCo-QueryPanel' + currId, // id of the html panel containing dropped concepts "conceptPanelId": "MedCo-QueryPanelConcepts" + currId }; currId++; // bind the panel to a date constraint model i2b2.MedCo.QT.dateConstraint.ctrlr.init(panel) // ------ initialize the view of the panel ------ document.getElementById("MedCo-QueryPanels").appendChild(htmlToElement( "
" + "
" + "
" + "" + "Clear" + "" + "
"+ "
Group " + i2b2.MedCo.QT.panels.length + "
" + "
" + "
" + "
" + "Dates"+ "
" + "
" + // "
" + "
" + "Exclude" + "
" + "
" + "
" + "
" + "
")); // // create and add the div for the dropped concepts i2b2.sdx.Master.AttachType(panel.model.conceptPanelId , 'CONCPT', {dropTarget: true}); i2b2.sdx.Master.setHandlerCustom(panel.model.conceptPanelId, 'CONCPT', 'DropHandler', function (sdxData) {panel.ctrlr.doDrop(sdxData);}); /* Instantiate a ContextMenu for this panel*/ YAHOO.util.Event.onContentReady(panel.model.conceptPanelId, function () { var conceptContextMenu = new YAHOO.widget.ContextMenu( panel.model.conceptPanelId+"ContextMenu", { trigger: document.getElementById(panel.model.conceptPanelId).childNodes, itemdata: ["Delete"], lazyload: true } ); // whenever the content of the panel changes, update the trigger property $(panel.model.conceptPanelId).on("DOMSubtreeModified",function(){ conceptContextMenu.cfg.setProperty("trigger", document.getElementById(panel.model.conceptPanelId).childNodes); }); function onContextMenuClick(p_sType, p_aArgs) { //p_aArgs[1]: MenuItem instance that was the target of the "click" event. var oItem = p_aArgs[1], // The MenuItem that was clicked oTarget = this.contextEventTarget, oLI; if (oItem) { // todo: also verify the class (sdxDefaultCONCPT)? oLI = oTarget.nodeName.toUpperCase() == "DIV" ? oTarget : YAHOO.util.Dom.getAncestorByClassName(oTarget, "sdxDefaultCONCPT"); switch (oItem.index) { case 0: // Delete // remove the concept from the view and from the model var panel_view = document.getElementById(panel.model.conceptPanelId) var index = Array.from(panel_view.children).indexOf(oLI) panel.model.content.splice(index, 1) panel_view.removeChild(oLI) break; // add here other cases if there are more menu items } } } // "render" event handler for the ewe context menu function onContextMenuRender(p_sType, p_aArgs) { // Add a "click" event handler to the ewe context menu this.subscribe("click", onContextMenuClick); } conceptContextMenu.subscribe("render", onContextMenuRender); // a bit rude bug fix... (hide menu when clicking on the panel) document.getElementById(panel.model.conceptPanelId).onclick = function () {conceptContextMenu.hide.call(conceptContextMenu)} }); panel.view.appendConcept = function (concept, output){ var conceptDiv = "
" + ((panel.model.exclude) ? "NOT" : "") + "" + concept + ((output)? (" (...)") : "") + "
"; // var panel.model.conceptPanelId = panel.view.getElementsByClassName('MedCo-QueryPanelConcepts')[0].id; var conceptPanel = document.getElementById(panel.model.conceptPanelId) conceptPanel.innerHTML += conceptDiv; return conceptPanel.lastChild }; panel.view.clear = function(){ panel.model.exclude = false; var excludeButton = document.getElementById(panel.model.panelId).getElementsByClassName("exclude")[0] excludeButton.style.textDecoration = panel.model.exclude? "underline":"none"; var concepts = document.getElementById(panel.model.conceptPanelId); while (concepts.firstChild) { concepts.removeChild(concepts.firstChild); } }; // ------ initialize the controller ------ panel.ctrlr.doDrop = function (sdxData) { var concept = sdxData[0]; // only interested in first record // CHECK: some useful functions: // alert(i2b2.h.getXNodeVal(sdxData.origData.xmlOrig, "level")) // alert(Object.getOwnPropertyNames(sdxData.origData)); // optimization to prevent requerying the hive for new results if the input dataset has not changed // i2b2.ExampTabs.model.dirtyResultsData = true; // check whether to show a popup or directly append the concept switch(concept.sdxInfo.sdxDisplayName) { case "Gene Name": i2b2.MedCo.popups.ByGene.ctrlr.show(i2b2.MedCo.QT.panels.indexOf(panel)); return; case "Protein Position": i2b2.MedCo.popups.ByProteinPosition.ctrlr.show(i2b2.MedCo.QT.panels.indexOf(panel)); return; case "Variant Name": i2b2.MedCo.popups.ByVariantName.ctrlr.show(i2b2.MedCo.QT.panels.indexOf(panel)); return; } var keyval = concept.sdxInfo.sdxKeyValue; var sensitive = !keyval.includes("nonsensitive") && !keyval.includes("non-sensitive"); var conceptModel = sensitive ? concept.origData.basecode.split(":")[1] : (concept.origData.key).replace(/ NOT  var not = document.createElement("span") not.className = "itemExclude" not.title = "This item is being excluded" not.innerHTML = "NOT" conceptsDiv[i].insertBefore(not, conceptsDiv[i].firstChild); } } else { // remove the "NOT" in front of the concepts in the div for (var i = 0; i < conceptsDiv.length; i++) { conceptsDiv[i].removeChild(conceptsDiv[i].getElementsByClassName("itemExclude")[0]) } } }; panel.ctrlr.delete = function(){ var index = i2b2.MedCo.QT.panels.indexOf(panel) if (index == -1){ alert("Impossible to delete the panel, panel inexistent.") return } // delete the panel from the model i2b2.MedCo.QT.panels.splice(index, 1) // delete the panel from the view var panels_container = document.getElementById("MedCo-QueryPanels") var panels = panels_container.childElements() panels_container.removeChild(panels[index]); // update the group number in the header of the panels panels = panels_container.childElements() for (var i = 0; i < panels.length; i++) { // i2b2.MedCo.QT.panels[i].group = (i + 1); var header = panels[i].getElementsByClassName("groupId")[0] header.innerHTML = 'Group ' + (i+1); } if (i2b2.MedCo.QT.panels.length != panels.length){ alert("Something went wrong: model and view are inconsistent.") } } panel.ctrlr.isEmpty = function(){ // check there is at least a row which contains something for (var i=0; i < panel.model.content.length; i++) { if (panel.model.content[i].values.length > 0) { return false; } } return true; } }; i2b2.MedCo.QT.ctrlr.deletePanel = function(HTMLelem){ // HTMLelem: (clicked) html element // first get the index of the panel var index = getPanelIndex(HTMLelem); if (index < 0){ alert("Panel not found"); return } // then call the delete function on it i2b2.MedCo.QT.panels[index].ctrlr.delete() }; i2b2.MedCo.QT.ctrlr.excludePanel = function(HTMLelem){ // HTMLelem: (clicked) html element // first get the index of the panel var index = getPanelIndex(HTMLelem) if (index < 0){ alert("Panel not found") return } // then call the exclude function on it i2b2.MedCo.QT.panels[index].ctrlr.exclude(HTMLelem) }; i2b2.MedCo.QT.ctrlr.showDateConstraint = function(HTMLelem){ var index = getPanelIndex(HTMLelem) if (index < 0){ alert("Panel not found") return } // alert("panel index:"+index) i2b2.MedCo.QT.panels[index].model.date.ctrlr.show() } \ No newline at end of file diff --git a/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_popups.js b/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_popups.js index 2f1bb7f53..bfdc40c1f 100644 --- a/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_popups.js +++ b/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/MedCo_popups.js @@ -1,1027 +1,1028 @@ i2b2.MedCo.popups = {}; i2b2.MedCo.popups.ctrlr = {}; // popups for building the query i2b2.MedCo.popups.ByVariantName = {}; i2b2.MedCo.popups.ByVariantName.ctrlr = {}; i2b2.MedCo.popups.ByGene = {}; i2b2.MedCo.popups.ByGene.ctrlr = {}; i2b2.MedCo.popups.ByProteinPosition = {}; i2b2.MedCo.popups.ByProteinPosition.ctrlr = {}; // popup for managing the query type i2b2.MedCo.popups.SelectQueryType = {}; i2b2.MedCo.popups.SelectQueryType.ctrlr = {}; // popup for authentication i2b2.MedCo.popups.Authentication = {}; i2b2.MedCo.popups.Authentication.ctrlr = {}; i2b2.MedCo.popups.ctrlr.init = function (){ i2b2.MedCo.popups.ByVariantName.ctrlr.init(); i2b2.MedCo.popups.ByGene.ctrlr.init(); i2b2.MedCo.popups.ByProteinPosition.ctrlr.init(); i2b2.MedCo.popups.SelectQueryType.ctrlr.init(); i2b2.MedCo.popups.Authentication.ctrlr.init(); }; i2b2.MedCo.popups.ctrlr.clear = function(){ i2b2.MedCo.popups.ByVariantName.ctrlr.clear(); i2b2.MedCo.popups.ByGene.ctrlr.clear(); i2b2.MedCo.popups.ByProteinPosition.ctrlr.clear(); i2b2.MedCo.popups.SelectQueryType.ctrlr.clear(); i2b2.MedCo.popups.Authentication.ctrlr.clear(); }; i2b2.MedCo.popups.ctrlr.unload = function() { // delete view of everything i2b2.MedCo.popups.ByVariantName.ctrlr.unload(); i2b2.MedCo.popups.ByGene.ctrlr.unload(); i2b2.MedCo.popups.ByProteinPosition.ctrlr.unload(); i2b2.MedCo.popups.SelectQueryType.ctrlr.unload(); i2b2.MedCo.popups.Authentication.ctrlr.unload(); }; // Manage the php requests. // success: function that takes as input the response form the server // error: function that takes as input the error code returned by the server // phpQuery: string that contains the path and the parameters of the request function phpGETRequest(success, error, phpQuery) { // create the request object to send the request and manage the response var req = false; try { // most browsers req = new XMLHttpRequest(); } catch (e) { // IE try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { // try an older version try { req = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { return false; } } } // if the http request obj has not been created then return if (!req) return false; // update the request object to act when receiving the response req.onreadystatechange = function () { if (req.readyState == 4) { return req.status === 200 ? success(req.responseText) : error(req.status); } }; req.open("GET", phpQuery, true); req.send(null); return req; }; // Function used to initialize gene, protein and variant popup (they have the same structure). // -popup: contains variables used to create the pupup (e.g. title, description) // -fetchParametersQuery: php query used retrieve the parameters (e.g. genes, proteins or variants). Contains // "¶mValue" that must be filled with the value of the parameter selected from the popup // -fetchVariantsQuery: the final objective of the popup is to fetch the variants. This should be the path to the php // file that fetches the variants from the database. i2b2.MedCo.popups.ctrlr.MedCo_queryPopupInit = function (fetchParametersQuery, fetchVariantsQuery, popup, ctrlr){ // ------ Initialize the model ------ var model = {}; model.choiceList = []; // here is stored the list of suggested parameters // the panel number is passed to the popup when opened (so to bind the popup with a panel) model.panelIndex = 0; // ------ Initialize the view ------ popup.Id = "MedCo_" + popup.name + "Popup" popup.paramInputBoxId = "search" + popup.name + "InputBox"; popup.heteroId = "heterozygous" + popup.name + "Popup"; popup.homoId = "homozygous" + popup.name + "Popup"; popup.unknId = "unknown" + popup.name + "Popup"; document.getElementById("MedCo-CONTENT").appendChild(htmlToElement( "\n
" + "
" + popup.text.title + "
" + "
" + "

" + popup.text.description + "

"+ "
"+ "
"+ "
" + popup.text.paramName + "
"+ "
"+ "" + "
"+ "
"+ "
"+ "
"+ "
Zygosity*
"+ "
"+ ""+ ""+ ""+ "
"+ "
"+ "
"+ "
"+ "
")); var view = {}; view.popup = document.getElementById(popup.Id); view.inputBox = document.querySelector("#"+ popup.Id + " #" +popup.paramInputBoxId); view.zygosityList = { heterozygousCheckbox: document.querySelector("#"+ popup.Id + " #" + popup.heteroId), homozygousCheckbox: document.querySelector("#"+ popup.Id + " #" + popup.homoId), unknownCheckbox: document.querySelector("#"+ popup.Id + " #" + popup.unknId) }; // autocompletion for the inputBox new autoComplete({ cache: false, selector: '#' + popup.paramInputBoxId, minChars: 1, source: function(term, suggest){ term = term.toLowerCase(); var matches = []; for (var i=0; i20) break } } suggest(matches); } }); // ------ Initialize the controller ------ // function used to check if the input box content reflect the selected option in the dropdown list view.inputBox.Validate = function (){ var parameter = view.inputBox.value.replace(/\s/g, '') if (parameter!="" && model.choiceList.includes(view.inputBox.value)){ return true } // The user has still to pick one from the dropdown list alert("Please, write and pick a " + popup.name + " from the dropdown list."); return false; }; view.zygosityList.Validate = function (){ if (view.zygosityList.heterozygousCheckbox.checked == false && view.zygosityList.homozygousCheckbox.checked == false && view.zygosityList.unknownCheckbox.checked == false) { alert("Please, select at least one zygosity option."); return false; } return true; }; // fetchParameters manage the fetch of the suggested parameters of the query (e.g. genes, proteins, variants) ctrlr.fetchParameters = { // if previousParameter (which stores the "old" view.inputBox content) stays the same for a certain period // of time then the user stopped writing => I retrieve from the database the matching parameters previousParameter: "", // here I store the executed queries so to avoid repeating it queriedParameters: [""], // here I store the timeout (so that I can stop it when I want) used to check when the user stops writing timeout: null, // waitThenRun allows you to wait for the user to finish writing before running the query waitThenRun: function () { // we call waitThenRun until the user stops writing // stop the previous call (every new digits trigger a fetchParameters.run() call) clearTimeout(ctrlr.fetchParameters.timeout); // take the current value removing whitespaces var currParameter = view.inputBox.value.replace(/\s/g, ''); if (ctrlr.fetchParameters.queriedParameters.includes(currParameter)) { // if it is equal to a previous query then I already have all the matching ones => just update // the dropdown list (simulate onchange to trigger the autocompletion) ctrlr.fetchParameters.previousParameter = currParameter; view.inputBox.dispatchEvent(new Event('onchange')); return; } if (currParameter != ctrlr.fetchParameters.previousParameter) { // if the current parameter is different from the previous one then wait for the user to finish // to write ctrlr.fetchParameters.previousParameter = currParameter; ctrlr.fetchParameters.timeout = setTimeout(ctrlr.fetchParameters.waitThenRun, 500); return; } // If you ended up here then the user stopped writing and we can execute the query // store the current query so to avoid repeating it ctrlr.fetchParameters.queriedParameters.push(currParameter); // todo not fault tolerant (you store it before receiving the response of the query...) ctrlr.fetchParameters.run(); }, run: function () { var currParameter = view.inputBox.value.replace(/\s/g, ''); var success = function (responseText) { if (responseText.indexOf("error")>=0){ alert(responseText) return; } // merge the choice list with the new options var merged = new Set(model.choiceList.concat(JSON.parse(responseText))); model.choiceList = [...merged]; // convert to list (after evaluating the set) model.choiceList.sort() // update showed list (by pretending there a keyup event) view.inputBox.dispatchEvent(new Event('onchange')); }; var error = function (err) { alert("Error when trying to retrieve the " + popup.name + " values from the database: " + err) }; var phpQuery = fetchParametersQuery.replace("$paramValue", currParameter); phpGETRequest(success, error, phpQuery); } }; // fetchVariants manage the fetch of the variants ids (which is the final goal of the popup) ctrlr.fetchVariants = { run: function () { var parameterValue = view.inputBox.value; var zygosyty = []; if (view.zygosityList.heterozygousCheckbox.checked == true) { zygosyty.push("Heterozygous") } if (view.zygosityList.homozygousCheckbox.checked == true) { zygosyty.push("Homozygous") } if (view.zygosityList.unknownCheckbox.checked == true) { zygosyty.push("Unknown") } var panel = i2b2.MedCo.QT.panels[model.panelIndex]; // show the variants to the user var conceptView = popup.text.panelParam + parameterValue; var conceptModel = []; // still nothing to be stored (we have to wait of the response) - var sensitive = false; // it is sensitive but it does not need to be encrypted since already encrypted in the db + var sensitive = true; // it is sensitive but it is already encrypted in the db + var already_encrypted = true; var output = true; - var concept = panel.ctrlr.appendConcept(conceptView, conceptModel, sensitive, output); + var concept = panel.ctrlr.appendConcept(conceptView, conceptModel, sensitive, already_encrypted, output); var success = function (responseText) { if (responseText.indexOf("error")>=0){ alert(responseText) return; } var response = JSON.parse(responseText); // var panel = i2b2.MedCo.QT.panels[model.panelIndex] // update the model and the view of the concept concept.model.values = response.variants; concept.view.getElementsByTagName("output")[0].innerHTML = response.variants.length; // i2b2.MedCo.ctrlr.background.toBeEncrypted(response.variants); // the variant is encrypted and stored in the db so it is already fetched encrypted // I received the response so decrease the counter i2b2.MedCo.ctrlr.pendingVariantsQueries -= 1; }; var error = function (err) { alert("Error when trying to retrieve the variants from the database: " + err) }; // increase the number of responses I should wait for i2b2.MedCo.ctrlr.pendingVariantsQueries += 1; // The panel number will be returned with the response so to bind a query to a specific panel (if the // query is very slow it could happen that the user starts two queries in parallel which could be related // to two different panels). var phpQuery = fetchVariantsQuery.replace("$paramValue", parameterValue) + "&zygosity[]=" + zygosyty.join("&zygosity[]="); // build the array of zygosity options phpGETRequest(success, error, phpQuery); } }; // Popup 'cancel' and 'ok' handlers ctrlr.handleCancel = function() { // When the popup is closed just call the "cancel" on the YAHOO popup this.cancel(); }; ctrlr.handleOk = function() { // When the user clicks on "ok", check the query parameters and fetch the variants // validate the fields if (! view.inputBox.Validate()){return;} if (! view.zygosityList.Validate()){return;} // run the query to retrieve all the annotations with the specified parameters and zygosyty ctrlr.fetchVariants.run(); this.hide() }; // clear the popup fields ctrlr.clear = function(){ view.inputBox.value = ""; view.zygosityList.heterozygousCheckbox.checked = false; view.zygosityList.homozygousCheckbox.checked = false; view.zygosityList.unknownCheckbox.checked = false; }; ctrlr.unload = function(){ // destroy the html elements view.popup.parentNode.removeChild(view.popup); } // ------ Register controller functions on view elements ------ // when the user writes in the inputBox you have to run a query to fetch the parameters view.inputBox.addEventListener("keyup", ctrlr.fetchParameters.waitThenRun); // Create the popup with YAHOO.widget view.dialog = new YAHOO.widget.SimpleDialog(popup.Id, { draggable: true, zindex: 10000, width: "500px", // height: "200px", autofillheight: "body", constraintoviewport: true, context: ["showbtn", "tl", "bl"], fixedcenter: true, modal: true, buttons: [{ text: "OK", handler: ctrlr.handleOk, isDefault: true }, { text: "Cancel", handler: ctrlr.handleCancel }] }); ctrlr.show = function(panelIndex) { // show the YAHOO.widget popup // ctrlr.clear() // clear the fields model.panelIndex = panelIndex; view.dialog.render(document.body); view.dialog.show(); }; } i2b2.MedCo.popups.ByGene.ctrlr.init = function (){ // define the php queries and insert $something where the parameters will be inserted // note: these formats must conform what the relative php files expects as parameters var fetchParametersQuery = "js-i2b2/cells/plugins/MedCo/php/fetchGenes.php?" + "gene=$paramValue&" + "limit=20", // is it ok 20? fetchVariantsQuery = 'js-i2b2/cells/plugins/MedCo/php/fetchVariants.php?' + 'query_type=gene_and_zygosity&' + 'gene_value=$paramValue', // note the list "&zygosity[]=" of options will be added afterward popup = {}; popup.name = "gene" // define the text for the popup popup.text = {} popup.text.title = "Search by gene"; popup.text.description = "Use the gene name box to specify the set of variants for which to search. When you " + "begin typing in the search box below some matching options will appear."; popup.text.paramName = "Gene Name*"; popup.text.panelParam = "Gene: "; i2b2.MedCo.popups.ctrlr.MedCo_queryPopupInit(fetchParametersQuery, fetchVariantsQuery, popup, i2b2.MedCo.popups.ByGene.ctrlr); }; i2b2.MedCo.popups.ByProteinPosition.ctrlr.init = function(){ // define the php queries and insert $something where the parameters will be inserted // note: these formats must conform what the relative php files expects as parameters var fetchParametersQuery = "js-i2b2/cells/plugins/MedCo/php/fetchProteinPositions.php?" + "proteinPosition=$paramValue&" + "limit=20", // is it ok 20? fetchVariantsQuery = 'js-i2b2/cells/plugins/MedCo/php/fetchVariants.php?' + 'query_type=annotation_and_zygosity&' + 'annotation_name=Protein_position&' + 'annotation_value=$paramValue', // note the list "&zygosity[]=" of options will be added afterward popup = {}; popup.name = "proteinPosition"; // define the text for the popup popup.text = {} popup.text.title = "Search by protein position"; popup.text.description = "Use the protein position annotation box to specify the variants for which to search. " + "When you begin typing in the search box below some matching options will appear."; popup.text.paramName = "Protein Position*"; popup.text.panelParam = "Protein position: "; i2b2.MedCo.popups.ctrlr.MedCo_queryPopupInit(fetchParametersQuery, fetchVariantsQuery, popup, i2b2.MedCo.popups.ByProteinPosition.ctrlr); }; i2b2.MedCo.popups.ByVariantName.ctrlr.init = function(){ // define the php queries and insert $something where the parameters will be inserted // note: these formats must conform what the relative php files expects as parameters var fetchParametersQuery = "js-i2b2/cells/plugins/MedCo/php/fetchVariantNames.php?" + "variant_name=$paramValue&" + "limit=20", // is it ok 20? fetchVariantsQuery = 'js-i2b2/cells/plugins/MedCo/php/fetchVariants.php?' + 'query_type=variantName_and_zygosity&' + 'variant_name=$paramValue', // note the list "&zygosity[]=" of options will be added afterward popup = {}; popup.name = "variantName" // define the text for the popup popup.text = {} popup.text.title = "Search by variant name"; popup.text.description = "Use the variant name box to specify the variant for which to search, according to the " + "HGVS format (e.g. 10:100150517:G>A). When you begin typing in the search box below some matching options " + "will appear."; popup.text.paramName = "Variant Name*"; popup.text.panelParam = "Variant ID: "; i2b2.MedCo.popups.ctrlr.MedCo_queryPopupInit(fetchParametersQuery, fetchVariantsQuery, popup, i2b2.MedCo.popups.ByVariantName.ctrlr); }; i2b2.MedCo.popups.SelectQueryType.ctrlr.init = function(){ var PopupId = "MedCo-SelectQueryTypePopup"; var queryNameId = "MedCo-QueryName"; var queryTypeId = "MedCo-QueryType"; // ------ Initialize the view ------ document.getElementById("MedCo-CONTENT").appendChild(htmlToElement( "
" + "
Run Query
" + "
" + "
" + "
Please type a name for the query:
" + "" + "

" + "
Please check the query result type(s):
" + "
" + "" + "" + "
" + "
" + "
" + "
" )); var view = {}; view.popup = document.getElementById(PopupId); view.queryName = document.querySelector("#"+ PopupId + " #" + queryNameId); view.queryTypes = []; // extract the pointer to all the input checkboxes representing the query types var types = document.querySelector("#"+ PopupId + " #" + queryTypeId).children; for (var i = 0; i < types.length; i++) { view.queryTypes.push(types[i].children[0]) // discard the