(function (window, document, $) { var annunciatorHolder = { dnaLoaded: false, slotExposed: false, slotSample: false, //Functions setSlotExposed: function (exp) { var annSlot = $("#annSlotExp"); if(exp && !annunciatorHolder.slotExposed) { annSlot.addClass("a-yellow-on"); } else if (annunciatorHolder.slotExposed) { annSlot.removeClass("a-yellow-on"); } annunciatorHolder.slotExposed = exp; }, setSlotSample: function (smp) { var annSlot = $("#annSlotSample"); if(smp && !annunciatorHolder.slotSample) { annSlot.addClass("a-green-on"); } else if (annunciatorHolder.slotSample) { annSlot.removeClass("a-green-on"); } annunciatorHolder.slotSample = smp; }, setLoadAnn: function (loaded) { setAnnunciator("#annDNALoad", loaded); setAnnunciator("#annDNANoLoad", !loaded); setAnnunciator("#annDNASplice", (loadedDna && loadedDna.isSplicing)) annunciatorHolder.dnaLoaded = loaded; } }; /* GENERAL VARS */ var viewPage = "", loadedDna = null, exposedSlot = 0, src = "123456", remoteHandlers = {}, bSuppressXmit = false, actPage = 0; /* var dummyData = {handleManipCallback:['{"success":true,"newSeq":"0001FFF000AAAA03000C000751878||189FFF|0B3|DEADBEEF0"}', '{"success":true,"newSeq":"0001FFF0FFAA1A03000C000751878||189FFF|0B3|DEADBEEF0"}'], handleAnalysisTestCallback: ['{"valid":true, "stable":1, "trans":-1, "seqs":["124", "ABC", "DEF", "FED", "CBA"], "conf":[80, 60, 40, 20, 0], "pred":25.1234568, "buttons":"DEADBEEF0"}', '{"valid":true, "stable":1, "trans":1, "seqs":["246", "ABC", "DEF"], "conf":[80, 60, 40], "pred":50, "buttons":"CAFEBABE1"}'], handleSplicePredCallback: ['{"pred":1}', '{"pred":2}', '{"pred":4}'], handleSpliceCompletionCallback: ['{"success":1, "newSeq":"0001000F00AAAA03000C000751878||189FFF|0B3|DEADBEEF0"}', '{"success":0, "newseq":""}'] }; */ function setRef(ref) { src = ref; xmit({ackref:1}); } window.setRef = setRef; function initializeUI () { /*BIND MAIN MENU BUTTON LISTENER*/ $("#mainMenu > .button").on("click", function() { if($(this).hasClass("button-disabled")) {return;} handleMainMenuClick(this.id); }); /*BIND SLOT CONTROL BUTTON LISTENER*/ $("#btnCloseSlot, #btnEjectSample").click( function(){handlePushButtonClick(this, handleExposeButtons);}); /*BIND DNA ANALYSIS CONTROL LISTENERS*/ $("#btnClrAnalysisCurr, #btnAnalysisLoad, #btnAnalysisDoTest").click(function() {handlePushButtonClick(this, handleGeneralAnalysisButtonClick);}); $(".display-known").click(function () {handlePushButtonClick(this, displayKnown);}); /*BIND MANIPULATOR CONTROL BUTTON LISTENER*/ $("#manipHolder .button").on("click", function(){ handlePushButtonClick(this, handleManipulatorButton);}); /*BIND SPLICING CONTROL BUTTON LISTENER*/ $("#spliceButtons .button").click(function() {handlePushButtonClick(this, handleSpliceCommenceButtonClick);}); $(".btn-seq-off").click(function() {handlePushButtonClick(this, handleSpliceTargetOffsetClick);}); $("#spliceActions .button").click(function() {handlePushButtonClick(this, handleSpliceActionClick);}); $("#btnSpliceFinish").click(function() {handlePushButtonClick(this, handleSpliceFinishClick);}); /* INITIALIZATION STUFF*/ setActivePage(0); createDnaSlots(3); createSplicingDnaSlots(3); updateAnalysisControlElements(); updateSpliceCommenceButtons(); updateMainMenuButtons(); //debug_createData(); updateLoadedDependents(); //debug_createDNAbuttons(); } function setActivePage(page) { $(".dataPage").hide(); var id = ""; switch (page) { case 1: //Loader viewPage = "#dpLoadSave"; id="btnLoadSave"; break; case 2: //Load / Save DNA viewPage = "#dpManip"; id="btnManip"; break; case 3: //Splicing DNA selection viewPage = "#dpSplice1"; id="btnSplice"; break; case 4://Splice in progress viewPage = "#dpSplice2"; id="btnSplice"; break; case 5: //Tester viewPage = "#dpTester"; id="btnTester"; break; default: //Welcome viewPage = "#dpWelcome"; id="btnRetMain"; } if(page < 1 || page > 5) { page = 0; } if(page != 4) { xmit({setstate:1,newstate:page}); } actPage=page; $("#mainMenu > .button").removeClass("button-selected"); $("#" + id).addClass("button-selected"); $(viewPage).show(); } //Worker functions /* MANIPULATION */ /* VARS */ var mutationData = new mutationHolder(), manipBusy = false; /* WORKERS */ function mutationHolder(mut, mts, adv, mal, sth) { if (mut === undefined) { this.mut = 0; } else { this.mut = mut; } if (mts === undefined) { this.mts = 0; } else { this.mts = mts; } if (adv === undefined) { this.adv = 0; } else { this.adv = adv; } if (mal === undefined) { this.mal = 0; } else { this.mal = mal; } if (sth === undefined) { this.sth = 0; } else { this.sth = sth; } } function doManipulation(task) { if(typeof(task) !== 'string') { return; } manipBusy = true; var taskList = task.split("="); if (taskList.length !== 2) { //Malformed task return; } updateManipReady(); var out = {manip:taskList[0],dir:taskList[1]}; xmit(out, "handleManipCallback"); } /* HANDLERS */ function handleManipulatorButton(clicked) { doManipulation( $(clicked).attr("data-tsk")); } function handleManipCallback(data) { //'{"success":0,"newSeq":"0001000F00000003000C000751878||189|0B3|240|B3A"}'] if (data != null) { manipBusy=false; var mutInfo = data.mutInfo; setAnnunciator("#aMutAck", data.success); setAnnunciator("#aMutNack", !data.success); if (!data.success) { loadedDna = null; } if(data.success && data.newseq) { loadedDna.seq = data.newseq; } updateLoadedDependents(false, true); updateManipReady(); } } remoteHandlers.handleManipCallback = handleManipCallback; function parseDnaStringToManip(sDNA) { var dat = [], i, mutObj = new mutationHolder(); if (typeof(sDNA) === 'string' && sDNA.length > 24) { var s; for ( i = 4; i < 24; i+=4) { s = sDNA.substr(i, 4); dat.push(hex2signedint(s)); } i = 0; for (var k in mutObj) { mutObj[k] = dat[i++]; } } return mutObj; } function hex2signedint(hex) { var a = parseInt(hex, 16), bytes = hex.replace('0x','').length, msb = 1 << ((bytes * 4) -1); if ((a & msb) > 0 ) { a -= (msb << 1) - 1 } return a; } function safeSetMutationData(bExternal) { if(bExternal === undefined) {bExternal=false;} var dat; if(loadedDna) { dat = parseDnaStringToManip(loadedDna.seq); } else { dat = new mutationHolder(); } updateManipList(bExternal, dat); } /* UPDATERS */ function updateManipList(bExternal, newData) { if(bExternal === undefined) {bExternal=false;} if(newData===undefined) {newData=null;} //Clear update info on the manipulation list $("#manipHolder .text-field").removeClass("tf-c-red"); var data = newData ? newData : mutationData; for(var k in data) { var t = $("#txt" + k.charAt(0).toUpperCase() + k.slice(1,3)); if(t.hasClass("text-field")) { t.text(data[k]); if(bExternal && data[k] !== mutationData[k]) { safeAddClass(t, "tf-c-red"); } } if(newData && mutationData.hasOwnProperty(k)) { mutationData[k] = data[k]; } } } function updateManipReady() { var bReady = true; if(!loadedDna || loadedDna.isSplicing) { setAnnunciator("#aMutSample", true); bReady=false; } else { setAnnunciator("#aMutSample", false); } if(exposedSlot > 0) { setAnnunciator("#aMutOpen", true); bReady=false; } else { setAnnunciator("#aMutOpen", false); } manipBusy = manipBusy && bReady; //Can't be busy if it's not ready. setButtonEnabled("#manipHolder .button", bReady); bReady = bReady && !manipBusy; //Can't be ready if it's busy setAnnunciator("#aMutRdy", bReady); setAnnunciator("#aMutIrr", manipBusy); } /* ANALYSIS */ /* VARS */ var curSeq = [], prevSeq = [], iStable = 0, iTrans = 0; /* WORKERS */ function seqEntry(seq, certainty) { this.seq = seq; if(certainty != null) { this.certainty = Math.min(100, Math.max(certainty*100, 0)); } else { this.certainty = null; } this.getColour = function () { var hexComp = function(c) { c = Math.round(c).toString(16); return c.length === 1 ? '0' + c : c; }, r = 255 * (100 - this.certainty) / 100; g = 255 * this.certainty / 100; return '#' + hexComp(r) + hexComp(g) + '00'; }; } function displayKnown() { xmit({showknown:1}); } function addSequence(seq, ind) { if(curSeq.length >= 5) { return; } curSeq.push(new seqEntry(seq)); updateAnalyzerSequence("#currAnalysis", curSeq); updateAnalysisControlElements(); if(ind !== 'undefined' && ind >= 0) { xmit({analysisappend:ind}); } } function clearAnalysisBuffer(skipTransmit) { if(curSeq.length > 0) { curSeq = []; updateAnalyzerSequence("#currAnalysis", null); updateAnalysisControlElements(); if(!skipTransmit) { xmit({analysisclear:1}); } } } function doAnalysisTest() { var s = ""; for(var i = 0; i < curSeq.length; i++) { s += curSeq[i].seq; } var d = {}; d.analysisdo = s; xmit(d, "handleAnalysisTestCallback"); } function doAnalysisLoad() { if(!loadedDna) {return;} var buttons = []; var dnaSequence = loadedDna.seq; dnaSequence = dnaSequence.slice(dnaSequence.search(/[\w\d]{3}\|\|/)); dnaSequence = dnaSequence.replace(/\|/g, ""); for(var i = 0; i < dnaSequence.length; i+=3) { buttons.push(dnaSequence.slice(i, i+3)); } loadedDna = null; xmit({analysisdestroy:1}) updateAnalysisButtons(buttons); updateLoadedDependents(); } /* HANDLERS */ function handleAnalysisComponentClick(clicked) { if(curSeq.length >= 5) { return; } var seq = $(clicked).text(); var ind = $(clicked).index(); $(clicked).remove(); addSequence(seq, ind); } function handleGeneralAnalysisButtonClick(clicked) { var id = clicked.id; switch (id) { case "btnAnaDispKno": displayKnown(); break; case "btnClrAnalysisCurr": clearAnalysisBuffer(); break; case "btnAnalysisLoad": doAnalysisLoad(); break; case "btnAnalysisDoTest": doAnalysisTest(); break; } } function handleAnalysisTestCallback(data) { /* Expected response: {valid:true/false, stable:1,0,-1, trans,1,0,-1, seqs:["123", "456" ...] conf:[25, 100 ...], pred:25.24561 buttons:"DEADBEEF0 ..."} */ setAnnunciator("#annErrNack", !data.valid); if(data.valid) { prevSeq = []; iStable = data.stable; iTrans = data.trans; var s = data.seqs, c = data.conf; if(s.length === c.length) { var minLen = Math.min(s.length, 5); for (var i = 0; i < minLen; i++) { prevSeq.push(new seqEntry(s[i], c[i])); } } else { for (var i = 0; i < s.length; i++) { prevSeq.push(new seqEntry(s[i])); } } if(iStable < 0 && !data.noclear) { clearAnalysisBuffer(true); } updateAnalyzerSequence("#prevAnalysis", prevSeq); //updateAnalysisButtonsFromString(data.buttons); updateAnalysisControlElements(); } else { clearAnalysisBuffer(true); xmit({analysisupdate:1}); //Should be handled via regular analysis screen update handler } //Process data, set annunciators. /* Send entire current buffer to server. It will validate this. If it's valid: Set annunciators stable / transient. Update previous analysis with data. If not stable clear current analysis. If invalid: Set NACK annunciator. Clear current analysis buffer Update testing buffer Optional: gib user (f u href butte) */ } remoteHandlers.handleAnalysisTestCallback = handleAnalysisTestCallback; /* UPDATERS */ function updateAnalyzerSequence(id, newList) { $(id + " .text-field").text(""); if(newList) { for(var i = 0; i < newList.length; i++) { var s = newList[i], c = null, t = s.seq; if(s.certainty !== null) { c = s.getColour(); t = "" + t + "" } $(id + i).html(t); } } } function updateAnalysisControlElements() { setAnnunciator("#annStableYes", iStable == 1); setAnnunciator("#annStableNo", iStable == -1); setAnnunciator("#annTransYes", iTrans == 1); setAnnunciator("#annTransNo", iTrans == -1); var invalid = !loadedDna || loadedDna.isSplicing; setAnnunciator("#annErrSample", invalid); setButtonEnabled("#btnAnalysisLoad", !invalid) invalid = curSeq.length >= 5; setAnnunciator("#annErrBuffer", invalid); setButtonEnabled("#analyzeComponents .button", !invalid); invalid = curSeq.length === 0; setAnnunciator("#annErrData", invalid); setButtonEnabled("#btnAnalysisDoTest", !invalid); } function updateAnalysisButtonsFromString(s) { var bList = []; for(var i = 0; i < s.length; i+=3) { bList.push(s.slice(i, i+3)); } updateAnalysisButtons(bList); } function updateAnalysisButtons(buttonList) { var holder = $("#analyzeComponents"); holder.empty(); if(typeof(buttonList) === "object") { for(var i = 0; i < buttonList.length; i++) { $("
", { 'class': 'button btn-tinyish', id: 'anaComp' + i }).appendTo(holder).text(buttonList[i]); } //Bind our listener $("#analyzeComponents .button").click( function() {handlePushButtonClick(this, handleAnalysisComponentClick);}); } } /* DNA LOAD / SAVE */ /* VARS */ var dnaSlots = [], dnaDetails = []; /* WORKERS */ function doLoadDna(slot) { var index = slot-1, //0-based dna = dnaDetails[index], slotObj = dnaSlots[index]; if(loadedDna === null && dna.seq) { xmit({load:slot}); loadedDna = dna; dnaDetails[index] = new dnaSlotInfo(); updateLoadedDependents(); } } function doSaveDna(slot) { var index = slot-1, //0-based dna = loadedDna, target = dnaDetails[index]; if(loadedDna && !target.seq) { xmit({save:slot}); dnaDetails[index] = loadedDna; loadedDna = null; updateLoadedDependents(); updateDnaSlot(slot); } } function doExchangeDna(slot) { var index = slot -1, lDna = loadedDna, tDna = dnaDetails[index]; temp = lDna; if(lDna && tDna.seq) { xmit({exchange:slot}); loadedDna = tDna; dnaDetails[index] = temp; } updateLoadedDependents(true); updateDnaSlot(slot); } function doClearDna(slot) { xmit({remove:slot}); dnaDetails[slot-1] = new dnaSlotInfo(); updateDnaSlot(slot); } function doExposeSlot(slot, bSuppressXmit) { if(bSuppressXmit === undefined) {bSuppressXmit = false;} var oldSlot = exposedSlot; exposedSlot = slot; if (oldSlot > 0) { dnaDetails[oldSlot-1].exposed = 0; updateDnaSlot(oldSlot); } var output; if(slot > 0) { dnaDetails[slot-1].exposed = 1; updateDnaSlot(slot); output = {expose:slot}; } else { output = {lock:1}; } if(!bSuppressXmit && oldSlot != exposedSlot) { xmit(output); } updateExposedIndicator(); updateManipReady(); updateSpliceCommenceButtons(); } function createDnaSlots(num) { dnaSlots = []; $("#dnaSlotHolder").empty(); var finalObj = null; var tempObj = null; for (var i = 1; i <= num; i++) { //Main div finalObj = $("
", { id: "dnaSlot" + i, 'class':'dna-slot', 'data-id': i }); //Container to hold values tempObj = $("
", { 'class':'noborder' }).appendTo(finalObj); //Slot counter $("
", { 'class':"text-field tf-narrow" }).appendTo(tempObj).text(i.toString()); //Annunciators $("
", { id: 'annDnaEmp' + i, 'class':'annunciator a-red' }).appendTo(tempObj).text("EMPTY"); $("
", { id: 'annDnaExp' + i, 'class':'annunciator a-yellow' }).appendTo(tempObj).text("EXPOSED"); $("
", { id: 'btnDnaLoad' + i, 'class':'button btn-small', }).appendTo(tempObj).text("LOAD"); $("
", { id: 'btnDnaSave' + i, 'class':'button btn-small', }).appendTo(tempObj).text("SAVE"); $("
", { id: 'btnDnaXchg' + i, 'class':'button btn-small', }).appendTo(tempObj).text("XCHG"); $("
", { id: 'btnDnaClear' + i, 'class':'button btn-small', }).appendTo(tempObj).text("CLEAR"); $("
", { id: 'btnDnaSlotExpose' + i, 'class':'button btn-small', }).appendTo(tempObj).text("EXPOSE"); //new tempObj for the next row tempObj = $("
", { 'class':'noborder' }).appendTo(finalObj); $("", {'class':'label'}).appendTo(tempObj).text("Seq:"); $("
", {id: 'dnaSequence' + i, 'class':"text-field tf-long"}).appendTo(tempObj); //Append finalObj to the holder finalObj.appendTo("#dnaSlotHolder"); dnaSlots.push(finalObj); } //Attach our event handler $(".dna-slot .button").on("click", function() { var number = $(this).closest('.dna-slot').attr('data-id'); handleDNAClick(number, this.id, this); }); } function dnaSlotInfo(seq, pathogenName, pathogenType, isSplicing) { if(seq === undefined) { this.seq = null; }else { this.seq = seq; } if(pathogenName === undefined) { this.pathogenName = null; }else { this.pathogenName = pathogenName; } if(pathogenType === undefined) { this.pathogenType = null; } else { this.pathogenType = pathogenType; } if(isSplicing === undefined) { this.isSplicing = null; } else { this.isSplicing = isSplicing; } } /* HANDLERS */ function handleDNAClick(slot, id, clicked) { if(!handlePushButtonClick(clicked)) {return;} var detClick = id.slice(0, id.length - slot.toString().length); switch(detClick) { case "btnDnaLoad": doLoadDna(slot); break; case "btnDnaSave": doSaveDna(slot); break; case "btnDnaXchg": doExchangeDna(slot); break; case "btnDnaClear": doClearDna(slot); break; case "btnDnaSlotExpose": doExposeSlot(slot); break; } } /* UPDATERS */ function updateLoadedSection(cancGlobalUpdate) { if (loadedDna) { $("#txtPName").text(loadedDna.pathogenName); $("#txtPType").text(loadedDna.pathogenType); $("#txtPSeq").text(loadedDna.seq); } else { $("#txtPName").text(""); $("#txtPType").text(""); $("#txtPSeq").text(""); } annunciatorHolder.setLoadAnn(loadedDna); if(!cancGlobalUpdate) { updateAllDnaSlots(); } } function updateAllDnaSlots() { for(var i = 1; i <= dnaSlots.length; i++) { updateDnaSlot(i); } } function updateDnaSlot(slot) { var t = dnaSlots[slot-1], dna = dnaDetails[slot - 1]; if (dna == null) dna = dnaDetails[slot - 1] = new dnaSlotInfo(); if(dna.seq == null) { setAnnunciator("#annDnaEmp" + slot, true); $("#dnaSequence" + slot).text(""); setButtonEnabled("#btnDnaClear" + slot, false); } else { setAnnunciator("#annDnaEmp" + slot, false); $("#dnaSequence" + slot).text(dna.seq); setButtonEnabled("#btnDnaClear" + slot, true); } if(loadedDna || dna.seq === null) { setButtonEnabled("#btnDnaLoad" + slot, false); } else { setButtonEnabled("#btnDnaLoad" + slot, true); } if(loadedDna && dna.seq === null && !loadedDna.isSplicing){ setButtonEnabled("#btnDnaSave" + slot, true); } else { setButtonEnabled("#btnDnaSave" + slot, false); } if( loadedDna && dna.seq !== null && !loadedDna.isSplicing) { setButtonEnabled("#btnDnaXchg" + slot, true); } else { setButtonEnabled("#btnDnaXchg" + slot, false); } if( !(loadedDna && loadedDna.isSplicing) ) { if(slot == exposedSlot ) { setAnnunciator("#annDnaExp" + slot, true); setButtonEnabled("#btnDnaSlotExpose" + slot, false); updateExposedIndicator(); } else { setAnnunciator("#annDnaExp" + slot, false); setButtonEnabled("#btnDnaSlotExpose" + slot, true); } } else { setButtonEnabled("#btnDnaSlotExpose" + slot, false); } updateSpliceSlot(slot); //Tied in with the load / save functionality } /* SPLICING */ /* VARS */ var spliceSourceDna = null, selectedSpliceSource = 0; /* WORKERS */ function createSplicingDnaSlots(num) { $("#spliceSlots").empty() for (var i = 1; i <= num; i++) { var finalObj = $("
", { id: "spliceSlot" + i, 'class': 'dna-slot', 'data-id': i }).appendTo("#spliceSlots"); //ROW OF ANNUNCIATORS + BUTTONS var tempObj = $("
", { 'class':'noborder' }).appendTo(finalObj); //Slot field $("
", { 'class':'text-field tf-narrow' }).appendTo(tempObj).text(i); /* //Target annunciator $("
", { id:'annSpliceTarget' + i, 'class':'annunciator a-green' }).appendTo(tempObj).text("TARGET"); */ //Source annunciator $("
", { id:'annSpliceSource' + i, 'class':'annunciator a-green' }).appendTo(tempObj).text("SOURCE"); //Load button $("
", { id:'btnSpliceLoad' + i, 'class':'button btn-small' }).appendTo(tempObj).text("LOAD"); //Splice button $("
", { id:'btnSpliceSource' + i, 'class':'button btn-small' }).appendTo(tempObj).text("SOURCE"); //SEQUENCE LISTING tempObj = $("
", { 'class':'noborder' }).appendTo(finalObj); $("", { 'class':'label'}).appendTo(tempObj).text("Seq:"); $("
", { id:'txtSpliceSeq' +i, 'class':'text-field tf-long' }).appendTo(tempObj); } //Bind listeners $("#spliceSlots .button").click(function() {handlePushButtonClick(this, handleSpliceSelectionClick);}); } function setSpliceSource(slot) { var lastSlot = selectedSpliceSource; selectedSpliceSource = slot; if (lastSlot > 0) { updateSpliceSlot(lastSlot); } if (selectedSpliceSource > 0) { updateSpliceSlot(selectedSpliceSource); } if(slot === 0) { xmit({cancel:1}); } else { xmit({splice:slot}); } updateSpliceCommenceButtons(); } function beginSplice() { //Validate UI state var dna = dnaDetails[selectedSpliceSource-1]; if(exposedSlot > 0 || dna.seq == null || !loadedDna) { return; } //Clear source DNA spliceSourceDna = dna; dnaDetails[selectedSpliceSource-1] = new dnaSlotInfo(); //Set DNA status selectedSpliceSource=0; loadedDna.isSplicing = true; xmit({beginsplice:1}); updateCompleteSpliceData(loadedDna.seq, true, spliceSourceDna.seq, true); updateLoadedDependents(); setActivePage(4); updateMainMenuButtons(); } function cancelSplice() { setSpliceSource(0); } function parseDna(jqTarget, dnaData, bRaw) { if(bRaw === undefined) {bRaw = false;} var t = $(jqTarget); t.empty(); if(typeof(dnaData) !== 'string') {return;} var i = 0; if(bRaw) { //We got an entire DNA string. Skip the private component var i = dnaData.search(/\|\|/) - 3; //lol if(i < 0) { return; } } while (i < dnaData.length) { var s = ""; if(dnaData[i] === '|') { s = '|'; i += 1; } else if(i + 3 <= dnaData.length ) { s = dnaData.slice(i,i+3); i += 3; } $("", { 'class':'splice-ent'}).appendTo(t).text(s); } //Bind handler $(".splice-ent", t).click(function() {handleSpliceSequenceClick(this);}); t.children().first().addClass("splice-selected"); updateSpliceControls(); } function shiftSelectedSpliceSeq(jqT, dir) { var t = $(jqT), curSel = $(".splice-selected", jqT); dir = dir/Math.abs(dir); if(dir < 0) { curSel = curSel.prev(); } else { curSel = curSel.next(); } if(curSel.length) { $(".splice-selected", jqT).removeClass("splice-selected"); curSel.addClass("splice-selected"); } sendSelectionUpdate(curSel.index(), t.attr("id") === "spliceTargetField"); updateSpliceControls(); } function doSpliceAction(dir) { if(dir != 0) {dir= dir/Math.abs(dir);} var s = $("#txtSpliceSource .splice-selected"), t = $("#txtSpliceTarget .splice-selected"), iRel = 0, //Relative position to index iPos = 0, //Position from which the item is added / removed iTargetPos =0; //The index holding the value we add if(s.length===0 && dir != 0) { return; } //No sense in continuing without a target if(dir != 0) { //Shift source selection if(s.next().length > 0) { s.next().addClass("splice-selected").siblings().removeClass("splice-selected"); } else if(s.prev().length > 0) { s.prev().addClass("splice-selected").siblings().removeClass("splice-selected"); } t.removeClass("splice-selected"); } else if(t.length) { //Shift target selection if(t.next().length > 0) { t.next().addClass("splice-selected"); } else if(t.prev().length > 0) { t.prev().addClass("splice-selected"); } } iPos = t.index(); iSourcePos = s.index(); //Perform the operation if(t.length===0 && dir != 0) { s.appendTo("#txtSpliceTarget"); s.addClass("splice-selected"); } else if ( dir > 0 ) { s.insertAfter(t); iRel = 1; s.addClass("splice-selected"); } else if ( dir < 0 ) { s.insertBefore(t); iRel = -1; s.addClass("splice-selected"); } else { t.remove(); } updateSpliceControls(); xmit({splicemod:1, SourcePos:iSourcePos,pos:iPos,rel:iRel}, "setUIState"); } function finishSplice() { xmit({splicefinish:1}, "handleSpliceCompletionCallback") } function selectSpliceByIndex(index, container) { index--; //BYOND's index is 1-based var c = $(".splice-ent", $(container)); if(index >= 0 && index < c.length) { c.removeClass("splice-selected"); $(c.get(index)).addClass("splice-selected"); } } function sendSelectionUpdate(index, isTarget) { var d = {splicesel:index}; if(isTarget) {d.target=1;} xmit(d); } /* HANDLERS */ function handleSpliceSelectionClick(clicked) { var slot = $(clicked).closest(".dna-slot").attr("data-id"); if (slot == null) return; var id = clicked.id.slice(0, clicked.id.length - slot.toString().length); switch(id) { case "btnSpliceLoad": if (loadedDna) { doExchangeDna(slot); } else { doLoadDna(slot); } break; case "btnSpliceSource": setSpliceSource(slot); break; } } function handleSpliceCommenceButtonClick(clicked) { switch (clicked.id) { case "btnSpliceBegin": beginSplice(); break; case "btnSpliceCancel": cancelSplice(); break; } } function handleSpliceSequenceClick(clicked) { var tf = $(clicked).closest(".text-field"); $(".splice-ent", tf).removeClass("splice-selected"); $(clicked).addClass("splice-selected"); sendSelectionUpdate($(clicked).index(), tf.attr("id") === "txtSpliceTarget"); updateSpliceControls(); } function handleSpliceTargetOffsetClick(clicked) { var t = $(clicked).closest(".splice-sequence"), d = $(clicked).attr("dir"); shiftSelectedSpliceSeq(t, d); } function handleSpliceActionClick(clicked) { var d = $(clicked).attr("dir"); doSpliceAction(d); } function handleSpliceFinishClick(clicked) { finishSplice(); } function setSplicePred(pred) { /*Expects {pred:0,1,2,4 //Bitflags for SUCCESS, UNKNOWN and FAIL } */ setAnnunciator("#annPredSuccess", pred & 1); setAnnunciator("#annPredUnk", pred & 2); setAnnunciator("#annPredFail", pred & 4); } function handleSpliceCompletionCallback(data) { /*Expects {success:0,1 //If the splicing succeeded or not. newseq:"00011512452..." //The newly created DNA sequence } */ setAnnunciator("#annSpliceSuccess", data.success); setAnnunciator("#annSpliceFail", !data.success); if(data.success) { loadedDna.seq = data.newseq; loadedDna.isSplicing=false; } else { loadedDna = null; } spliceSourceDna = null; updateSpliceControls(); updateLoadedDependents(); updateMainMenuButtons(); } remoteHandlers.handleSpliceCompletionCallback = handleSpliceCompletionCallback; /* UPDATERS */ function updateSpliceSlot(slot) { var dna = dnaDetails[slot -1]; if (dna == null) { dna = dnaDetails[slot - 1] = new dnaSlotInfo(); } //Annunciators if (dna.seq == null) { $("#txtSpliceSeq" + slot).text(""); if(selectedSpliceSource == slot) { selectedSpliceSource=0; } } else { $("#txtSpliceSeq" + slot).text(dna.seq); } setAnnunciator("#annSpliceSource" + slot, slot == selectedSpliceSource); //BUTTONS setButtonEnabled("#btnSpliceLoad" + slot, dna.seq != null); setButtonEnabled("#btnSpliceSource" + slot, slot !== selectedSpliceSource && dna.seq != null); } function updateSpliceCommenceButtons() { var a = exposedSlot > 0, b = selectedSpliceSource > 0 && dnaDetails[selectedSpliceSource-1].seq != null, c = loadedDna != null; setAnnunciator("#annSpliceStatExp", a); setAnnunciator("#annSpliceStatSource", b); setAnnunciator("#annSpliceStatTarget", c); setButtonEnabled("#btnSpliceBegin", !a && b && c); } function updateSpliceControls() { var bValidSplice = loadedDna != null && loadedDna.isSplicing; //FINISH setButtonEnabled("#btnSpliceFinish", bValidSplice); //TARGET setButtonEnabled("#spliceTargetField .button", bValidSplice && $("#txtSpliceTarget span").length > 1); setAnnunciator("#annSpliceTargetEmpty", $("#txtSpliceTarget span").length <= 0); //SOURCE setButtonEnabled("#spliceSourceField .button", bValidSplice && $("#txtSpliceSource span").length > 1); setAnnunciator("#annSpliceSourceEmpty", $("#txtSpliceSource span").length <= 0); //BUTTONS setButtonEnabled("#spliceActions .button[dir!='0']", bValidSplice && $("#txtSpliceSource .splice-selected").length > 0); setButtonEnabled("#spliceActions .button[dir='0']", bValidSplice && $("#txtSpliceTarget .splice-selected").length > 0); } function updateCompleteSpliceData(target, btRaw, source, bsRaw) { //Update Splicing Source menu items parseDna("#txtSpliceTarget", target, btRaw); parseDna("#txtSpliceSource", source, bsRaw); setAnnunciator("#spliceFinalButtons > .annunciator", false); setAnnunciator("#annPredSuccess", true); } /* GENERAL */ /* UI STATE UPDATER */ function setUIState(data) { bSuppressXmit = true; //Which page was used last if(data.hasOwnProperty("actPage")) { setActivePage(data.actPage); } //Which DNA sequence is currently loaded if(data.hasOwnProperty("loadedDna")) { loadedDna = data.loadedDna; updateLoadedDependents(cancGlobalUpdate=true); } //The DNA slots themselves if(data.hasOwnProperty("dnaDetails") && typeof(data.dnaDetails) === 'object') { for(var i = 0; i < data.dnaDetails.length; i++) { var d = data.dnaDetails[i]; if (!d) { d = new dnaSlotInfo();} dnaDetails[i] = d; updateDnaSlot(i+1); } } //If a slot is exposed if(data.hasOwnProperty("exposed")) { doExposeSlot(data.exposed, true); } //Splicing if(data.hasOwnProperty("splice")) { var s = data.splice; //{"source":'dna string', "sRaw":0, "target":'dna-string', "tRaw":1, "selected":0, "pred":(0,1,2,4)} if(s.hasOwnProperty("source") || s.hasOwnProperty("target")) { var source = null; if(s.source) { source = s.source; } var target = null; if(s.target) { target = s.target; } updateCompleteSpliceData(target, false, source, false); updateSpliceControls(); } if(s.hasOwnProperty("selected")) { setSpliceSource(s.selected); } if(s.hasOwnProperty("pred")) { setSplicePred(s.pred); } if(s.hasOwnProperty("selSource")) { selectSpliceByIndex(s.selSource, "#txtSpliceSource"); } if(s.hasOwnProperty("selTarget")) { selectSpliceByIndex(s.selTarget, "#txtSpliceTarget"); } } if (data.hasOwnProperty("analysis")) { var a = data.analysis; // { "curr":"ABCDEF..." "prev":{analysisCallback(see below)}, "buttons":'ABCDEF'} if(a.hasOwnProperty("curr")) { clearAnalysisBuffer(true); for(var i = 0; i < a.curr.length && i < 15; i+=3) { addSequence(a.curr.slice(i, i+3)); } } if(a.hasOwnProperty("predeffect")) { $(".txtPredEffect").text((Math.round(a.predeffect * 100) / 100.0) + ' %'); //lol fuck you js } if(a.hasOwnProperty("buttons")) updateAnalysisButtonsFromString(a.buttons); if(a.hasOwnProperty("prev")) { /* {valid:true/false, stable:1,0,-1, trans,1,0,-1, seqs:["123", "456" ...] conf:[25, 100 ...]} */ a.prev.noclear = true; handleAnalysisTestCallback(a.prev); } updateAnalysisControlElements(); } bSuppressXmit = false; } /* DATA TRANSMISSION */ function xmit(data, callbackHandler) { if(bSuppressXmit || !typeof(data) == 'object' ) { return; } /* if(callbackHandler) { data.callbackHandler = callbackHandler; setTimeout(function () {receiveData(callbackHandler, debug_getCallbackDummyData(callbackHandler)) }, Math.random() * 4000); setAnnunciator("#annSynch", true); } */ var params = []; for(var k in data) { params.push(encodeURI(k) + "=" + encodeURI(data[k])); } var target = "?src=" + src + ";" + params.join(";"); //console.log(target); window.location = target; //alert(target); } /* DATA RECEIPT */ function receiveData(handler, data) { setAnnunciator("#annSynch", false); //alert("Received data: " + data + "\nProcessing with: " + handler); if(remoteHandlers[handler]) { data = JSON.parse(data); remoteHandlers[handler](data); } } /*WORKERS*/ function doEjectSample() { if(exposedSlot > 0) { xmit({eject:1}); dnaDetails[exposedSlot-1] = new dnaSlotInfo(); updateDnaSlot(exposedSlot); updateExposedIndicator(); } } function updateLoadedDependents(cancGlobalUpdate, externalManip ) { if(cancGlobalUpdate === undefined) { cancGlobalUpdate = false;} if(externalManip === undefined) {externalManip=false;} updateLoadedSection(cancGlobalUpdate); safeSetMutationData(externalManip); updateManipReady(); updateAnalysisControlElements(); updateSpliceCommenceButtons(); updateMainMenuButtons(); } /* HANDLERS */ function handleMainMenuClick(id) { if($("#" + id).hasClass("button-disabled")) {return;} if(!isSplicing()) { //Not splicing, everything's fine + dandy with the world. /* $("#mainMenu > .button").removeClass("button-selected"); $("#" + id).addClass("button-selected"); */ switch(id) { case "btnManip": setActivePage(2); break; case "btnSplice": setActivePage(3); break; case "btnTester": setActivePage(5); break; case "btnLoadSave": setActivePage(1); break; case "btnRetMain": setActivePage(0); } } } function handleExposeButtons(clicked) { switch (clicked.id) { case "btnCloseSlot": doExposeSlot(0); break; case "btnEjectSample": doEjectSample(); break; } } function handlePushButtonClick(clicked, handler) { if($(clicked).hasClass("button-disabled")) {return 0;} $(clicked).addClass("button-selected"); setTimeout(function() {$(clicked).removeClass("button-selected");}, 50); if(handler) { handler(clicked); } return 1; } /* UPDATERS */ function updateExposedIndicator() { var expDna = dnaDetails[exposedSlot -1]; if(exposedSlot > 0) { setAnnunciator("#annSlotExp", true); setButtonEnabled("#btnCloseSlot", true); $("#txtExpSlot").text(exposedSlot.toString()); } else { setAnnunciator("#annSlotExp", false); setButtonEnabled("#btnCloseSlot", false); $("#txtExpSlot").text("0"); } if(!expDna || expDna.seq === null) { setAnnunciator("#annSlotSample", false); setButtonEnabled("#btnEjectSample", false); } else { setAnnunciator("#annSlotSample", true); setButtonEnabled("#btnEjectSample", true); } } function updateMainMenuButtons() { if (!isSplicing()) { setButtonEnabled("#mainMenu > .button", true); } else { setButtonEnabled("#mainMenu > .button", false); setActivePage(4); $("#mainMenu > .button").removeClass("button-selected"); setButtonEnabled("#btnSplice", true) $("#btnSplice").addClass("button-selected"); } } /* HELPERS */ function safeAddClass(ident, className) { if(!$(ident).hasClass(className)) { $(ident).addClass(className); } } function isSplicing() { return (loadedDna && loadedDna.isSplicing); } /* CONTROL SETTERS */ function setAnnunciator(jqSel, bActive) { //Sets the state of an annunciator in a sane fashion var ann = $(jqSel); if(!ann.hasClass("annunciator")) { return 1; } var color = "red"; if(ann.hasClass("a-green")) { color = "green"; } else if (ann.hasClass("a-yellow")) { color = "yellow"; } if(bActive) { safeAddClass(ann, "a-" + color + "-on"); } else { ann.removeClass("a-" + color + "-on"); } return 0; } function setButtonEnabled(jqSel, bEnable) { var btn = $(jqSel); if(!btn.hasClass("button")) { return 1; } if(bEnable) { btn.removeClass("button-disabled"); } else { safeAddClass(btn, "button-disabled"); } }; remoteHandlers.setUIState = setUIState; /* DEBUG */ function debug_createData() { var dataObject = {src:'AB12CD', actPage:3, exposed:2, loadedDna:{seq:"00020006FFFDFFF60000FFF53142E||FCB055|6A86A8|0F4CC6CC6", pathogenName:"TEST", pathogenType:"gmcell", isSplicing:1}, dnaDetails:[ {seq:"00020006000500000000FFFB3142E||055", pathogenName:"ABC2", pathogenType:"bacteria", isSplicing:0}, null, {seq:"00040003FFED0005FFF3FFEF51B8E||FCB", pathogenName:"ABC3", pathogenType:"parasite", isSplicing:0} ], splice: { source:"123|ABC||321|CBAFED", sRaw:0, target:"ABC345DEF|ADE||DDA|DDF", tRaw:0, selected:0, }, analysis: { curr:["ABC", "DEF", "123", "456"], prev:{ valid:1, trans:1, stable:-1, seqs:["ABC", "DEF", "123", "456"], conf:[100, 75, 34, 15], pred:25, buttons:"FFFEEEDDDCCCAAA999888777666555444333222111000" } } }; setUIState(dataObject); //new (seq, pathogenName, pathogenType, exposed, isSplicing) /* dnaDetails.push(new dnaSlotInfo("00020006FFFDFFF60000FFF53142E||FCB055|6A86A8|0F4CC6CC6", "ABC1", "virus", false)); dnaDetails.push(new dnaSlotInfo("00020006000500000000FFFB3142E||055", "ABC2", "bacteria", false)); dnaDetails.push(new dnaSlotInfo("00040003FFED0005FFF3FFEF51B8E||FCB", "ABC3", "parasite", false)); updateLoadedSection(); for(var i = 1; i <= 3; i++){ updateDnaSlot(i); } doExposeSlot(0); */ } function debug_createDNAbuttons() { var L = [] for(var i = 0; i < 50; i++) { var s = Math.round(4096 * Math.random()); s = '00' + s.toString(16); s = s.slice(s.length - 3); L.push(s.toUpperCase()); } updateAnalysisButtons(L); } function debug_getCallbackDummyData( callbackHandler ) { var d = dummyData[callbackHandler]; var r = null; if(typeof(d) === 'object') { var maxLen = d.length -1; r = d[Math.round(maxLen * Math.random())]; } return r; } window.receiveData = receiveData; $(document).ready(initializeUI); })(window, document, jQuery);