diff --git a/assets/js/needlemanwunsch.js b/assets/js/needlemanwunsch.js index 9e96642..a275008 100644 --- a/assets/js/needlemanwunsch.js +++ b/assets/js/needlemanwunsch.js @@ -6,14 +6,32 @@ var gap = document.getElementById("gap"); const table = document.getElementById('table1'); -createTable() -s1.addEventListener("keyup", createTable); -s2.addEventListener("keyup", createTable); -match.addEventListener("keyup", wipeTable); -mismatch.addEventListener("keyup", wipeTable); -gap.addEventListener("keyup", wipeTable); -function createTable() { - const table = document.getElementById('table1'); +createTables() +s1.addEventListener("keyup", createTables); +s2.addEventListener("keyup", createTables); +match.addEventListener("keyup", createTables); +mismatch.addEventListener("keyup", createTables); +gap.addEventListener("keyup", createTables); + + +function createHeader(name, sub) { + var subscript = document.createElement('sub'); + var d = document.createElement('P'); + subscript.textContent = sub; + d.textContent = name; + d.appendChild(subscript); + return d + +} + +function createTables() { + var d = createHeader("D", "i,j") + createTable("table1", d); + +} + +function createTable(tid, headContent) { + const table = document.getElementById(tid); while (table.firstChild) { table.removeChild(table.firstChild); } @@ -28,34 +46,57 @@ function createTable() { var td; if ( row == 0 && col == 0 ) { td = document.createElement('th') - td.textContent = "D(ij)"; + td.appendChild(headContent) } else if (row == 0) { td = document.createElement('th'); + var subscript = document.createElement('sub'); + subscript.textContent = col-1; td.textContent = s2[col-1]; + td.appendChild(subscript); + } else if (col == 0) { td = document.createElement('td'); + var subscript = document.createElement('sub'); + subscript.textContent = row-1; td.textContent = s1[row-1]; td.style.fontWeight = "bold"; td.style.width = "5%" td.style.textAlign = "center"; td.style.border = "2px solid"; + td.appendChild(subscript) } else { td = document.createElement('td'); + const newDiv = document.createElement('div'); + newDiv.classList.add("full-parent") + newDiv.style.zIndex=1; + const newSpan = document.createElement('span'); + newSpan.classList.add("smallText") + newDiv.appendChild(newSpan) + var input = document.createElement("input"); - input.type = "number"; input.name = "member" + row + "-" + col; input.style.backgroundColor = "transparent" + input.style.zIndex=2; + td.appendChild(input); + td.appendChild(newDiv) + newDiv.addEventListener('click', function() { + // Toggle the 'highlight' class + newDiv.classList.toggle('selectedTracebackCell'); + newDiv.style.border = null; + }); + } tr.appendChild(td); } table.appendChild(tr); } } + function checkTable() { var s1 = document.getElementById("seqA").value; var s2 = document.getElementById("seqB").value; @@ -83,18 +124,26 @@ function checkTable() { } } -function wipeTable() { - const table = document.getElementById('table1'); +function wipeTable(tid) { + if (typeof tid === 'undefined') {return ""} + const table = document.getElementById(tid); var s1 = document.getElementById("seqA").value; var s2 = document.getElementById("seqB").value; for (let row = 0; row < s1.length + 1; row++) { for (let col = 0; col < s2.length + 1; col++) { const cellElement = table.rows[row + 1].cells[col + 1] - cellElement.style.backgroundColor = "white" + cellElement.style.backgroundColor = null; + cellElement.childNodes[0].value = ""; + cellElement.childNodes[1].style.backgroundColor = null; + cellElement.childNodes[1].style.border = null; } } } +function wipeTables() { + wipeTable("table1"); +} + function needlemanWunsch(seqA, seqB, matchScore, mismatchScore, gapPenalty) { matchScore = Number(matchScore); mismatchScore = Number(mismatchScore); @@ -131,6 +180,29 @@ function needlemanWunsch(seqA, seqB, matchScore, mismatchScore, gapPenalty) { return matrix; } +function fillTable() { + var s1 = document.getElementById("seqA").value; + var s2 = document.getElementById("seqB").value; + var match = parseInt(document.getElementById("match").value); + var mismatch = parseInt(document.getElementById("mismatch").value); + var gapPenalty = parseInt(document.getElementById("gap").value); + + matrix = needlemanWunsch(s1, s2, match, mismatch, gapPenalty); + + const table = document.getElementById(`table1`); + + for (let row = 0; row < s1.length + 1; row++){ + for (let col = 0; col < s2.length + 1; col++){ + const cellElement = table.rows[row+1].cells[col+1] + let expected = matrix[row][col] + + + cellElement.childNodes[0].value = expected; + } + } +} + + document.addEventListener('DOMContentLoaded', function () { const table = document.getElementById('table1'); @@ -167,3 +239,373 @@ document.addEventListener('DOMContentLoaded', function () { }); }); +function swapTableMode(tid, tracebackMode) { + const table = document.getElementById(tid); + var s1 = document.getElementById("seqA").value; + var s2 = document.getElementById("seqB").value; + if (tracebackMode) { + for (let row = 0; row < s1.length + 1; row++) { + for (let col = 0; col < s2.length + 1; col++) { + const cellElement = table.rows[row + 1].cells[col + 1] + const ip = cellElement.childNodes[0]; + const div = cellElement.childNodes[1]; + div.style.zIndex = 2; + ip.style.zIndex = 1; + + } + } + + } else { + for (let row = 0; row < s1.length + 1; row++) { + for (let col = 0; col < s2.length + 1; col++) { + const cellElement = table.rows[row + 1].cells[col + 1] + const ip = cellElement.childNodes[0]; + const div = cellElement.childNodes[1]; + div.style.zIndex = 1; + ip.style.zIndex = 2; + + } + + } + + } + +} +function swapTableModes(traceBackMode) { + swapTableMode("table1", traceBackMode); +} + +const toggleCheckbox = document.getElementById('tracebackToggle'); +const divToHide = document.getElementById('tableCheckBtn'); +const divToShow = document.getElementById('tracebackCheckBtn'); + +function determineMode(divToShow, divToHide) { + // If the checkbox is checked, show divToShow and hide divToHide + if (toggleCheckbox.checked) { + divToShow.classList.remove('hidden'); + divToHide.classList.add('hidden'); + swapTableModes(true); + } else { + // If the checkbox is not checked, hide divToShow and show divToHide + divToShow.classList.add('hidden'); + divToHide.classList.remove('hidden'); + swapTableModes(false); + + } +} + +determineMode(divToShow, divToHide); + +toggleCheckbox.addEventListener('change', function(){determineMode(divToShow, divToHide)}); + +// ____________________________________________________________ + +function getScoringAndSeqs() { + var s1 = document.getElementById("seqA").value; + var s2 = document.getElementById("seqB").value; + var match = parseInt(document.getElementById("match").value); + var mismatch = parseInt(document.getElementById("mismatch").value); + var gapIntro = parseInt(document.getElementById("gap").value); + scoring = {"seqA": s1, "seqB": s2, "match": match, "mismatch": mismatch, "gap_introduction": gapIntro} + return scoring + +} + +function buildTracbackPathes(){ + var scoring = getScoringAndSeqs(); + matrix = needlemanWunsch( + scoring["seqA"], + scoring["seqB"], + scoring["match"], + scoring["mismatch"], + scoring["gap_introduction"], + ) + + list_pathes = buildAllTracebackPathsCorrect( + scoring["seqA"], scoring["seqB"], scoring, matrix + ); + return list_pathes; +} + + + +function checkTracebackTable(tid, s1, s2){ + const table = document.getElementById(`table${tid}`); + var l = [] + for (let row = 0; row < s1.length + 1; row++) { + for (let col = 0; col < s2.length + 1; col++) { + const cellElement = table.rows[row + 1].cells[col + 1] + const tbdiv = cellElement.childNodes[1] + if (tbdiv.classList.contains('selectedTracebackCell')){ + l.push([row, col]) + } + } + } + return l +} + + + +function stringifyArray(arr) { + const [a, b] = arr; + return `(${a},${b})`; +} + +function arraysEqualIgnoreOrder(arr1, arr2) { + arr1s = [] + arr2s = [] + + + arr1.forEach((arr) => { + const result = stringifyArray(arr); + arr1s.push(result); + }); + arr2.forEach((arr) => { + const result = stringifyArray(arr); + arr2s.push(result); + }); + + const set1 = new Set(arr1s); + const set2 = new Set(arr2s); + if (set1.size !== set2.size) { + return false; + } + + for (const item of set1) { + if (!set2.has(item)) { + + return false; + } + } + + return true; +} + + + +function highlightCell(tid, i, j, color) { + const table = document.getElementById(tid); + const cellElement = table.rows[i+1].cells[j+1]; + cellElement.childNodes[1].style.border = `2px solid ${color}`; +} + +function previousCellsCorrect(seq1, seq2, scoring, swMatrix, cell) { + const prevCells = []; + console.log(cell, "cell") + const [row, column] = cell; + + const top = row > 0 ? [row - 1, column] : null; + const left = column > 0 ? [row, column - 1] : null; + const diagonal = row > 0 && column > 0 ? [row - 1, column - 1] : null; + + const curVal = swMatrix[row][column]; + const charFirst = seq1[row - 1]; + const charSecond = seq2[column - 1]; + const matchScore = charFirst === charSecond ? scoring.match : scoring.mismatch; + const gapScore = scoring.gap_introduction; + + if (diagonal) { + const diagonalVal = swMatrix[diagonal[0]][diagonal[1]]; + if (diagonalVal + matchScore === curVal) { + prevCells.push(diagonal); + } + } + + if (top) { + const topVal = swMatrix[top[0]][top[1]]; + if (topVal + gapScore === curVal) { + prevCells.push(top); + } + } + + if (left) { + const leftVal = swMatrix[left[0]][left[1]]; + if (leftVal + gapScore === curVal) { + prevCells.push(left); + } + } + + return prevCells; +} + +function buildAllTracebackPathsCorrect(seq1, seq2, scoring, swMatrix) { + const listTracebackPaths = []; + + const flat = [].concat(...swMatrix); + const maxVal = Math.max(...flat); + + + const frontier = [[[swMatrix.length - 1, swMatrix[0].length - 1]]]; + + while (frontier.length > 0) { + const partialPath = frontier.pop(); + const lastCellPartial = partialPath[partialPath.length - 1]; + const nextSteps = previousCellsCorrect(seq1, seq2, scoring, swMatrix, lastCellPartial); + console.log(lastCellPartial, nextSteps) + + for (const nextStep of nextSteps) { + const newTracebackPath = [...partialPath, nextStep]; + const [row, column] = nextStep; + const matrixValue = swMatrix[row][column]; + + + if (row === 0 && column === 0) { + listTracebackPaths.push(newTracebackPath); + } else { + frontier.push(newTracebackPath); + } + } + } + console.log(listTracebackPaths) + + return listTracebackPaths; +} + +var l1 = document.getElementById("alignment-line1"); +var l2 = document.getElementById("alignment-line2"); +const edges = document.getElementById("alignment-edges") + +function addAlignmentEdges(){ + line1 = l1.value; + line2 = l2.value; + var alignmentString = ""; + for (var i = 0; i < line1.length; i++) { + var char1 = line1[i]; + var char2 = line2[i]; + + + if (char1 === "-" || char2 === "-" || char2 === undefined) { + // Whitespace if either character is "-" + alignmentString += " "; + } else if (char1 === char2) { + // "|" if characters match + alignmentString += "|"; + } else { + // ":" if characters don't match + alignmentString += ":"; + } + } + edges.value = alignmentString; + edges.setAttribute('value', 'defaultValue'); + + +} +addAlignmentEdges() +l1.addEventListener("keyup", addAlignmentEdges); +l2.addEventListener("keyup", addAlignmentEdges); + + +function checkTracebacks(){ + scoring = getScoringAndSeqs() + traceback_paths = buildTracbackPathes(); + path = checkTracebackTable("1", scoring["seqA"], scoring["seqB"]) + var correct = false; + for (let p = 0; p < traceback_paths.length; p++) { + console.log("path", traceback_paths[p]) + + if (arraysEqualIgnoreOrder(traceback_paths[p], path)) { + correct = true; + } + } + var color; + if (correct){ + color = "green" + + } else { + color = "red" + } + for (let x = 0; x < path.length; x++) { + var i = path[x][0]; + var j = path[x][1]; + highlightCell(`table1`, i, j, color) + } + if (correct & l1.value.length > 0 & l2.value.length > 0) { + var [as1, as2] = buildAlignmentCorrect(scoring["seqA"], scoring["seqB"], path) + if (as1 === l1.value & as2 === l2.value) { + l1.style.backgroundColor = "var(--right-answer)" + l2.style.backgroundColor = "var(--right-answer)" + edges.style.backgroundColor = "var(--right-answer)" + } else { + l1.style.backgroundColor = "var(--wrong-answer)" + l2.style.backgroundColor = "var(--wrong-answer)" + edges.style.backgroundColor = "var(--wrong-answer)" + } + + } + +} + + +function buildAlignmentCorrect(seq1, seq2, alignmentPath) { + let alignSeq1 = ""; + let alignSeq2 = ""; + if (!alignmentPath.length) { + return ["", ""]; + } + + let prevCell = alignmentPath[0]; + console.log(alignmentPath) + + for (let i = 1; i < alignmentPath.length; i++) { + let prevRow = prevCell[0]; + let prevColumn = prevCell[1]; + + let cell = alignmentPath[i]; + let row = cell[0]; + let column = cell[1]; + + if (row > prevRow && column > prevColumn) { + alignSeq1 += seq1[row - 1]; + alignSeq2 += seq2[column - 1]; + } else if (row > prevRow) { + alignSeq1 += seq1[row - 1]; + alignSeq2 += "-"; + } else if (column > prevColumn) { + alignSeq1 += "-"; + alignSeq2 += seq2[column - 1]; + } + + prevCell = cell; + } + + return [alignSeq1, alignSeq2]; +} + +document.addEventListener('DOMContentLoaded', function () { + const table = document.getElementById('table1'); + + table.addEventListener('keydown', function (event) { + const currentCell = event.target.parentElement; + const currentRow = currentCell.parentElement; + const currentRowIndex = currentRow.rowIndex; + const currentCellIndex = currentCell.cellIndex; + + let nextCell; + + switch (event.key) { + case 'ArrowUp': + nextCell = table.rows[currentRowIndex - 1]?.cells[currentCellIndex]; + break; + case 'ArrowDown': + nextCell = table.rows[currentRowIndex + 1]?.cells[currentCellIndex]; + break; + case 'ArrowLeft': + nextCell = currentCellIndex > 0 ? currentRow.cells[currentCellIndex - 1] : null; + break; + case 'ArrowRight': + nextCell = currentRow.cells[currentCellIndex + 1]; + break; + default: + break; + } + + if (nextCell && nextCell.querySelector('input')) { + nextCell.querySelector('input').focus(); + event.preventDefault(); + } + }); + }); + + + + diff --git a/assets/js/smithwaterman.js b/assets/js/smithwaterman.js index ca38baf..148e5dc 100644 --- a/assets/js/smithwaterman.js +++ b/assets/js/smithwaterman.js @@ -126,8 +126,6 @@ function checkTable() { } - - } diff --git a/exercise-sheet-3.Rmd b/exercise-sheet-3.Rmd index e18f35e..67b9d38 100644 --- a/exercise-sheet-3.Rmd +++ b/exercise-sheet-3.Rmd @@ -133,7 +133,7 @@ The Needleman-Wunsch algorithm enables the calculation of the optimal pairwise s \begin{align} - S1 &= TACCGCGC\\ + S1 &= TACGCGC\\ S2 &= TCCGA \end{align} diff --git a/html/needlemanwunsch.html b/html/needlemanwunsch.html index b521494..be1d046 100644 --- a/html/needlemanwunsch.html +++ b/html/needlemanwunsch.html @@ -25,13 +25,63 @@
-
+
+ +
+ + +
+ + + + +
- +
+
+
- +
+
+ + +
+

Alignment

+ +
+
+ +
+ +
+
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +