Files
2016-03-06 21:35:53 +01:00

1535 lines
40 KiB
JavaScript

(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 = "<span style='color:" + c +";'>" + t + "</span>"
}
$(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++) {
$("<div>", {
'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 = $("<div>", {
id: "dnaSlot" + i,
'class':'dna-slot',
'data-id': i
});
//Container to hold values
tempObj = $("<div></div>", {
'class':'noborder'
}).appendTo(finalObj);
//Slot counter
$("<div>", {
'class':"text-field tf-narrow"
}).appendTo(tempObj).text(i.toString());
//Annunciators
$("<div>", {
id: 'annDnaEmp' + i,
'class':'annunciator a-red'
}).appendTo(tempObj).text("EMPTY");
$("<div>", {
id: 'annDnaExp' + i,
'class':'annunciator a-yellow'
}).appendTo(tempObj).text("EXPOSED");
$("<div>", {
id: 'btnDnaLoad' + i,
'class':'button btn-small',
}).appendTo(tempObj).text("LOAD");
$("<div>", {
id: 'btnDnaSave' + i,
'class':'button btn-small',
}).appendTo(tempObj).text("SAVE");
$("<div>", {
id: 'btnDnaXchg' + i,
'class':'button btn-small',
}).appendTo(tempObj).text("XCHG");
$("<div>", {
id: 'btnDnaClear' + i,
'class':'button btn-small',
}).appendTo(tempObj).text("CLEAR");
$("<div>", {
id: 'btnDnaSlotExpose' + i,
'class':'button btn-small',
}).appendTo(tempObj).text("EXPOSE");
//new tempObj for the next row
tempObj = $("<div></div>", {
'class':'noborder'
}).appendTo(finalObj);
$("<span>", {'class':'label'}).appendTo(tempObj).text("Seq:");
$("<div>", {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 = $("<div></div>", {
id: "spliceSlot" + i,
'class': 'dna-slot',
'data-id': i
}).appendTo("#spliceSlots");
//ROW OF ANNUNCIATORS + BUTTONS
var tempObj = $("<div></div>", {
'class':'noborder'
}).appendTo(finalObj);
//Slot field
$("<div></div>", {
'class':'text-field tf-narrow'
}).appendTo(tempObj).text(i);
/*
//Target annunciator
$("<div></div>", {
id:'annSpliceTarget' + i,
'class':'annunciator a-green'
}).appendTo(tempObj).text("TARGET");
*/
//Source annunciator
$("<div></div>", {
id:'annSpliceSource' + i,
'class':'annunciator a-green'
}).appendTo(tempObj).text("SOURCE");
//Load button
$("<div></div>", {
id:'btnSpliceLoad' + i,
'class':'button btn-small'
}).appendTo(tempObj).text("LOAD");
//Splice button
$("<div></div>", {
id:'btnSpliceSource' + i,
'class':'button btn-small'
}).appendTo(tempObj).text("SOURCE");
//SEQUENCE LISTING
tempObj = $("<div></div>", {
'class':'noborder'
}).appendTo(finalObj);
$("<span></span>", {
'class':'label'}).appendTo(tempObj).text("Seq:");
$("<div></div>", {
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;
}
$("<span>", {
'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);