From ac093ed3cdd7c086d38cd480f61bba04e3e1b34e Mon Sep 17 00:00:00 2001 From: Adrian Date: Sun, 14 Dec 2025 17:49:00 +0100 Subject: [PATCH 01/48] Implementation of collapse function and first steps of spacing --- matt/static/js.js | 603 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 599 insertions(+), 4 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 3365e18..03b7be0 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -41,6 +41,7 @@ $(function () { let interval; let enableLengths; let alignLabels = true; + let collapsedmap = {}; // Gets the options initially getOptions(); @@ -397,6 +398,9 @@ $(function () { set_testing(xhr.getResponseHeader("Testing")); } + + + // Enables the undo and redo buttons (depending on edge cases) if (counter_of_trees > 1) { $("#undo-button").prop("disabled", false); @@ -417,12 +421,20 @@ $(function () { // Calls the draw function with the chosen tree (with or without branch lengths) if (typeof xhr !== "undefined" && xhr.getResponseHeader("Length")) { + data.forEach(n => { + collapsedmap[n["id"]] = {left: false, right: false}; + }); draw(JSON.parse(trees[counter_of_trees - 1][2])); } else { + draw(JSON.parse(trees[counter_of_trees - 1][1])); } + + } + + /** * Enables or disables testing capabilities * @param testing whether testing should be enabled or not @@ -662,8 +674,20 @@ $(function () { * @param data tree informations */ function draw(data) { + + //const collapsedmap = {}; + if(Object.keys(collapsedmap).length === 0){ + data.forEach(n => { + collapsedmap[n["id"]] = {left: false, right: false, left_id: null, right_id: null, label: "Collapsed", + "l-d": null,"r-d":null, "l_edge-Y": null, "r_edge-Y": null, "collapsed-line-Y":null, + "minimap-r-m": null, "minimap-l-m": null}; + }); + } + + $("#outgroup-button").css("display", "none"); + xBefore = getTransform("x"); yBefore = getTransform("y"); scaleBefore = getTransform("scale"); @@ -727,6 +751,10 @@ $(function () { g = svg.g(); lines = g.g(); + + + + // Draws the frame around the tree g.add(svg.path("M0,0H" + maxX + "V" + maxY + "H0Z").attr({ fill: 'none', @@ -896,6 +924,372 @@ $(function () { toggleOutgroupButton(); } + + /** + * Gets subtree of a givin node + * @param start clicked node + * @returns {*[]} Subtreelist with ids of the subnodes + */ + function getTree(start){ + const children = []; + const stack = [start] + + while(stack.length > 0){ + const a = stack.pop(); + const node = data.find(n => n["id"] === a); + if(node == null) continue; + ["l_child","r_child"].forEach(child => { + const childid = node[child]; + if(childid !== undefined && childid !== null){ + children.push(childid); + stack.push(childid); + } + }); + } + return children.filter(elements => elements); + } + + + /** + * Collapse function. Collapses / Expands a node with the subtree + * @param childitem childnode (l_node / r_node) + * @param start startnode + * @param collapsed_check check if tree at given node is collapsed or not + * @param direct direction of the node (right / left) + */ + function collapse(childitem, start, collapsed_check, direct){ + const sub = getTree(childitem); + //MERGE TREES IF ONE IS ALREADY COLLAPSED + const parent = data.find(d => d["id"] === start); + let side; + + if(direct === "right") side = "r_child"; + if(direct === "left") side = "l_child"; + const child = parent[side]; + if(child === null) return; + + //'GRANDPARENT' Problem fix => remove lines && custom label + const temp = getTree(child); + temp.push(child); + temp.forEach(id =>{ + const clean_lines =svg.selectAll(`[data-collapsed-Line^='${id}']`); + if(clean_lines !== null) clean_lines.forEach(i => i.remove()); + }); + const currentcollapsed = svg.select(`[data-collapsed-Line^='${child}']`); + + if(currentcollapsed !== null){ + svg.selectAll(`[data-collapsed-Line^='${child}']`).remove() + const childsub = getTree(child); + childsub.forEach(c => { + //MERGE: Push all c elements to subtree + if(!sub.includes(c)) sub.push(c); + }); + } + //Resets state for concat collapse + const temptree = getTree(start); + temptree.forEach(childitem => { + if(collapsedmap[childitem]){ + collapsedmap[childitem]["left"] = false; + collapsedmap[childitem]["right"] = false; + } + }); + + // COLAPSE + if(collapsed_check){ + svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "none"}); + minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "none"}); + sub.forEach(child => { + svg.selectAll(`[data-id='${child}']`).attr({display: "none"}); + svg.selectAll(`circle[data-id='${child}']`).attr({display: "none"}); + minimap.selectAll(`path[data-id='${child}']`).attr({display: "none"}); + }); + + + draw_collapsed_line(childitem, start, collapsed_check, direct); + updateSpacing(start, collapsed_check, direct); + + + //EXPAND + }else{ + svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "inline"}); + minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "inline"}); + sub.forEach(child => { + svg.selectAll(`[data-id='${child}']`).attr({display: "inline"}); + svg.selectAll(`circle[data-id='${child}']`).attr({display: "inline"}); + minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); + + + }); + + const temp = getTree(start); + temp.forEach(child => { + if(collapsedmap[child]["l-d"]) svg.select(`path[data-id='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["l-d"]}); + if(collapsedmap[child]["r-d"]) svg.select(`path[data-id='${collapsedmap[child]["right_id"]}']`).attr({d: collapsedmap[child]["r-d"]}); + if(collapsedmap[child]["l_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='left']`).attr({cy: collapsedmap[child]["l_edge-Y"], "data-v":collapsedmap[child]["l_edge-Y"]}); + if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); + }); + + draw_collapsed_line(childitem, start, collapsed_check, direct); + updateSpacing(start, collapsed_check, direct); + console.log(collapsedmap) + + } + } + + /** + * Draws collapsed related stuff. + * @param childitem childnode (l_node / r_node) + * @param start startnode + * @param collapsed_check check if tree at given node is collapsed or not + * @param direct direction of the node (right / left) + */ + function draw_collapsed_line(childitem, start, collapsed_check, direct){ + + const circle = svg.select(`circle[data-id='${start}'][data-direct='${direct}']`); + const sub = getTree(childitem); + const item_counter = sub.filter(id => { + const n = data.find(d => d["id"] === id); + return n["name"] !== "None"; + }).length; + + const item_name_container = sub. + map(id=>data.find(d => d["id"] === id)). + filter(n => n["name"] !== "None"). + map(n => n["name"]); + const preview_containing = item_name_container.slice(0,10).join(", "); + + let coll_line = null; + let nameText = null; + + if(collapsed_check){ + nameText = svg.text(maxX - offset, circle.attr("data-v"), "'"+collapsedmap[start]["label"]+"'"+ " (" + item_counter+ ")").attr({ + dominantBaseline: 'middle', + fontSize: fontSize, + fill: "black", + display: "inline", + "data-collapsed-Line": `${start}-${direct}`, + textAnchor: 'end', + fontStyle: "italic" + }); + g.add(nameText); + + coll_line = svg.line(circle.attr("data-h"), circle.attr("data-v"), maxX - (1.5 * offset) - Math.ceil(nameText.getBBox().width), circle.attr("data-v")).attr({ + stroke: "black", + display: "inline", + "data-collapsed-Line": `${start}-${direct}`, + strokeWidth: 2, + }); + g.add(coll_line); + + //TODO: Draw line for collapsed. + + // coll_minimap_line = minimap.line(); + + nameText.hover( + function (){ + this.node.style.cursor = "pointer"; + this.attr({fill:"#1e90ff"}); + const box = this.getBBox(); + let text; + if(item_counter <= 10) { + text = svg.text(box.x + box.width + 10, box.y + box.height / 2, + "'"+collapsedmap[start]["label"]+"' "+"contains "+item_counter+" taxa: " + preview_containing + ", ... ( 0 More )" ).attr({ + fill: "#171515", + "collapse-hover-id": start + }); + + }else if(item_counter > 10){ + text = svg.text(box.x + box.width + 10, box.y + box.height / 2, + "'"+collapsedmap[start]["label"]+"' "+"contains "+item_counter+" taxa: " + preview_containing + ", ... ( "+ (item_counter-10)+ " More )" ).attr({ + fill: "#171515", + "collapse-hover-id": start + }); + + } + const textbox = text.getBBox(); + const desc = svg.rect(textbox.x - 5, textbox.y-3,textbox.width+80, textbox.height+25,5,5).attr({ + strokeWidth: 0.5, + fill: "#bdbdbd", + stroke: "black", + "collapse-hover-id": start + }); + + g.add(desc); + g.add(text); + + }, + function (){ + this.node.style.cursor = "default"; + this.attr({fill:"#000000"}); + svg.selectAll(`[collapse-hover-id='${start}']`).remove(); + } + ); + + //UPDATE NAMETAG IF NAME CHANGED + nameText.click(function () { + const box = this.getBBox(); + const ctm = this.node.getScreenCTM(); + const point = svg.node.createSVGPoint(); + point.x = box.x; + point.y = box.y; + const spoint = point.matrixTransform(ctm); + const input = document.createElement("input"); + input.classList.add("svg-edit-input"); + input["type"] = "text"; + input["value"] = "New label(Not empty)"; + input["style"]["position"] = "absolute"; + input["style"]["left"] = `${spoint.x}px`; + input["style"]["top"] = `${spoint.y}px`; + document.body.append(input); + input.focus(); + + input.addEventListener("keydown", e=>{ + if(e.key === "Enter"){ + if(input["value"] !== ""){ + const newtext = input["value"]; + collapsedmap[start]["label"] = newtext; + this.attr({ + text: "\""+newtext+"\"" + "("+item_counter+")", + fontStyle: "italic" + }) + const newlinelength = Math.ceil(nameText.getBBox().width); + coll_line.attr({ + x2: maxX - (1.5 * offset) - newlinelength + }); + input.remove(); + } + } + }); + + }); + }else{ + svg.selectAll(`[data-collapsed-Line='${start}-${direct}']`).remove() + } + } + + /** + * Updates the spacing after collapse + * @param start startnode + * @param collapsed_check collapse check (true/false) at certain node + * @param direct (direction: left/right) + */ + function updateSpacing(start, collapsed_check, direct){ + + const node = data.find(d=> d["id"] === start); + + let child; + if(direct === "left") child = node["l_child"]; + if(direct === "right") child = node["r_child"]; + if(!child) return; + + const path = svg.select(`path[data-id='${child}']`); + if(!path) return; + + let directKey; + if(direct === "left")directKey = "l-d"; + if(direct === "right")directKey = "r-d"; + + + if(!collapsedmap[start][directKey]) collapsedmap[start][directKey] = path.attr("d"); + + //TODO: Write this nicer and better + if(collapsed_check){ + let minimapD; + const pathComponents = path.attr("d").split(/[MHV]/).filter(x => x.trim() !== ""); + const mX = parseFloat(pathComponents[0].split(",")[0]); + const mY = parseFloat(pathComponents[0].split(",")[1]); + const hX = parseFloat(pathComponents[2]); + + let newVY; + if(direct === "left"){ + newVY = mY-25; + minimapD = collapsedmap[start]["minimap-l-m"]; + } + if(direct === "right"){ + newVY = mY+25; + minimapD = collapsedmap[start]["minimap-r-m"]; + } + + const minimapAttributes = minimapD.split(/[MHV]/).filter(x => x.trim() !== ""); + const miniDX = parseFloat(minimapAttributes[0].split(",")[0]); + const miniDY = parseFloat(minimapAttributes[0].split(",")[1]); + const miniDhx = parseFloat(minimapAttributes[2]); + let newMiniY ; + let minimapPath; + + + if(direct === "left"){ + minimapPath = minimap.select(`path[data-child='${child}']`); + path.attr({d: `M${mX},${mY} V${newVY} H${hX}`}); + newMiniY = miniDY - 7; + minimapPath.attr({d:`M${miniDX},${miniDY} V${newMiniY} H${miniDhx}`}); + } + if(direct === "right") { + minimapPath = minimap.select(`path[data-child='${child}']`); + path.attr({d: `M${mX},${mY} V${newVY} H${hX}`}); + newMiniY = miniDY + 7; + minimapPath.attr({d:`M${miniDX},${miniDY} V${newMiniY} H${miniDhx}`}); + } + const collapsedItems = svg.selectAll(`[data-collapsed-Line='${start}-${direct}']`); + const circle = svg.select(`circle[data-id='${start}'][data-direct='${direct}']`); + + collapsedItems.forEach(item =>{ + const t = item.type; + + if(t === "text"){ + item.attr({y: newVY}); + } + if(t === "line"){ + item.attr({y1: newVY, y2:newVY}); + } + }); + + if(direct === "left")collapsedmap[start]["l_edge-Y"] = circle.attr("data-v"); + if(direct === "right") collapsedmap[start]["r_edge-Y"] = circle.attr("data-v"); + + + circle.attr({cy: newVY, "data-v":newVY}); + + }else{ + path.attr({d: collapsedmap[start][directKey]}); + const collapsedItems = svg.selectAll(`[data-collapsed-Line='${start}-${direct}']`); + const circle = svg.select(`circle[data-id='${start}'][data-direct='${direct}']`); + let minimapPath; + + collapsedItems.forEach(item =>{ + const t = item.type; + if(t === "text"){ + item.attr({y: collapsedmap[start]["collapsed-line-Y"]}); + } + if(t === "line"){ + item.attr({y1: collapsedmap[start]["collapsed-line-Y"], y2:collapsedmap[start]["collapsed-line-Y"]}); + } + }); + + let oldCircleY; + let oldMinimapD; + if(direct === "left"){ + minimapPath = minimap.select(`path[data-child='${child}']`); + + oldCircleY = collapsedmap[start]["l_edge-Y"]; + oldMinimapD = collapsedmap[start]["minimap-l-m"]; + } + if(direct === "right"){ + minimapPath = minimap.select(`path[data-child='${child}']`); + + oldCircleY = collapsedmap[start]["r_edge-Y"]; + oldMinimapD = collapsedmap[start]["minimap-r-m"]; + } + + circle.attr({cy: oldCircleY, "data-v":oldCircleY}); + minimapPath.attr({d: oldMinimapD}); + + } + + + } + + // Draws the branches and texts data.forEach(function (item, index, array) { // Text @@ -903,7 +1297,6 @@ $(function () { // TODO draw all texts at the right // with path stroke dasharray //g.add(svg.text(item["total_length"] * scaleX + (1.5 * offset), (index + 1) * scaleY, item["name"]).attr({dominantBaseline: 'middle', fontSize: fontSize, 'data-id': item["id"]})); - // Draw at the far right with a dashed line if (alignLabels) { nameText = svg.text(maxX - offset, (index + 1) * scaleY, item["name"]).attr({ @@ -918,7 +1311,8 @@ $(function () { fill: 'none', stroke: 'black', strokeWidth: 2, - strokeDasharray: 4 + strokeDasharray: 4, + "data-id": item["id"] })); } // Draw next to the leaf without a dashed line @@ -930,6 +1324,7 @@ $(function () { }); g.add(nameText); } + // Branch } else { // Draw bootstrap on the line if provided @@ -971,8 +1366,93 @@ $(function () { "data-id": array[r_child]["id"], "data-parent": item["id"] }); + g.add(left, right); + left.hover( + function () { + console.log(item["id"]) + }, function () { + + }); + + right.hover( + function () { + console.log(item["id"]) + }, function () { + + }); + + + + // NODE FOR COLLAPSE / SIMPLIFICATION + const r_edge = svg.circle(hRight, vRight, 8).attr({ + fill: "black", + stroke: "black", + strokeWidth: 2, + "data-id": item["id"], + "data-h": hRight, + "data-v": vRight, + "data-direct": "right", + }); + + // NODE FOR COLLAPSE / SIMPLIFICATION + const l_edge = svg.circle(hLeft, vLeft, 8).attr({ + fill: "black", + stroke: "black", + strokeWidth: 2, + "data-id": item["id"], + "data-h": hLeft, + "data-v": vLeft, + "data-direct": "left", + }); + + l_edge.hover( + function () { + this.node.style.cursor = "pointer"; + console.log(item["id"]) + }, function () { + this.node.style.cursor = "default"; + }); + r_edge.hover( + function () { + this.node.style.cursor = "pointer"; + console.log(item["id"]) + + }, function () { + this.node.style.cursor = "default"; + + }); + + // Leave isn't leave => check one stage before and delete nodes + const r_child_check = data.find(d => d["id"] === item["r_child"]); + const l_child_check = data.find(d => d["id"] === item["l_child"]); + + + r_edge.click(function(){ + document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); + collapsedmap[item["id"]]["collapsed-line-Y"] = r_edge.attr("data-v"); + collapse(item["r_child"],item["id"], !collapsedmap[item["id"]]["right"], "right"); + collapsedmap[item["id"]]["right"] = !collapsedmap[item["id"]]["right"]; + collapsedmap[item["id"]]["right_id"] = item["r_child"]; + + }); + l_edge.click(function(){ + document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); + collapsedmap[item["id"]]["collapsed-line-Y"] = l_edge.attr("data-v"); + collapse(item["l_child"],item["id"], !collapsedmap[item["id"]]["left"], "left"); + collapsedmap[item["id"]]["left"] = !collapsedmap[item["id"]]["left"]; + collapsedmap[item["id"]]["left_id"] = item["l_child"]; + + }); + g.add(r_edge, l_edge); + + + //Removing false leaves + if(r_child_check["name"] !== "None") r_edge.remove(); + if(l_child_check["name"] !== "None") l_edge.remove(); + + // Repeat almost everything for the minimap mXMinimap = minimapMinX + mX / ratio; mYLeftMinimap = minimapMinY + mYLeft / ratio; @@ -987,14 +1467,26 @@ $(function () { minimapPaths = [minimapLeft, minimapRight]; minimapPaths.forEach(function (itemMinimapPath, indexMinimapPath, arrayMinimapPath) { + + if(indexMinimapPath === 0) collapsedmap[item["id"]]["minimap-l-m"] = itemMinimapPath.attr("d"); + if(indexMinimapPath === 1) collapsedmap[item["id"]]["minimap-r-m"] = itemMinimapPath.attr("d"); + let data_child; + if(indexMinimapPath === 0) data_child = item["l_child"]; + if(indexMinimapPath === 1) data_child = item["r_child"]; + + itemMinimapPath.attr({ fill: 'none', stroke: 'black', strokeWidth: strokeWidth / ratio, - strokeLinecap: 'square' + strokeLinecap: 'square', + "data-id": item["id"], + "data-child": data_child // r or l child + }); }); + if (array[l_child]["bootstrap"] != "") { left.append(Snap.parse('' + array[l_child]["bootstrap"] + '')); } @@ -1014,7 +1506,6 @@ $(function () { "data-r_child": array[r_child]["r_child"] }); } - // Configure path hovering and clicking paths = [left, right]; @@ -1057,6 +1548,7 @@ $(function () { itemPath.attr({ strokeOpacity: 0.25 }); + childrenIds = [itemPath.attr("data-l_child"), itemPath.attr("data-r_child")]; while ((typeof childrenIds !== "undefined") && (childrenIds.length > 0)) { childId = childrenIds.shift(); @@ -1117,12 +1609,17 @@ $(function () { } else { // Send data to the backend either with the last tree if (counter_of_trees == number_of_trees) { + + load("get", { 'from': clickedPath.attr("data-id"), 'to': itemPath.attr("data-id") }); clicked_id = null; clickedPath = null; + + + // or with the currently shown tree } else { load("get", { @@ -1140,6 +1637,95 @@ $(function () { } }); + + Object.keys(collapsedmap).forEach(id => { + document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); + const node = data.find(d => d["id"] === id); + if(node === null) return; + + collapsedmap[id]["l-d"] = null; + collapsedmap[id]["r-d"] = null; + collapsedmap[id]["l_edge-Y"] = null; + collapsedmap[id]["r_edge-Y"] = null; + collapsedmap[id]["collapsed-line-Y"] = null; + + //SIDE SWAP CHECK IF STATEMENTS AFTER BRANCH SWAP + if(collapsedmap[id]["right"] && collapsedmap[id]["right_id"] !== null){ + if(node["r_child"] !== collapsedmap[id]["right_id"]){ + if(node["l_child"] === collapsedmap[id]["right_id"]){ + collapsedmap[id]["left"] = true; + collapsedmap[id]["right"] = false; + collapsedmap[id]["left_id"] = collapsedmap[id]["right_id"]; + collapsedmap[id]["right_id"] = null; + }else{ + collapsedmap[id]["right_id"] = null; + collapsedmap[id]["right"] = false; + } + } + } + if(collapsedmap[id]["left"] && collapsedmap[id]["left_id"] !== null){ + if(node["l_child"] !== collapsedmap[id]["left_id"]){ + if(node["r_child"] === collapsedmap[id]["left_id"]){ + collapsedmap[id]["right"] = true; + collapsedmap[id]["left"] = false; + collapsedmap[id]["right_id"] = collapsedmap[id]["left_id"]; + collapsedmap[id]["left_id"] = null; + collapsedmap[id]["minimap-l-m"] = null; + }else{ + collapsedmap[id]["left_id"] = null; + collapsedmap[id]["left"] = false; + collapsedmap[id]["minimap-l-m"] = null; + } + } + } + //TODO: Check if this code is still necessary (this was a hotfix) + + + if(collapsedmap[id]["right"] && collapsedmap[id]["right_id"]){ + const child = collapsedmap[id]["right_id"]; + const path = svg.select(`path[data-id='${child}']`); + if(path) collapsedmap[id]["r-d"] = path.attr("d"); + const tree = getTree(id); + tree.forEach(children =>{ + if(collapsedmap[children]["r-d"]){ + const rightpath_id = collapsedmap[children]["right_id"]; + const children_path = svg.select(`path[data-id='${rightpath_id}']`); + collapsedmap[children]["r-d"] = children_path.attr("d"); + }else if(collapsedmap[children]["l-d"]){ + const leftpath_id = collapsedmap[children]["left_id"]; + const children_path = svg.select(`path[data-id='${leftpath_id}']`); + collapsedmap[children]["l-d"] = children_path.attr("d"); + } + + }); + } + if(collapsedmap[id]["left"] && collapsedmap[id]["left_id"]){ + const child = collapsedmap[id]["left_id"]; + const path = svg.select(`path[data-id='${child}']`); + if(path) collapsedmap[id]["l-d"] = path.attr("d"); + const tree = getTree(id); + tree.forEach(children =>{ + if(collapsedmap[children]["r-d"]){ + const rightpath_id = collapsedmap[children]["right_id"]; + const children_path = svg.select(`path[data-id='${rightpath_id}']`); + collapsedmap[children]["r-d"] = children_path.attr("d"); + }else if(collapsedmap[children]["l-d"]){ + const leftpath_id = collapsedmap[children]["left_id"]; + const children_path = svg.select(`path[data-id='${leftpath_id}']`); + collapsedmap[children]["l-d"] = children_path.attr("d"); + } + }); + } + if(collapsedmap[id]["right"]){ + collapse(node["r_child"], id ,collapsedmap[id]["right"], "right"); + } + if(collapsedmap[id]["left"]){ + collapse(node["l_child"], id ,collapsedmap[id]["left"], "left"); + } + }); + + + // Sets the initial position if (typeof xBefore === "undefined") { xBefore = 20; @@ -1321,6 +1907,8 @@ $(function () { } } + + $(svg.node).mousedown(funcMouseDown); $(svg.node).mouseup(funcMouseUp); $(svg.node).mousemove(funcMouseMove); @@ -1434,10 +2022,12 @@ $(function () { // Calls the search function with input from the search field for button and context option $("#search-button").click(function (event) { + document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); search($("#search-text").val()); }); $('#search-text').keypress(function (event) { + document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); if (event.which == '13') { search($("#search-text").val()); } @@ -1508,6 +2098,7 @@ $(function () { function undo() { if (counter_of_trees > 1) { counter_of_trees -= 1; + } if (counter_of_trees == 1) { $("#undo-button").prop("disabled", true); @@ -1627,8 +2218,12 @@ $(function () { $("#context-menu").removeClass("visible"); } }); + + } + + /** * Enables the custom buttons for alignment file handling */ From a400cd9277683f53dcfa3730b2621180c096504e Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 15 Dec 2025 18:49:45 +0100 Subject: [PATCH 02/48] Minor fix of Minimap (Stacked collapse bug fix) --- matt/static/js.js | 43 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 03b7be0..9f7d5fb 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -421,9 +421,6 @@ $(function () { // Calls the draw function with the chosen tree (with or without branch lengths) if (typeof xhr !== "undefined" && xhr.getResponseHeader("Length")) { - data.forEach(n => { - collapsedmap[n["id"]] = {left: false, right: false}; - }); draw(JSON.parse(trees[counter_of_trees - 1][2])); } else { @@ -959,7 +956,6 @@ $(function () { */ function collapse(childitem, start, collapsed_check, direct){ const sub = getTree(childitem); - //MERGE TREES IF ONE IS ALREADY COLLAPSED const parent = data.find(d => d["id"] === start); let side; @@ -987,12 +983,12 @@ $(function () { } //Resets state for concat collapse const temptree = getTree(start); - temptree.forEach(childitem => { - if(collapsedmap[childitem]){ - collapsedmap[childitem]["left"] = false; - collapsedmap[childitem]["right"] = false; - } - }); + temptree.forEach(ci => { + if(collapsedmap[ci]){ + collapsedmap[ci]["left"] = false; + collapsedmap[ci]["right"] = false; + } + }); // COLAPSE if(collapsed_check){ @@ -1009,6 +1005,7 @@ $(function () { updateSpacing(start, collapsed_check, direct); + //EXPAND }else{ svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "inline"}); @@ -1018,7 +1015,6 @@ $(function () { svg.selectAll(`circle[data-id='${child}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); - }); const temp = getTree(start); @@ -1028,10 +1024,13 @@ $(function () { if(collapsedmap[child]["l_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='left']`).attr({cy: collapsedmap[child]["l_edge-Y"], "data-v":collapsedmap[child]["l_edge-Y"]}); if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); }); + getTree(start).forEach(child=>{ + if(collapsedmap[child]["r-d"]) minimap.select(`path[data-child='${collapsedmap[child]["right_id"]}`).attr({d: collapsedmap[child]["minimap-r-m"]}); + if(collapsedmap[child]["l-d"]) minimap.select(`path[data-child='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["minimap-r-m"]}); + }); draw_collapsed_line(childitem, start, collapsed_check, direct); updateSpacing(start, collapsed_check, direct); - console.log(collapsedmap) } } @@ -1369,20 +1368,6 @@ $(function () { g.add(left, right); - left.hover( - function () { - console.log(item["id"]) - }, function () { - - }); - - right.hover( - function () { - console.log(item["id"]) - }, function () { - - }); - // NODE FOR COLLAPSE / SIMPLIFICATION @@ -1410,14 +1395,14 @@ $(function () { l_edge.hover( function () { this.node.style.cursor = "pointer"; - console.log(item["id"]) + }, function () { this.node.style.cursor = "default"; }); r_edge.hover( function () { this.node.style.cursor = "pointer"; - console.log(item["id"]) + }, function () { this.node.style.cursor = "default"; @@ -1466,6 +1451,8 @@ $(function () { minimapPaths = [minimapLeft, minimapRight]; + + minimapPaths.forEach(function (itemMinimapPath, indexMinimapPath, arrayMinimapPath) { if(indexMinimapPath === 0) collapsedmap[item["id"]]["minimap-l-m"] = itemMinimapPath.attr("d"); From 4ca037c0190b50a4d53e7cd308c0ff88e8edb25b Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 19 Dec 2025 18:59:47 +0100 Subject: [PATCH 03/48] Fixxed inconsitent managment on stacked collapse. Todo: watch line 1025. --- matt/static/js.js | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 9f7d5fb..5875db5 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -982,12 +982,20 @@ $(function () { }); } //Resets state for concat collapse - const temptree = getTree(start); + const temptree = getTree(child); + temptree.push(parent[side]) + temptree.forEach(ci => { - if(collapsedmap[ci]){ + if(collapsedmap[ci]["left"]){ collapsedmap[ci]["left"] = false; + svg.selectAll(`[data-id='${ci}']`).attr({stroke: "red"}); + } + if(collapsedmap[ci]["right"]){ collapsedmap[ci]["right"] = false; + svg.selectAll(`[data-id='${ci}']`).attr({stroke: "red"}); + } + }); // COLAPSE @@ -1000,12 +1008,10 @@ $(function () { minimap.selectAll(`path[data-id='${child}']`).attr({display: "none"}); }); - draw_collapsed_line(childitem, start, collapsed_check, direct); updateSpacing(start, collapsed_check, direct); - //EXPAND }else{ svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "inline"}); @@ -1016,18 +1022,16 @@ $(function () { minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); }); + //TODO: FIX SPACING: DRAWING WRONG BUT STATES ARE CORRECT + getTree(start).forEach(child => { + if(collapsedmap[child]["l-d"]) svg.select(`path[data-id='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["l-d"], stroke: "red"}); + if(collapsedmap[child]["r-d"]) svg.select(`path[data-id='${collapsedmap[child]["right_id"]}']`).attr({d: collapsedmap[child]["r-d"], stroke: "red"}); + if(collapsedmap[child]["l_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='left']`).attr({cy: collapsedmap[child]["l_edge-Y"], "data-v":collapsedmap[child]["l_edge-Y"]}); + if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); + if(collapsedmap[child]["r-d"]) minimap.select(`path[data-child='${collapsedmap[child]["right_id"]}`).attr({d: collapsedmap[child]["minimap-r-m"]}); + if(collapsedmap[child]["l-d"]) minimap.select(`path[data-child='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["minimap-r-m"]}); + }); - const temp = getTree(start); - temp.forEach(child => { - if(collapsedmap[child]["l-d"]) svg.select(`path[data-id='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["l-d"]}); - if(collapsedmap[child]["r-d"]) svg.select(`path[data-id='${collapsedmap[child]["right_id"]}']`).attr({d: collapsedmap[child]["r-d"]}); - if(collapsedmap[child]["l_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='left']`).attr({cy: collapsedmap[child]["l_edge-Y"], "data-v":collapsedmap[child]["l_edge-Y"]}); - if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); - }); - getTree(start).forEach(child=>{ - if(collapsedmap[child]["r-d"]) minimap.select(`path[data-child='${collapsedmap[child]["right_id"]}`).attr({d: collapsedmap[child]["minimap-r-m"]}); - if(collapsedmap[child]["l-d"]) minimap.select(`path[data-child='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["minimap-r-m"]}); - }); draw_collapsed_line(childitem, start, collapsed_check, direct); updateSpacing(start, collapsed_check, direct); From f2fa682711fcdeea8a1832e4857ced1f86a0ac99 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sat, 20 Dec 2025 18:31:18 +0100 Subject: [PATCH 04/48] Fixxed inconsitent managment on stacked collapse within the update of spacing. --- matt/static/js.js | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 5875db5..e0e134e 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -983,19 +983,11 @@ $(function () { } //Resets state for concat collapse const temptree = getTree(child); - temptree.push(parent[side]) + temptree.push(parent[side]); temptree.forEach(ci => { - if(collapsedmap[ci]["left"]){ - collapsedmap[ci]["left"] = false; - svg.selectAll(`[data-id='${ci}']`).attr({stroke: "red"}); - } - if(collapsedmap[ci]["right"]){ - collapsedmap[ci]["right"] = false; - svg.selectAll(`[data-id='${ci}']`).attr({stroke: "red"}); - - } - + if(collapsedmap[ci]["left"]) collapsedmap[ci]["left"] = false; + if(collapsedmap[ci]["right"]) collapsedmap[ci]["right"] = false; }); // COLAPSE @@ -1022,15 +1014,16 @@ $(function () { minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); }); - //TODO: FIX SPACING: DRAWING WRONG BUT STATES ARE CORRECT - getTree(start).forEach(child => { - if(collapsedmap[child]["l-d"]) svg.select(`path[data-id='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["l-d"], stroke: "red"}); - if(collapsedmap[child]["r-d"]) svg.select(`path[data-id='${collapsedmap[child]["right_id"]}']`).attr({d: collapsedmap[child]["r-d"], stroke: "red"}); - if(collapsedmap[child]["l_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='left']`).attr({cy: collapsedmap[child]["l_edge-Y"], "data-v":collapsedmap[child]["l_edge-Y"]}); - if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); - if(collapsedmap[child]["r-d"]) minimap.select(`path[data-child='${collapsedmap[child]["right_id"]}`).attr({d: collapsedmap[child]["minimap-r-m"]}); - if(collapsedmap[child]["l-d"]) minimap.select(`path[data-child='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["minimap-r-m"]}); - }); + const temptree = getTree(child); + temptree.push(parent[side]); + temptree.forEach(child => { + if(collapsedmap[child]["l-d"]) svg.select(`path[data-id='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["l-d"], stroke: "red"}); + if(collapsedmap[child]["r-d"]) svg.select(`path[data-id='${collapsedmap[child]["right_id"]}']`).attr({d: collapsedmap[child]["r-d"], stroke: "red"}); + if(collapsedmap[child]["l_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='left']`).attr({cy: collapsedmap[child]["l_edge-Y"], "data-v":collapsedmap[child]["l_edge-Y"]}); + if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); + if(collapsedmap[child]["r-d"]) minimap.select(`path[data-child='${collapsedmap[child]["right_id"]}`).attr({d: collapsedmap[child]["minimap-r-m"]}); + if(collapsedmap[child]["l-d"]) minimap.select(`path[data-child='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["minimap-r-m"]}); + }); draw_collapsed_line(childitem, start, collapsed_check, direct); From d31ace1f2db065f01f7874941016eb91df793beb Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 7 Jan 2026 13:33:37 +0100 Subject: [PATCH 05/48] Wrote code little bit better and watch todo concerning collapse minimap function. --- matt/static/js.js | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index e0e134e..3bbc2ec 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -990,7 +990,7 @@ $(function () { if(collapsedmap[ci]["right"]) collapsedmap[ci]["right"] = false; }); - // COLAPSE + // COLAPSE //TODO: Fix minimap stacked collapse bug if(collapsed_check){ svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "none"}); @@ -1017,8 +1017,8 @@ $(function () { const temptree = getTree(child); temptree.push(parent[side]); temptree.forEach(child => { - if(collapsedmap[child]["l-d"]) svg.select(`path[data-id='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["l-d"], stroke: "red"}); - if(collapsedmap[child]["r-d"]) svg.select(`path[data-id='${collapsedmap[child]["right_id"]}']`).attr({d: collapsedmap[child]["r-d"], stroke: "red"}); + if(collapsedmap[child]["l-d"]) svg.select(`path[data-id='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["l-d"]}); + if(collapsedmap[child]["r-d"]) svg.select(`path[data-id='${collapsedmap[child]["right_id"]}']`).attr({d: collapsedmap[child]["r-d"]}); if(collapsedmap[child]["l_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='left']`).attr({cy: collapsedmap[child]["l_edge-Y"], "data-v":collapsedmap[child]["l_edge-Y"]}); if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); if(collapsedmap[child]["r-d"]) minimap.select(`path[data-child='${collapsedmap[child]["right_id"]}`).attr({d: collapsedmap[child]["minimap-r-m"]}); @@ -1174,17 +1174,20 @@ $(function () { const node = data.find(d=> d["id"] === start); let child; - if(direct === "left") child = node["l_child"]; - if(direct === "right") child = node["r_child"]; - if(!child) return; + let directKey; + if(direct === "left"){ + child = node["l_child"]; + directKey = "l-d"; + } + if(direct === "right"){ + child = node["r_child"]; + directKey = "r-d"; + } + if(!child) return; const path = svg.select(`path[data-id='${child}']`); if(!path) return; - let directKey; - if(direct === "left")directKey = "l-d"; - if(direct === "right")directKey = "r-d"; - if(!collapsedmap[start][directKey]) collapsedmap[start][directKey] = path.attr("d"); @@ -1451,12 +1454,16 @@ $(function () { minimapPaths.forEach(function (itemMinimapPath, indexMinimapPath, arrayMinimapPath) { - - if(indexMinimapPath === 0) collapsedmap[item["id"]]["minimap-l-m"] = itemMinimapPath.attr("d"); - if(indexMinimapPath === 1) collapsedmap[item["id"]]["minimap-r-m"] = itemMinimapPath.attr("d"); let data_child; - if(indexMinimapPath === 0) data_child = item["l_child"]; - if(indexMinimapPath === 1) data_child = item["r_child"]; + if(indexMinimapPath === 0){ + collapsedmap[item["id"]]["minimap-l-m"] = itemMinimapPath.attr("d"); + data_child = item["l_child"];data_child = item["l_child"]; + } + if(indexMinimapPath === 1){ + collapsedmap[item["id"]]["minimap-r-m"] = itemMinimapPath.attr("d"); + data_child = item["r_child"]; + } + itemMinimapPath.attr({ From df164bfbe5d07bba6c729c93eead3c095f1c2e48 Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 7 Jan 2026 14:46:24 +0100 Subject: [PATCH 06/48] Fixxed little bug concerning the minimap on collapse --- matt/static/js.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matt/static/js.js b/matt/static/js.js index 3bbc2ec..9dfd50b 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1022,7 +1022,7 @@ $(function () { if(collapsedmap[child]["l_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='left']`).attr({cy: collapsedmap[child]["l_edge-Y"], "data-v":collapsedmap[child]["l_edge-Y"]}); if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); if(collapsedmap[child]["r-d"]) minimap.select(`path[data-child='${collapsedmap[child]["right_id"]}`).attr({d: collapsedmap[child]["minimap-r-m"]}); - if(collapsedmap[child]["l-d"]) minimap.select(`path[data-child='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["minimap-r-m"]}); + if(collapsedmap[child]["l-d"]) minimap.select(`path[data-child='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["minimap-l-m"]}); }); From a591f386afb285a433171ed56f7651e55dd0c71a Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 19 Feb 2026 20:51:40 +0100 Subject: [PATCH 07/48] First steps to refresh button for compact collapse display. (Small implementation) --- matt/static/css.css | 6 ++++++ matt/static/js.js | 11 ++++++----- matt/templates/index.html | 6 ++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/matt/static/css.css b/matt/static/css.css index c465f5e..67541e7 100644 --- a/matt/static/css.css +++ b/matt/static/css.css @@ -103,6 +103,12 @@ html, body { right: calc(15vw + 156px); } +#compact-button { + position: absolute; + top: 10px; + right: calc(15vw + 200px); +} + #labels-button { position: absolute; top: 10px; diff --git a/matt/static/js.js b/matt/static/js.js index 9dfd50b..c5bde51 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -946,6 +946,10 @@ $(function () { return children.filter(elements => elements); } + function getRootPathWay(start){ + + } + /** * Collapse function. Collapses / Expands a node with the subtree @@ -990,7 +994,7 @@ $(function () { if(collapsedmap[ci]["right"]) collapsedmap[ci]["right"] = false; }); - // COLAPSE //TODO: Fix minimap stacked collapse bug + // COLAPSE if(collapsed_check){ svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "none"}); @@ -1077,9 +1081,6 @@ $(function () { }); g.add(coll_line); - //TODO: Draw line for collapsed. - - // coll_minimap_line = minimap.line(); nameText.hover( function (){ @@ -1457,7 +1458,7 @@ $(function () { let data_child; if(indexMinimapPath === 0){ collapsedmap[item["id"]]["minimap-l-m"] = itemMinimapPath.attr("d"); - data_child = item["l_child"];data_child = item["l_child"]; + data_child = item["l_child"]; } if(indexMinimapPath === 1){ collapsedmap[item["id"]]["minimap-r-m"] = itemMinimapPath.attr("d"); diff --git a/matt/templates/index.html b/matt/templates/index.html index 31d8298..3927e78 100644 --- a/matt/templates/index.html +++ b/matt/templates/index.html @@ -194,6 +194,12 @@

Import

+ @@ -205,6 +204,7 @@

Import

Set Root + @@ -591,6 +591,7 @@
Align/Attach labels
Options
Help
+
Compact mode
From 929852c1dc7d3084795a9ffbac39a730eff73824 Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 4 Mar 2026 13:17:15 +0100 Subject: [PATCH 09/48] Mini update for the compact button. --- matt/static/css.css | 2 +- matt/static/js.js | 3 ++- matt/templates/index.html | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/matt/static/css.css b/matt/static/css.css index 68577b5..77d8569 100644 --- a/matt/static/css.css +++ b/matt/static/css.css @@ -106,7 +106,7 @@ html, body { #compact-button { position: absolute; top: 10px; - right: calc(15vw + 217px); + right: calc(15vw + 203px); } #labels-button { diff --git a/matt/static/js.js b/matt/static/js.js index c5bde51..1f399b6 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1919,6 +1919,7 @@ $(function () { $("#lengths-button").prop("disabled", false); $("#labels-button").prop("disabled", false); $("#snapshots-button").prop("disabled", false); + $("#compact-button").prop("disabled", false); // Calls the undo function for button and context option $("#undo-button").click(function (event) { @@ -2290,7 +2291,7 @@ $(function () { instructions_lines = instructions_g.g(); instructions_texts = instructions_g.g(); - buttons_names = ["undo", "redo", "save", "snapshots", "zoom-in", "zoom-out", "search", "lengths", "labels", "options", "help"]; + buttons_names = ["undo", "redo", "save", "snapshots", "zoom-in", "zoom-out", "help", "lengths", "labels", "options", "compact", "search"]; buttons_names.forEach(function (value, index) { current_button = $("#" + value + "-button"); diff --git a/matt/templates/index.html b/matt/templates/index.html index 313b0d3..db6ad93 100644 --- a/matt/templates/index.html +++ b/matt/templates/index.html @@ -194,7 +194,7 @@

Import

- From 02374b831646dc2249e18abdfdce2645f3d13a5a Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 11 Mar 2026 15:35:59 +0100 Subject: [PATCH 11/48] First steps into small rendering function after collapse... --- matt/static/js.js | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 470e073..6ebd82f 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -949,9 +949,6 @@ $(function () { return children.filter(elements => elements); } - function getRootPathWay(start){ - - } /** @@ -975,7 +972,7 @@ $(function () { const temp = getTree(child); temp.push(child); temp.forEach(id =>{ - const clean_lines =svg.selectAll(`[data-collapsed-Line^='${id}']`); + const clean_lines = svg.selectAll(`[data-collapsed-Line^='${id}']`); if(clean_lines !== null) clean_lines.forEach(i => i.remove()); }); const currentcollapsed = svg.select(`[data-collapsed-Line^='${child}']`); @@ -2056,23 +2053,22 @@ $(function () { outgroup(); }); $('#compact-button').off("click").on("click",function (event) { - let temptree; - let compacttree; if(compactmode === false) { - temptree = currenttree; - compacttree = JSON.parse(currenttree); + const compacttree = JSON.parse(currenttree); console.log("TEST - COMPACT VIEW ON"); $("#compact-button-show").hide(); $("#compact-button-hide").show(); // MANIPULATE COMPACTREE + calculateCompactTree(compacttree, collapsedmap); // compactmode = true; }else{ console.log("TEST - COMPACT VIEW OFF"); + draw(JSON.parse(currenttree)); $("#compact-button-show").show(); $("#compact-button-hide").hide(); compactmode = false; @@ -2215,7 +2211,37 @@ $(function () { * @param tree current tree * @param map current collapsed info map */ - function calculateCompactTree(tree, map){ + function calculateCompactTree(tree, colmap){ + //TODO: GET COLLAPSED NODES FROM MAP. USE GETTREE FUNCTION TO REMOVE SUBTREE VON CURRENTREE AND INSERT NEW DUMMY NODES. + + const collapsednodes = Object.keys(colmap).map(id => { + const temparray = []; + if(collapsedmap[id]["left"] === true) temparray.push(collapsedmap[id]["left_id"]); + if(collapsedmap[id]["right"] === true) temparray.push(collapsedmap[id]["right_id"]); + return temparray + }).flat(); + + console.log("COLLAPSED NODES: "+collapsednodes); + + + + const removeablenodes = collapsednodes.map(id => { + return getTree(id); + }).flat(); + + console.log("REMOVEABLE SUBTREE NODES: "+removeablenodes); + + + + let newtree = tree.filter(node => !removeablenodes.includes(node["id"])); + collapsednodes.map(id => { + const neededchange = newtree.find(i => i["id"] === id); + console.log(neededchange) + neededchange["name"] = colmap[id]["label"]; + console.log(neededchange) + }); + + } From 8d13b40ba9dc0b02fde34fe21c64d81081faade0 Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 11 Mar 2026 20:34:29 +0100 Subject: [PATCH 12/48] Small test (not important) => TODO: Fix bugged state on compact mode. --- matt/static/js.js | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 6ebd82f..b96cb9a 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -2061,8 +2061,7 @@ $(function () { $("#compact-button-show").hide(); $("#compact-button-hide").show(); // MANIPULATE COMPACTREE - calculateCompactTree(compacttree, collapsedmap); - + draw(calculateCompactTree(compacttree, collapsedmap)); // compactmode = true; @@ -2212,8 +2211,7 @@ $(function () { * @param map current collapsed info map */ function calculateCompactTree(tree, colmap){ - //TODO: GET COLLAPSED NODES FROM MAP. USE GETTREE FUNCTION TO REMOVE SUBTREE VON CURRENTREE AND INSERT NEW DUMMY NODES. - + //TODO: FIX PROGRAMM INFUNCTIONALITY IN COMPACT MODE !!!!! const collapsednodes = Object.keys(colmap).map(id => { const temparray = []; if(collapsedmap[id]["left"] === true) temparray.push(collapsedmap[id]["left_id"]); @@ -2221,28 +2219,23 @@ $(function () { return temparray }).flat(); - console.log("COLLAPSED NODES: "+collapsednodes); - + // console.log("COLLAPSED NODES: "+collapsednodes); const removeablenodes = collapsednodes.map(id => { return getTree(id); }).flat(); - console.log("REMOVEABLE SUBTREE NODES: "+removeablenodes); - + // console.log("REMOVEABLE SUBTREE NODES: "+removeablenodes); let newtree = tree.filter(node => !removeablenodes.includes(node["id"])); collapsednodes.map(id => { const neededchange = newtree.find(i => i["id"] === id); - console.log(neededchange) - neededchange["name"] = colmap[id]["label"]; - console.log(neededchange) + neededchange["name"] = "'"+colmap[id]["label"]+"'"; }); - - + return newtree; } /** From fe2d1eb27b39d17492d975ad9ed34d46020376db Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 12 Mar 2026 15:22:51 +0100 Subject: [PATCH 13/48] Compact Mode implemented (TEST PHHASE - TEST ON ANY BUGS). --- matt/static/js.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index b96cb9a..106e751 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -995,7 +995,7 @@ $(function () { }); // COLAPSE - if(collapsed_check){ + if(collapsed_check && compactmode === false){ svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "none"}); sub.forEach(child => { @@ -1061,7 +1061,7 @@ $(function () { let coll_line = null; let nameText = null; - if(collapsed_check){ + if(collapsed_check && compactmode === false){ nameText = svg.text(maxX - offset, circle.attr("data-v"), "'"+collapsedmap[start]["label"]+"'"+ " (" + item_counter+ ")").attr({ dominantBaseline: 'middle', fontSize: fontSize, @@ -1193,7 +1193,7 @@ $(function () { if(!collapsedmap[start][directKey]) collapsedmap[start][directKey] = path.attr("d"); //TODO: Write this nicer and better - if(collapsed_check){ + if(collapsed_check && compactmode === false){ let minimapD; const pathComponents = path.attr("d").split(/[MHV]/).filter(x => x.trim() !== ""); const mX = parseFloat(pathComponents[0].split(",")[0]); @@ -1435,8 +1435,8 @@ $(function () { //Removing false leaves - if(r_child_check["name"] !== "None") r_edge.remove(); - if(l_child_check["name"] !== "None") l_edge.remove(); + if(r_child_check["name"] !== "None" && compactmode === false) r_edge.remove(); + if(l_child_check["name"] !== "None" && compactmode === false) l_edge.remove(); // Repeat almost everything for the minimap @@ -2054,23 +2054,20 @@ $(function () { }); $('#compact-button').off("click").on("click",function (event) { if(compactmode === false) { + compactmode = true; const compacttree = JSON.parse(currenttree); - console.log("TEST - COMPACT VIEW ON"); - $("#compact-button-show").hide(); $("#compact-button-hide").show(); - // MANIPULATE COMPACTREE + draw(calculateCompactTree(compacttree, collapsedmap)); - // - compactmode = true; }else{ - console.log("TEST - COMPACT VIEW OFF"); + compactmode = false; draw(JSON.parse(currenttree)); $("#compact-button-show").show(); $("#compact-button-hide").hide(); - compactmode = false; + } }); @@ -2219,22 +2216,26 @@ $(function () { return temparray }).flat(); - // console.log("COLLAPSED NODES: "+collapsednodes); + console.log("COLLAPSED NODES: "+collapsednodes); const removeablenodes = collapsednodes.map(id => { return getTree(id); }).flat(); - // console.log("REMOVEABLE SUBTREE NODES: "+removeablenodes); + console.log("REMOVEABLE SUBTREE NODES: "+removeablenodes); let newtree = tree.filter(node => !removeablenodes.includes(node["id"])); collapsednodes.map(id => { const neededchange = newtree.find(i => i["id"] === id); neededchange["name"] = "'"+colmap[id]["label"]+"'"; - }); + neededchange["l_child"] = ""; + neededchange["r_child"] = ""; + }); + console.log("OLDTTREE:" + JSON.stringify(tree)) + console.log("NEWTREE:" + JSON.stringify(newtree)) return newtree; } From 52fe4256a5c7e809d34d37ca26e16f32a4f56032 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 13 Mar 2026 14:08:51 +0100 Subject: [PATCH 14/48] Fixxed small bugs, including: stack collapse bug after compact button pressed. Also wrong custom label for collapsed subtrees after compact mode. --- matt/static/js.js | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 106e751..40c90ad 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -44,6 +44,7 @@ $(function () { let collapsedmap = {}; let compactmode = false; let currenttree; + let activatecompact = false; // Gets the options initially getOptions(); @@ -679,8 +680,8 @@ $(function () { if(Object.keys(collapsedmap).length === 0){ data.forEach(n => { collapsedmap[n["id"]] = {left: false, right: false, left_id: null, right_id: null, label: "Collapsed", - "l-d": null,"r-d":null, "l_edge-Y": null, "r_edge-Y": null, "collapsed-line-Y":null, - "minimap-r-m": null, "minimap-l-m": null}; + "collapsed-line-Y":null, + }; }); } @@ -1005,7 +1006,7 @@ $(function () { }); draw_collapsed_line(childitem, start, collapsed_check, direct); - updateSpacing(start, collapsed_check, direct); + // updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE //EXPAND @@ -1016,7 +1017,6 @@ $(function () { svg.selectAll(`[data-id='${child}']`).attr({display: "inline"}); svg.selectAll(`circle[data-id='${child}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); - }); const temptree = getTree(child); temptree.push(parent[side]); @@ -1031,7 +1031,7 @@ $(function () { draw_collapsed_line(childitem, start, collapsed_check, direct); - updateSpacing(start, collapsed_check, direct); + //updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE } } @@ -1144,7 +1144,7 @@ $(function () { if(e.key === "Enter"){ if(input["value"] !== ""){ const newtext = input["value"]; - collapsedmap[start]["label"] = newtext; + collapsedmap[childitem]["label"] = newtext; this.attr({ text: "\""+newtext+"\"" + "("+item_counter+")", fontStyle: "italic" @@ -1192,7 +1192,7 @@ $(function () { if(!collapsedmap[start][directKey]) collapsedmap[start][directKey] = path.attr("d"); - //TODO: Write this nicer and better + if(collapsed_check && compactmode === false){ let minimapD; const pathComponents = path.attr("d").split(/[MHV]/).filter(x => x.trim() !== ""); @@ -1435,8 +1435,8 @@ $(function () { //Removing false leaves - if(r_child_check["name"] !== "None" && compactmode === false) r_edge.remove(); - if(l_child_check["name"] !== "None" && compactmode === false) l_edge.remove(); + if((r_child_check["name"] !== "None") && !collapsedmap[item["id"]]["right"]) r_edge.remove(); + if((l_child_check["name"] !== "None") && !collapsedmap[item["id"]]["left"]) l_edge.remove(); // Repeat almost everything for the minimap @@ -2213,19 +2213,14 @@ $(function () { const temparray = []; if(collapsedmap[id]["left"] === true) temparray.push(collapsedmap[id]["left_id"]); if(collapsedmap[id]["right"] === true) temparray.push(collapsedmap[id]["right_id"]); + return temparray }).flat(); - console.log("COLLAPSED NODES: "+collapsednodes); - - const removeablenodes = collapsednodes.map(id => { return getTree(id); }).flat(); - console.log("REMOVEABLE SUBTREE NODES: "+removeablenodes); - - let newtree = tree.filter(node => !removeablenodes.includes(node["id"])); collapsednodes.map(id => { const neededchange = newtree.find(i => i["id"] === id); @@ -2234,8 +2229,6 @@ $(function () { neededchange["r_child"] = ""; }); - console.log("OLDTTREE:" + JSON.stringify(tree)) - console.log("NEWTREE:" + JSON.stringify(newtree)) return newtree; } From f819bce1818ef4b7cbaaa325b58f6129ea2821a7 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 13 Mar 2026 19:48:40 +0100 Subject: [PATCH 15/48] Fixxed small bug, including: Wrong button implementation --- matt/static/js.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 40c90ad..d45b6f6 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -44,7 +44,7 @@ $(function () { let collapsedmap = {}; let compactmode = false; let currenttree; - let activatecompact = false; + let counter_of_nodes = 0; // Gets the options initially getOptions(); @@ -425,17 +425,17 @@ $(function () { // Calls the draw function with the chosen tree (with or without branch lengths) if (typeof xhr !== "undefined" && xhr.getResponseHeader("Length")) { currenttree = (trees[counter_of_trees - 1][2]); + counter_of_nodes = JSON.parse(currenttree).length - 1 draw(JSON.parse(trees[counter_of_trees - 1][2])); } else { currenttree = (trees[counter_of_trees - 1][1]); + counter_of_nodes = JSON.parse(currenttree).length - 1 draw(JSON.parse(trees[counter_of_trees - 1][1])); } } - - /** * Enables or disables testing capabilities * @param testing whether testing should be enabled or not @@ -969,7 +969,6 @@ $(function () { const child = parent[side]; if(child === null) return; - //'GRANDPARENT' Problem fix => remove lines && custom label const temp = getTree(child); temp.push(child); temp.forEach(id =>{ @@ -1003,8 +1002,8 @@ $(function () { svg.selectAll(`[data-id='${child}']`).attr({display: "none"}); svg.selectAll(`circle[data-id='${child}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "none"}); + counter_of_nodes -= 1; }); - draw_collapsed_line(childitem, start, collapsed_check, direct); // updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE @@ -1017,6 +1016,7 @@ $(function () { svg.selectAll(`[data-id='${child}']`).attr({display: "inline"}); svg.selectAll(`circle[data-id='${child}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); + counter_of_nodes += 1; }); const temptree = getTree(child); temptree.push(parent[side]); @@ -1433,7 +1433,6 @@ $(function () { }); g.add(r_edge, l_edge); - //Removing false leaves if((r_child_check["name"] !== "None") && !collapsedmap[item["id"]]["right"]) r_edge.remove(); if((l_child_check["name"] !== "None") && !collapsedmap[item["id"]]["left"]) l_edge.remove(); @@ -1919,7 +1918,12 @@ $(function () { $("#lengths-button").prop("disabled", false); $("#labels-button").prop("disabled", false); $("#snapshots-button").prop("disabled", false); - $("#compact-button").prop("disabled", false); + if(2 < counter_of_nodes <= (data.length - 1)) { + $("#compact-button").prop("disabled", false); + }else{ + $("#compact-button").prop("disabled", true); + } + // Calls the undo function for button and context option $("#undo-button").click(function (event) { @@ -2208,7 +2212,7 @@ $(function () { * @param map current collapsed info map */ function calculateCompactTree(tree, colmap){ - //TODO: FIX PROGRAMM INFUNCTIONALITY IN COMPACT MODE !!!!! + console.log("NODES:"+counter_of_nodes) const collapsednodes = Object.keys(colmap).map(id => { const temparray = []; if(collapsedmap[id]["left"] === true) temparray.push(collapsedmap[id]["left_id"]); From 1ab18ab3ab3cfd26cc9aef4038f3fa73296a02bb Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 13 Mar 2026 22:53:48 +0100 Subject: [PATCH 16/48] MINI UPDATE --- matt/static/js.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/matt/static/js.js b/matt/static/js.js index d45b6f6..b3cc75a 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1003,6 +1003,7 @@ $(function () { svg.selectAll(`circle[data-id='${child}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "none"}); counter_of_nodes -= 1; + console.log("-1") }); draw_collapsed_line(childitem, start, collapsed_check, direct); // updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE @@ -1017,6 +1018,7 @@ $(function () { svg.selectAll(`circle[data-id='${child}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); counter_of_nodes += 1; + console.log("+1") }); const temptree = getTree(child); temptree.push(parent[side]); @@ -2064,9 +2066,18 @@ $(function () { $("#compact-button-show").hide(); $("#compact-button-hide").show(); + $("#info-modal-label").text("Calculating compact tree.") + $("#info-modal-body").text("For larger trees this might take a while") + $("#info-modal").modal("show"); + draw(calculateCompactTree(compacttree, collapsedmap)); }else{ + + $("#info-modal-label").text("Expanding back to original tree structure") + $("#info-modal-body").text("For larger trees this might take a while") + $("#info-modal").modal("show"); + compactmode = false; draw(JSON.parse(currenttree)); $("#compact-button-show").show(); From c05b744cb1e3d11f698b07a01d24bbff6430a5a7 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sat, 14 Mar 2026 15:25:20 +0100 Subject: [PATCH 17/48] MINI UPDATE small bug fixxes --- matt/static/js.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index b3cc75a..6724827 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -415,6 +415,12 @@ $(function () { $("#redo-button").prop("disabled", true); } + if(2 < counter_of_nodes <= (data.length - 1)) { + $("#compact-button").prop("disabled", false); + }else{ + $("#compact-button").prop("disabled", true); + } + // Saves the first snapshot // TODO This should be toggleable in options if (snapshotTrees.length == 0) { @@ -1003,7 +1009,6 @@ $(function () { svg.selectAll(`circle[data-id='${child}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "none"}); counter_of_nodes -= 1; - console.log("-1") }); draw_collapsed_line(childitem, start, collapsed_check, direct); // updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE @@ -1011,6 +1016,7 @@ $(function () { //EXPAND }else{ + if(compactmode === true) return; svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "inline"}); sub.forEach(child => { @@ -1018,7 +1024,6 @@ $(function () { svg.selectAll(`circle[data-id='${child}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); counter_of_nodes += 1; - console.log("+1") }); const temptree = getTree(child); temptree.push(parent[side]); @@ -1162,6 +1167,7 @@ $(function () { }); }else{ + if(compactmode === true) return; svg.selectAll(`[data-collapsed-Line='${start}-${direct}']`).remove() } } @@ -1698,11 +1704,11 @@ $(function () { if(path) collapsedmap[id]["l-d"] = path.attr("d"); const tree = getTree(id); tree.forEach(children =>{ - if(collapsedmap[children]["r-d"]){ + if(collapsedmap[children]["r-d"] && collapsedmap[children]["right_id"]){ const rightpath_id = collapsedmap[children]["right_id"]; const children_path = svg.select(`path[data-id='${rightpath_id}']`); collapsedmap[children]["r-d"] = children_path.attr("d"); - }else if(collapsedmap[children]["l-d"]){ + }else if(collapsedmap[children]["l-d"] && collapsedmap[children]["left_id"]){ const leftpath_id = collapsedmap[children]["left_id"]; const children_path = svg.select(`path[data-id='${leftpath_id}']`); collapsedmap[children]["l-d"] = children_path.attr("d"); @@ -1920,11 +1926,6 @@ $(function () { $("#lengths-button").prop("disabled", false); $("#labels-button").prop("disabled", false); $("#snapshots-button").prop("disabled", false); - if(2 < counter_of_nodes <= (data.length - 1)) { - $("#compact-button").prop("disabled", false); - }else{ - $("#compact-button").prop("disabled", true); - } // Calls the undo function for button and context option From 154c2aaadbbbcf2ee09cba6e3f4f01eadabd45f0 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 20 Mar 2026 23:24:45 +0100 Subject: [PATCH 18/48] Fixxed some button overlapsing buggs also fixxed compact button wrong activation/deactivation based on collapsed nodes. --- matt/static/js.js | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 6724827..e2fe01b 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -415,11 +415,6 @@ $(function () { $("#redo-button").prop("disabled", true); } - if(2 < counter_of_nodes <= (data.length - 1)) { - $("#compact-button").prop("disabled", false); - }else{ - $("#compact-button").prop("disabled", true); - } // Saves the first snapshot // TODO This should be toggleable in options @@ -1010,13 +1005,18 @@ $(function () { minimap.selectAll(`path[data-id='${child}']`).attr({display: "none"}); counter_of_nodes -= 1; }); + counter_of_nodes -= 1; draw_collapsed_line(childitem, start, collapsed_check, direct); // updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE + if((2 < counter_of_nodes && counter_of_nodes <= (data.length - 1)) || compactmode === true) { + $("#compact-button").prop("disabled", false); + }else{ + $("#compact-button").prop("disabled", true); + } //EXPAND }else{ - if(compactmode === true) return; svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "inline"}); sub.forEach(child => { @@ -1025,6 +1025,8 @@ $(function () { minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); counter_of_nodes += 1; }); + counter_of_nodes += 1; + const temptree = getTree(child); temptree.push(parent[side]); temptree.forEach(child => { @@ -1034,11 +1036,19 @@ $(function () { if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); if(collapsedmap[child]["r-d"]) minimap.select(`path[data-child='${collapsedmap[child]["right_id"]}`).attr({d: collapsedmap[child]["minimap-r-m"]}); if(collapsedmap[child]["l-d"]) minimap.select(`path[data-child='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["minimap-l-m"]}); + }); + draw_collapsed_line(childitem, start, collapsed_check, direct); //updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE + if((2 < counter_of_nodes && counter_of_nodes <= (data.length - 1)) || compactmode === true) { + $("#compact-button").prop("disabled", false); + }else{ + $("#compact-button").prop("disabled", true); + } + } } @@ -1717,9 +1727,11 @@ $(function () { } if(collapsedmap[id]["right"]){ collapse(node["r_child"], id ,collapsedmap[id]["right"], "right"); + counter_of_nodes = JSON.parse(currenttree).length - 1 } if(collapsedmap[id]["left"]){ collapse(node["l_child"], id ,collapsedmap[id]["left"], "left"); + counter_of_nodes = JSON.parse(currenttree).length - 1 } }); @@ -2068,11 +2080,14 @@ $(function () { $("#compact-button-hide").show(); $("#info-modal-label").text("Calculating compact tree.") - $("#info-modal-body").text("For larger trees this might take a while") + $("#info-modal-body").text("For larger trees this might take a while. Attaching & Show/hiding branch lengths is disabled while compactmode is enabled.") $("#info-modal").modal("show"); draw(calculateCompactTree(compacttree, collapsedmap)); + $("#lengths-button").prop("disabled", true); + $("#labels-button").prop("disabled", true); + }else{ $("#info-modal-label").text("Expanding back to original tree structure") @@ -2081,6 +2096,10 @@ $(function () { compactmode = false; draw(JSON.parse(currenttree)); + + $("#lengths-button").prop("disabled", false); + $("#labels-button").prop("disabled", false); + $("#compact-button-show").show(); $("#compact-button-hide").hide(); @@ -2224,7 +2243,6 @@ $(function () { * @param map current collapsed info map */ function calculateCompactTree(tree, colmap){ - console.log("NODES:"+counter_of_nodes) const collapsednodes = Object.keys(colmap).map(id => { const temparray = []; if(collapsedmap[id]["left"] === true) temparray.push(collapsedmap[id]["left_id"]); From bde444b8134b2f139990e67cff2076f3b5b13954 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sun, 29 Mar 2026 15:03:03 +0200 Subject: [PATCH 19/48] Fixxed inconsitent compact button trigger requirment. --- matt/static/js.js | 183 +++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 92 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index e2fe01b..babb672 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -44,7 +44,7 @@ $(function () { let collapsedmap = {}; let compactmode = false; let currenttree; - let counter_of_nodes = 0; + let counter_of_nodes = [] // Gets the options initially getOptions(); @@ -426,11 +426,9 @@ $(function () { // Calls the draw function with the chosen tree (with or without branch lengths) if (typeof xhr !== "undefined" && xhr.getResponseHeader("Length")) { currenttree = (trees[counter_of_trees - 1][2]); - counter_of_nodes = JSON.parse(currenttree).length - 1 draw(JSON.parse(trees[counter_of_trees - 1][2])); } else { currenttree = (trees[counter_of_trees - 1][1]); - counter_of_nodes = JSON.parse(currenttree).length - 1 draw(JSON.parse(trees[counter_of_trees - 1][1])); } @@ -1003,12 +1001,12 @@ $(function () { svg.selectAll(`[data-id='${child}']`).attr({display: "none"}); svg.selectAll(`circle[data-id='${child}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "none"}); - counter_of_nodes -= 1; }); - counter_of_nodes -= 1; + counter_of_nodes.push(...sub); + console.log(counter_of_nodes) draw_collapsed_line(childitem, start, collapsed_check, direct); // updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE - if((2 < counter_of_nodes && counter_of_nodes <= (data.length - 1)) || compactmode === true) { + if((counter_of_nodes.length > 0)) { $("#compact-button").prop("disabled", false); }else{ $("#compact-button").prop("disabled", true); @@ -1023,9 +1021,11 @@ $(function () { svg.selectAll(`[data-id='${child}']`).attr({display: "inline"}); svg.selectAll(`circle[data-id='${child}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); - counter_of_nodes += 1; + counter_of_nodes = counter_of_nodes.filter(x=> x !== child); }); - counter_of_nodes += 1; + console.log(counter_of_nodes) + + const temptree = getTree(child); temptree.push(parent[side]); @@ -1043,7 +1043,7 @@ $(function () { draw_collapsed_line(childitem, start, collapsed_check, direct); //updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE - if((2 < counter_of_nodes && counter_of_nodes <= (data.length - 1)) || compactmode === true) { + if((counter_of_nodes.length > 0)) { $("#compact-button").prop("disabled", false); }else{ $("#compact-button").prop("disabled", true); @@ -1646,95 +1646,93 @@ $(function () { } }); - - Object.keys(collapsedmap).forEach(id => { - document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); - const node = data.find(d => d["id"] === id); - if(node === null) return; - - collapsedmap[id]["l-d"] = null; - collapsedmap[id]["r-d"] = null; - collapsedmap[id]["l_edge-Y"] = null; - collapsedmap[id]["r_edge-Y"] = null; - collapsedmap[id]["collapsed-line-Y"] = null; - - //SIDE SWAP CHECK IF STATEMENTS AFTER BRANCH SWAP - if(collapsedmap[id]["right"] && collapsedmap[id]["right_id"] !== null){ - if(node["r_child"] !== collapsedmap[id]["right_id"]){ - if(node["l_child"] === collapsedmap[id]["right_id"]){ - collapsedmap[id]["left"] = true; - collapsedmap[id]["right"] = false; - collapsedmap[id]["left_id"] = collapsedmap[id]["right_id"]; - collapsedmap[id]["right_id"] = null; - }else{ - collapsedmap[id]["right_id"] = null; - collapsedmap[id]["right"] = false; + if(compactmode === false) { + Object.keys(collapsedmap).forEach(id => { + document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); + const node = data.find(d => d["id"] === id); + if (node === null) return; + + collapsedmap[id]["l-d"] = null; + collapsedmap[id]["r-d"] = null; + collapsedmap[id]["l_edge-Y"] = null; + collapsedmap[id]["r_edge-Y"] = null; + collapsedmap[id]["collapsed-line-Y"] = null; + + //SIDE SWAP CHECK IF STATEMENTS AFTER BRANCH SWAP + if (collapsedmap[id]["right"] && collapsedmap[id]["right_id"] !== null) { + if (node["r_child"] !== collapsedmap[id]["right_id"]) { + if (node["l_child"] === collapsedmap[id]["right_id"]) { + collapsedmap[id]["left"] = true; + collapsedmap[id]["right"] = false; + collapsedmap[id]["left_id"] = collapsedmap[id]["right_id"]; + collapsedmap[id]["right_id"] = null; + } else { + collapsedmap[id]["right_id"] = null; + collapsedmap[id]["right"] = false; + } } } - } - if(collapsedmap[id]["left"] && collapsedmap[id]["left_id"] !== null){ - if(node["l_child"] !== collapsedmap[id]["left_id"]){ - if(node["r_child"] === collapsedmap[id]["left_id"]){ - collapsedmap[id]["right"] = true; - collapsedmap[id]["left"] = false; - collapsedmap[id]["right_id"] = collapsedmap[id]["left_id"]; - collapsedmap[id]["left_id"] = null; - collapsedmap[id]["minimap-l-m"] = null; - }else{ - collapsedmap[id]["left_id"] = null; - collapsedmap[id]["left"] = false; - collapsedmap[id]["minimap-l-m"] = null; + if (collapsedmap[id]["left"] && collapsedmap[id]["left_id"] !== null) { + if (node["l_child"] !== collapsedmap[id]["left_id"]) { + if (node["r_child"] === collapsedmap[id]["left_id"]) { + collapsedmap[id]["right"] = true; + collapsedmap[id]["left"] = false; + collapsedmap[id]["right_id"] = collapsedmap[id]["left_id"]; + collapsedmap[id]["left_id"] = null; + collapsedmap[id]["minimap-l-m"] = null; + } else { + collapsedmap[id]["left_id"] = null; + collapsedmap[id]["left"] = false; + collapsedmap[id]["minimap-l-m"] = null; + } } } - } - //TODO: Check if this code is still necessary (this was a hotfix) + if (collapsedmap[id]["right"] && collapsedmap[id]["right_id"]) { + const child = collapsedmap[id]["right_id"]; + const path = svg.select(`path[data-id='${child}']`); + if (path) collapsedmap[id]["r-d"] = path.attr("d"); + const tree = getTree(id); + tree.forEach(children => { + if (collapsedmap[children]["r-d"]) { + const rightpath_id = collapsedmap[children]["right_id"]; + const children_path = svg.select(`path[data-id='${rightpath_id}']`); + collapsedmap[children]["r-d"] = children_path.attr("d"); + } else if (collapsedmap[children]["l-d"]) { + const leftpath_id = collapsedmap[children]["left_id"]; + const children_path = svg.select(`path[data-id='${leftpath_id}']`); + collapsedmap[children]["l-d"] = children_path.attr("d"); + } - if(collapsedmap[id]["right"] && collapsedmap[id]["right_id"]){ - const child = collapsedmap[id]["right_id"]; - const path = svg.select(`path[data-id='${child}']`); - if(path) collapsedmap[id]["r-d"] = path.attr("d"); - const tree = getTree(id); - tree.forEach(children =>{ - if(collapsedmap[children]["r-d"]){ - const rightpath_id = collapsedmap[children]["right_id"]; - const children_path = svg.select(`path[data-id='${rightpath_id}']`); - collapsedmap[children]["r-d"] = children_path.attr("d"); - }else if(collapsedmap[children]["l-d"]){ - const leftpath_id = collapsedmap[children]["left_id"]; - const children_path = svg.select(`path[data-id='${leftpath_id}']`); - collapsedmap[children]["l-d"] = children_path.attr("d"); - } + }); + } + if (collapsedmap[id]["left"] && collapsedmap[id]["left_id"]) { + const child = collapsedmap[id]["left_id"]; + const path = svg.select(`path[data-id='${child}']`); + if (path) collapsedmap[id]["l-d"] = path.attr("d"); + const tree = getTree(id); + tree.forEach(children => { + if (collapsedmap[children]["r-d"] && collapsedmap[children]["right_id"]) { + const rightpath_id = collapsedmap[children]["right_id"]; + const children_path = svg.select(`path[data-id='${rightpath_id}']`); + collapsedmap[children]["r-d"] = children_path.attr("d"); + } else if (collapsedmap[children]["l-d"] && collapsedmap[children]["left_id"]) { + const leftpath_id = collapsedmap[children]["left_id"]; + const children_path = svg.select(`path[data-id='${leftpath_id}']`); + collapsedmap[children]["l-d"] = children_path.attr("d"); + } + }); + } + if (collapsedmap[id]["right"]) { + collapse(node["r_child"], id, collapsedmap[id]["right"], "right"); - }); - } - if(collapsedmap[id]["left"] && collapsedmap[id]["left_id"]){ - const child = collapsedmap[id]["left_id"]; - const path = svg.select(`path[data-id='${child}']`); - if(path) collapsedmap[id]["l-d"] = path.attr("d"); - const tree = getTree(id); - tree.forEach(children =>{ - if(collapsedmap[children]["r-d"] && collapsedmap[children]["right_id"]){ - const rightpath_id = collapsedmap[children]["right_id"]; - const children_path = svg.select(`path[data-id='${rightpath_id}']`); - collapsedmap[children]["r-d"] = children_path.attr("d"); - }else if(collapsedmap[children]["l-d"] && collapsedmap[children]["left_id"]){ - const leftpath_id = collapsedmap[children]["left_id"]; - const children_path = svg.select(`path[data-id='${leftpath_id}']`); - collapsedmap[children]["l-d"] = children_path.attr("d"); - } - }); - } - if(collapsedmap[id]["right"]){ - collapse(node["r_child"], id ,collapsedmap[id]["right"], "right"); - counter_of_nodes = JSON.parse(currenttree).length - 1 - } - if(collapsedmap[id]["left"]){ - collapse(node["l_child"], id ,collapsedmap[id]["left"], "left"); - counter_of_nodes = JSON.parse(currenttree).length - 1 - } - }); + } + if (collapsedmap[id]["left"]) { + collapse(node["l_child"], id, collapsedmap[id]["left"], "left"); + } + }); + } // Sets the initial position @@ -2072,6 +2070,7 @@ $(function () { outgroup(); }); $('#compact-button').off("click").on("click",function (event) { + console.log("IJSDOIJASID") if(compactmode === false) { compactmode = true; const compacttree = JSON.parse(currenttree); @@ -2245,8 +2244,8 @@ $(function () { function calculateCompactTree(tree, colmap){ const collapsednodes = Object.keys(colmap).map(id => { const temparray = []; - if(collapsedmap[id]["left"] === true) temparray.push(collapsedmap[id]["left_id"]); - if(collapsedmap[id]["right"] === true) temparray.push(collapsedmap[id]["right_id"]); + if(colmap[id]["left"] === true) temparray.push(colmap[id]["left_id"]); + if(colmap[id]["right"] === true) temparray.push(colmap[id]["right_id"]); return temparray }).flat(); From da5273688bab9a1c20ec730a3f97fe800c7552af Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 30 Mar 2026 13:30:53 +0200 Subject: [PATCH 20/48] Fixxed Align/Attach lables bug for compact mode. --- matt/static/js.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index babb672..d2b56d0 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -44,6 +44,7 @@ $(function () { let collapsedmap = {}; let compactmode = false; let currenttree; + let cct; let counter_of_nodes = [] // Gets the options initially @@ -1003,7 +1004,6 @@ $(function () { minimap.selectAll(`path[data-id='${child}']`).attr({display: "none"}); }); counter_of_nodes.push(...sub); - console.log(counter_of_nodes) draw_collapsed_line(childitem, start, collapsed_check, direct); // updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE if((counter_of_nodes.length > 0)) { @@ -1023,9 +1023,6 @@ $(function () { minimap.selectAll(`path[data-id='${child}']`).attr({display: "inline"}); counter_of_nodes = counter_of_nodes.filter(x=> x !== child); }); - console.log(counter_of_nodes) - - const temptree = getTree(child); temptree.push(parent[side]); @@ -2070,7 +2067,6 @@ $(function () { outgroup(); }); $('#compact-button').off("click").on("click",function (event) { - console.log("IJSDOIJASID") if(compactmode === false) { compactmode = true; const compacttree = JSON.parse(currenttree); @@ -2081,11 +2077,11 @@ $(function () { $("#info-modal-label").text("Calculating compact tree.") $("#info-modal-body").text("For larger trees this might take a while. Attaching & Show/hiding branch lengths is disabled while compactmode is enabled.") $("#info-modal").modal("show"); - - draw(calculateCompactTree(compacttree, collapsedmap)); + cct = calculateCompactTree(compacttree, collapsedmap) + draw(cct); $("#lengths-button").prop("disabled", true); - $("#labels-button").prop("disabled", true); + //$("#labels-button").prop("disabled", true); }else{ @@ -2097,7 +2093,7 @@ $(function () { draw(JSON.parse(currenttree)); $("#lengths-button").prop("disabled", false); - $("#labels-button").prop("disabled", false); + //$("#labels-button").prop("disabled", false); $("#compact-button-show").show(); $("#compact-button-hide").hide(); @@ -2196,10 +2192,15 @@ $(function () { $("#labels-button-align").show(); alignLabels = true; } - if (!enableLengths) { + if (!enableLengths && compactmode === false) { draw(JSON.parse(trees[counter_of_trees - 1][1])); - } else if (trees[counter_of_trees - 1][2] != null) { + } else if (trees[counter_of_trees - 1][2] != null && compactmode === false) { draw(JSON.parse(trees[counter_of_trees - 1][2])); + }else if(!enableLengths && compactmode === true){ + draw(calculateCompactTree(JSON.parse(trees[counter_of_trees - 1][1]), collapsedmap)); + }else if( compactmode === true && trees[counter_of_trees - 1][2] != null) { + draw(calculateCompactTree(JSON.parse(trees[counter_of_trees - 1][2]), collapsedmap)); + } } From 6f3eb87175b6ee9f16884135d19b58f53f0a0d6c Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 30 Mar 2026 13:41:09 +0200 Subject: [PATCH 21/48] Small code cleanup + brench lengths also now in compact mode enabled --- matt/static/js.js | 163 +++++----------------------------------------- 1 file changed, 15 insertions(+), 148 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index d2b56d0..773e7b5 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -44,7 +44,6 @@ $(function () { let collapsedmap = {}; let compactmode = false; let currenttree; - let cct; let counter_of_nodes = [] // Gets the options initially @@ -1005,7 +1004,6 @@ $(function () { }); counter_of_nodes.push(...sub); draw_collapsed_line(childitem, start, collapsed_check, direct); - // updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE if((counter_of_nodes.length > 0)) { $("#compact-button").prop("disabled", false); }else{ @@ -1024,22 +1022,9 @@ $(function () { counter_of_nodes = counter_of_nodes.filter(x=> x !== child); }); - const temptree = getTree(child); - temptree.push(parent[side]); - temptree.forEach(child => { - if(collapsedmap[child]["l-d"]) svg.select(`path[data-id='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["l-d"]}); - if(collapsedmap[child]["r-d"]) svg.select(`path[data-id='${collapsedmap[child]["right_id"]}']`).attr({d: collapsedmap[child]["r-d"]}); - if(collapsedmap[child]["l_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='left']`).attr({cy: collapsedmap[child]["l_edge-Y"], "data-v":collapsedmap[child]["l_edge-Y"]}); - if(collapsedmap[child]["r_edge-Y"]) svg.select(`circle[data-id='${child}'][data-direct='right']`).attr({cy: collapsedmap[child]["r_edge-Y"], "data-v":collapsedmap[child]["r_edge-Y"]}); - if(collapsedmap[child]["r-d"]) minimap.select(`path[data-child='${collapsedmap[child]["right_id"]}`).attr({d: collapsedmap[child]["minimap-r-m"]}); - if(collapsedmap[child]["l-d"]) minimap.select(`path[data-child='${collapsedmap[child]["left_id"]}']`).attr({d: collapsedmap[child]["minimap-l-m"]}); - - }); - draw_collapsed_line(childitem, start, collapsed_check, direct); - //updateSpacing(start, collapsed_check, direct); // REPLACED BY COMPACT MODE if((counter_of_nodes.length > 0)) { $("#compact-button").prop("disabled", false); }else{ @@ -1179,131 +1164,6 @@ $(function () { } } - /** - * Updates the spacing after collapse - * @param start startnode - * @param collapsed_check collapse check (true/false) at certain node - * @param direct (direction: left/right) - */ - function updateSpacing(start, collapsed_check, direct){ - - const node = data.find(d=> d["id"] === start); - - let child; - let directKey; - - if(direct === "left"){ - child = node["l_child"]; - directKey = "l-d"; - } - if(direct === "right"){ - child = node["r_child"]; - directKey = "r-d"; - } - if(!child) return; - const path = svg.select(`path[data-id='${child}']`); - if(!path) return; - - - if(!collapsedmap[start][directKey]) collapsedmap[start][directKey] = path.attr("d"); - - - if(collapsed_check && compactmode === false){ - let minimapD; - const pathComponents = path.attr("d").split(/[MHV]/).filter(x => x.trim() !== ""); - const mX = parseFloat(pathComponents[0].split(",")[0]); - const mY = parseFloat(pathComponents[0].split(",")[1]); - const hX = parseFloat(pathComponents[2]); - - let newVY; - if(direct === "left"){ - newVY = mY-25; - minimapD = collapsedmap[start]["minimap-l-m"]; - } - if(direct === "right"){ - newVY = mY+25; - minimapD = collapsedmap[start]["minimap-r-m"]; - } - - const minimapAttributes = minimapD.split(/[MHV]/).filter(x => x.trim() !== ""); - const miniDX = parseFloat(minimapAttributes[0].split(",")[0]); - const miniDY = parseFloat(minimapAttributes[0].split(",")[1]); - const miniDhx = parseFloat(minimapAttributes[2]); - let newMiniY ; - let minimapPath; - - - if(direct === "left"){ - minimapPath = minimap.select(`path[data-child='${child}']`); - path.attr({d: `M${mX},${mY} V${newVY} H${hX}`}); - newMiniY = miniDY - 7; - minimapPath.attr({d:`M${miniDX},${miniDY} V${newMiniY} H${miniDhx}`}); - } - if(direct === "right") { - minimapPath = minimap.select(`path[data-child='${child}']`); - path.attr({d: `M${mX},${mY} V${newVY} H${hX}`}); - newMiniY = miniDY + 7; - minimapPath.attr({d:`M${miniDX},${miniDY} V${newMiniY} H${miniDhx}`}); - } - const collapsedItems = svg.selectAll(`[data-collapsed-Line='${start}-${direct}']`); - const circle = svg.select(`circle[data-id='${start}'][data-direct='${direct}']`); - - collapsedItems.forEach(item =>{ - const t = item.type; - - if(t === "text"){ - item.attr({y: newVY}); - } - if(t === "line"){ - item.attr({y1: newVY, y2:newVY}); - } - }); - - if(direct === "left")collapsedmap[start]["l_edge-Y"] = circle.attr("data-v"); - if(direct === "right") collapsedmap[start]["r_edge-Y"] = circle.attr("data-v"); - - - circle.attr({cy: newVY, "data-v":newVY}); - - }else{ - path.attr({d: collapsedmap[start][directKey]}); - const collapsedItems = svg.selectAll(`[data-collapsed-Line='${start}-${direct}']`); - const circle = svg.select(`circle[data-id='${start}'][data-direct='${direct}']`); - let minimapPath; - - collapsedItems.forEach(item =>{ - const t = item.type; - if(t === "text"){ - item.attr({y: collapsedmap[start]["collapsed-line-Y"]}); - } - if(t === "line"){ - item.attr({y1: collapsedmap[start]["collapsed-line-Y"], y2:collapsedmap[start]["collapsed-line-Y"]}); - } - }); - - let oldCircleY; - let oldMinimapD; - if(direct === "left"){ - minimapPath = minimap.select(`path[data-child='${child}']`); - - oldCircleY = collapsedmap[start]["l_edge-Y"]; - oldMinimapD = collapsedmap[start]["minimap-l-m"]; - } - if(direct === "right"){ - minimapPath = minimap.select(`path[data-child='${child}']`); - - oldCircleY = collapsedmap[start]["r_edge-Y"]; - oldMinimapD = collapsedmap[start]["minimap-r-m"]; - } - - circle.attr({cy: oldCircleY, "data-v":oldCircleY}); - minimapPath.attr({d: oldMinimapD}); - - } - - - } - // Draws the branches and texts data.forEach(function (item, index, array) { @@ -2074,13 +1934,12 @@ $(function () { $("#compact-button-show").hide(); $("#compact-button-hide").show(); - $("#info-modal-label").text("Calculating compact tree.") - $("#info-modal-body").text("For larger trees this might take a while. Attaching & Show/hiding branch lengths is disabled while compactmode is enabled.") + $("#info-modal-label").text("Drawing compact tree.") + $("#info-modal-body").text("For larger trees this might take a while.") $("#info-modal").modal("show"); - cct = calculateCompactTree(compacttree, collapsedmap) - draw(cct); + draw(calculateCompactTree(compacttree, collapsedmap)); - $("#lengths-button").prop("disabled", true); + //$("#lengths-button").prop("disabled", true); //$("#labels-button").prop("disabled", true); }else{ @@ -2092,7 +1951,7 @@ $(function () { compactmode = false; draw(JSON.parse(currenttree)); - $("#lengths-button").prop("disabled", false); + //$("#lengths-button").prop("disabled", false); //$("#labels-button").prop("disabled", false); $("#compact-button-show").show(); @@ -2164,11 +2023,11 @@ $(function () { * Shows the branch lengths if they are available and hides them if they are shown */ function toggleLength() { - if (enableLengths) { + if (enableLengths && compactmode === false) { draw(JSON.parse(trees[counter_of_trees - 1][1])); $("#lengths-button-hide").hide(); $("#lengths-button-show").show(); - } else { + } else if(!enableLengths && compactmode === false){ if (trees[counter_of_trees - 1][2] != null) { draw(JSON.parse(trees[counter_of_trees - 1][2])); $("#lengths-button-show").hide(); @@ -2176,6 +2035,14 @@ $(function () { } else { $("#compute-modal").modal("show"); } + }else if(enableLengths && compactmode === true){ + draw(calculateCompactTree(JSON.parse(trees[counter_of_trees - 1][1]), collapsedmap)); + $("#lengths-button-hide").hide(); + $("#lengths-button-show").show(); + }else if(trees[counter_of_trees - 1][2] != null && compactmode === true){ + draw(calculateCompactTree(JSON.parse(trees[counter_of_trees - 1][2]), collapsedmap)); + $("#lengths-button-show").hide(); + $("#lengths-button-hide").show(); } } From 89c1b68361c9ce717c0b1fdfb621ab64d7259310 Mon Sep 17 00:00:00 2001 From: Adrian Date: Tue, 31 Mar 2026 13:07:18 +0200 Subject: [PATCH 22/48] Changed structure of hovering taxa display(view) & and inconsistent name display on collapsed nodes --- matt/static/js.js | 52 ++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 773e7b5..7611912 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -423,7 +423,9 @@ $(function () { description(trees[counter_of_trees - 1][0], "Original"); } + // Calls the draw function with the chosen tree (with or without branch lengths) + if (typeof xhr !== "undefined" && xhr.getResponseHeader("Length")) { currenttree = (trees[counter_of_trees - 1][2]); draw(JSON.parse(trees[counter_of_trees - 1][2])); @@ -431,8 +433,6 @@ $(function () { currenttree = (trees[counter_of_trees - 1][1]); draw(JSON.parse(trees[counter_of_trees - 1][1])); } - - } /** @@ -685,6 +685,7 @@ $(function () { } + $("#outgroup-button").css("display", "none"); @@ -1086,22 +1087,35 @@ $(function () { this.node.style.cursor = "pointer"; this.attr({fill:"#1e90ff"}); const box = this.getBBox(); - let text; - if(item_counter <= 10) { - text = svg.text(box.x + box.width + 10, box.y + box.height / 2, - "'"+collapsedmap[start]["label"]+"' "+"contains "+item_counter+" taxa: " + preview_containing + ", ... ( 0 More )" ).attr({ - fill: "#171515", - "collapse-hover-id": start - }); - - }else if(item_counter > 10){ - text = svg.text(box.x + box.width + 10, box.y + box.height / 2, - "'"+collapsedmap[start]["label"]+"' "+"contains "+item_counter+" taxa: " + preview_containing + ", ... ( "+ (item_counter-10)+ " More )" ).attr({ - fill: "#171515", - "collapse-hover-id": start - }); + const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); + let lines = []; + lines.push("'"+collapsedmap[start]["label"]+"' "+"contains "+item_counter+" taxa:"); + lines.push(" ") + previewlist.forEach(taxa => lines.push("- "+taxa)); + lines.push(" "); + if(item_counter > 10){ + lines.push(" ... ( "+ (item_counter-10)+ " More )"); + }else{ + lines.push(" ... ( 0 More )"); } + let text = svg.text(box.x + box.width+10, box.y + box.height /2, lines).attr({ + fill: "#171515", + "collapse-hover-id": start + }); + + text.selectAll("tspan").forEach((tspan, i) =>{ + let dyc; + if(i === 0){ + dyc = 0; + }else{ + dyc = 16; + } + tspan.attr({ + x : box.x + box.width+10, + dy: dyc + }); + }); const textbox = text.getBBox(); const desc = svg.rect(textbox.x - 5, textbox.y-3,textbox.width+80, textbox.height+25,5,5).attr({ strokeWidth: 0.5, @@ -1143,7 +1157,7 @@ $(function () { if(e.key === "Enter"){ if(input["value"] !== ""){ const newtext = input["value"]; - collapsedmap[childitem]["label"] = newtext; + collapsedmap[start]["label"] = newtext; this.attr({ text: "\""+newtext+"\"" + "("+item_counter+")", fontStyle: "italic" @@ -2107,7 +2121,7 @@ $(function () { /** * calculates a new tree to draw for the compact view * @param tree current tree - * @param map current collapsed info map + * @param colmap current collapsed info map */ function calculateCompactTree(tree, colmap){ const collapsednodes = Object.keys(colmap).map(id => { @@ -2125,7 +2139,7 @@ $(function () { let newtree = tree.filter(node => !removeablenodes.includes(node["id"])); collapsednodes.map(id => { const neededchange = newtree.find(i => i["id"] === id); - neededchange["name"] = "'"+colmap[id]["label"]+"'"; + neededchange["name"] = "'"+colmap[neededchange["parent"]]["label"]+"'"; neededchange["l_child"] = ""; neededchange["r_child"] = ""; From c5074196e6ccda8840545124f03df4b7bd4c3296 Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 1 Apr 2026 00:18:55 +0200 Subject: [PATCH 23/48] Collapsing nodes function now available in compact mode. --- matt/static/js.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 7611912..94d580b 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -995,7 +995,7 @@ $(function () { }); // COLAPSE - if(collapsed_check && compactmode === false){ + if(collapsed_check){ svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "none"}); sub.forEach(child => { @@ -1061,7 +1061,7 @@ $(function () { let coll_line = null; let nameText = null; - if(collapsed_check && compactmode === false){ + if(collapsed_check){ nameText = svg.text(maxX - offset, circle.attr("data-v"), "'"+collapsedmap[start]["label"]+"'"+ " (" + item_counter+ ")").attr({ dominantBaseline: 'middle', fontSize: fontSize, @@ -1173,7 +1173,6 @@ $(function () { }); }else{ - if(compactmode === true) return; svg.selectAll(`[data-collapsed-Line='${start}-${direct}']`).remove() } } From febb1666ae807f2a5aa3f02a35fc124261a7c0c5 Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 1 Apr 2026 20:31:44 +0200 Subject: [PATCH 24/48] Swapping of branches now available in compact mode (test phase) --- matt/static/js.js | 59 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 94d580b..1044a09 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -425,14 +425,28 @@ $(function () { // Calls the draw function with the chosen tree (with or without branch lengths) - - if (typeof xhr !== "undefined" && xhr.getResponseHeader("Length")) { - currenttree = (trees[counter_of_trees - 1][2]); - draw(JSON.parse(trees[counter_of_trees - 1][2])); - } else { - currenttree = (trees[counter_of_trees - 1][1]); - draw(JSON.parse(trees[counter_of_trees - 1][1])); + if(compactmode === false) { + if (typeof xhr !== "undefined" && xhr.getResponseHeader("Length")) { + currenttree = (trees[counter_of_trees - 1][2]); + draw(JSON.parse(trees[counter_of_trees - 1][2])); + } else { + currenttree = (trees[counter_of_trees - 1][1]); + draw(JSON.parse(trees[counter_of_trees - 1][1])); + } + }else{ + //const tempcalc = draw(data).calculateCompactTree(); + let t; + if (typeof xhr !== "undefined" && xhr.getResponseHeader("Length")) { + currenttree = (trees[counter_of_trees - 1][2]); + t = draw().calculateCompactTree(JSON.parse(currenttree),collapsedmap); + draw(t); + } else { + currenttree = (trees[counter_of_trees - 1][1]); + t = draw().calculateCompactTree(JSON.parse(currenttree),collapsedmap); + draw(t); + } } + } /** @@ -683,6 +697,8 @@ $(function () { }; }); } + if(typeof data === "undefined") return { calculateCompactTree: calculateCompactTree }; + @@ -950,6 +966,31 @@ $(function () { return children.filter(elements => elements); } + /** + * Gets subtree of a givin node + * @param start clicked node + * @returns {*[]} Subtreelist with ids of the subnodes + */ + function getTreeCompact(start, source = data){ + const children = []; + const stack = [start] + + while(stack.length > 0){ + const a = stack.pop(); + const node = source.find(n => n["id"] === a); + if(node == null) continue; + ["l_child","r_child"].forEach(child => { + const childid = node[child]; + if(childid !== undefined && childid !== null){ + children.push(childid); + stack.push(childid); + } + }); + } + return children.filter(elements => elements); + } + + /** @@ -2132,7 +2173,7 @@ $(function () { }).flat(); const removeablenodes = collapsednodes.map(id => { - return getTree(id); + return getTreeCompact(id, tree); }).flat(); let newtree = tree.filter(node => !removeablenodes.includes(node["id"])); @@ -2173,8 +2214,6 @@ $(function () { $("#context-menu").removeClass("visible"); } }); - - } From c465c67554b0e4cfafd7220b2cbbc6e97b81f0c8 Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 2 Apr 2026 14:50:31 +0200 Subject: [PATCH 25/48] Hovering over collapsed taxa names now also available in compact mode (testing, see todo). --- matt/static/js.js | 95 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 1044a09..a206852 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1232,6 +1232,7 @@ $(function () { dominantBaseline: 'middle', fontSize: fontSize, 'data-id': item["id"], + 'data-parent': item["parent"], textAnchor: 'end' }); g.add(nameText); @@ -1241,7 +1242,8 @@ $(function () { stroke: 'black', strokeWidth: 2, strokeDasharray: 4, - "data-id": item["id"] + "data-id": item["id"], + 'data-parent': item["parent"] })); } // Draw next to the leaf without a dashed line @@ -1249,7 +1251,8 @@ $(function () { nameText = svg.text(item["total_length"] * scaleX + (1.5 * offset), (index + 1) * scaleY, item["name"]).attr({ dominantBaseline: 'middle', fontSize: fontSize, - 'data-id': item["id"] + 'data-id': item["id"], + 'data-parent': item["parent"] }); g.add(nameText); } @@ -1263,7 +1266,8 @@ $(function () { g.add(svg.text((parseFloat(array[parent]["total_length"]) + (item["length"] / 2)) * scaleX, (index + 1) * scaleY, item["bootstrap"]).attr({ dominantBaseline: 'baseline', fontSize: 0.5 * fontSize, - 'data-id': item["id"] + 'data-id': item["id"], + 'data-parent': item["parent"] })); // TODO data-id added graying out of child bootstraps but not selected bootstrap } @@ -1367,6 +1371,91 @@ $(function () { if((l_child_check["name"] !== "None") && !collapsedmap[item["id"]]["left"]) l_edge.remove(); + // hover function in compact mode + if(compactmode === true) { + const compactlabel = Object.values(collapsedmap).some(v => + ("'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || + "'" + v["label"] + "'" === l_child_check["name"]) && (v["right_id"] || v["left_id"]) + ); + + if (compactlabel) { + + const c = Object.entries(collapsedmap).find(([id, v]) => + "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || + "'" + v["label"] + "'" === l_child_check["name"] && (v["right_id"] || v["left_id"]) + ); + const sub = getTreeCompact(c[0],data); // TODO: FIX WRONG ID FOR SUBTREE + console.log(sub); + + const item_counter = sub.filter(id => { + const n = data.find(d => d["id"] === id); + return n["name"] !== "None"; + }).length; + + const item_name_container = sub. + map(id=>data.find(d => d["id"] === id)). + filter(n => n["name"] !== "None"). + map(n => n["name"]); + const preview_containing = item_name_container.slice(0,10).join(", "); + + + const taxa = svg.select(`text[data-parent='${c[0]}']`) + taxa.hover(function (){ + this.node.style.cursor = "pointer"; + this.attr({fill:"#1e90ff"}); + + const box = this.getBBox(); + + const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); + let lines = []; + lines.push("'"+collapsedmap[c[0]]["label"]+"' "+"contains "+item_counter+" taxa:"); + lines.push(" ") + previewlist.forEach(taxa => lines.push("- "+taxa)); + lines.push(" "); + if(item_counter > 10){ + lines.push(" ... ( "+ (item_counter-10)+ " More )"); + }else{ + lines.push(" ... ( 0 More )"); + } + let text = svg.text(box.x + box.width+10, box.y + box.height /2, lines).attr({ + fill: "#171515", + "collapse-hover-id": c[0] + }); + + text.selectAll("tspan").forEach((tspan, i) =>{ + let dyc; + if(i === 0){ + dyc = 0; + }else{ + dyc = 16; + } + tspan.attr({ + x : box.x + box.width+10, + dy: dyc + }); + }); + const textbox = text.getBBox(); + const desc = svg.rect(textbox.x - 5, textbox.y-3,textbox.width+80, textbox.height+25,5,5).attr({ + strokeWidth: 0.5, + fill: "#bdbdbd", + stroke: "black", + "collapse-hover-id": c[0] + }); + + g.add(desc); + g.add(text); + + },function (){ + this.node.style.cursor = "default"; + this.attr({fill:"#000000"}); + svg.selectAll(`[collapse-hover-id='${c[0]}']`).remove(); + }); + + } + } + + + // Repeat almost everything for the minimap mXMinimap = minimapMinX + mX / ratio; mYLeftMinimap = minimapMinY + mYLeft / ratio; From edc7a5e53226440b7a9ab8d50b52a09b2f19546c Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 2 Apr 2026 23:21:48 +0200 Subject: [PATCH 26/48] Improved hovering taxa function in compact mode. [!] => Fix the todo. --- matt/static/js.js | 148 +++++++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 68 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index a206852..65bad20 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1369,8 +1369,6 @@ $(function () { //Removing false leaves if((r_child_check["name"] !== "None") && !collapsedmap[item["id"]]["right"]) r_edge.remove(); if((l_child_check["name"] !== "None") && !collapsedmap[item["id"]]["left"]) l_edge.remove(); - - // hover function in compact mode if(compactmode === true) { const compactlabel = Object.values(collapsedmap).some(v => @@ -1379,78 +1377,92 @@ $(function () { ); if (compactlabel) { - - const c = Object.entries(collapsedmap).find(([id, v]) => - "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || - "'" + v["label"] + "'" === l_child_check["name"] && (v["right_id"] || v["left_id"]) - ); - const sub = getTreeCompact(c[0],data); // TODO: FIX WRONG ID FOR SUBTREE - console.log(sub); - - const item_counter = sub.filter(id => { - const n = data.find(d => d["id"] === id); - return n["name"] !== "None"; - }).length; - - const item_name_container = sub. - map(id=>data.find(d => d["id"] === id)). - filter(n => n["name"] !== "None"). - map(n => n["name"]); - const preview_containing = item_name_container.slice(0,10).join(", "); - - - const taxa = svg.select(`text[data-parent='${c[0]}']`) - taxa.hover(function (){ - this.node.style.cursor = "pointer"; - this.attr({fill:"#1e90ff"}); - - const box = this.getBBox(); - - const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); - let lines = []; - lines.push("'"+collapsedmap[c[0]]["label"]+"' "+"contains "+item_counter+" taxa:"); - lines.push(" ") - previewlist.forEach(taxa => lines.push("- "+taxa)); - lines.push(" "); - if(item_counter > 10){ - lines.push(" ... ( "+ (item_counter-10)+ " More )"); - }else{ - lines.push(" ... ( 0 More )"); - } - let text = svg.text(box.x + box.width+10, box.y + box.height /2, lines).attr({ - fill: "#171515", - "collapse-hover-id": c[0] - }); - - text.selectAll("tspan").forEach((tspan, i) =>{ - let dyc; - if(i === 0){ - dyc = 0; + const c = Object.entries(collapsedmap).map(([id, v]) => { + if(( "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || + "'" + v["label"] + "'" === l_child_check["name"] && v["right_id"])) { + return(v["right_id"]) + }else if(( "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || + "'" + v["label"] + "'" === l_child_check["name"] && v["left_id"])){ + return(v["left_id"]); + } + return null; + }).filter(v => v !== null); + let sub; + let item_counter; + let item_name_container; + let preview_containing; + let taxa; + + + c.forEach((entry, index) => { + sub = getTreeCompact(entry,JSON.parse(currenttree) ); + //TODO: FIX BUG WHEN 2 taxa have same name programm crashes + + + item_counter = sub.filter(id => { + const n = JSON.parse(currenttree).find(d => d["id"] === id); + return (n["name"] !== "None" && n["name"] !== undefined); + }).length; + + item_name_container = sub. + map(id=>JSON.parse(currenttree).find(d => d["id"] === id)). + filter(n => n["name"] !== "None" && n["name"] !== undefined). + map(n => n["name"]); + preview_containing = item_name_container.slice(0,10).join(", "); + + taxa = svg.select(`text[data-id='${entry}']`); + taxa.hover(function (){ + this.node.style.cursor = "pointer"; + this.attr({fill:"#1e90ff"}); + + const box = this.getBBox(); + + const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); + let lines = []; + lines.push("'"+collapsedmap[entry]["label"]+"' "+"contains "+item_counter+" taxa:"); + lines.push(" ") + previewlist.forEach(taxa => lines.push("- "+taxa)); + lines.push(" "); + if(item_counter > 10){ + lines.push(" ... ( "+ (item_counter-10)+ " More )"); }else{ - dyc = 16; + lines.push(" ... ( 0 More )"); } - tspan.attr({ - x : box.x + box.width+10, - dy: dyc + let text = svg.text(box.x + box.width+10, box.y + box.height /2, lines).attr({ + fill: "#171515", + "collapse-hover-id": entry }); - }); - const textbox = text.getBBox(); - const desc = svg.rect(textbox.x - 5, textbox.y-3,textbox.width+80, textbox.height+25,5,5).attr({ - strokeWidth: 0.5, - fill: "#bdbdbd", - stroke: "black", - "collapse-hover-id": c[0] - }); - g.add(desc); - g.add(text); + text.selectAll("tspan").forEach((tspan, i) =>{ + let dyc; + if(i === 0){ + dyc = 0; + }else{ + dyc = 16; + } + tspan.attr({ + x : box.x + box.width+10, + dy: dyc + }); + }); + const textbox = text.getBBox(); + const desc = svg.rect(textbox.x - 5, textbox.y-3,textbox.width+80, textbox.height+25,5,5).attr({ + strokeWidth: 0.5, + fill: "#bdbdbd", + stroke: "black", + "collapse-hover-id": entry + }); - },function (){ - this.node.style.cursor = "default"; - this.attr({fill:"#000000"}); - svg.selectAll(`[collapse-hover-id='${c[0]}']`).remove(); - }); + g.add(desc); + g.add(text); + },function (){ + this.node.style.cursor = "default"; + this.attr({fill:"#000000"}); + svg.selectAll(`[collapse-hover-id='${entry}']`).remove(); + }); + + }); } } From 7f4418c48e98a15c8b6af8d1b87d77904eda18b4 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 3 Apr 2026 00:30:49 +0200 Subject: [PATCH 27/48] Mini fix. --- matt/static/js.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/matt/static/js.js b/matt/static/js.js index 65bad20..be7e578 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1394,7 +1394,7 @@ $(function () { let taxa; - c.forEach((entry, index) => { + c.forEach(entry => { sub = getTreeCompact(entry,JSON.parse(currenttree) ); //TODO: FIX BUG WHEN 2 taxa have same name programm crashes @@ -1410,6 +1410,8 @@ $(function () { map(n => n["name"]); preview_containing = item_name_container.slice(0,10).join(", "); + + taxa = svg.select(`text[data-id='${entry}']`); taxa.hover(function (){ this.node.style.cursor = "pointer"; From a263b233036170201e02d07b0c559cae8c15972f Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 3 Apr 2026 00:53:04 +0200 Subject: [PATCH 28/48] Fixxed hover bug through a workaround in draw_collapsed_line function. --- matt/static/js.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index be7e578..7b28ae7 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -45,6 +45,7 @@ $(function () { let compactmode = false; let currenttree; let counter_of_nodes = [] + let label_blacklist = []; // Gets the options initially getOptions(); @@ -691,11 +692,13 @@ $(function () { //const collapsedmap = {}; if(Object.keys(collapsedmap).length === 0){ - data.forEach(n => { - collapsedmap[n["id"]] = {left: false, right: false, left_id: null, right_id: null, label: "Collapsed", + data.forEach((n, index) => { + collapsedmap[n["id"]] = {left: false, right: false, left_id: null, right_id: null, label: "Collapsed"+index, "collapsed-line-Y":null, }; + label_blacklist.push(collapsedmap[n["id"]]["label"]); }); + } if(typeof data === "undefined") return { calculateCompactTree: calculateCompactTree }; @@ -1177,7 +1180,7 @@ $(function () { ); //UPDATE NAMETAG IF NAME CHANGED - nameText.click(function () { + nameText.click(function () { //TODO: IMPLEMENT FUNCTION SO 2 collapsed tress cannot have same label. const box = this.getBBox(); const ctm = this.node.getScreenCTM(); const point = svg.node.createSVGPoint(); @@ -1198,6 +1201,12 @@ $(function () { if(e.key === "Enter"){ if(input["value"] !== ""){ const newtext = input["value"]; + if(label_blacklist.includes(newtext)){ + $("#info-modal-label").text("Warning") + $("#info-modal-body").text("Please use another name for ur label that haven't been already used."); + $("#info-modal").modal("show"); + return; + } collapsedmap[start]["label"] = newtext; this.attr({ text: "\""+newtext+"\"" + "("+item_counter+")", @@ -1387,17 +1396,15 @@ $(function () { } return null; }).filter(v => v !== null); + let sub; let item_counter; let item_name_container; let preview_containing; let taxa; - c.forEach(entry => { sub = getTreeCompact(entry,JSON.parse(currenttree) ); - //TODO: FIX BUG WHEN 2 taxa have same name programm crashes - item_counter = sub.filter(id => { const n = JSON.parse(currenttree).find(d => d["id"] === id); From 5f075a0236d02a766d08355a46ec3cf4dc95fc09 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 3 Apr 2026 00:54:56 +0200 Subject: [PATCH 29/48] Gramma change --- matt/static/js.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matt/static/js.js b/matt/static/js.js index 7b28ae7..7563a6d 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1203,7 +1203,7 @@ $(function () { const newtext = input["value"]; if(label_blacklist.includes(newtext)){ $("#info-modal-label").text("Warning") - $("#info-modal-body").text("Please use another name for ur label that haven't been already used."); + $("#info-modal-body").text("Please use a different name for your label that hasn't been already used."); $("#info-modal").modal("show"); return; } From ead82921ab6e77e47bb2046e2e0ea1305fe46fdc Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 3 Apr 2026 10:31:57 +0200 Subject: [PATCH 30/48] Fixxed wrong label display bug in compact mode while hovering. --- matt/static/js.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 7563a6d..e8e20c5 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1178,7 +1178,6 @@ $(function () { svg.selectAll(`[collapse-hover-id='${start}']`).remove(); } ); - //UPDATE NAMETAG IF NAME CHANGED nameText.click(function () { //TODO: IMPLEMENT FUNCTION SO 2 collapsed tress cannot have same label. const box = this.getBBox(); @@ -1207,7 +1206,11 @@ $(function () { $("#info-modal").modal("show"); return; } + + label_blacklist.push(newtext); + label_blacklist = label_blacklist.filter(x=> x !== collapsedmap[start]["label"]); collapsedmap[start]["label"] = newtext; + this.attr({ text: "\""+newtext+"\"" + "("+item_counter+")", fontStyle: "italic" @@ -1402,7 +1405,6 @@ $(function () { let item_name_container; let preview_containing; let taxa; - c.forEach(entry => { sub = getTreeCompact(entry,JSON.parse(currenttree) ); @@ -1428,7 +1430,8 @@ $(function () { const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); let lines = []; - lines.push("'"+collapsedmap[entry]["label"]+"' "+"contains "+item_counter+" taxa:"); + const temp = JSON.parse(currenttree).find(d => d["id"] === entry); + lines.push("'"+collapsedmap[temp["parent"]]["label"]+"' "+"contains "+item_counter+" taxa:"); lines.push(" ") previewlist.forEach(taxa => lines.push("- "+taxa)); lines.push(" "); From 10756924ae6f78ff5445f92ccc84f7c39de4d74d Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 3 Apr 2026 18:25:18 +0200 Subject: [PATCH 31/48] Search function now available in compact mode. --- matt/static/js.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index e8e20c5..0939d4f 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -2138,10 +2138,18 @@ $(function () { */ function search(value) { // Different x coordinate depending on whether branch lengths are shown - if (!(enableLengths)) { - data = JSON.parse(trees[counter_of_trees - 1][1]); - } else { - data = JSON.parse(trees[counter_of_trees - 1][2]); + if(compactmode === false) { + if (!(enableLengths)) { + data = JSON.parse(trees[counter_of_trees - 1][1]); + } else { + data = JSON.parse(trees[counter_of_trees - 1][2]); + } + }else{ + if (!(enableLengths)) { + data = calculateCompactTree(JSON.parse(currenttree), collapsedmap); + } else { + data = calculateCompactTree(JSON.parse(currenttree), collapsedmap); + } } data.forEach(function(item, index, array) { // Searches through the all texts, colors all and jumps to the last match From 76fdb71ea2689f6cece689b82a55c25ab03c82a0 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sun, 19 Apr 2026 22:39:43 +0200 Subject: [PATCH 32/48] Todo: Fix Bug in compact mode where Taxa is null. --- matt/static/js.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 0939d4f..97bda0c 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1390,11 +1390,11 @@ $(function () { if (compactlabel) { const c = Object.entries(collapsedmap).map(([id, v]) => { - if(( "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || - "'" + v["label"] + "'" === l_child_check["name"] && v["right_id"])) { + if((( "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || + "'" + v["label"] + "'" === l_child_check["name"]) && v["right_id"])) { return(v["right_id"]) - }else if(( "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || - "'" + v["label"] + "'" === l_child_check["name"] && v["left_id"])){ + }else if((( "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || + "'" + v["label"] + "'" === l_child_check["name"]) && v["left_id"])){ return(v["left_id"]); } return null; @@ -1405,9 +1405,10 @@ $(function () { let item_name_container; let preview_containing; let taxa; - c.forEach(entry => { + console.log(c) + console.log(collapsedmap) + c.forEach(entry => { //TODO: FIX EMPTY TAXA BUG sub = getTreeCompact(entry,JSON.parse(currenttree) ); - item_counter = sub.filter(id => { const n = JSON.parse(currenttree).find(d => d["id"] === id); return (n["name"] !== "None" && n["name"] !== undefined); @@ -1676,11 +1677,6 @@ $(function () { const node = data.find(d => d["id"] === id); if (node === null) return; - collapsedmap[id]["l-d"] = null; - collapsedmap[id]["r-d"] = null; - collapsedmap[id]["l_edge-Y"] = null; - collapsedmap[id]["r_edge-Y"] = null; - collapsedmap[id]["collapsed-line-Y"] = null; //SIDE SWAP CHECK IF STATEMENTS AFTER BRANCH SWAP if (collapsedmap[id]["right"] && collapsedmap[id]["right_id"] !== null) { @@ -2305,6 +2301,7 @@ $(function () { neededchange["r_child"] = ""; }); + console.log(newtree) return newtree; } From 9a8ce037bf21572939f12857ad9723dc9ee33cbe Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 20 Apr 2026 13:38:28 +0200 Subject: [PATCH 33/48] Right and left nodes now work in compact mode. Todo: fix bug with wrong id display override at calculatecompacttree. --- matt/static/js.js | 209 +++++++++++++++++++++++++--------------------- 1 file changed, 114 insertions(+), 95 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 97bda0c..54e55fc 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1382,102 +1382,7 @@ $(function () { if((r_child_check["name"] !== "None") && !collapsedmap[item["id"]]["right"]) r_edge.remove(); if((l_child_check["name"] !== "None") && !collapsedmap[item["id"]]["left"]) l_edge.remove(); // hover function in compact mode - if(compactmode === true) { - const compactlabel = Object.values(collapsedmap).some(v => - ("'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || - "'" + v["label"] + "'" === l_child_check["name"]) && (v["right_id"] || v["left_id"]) - ); - - if (compactlabel) { - const c = Object.entries(collapsedmap).map(([id, v]) => { - if((( "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || - "'" + v["label"] + "'" === l_child_check["name"]) && v["right_id"])) { - return(v["right_id"]) - }else if((( "'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === r_child_check["name"] || - "'" + v["label"] + "'" === l_child_check["name"]) && v["left_id"])){ - return(v["left_id"]); - } - return null; - }).filter(v => v !== null); - - let sub; - let item_counter; - let item_name_container; - let preview_containing; - let taxa; - console.log(c) - console.log(collapsedmap) - c.forEach(entry => { //TODO: FIX EMPTY TAXA BUG - sub = getTreeCompact(entry,JSON.parse(currenttree) ); - item_counter = sub.filter(id => { - const n = JSON.parse(currenttree).find(d => d["id"] === id); - return (n["name"] !== "None" && n["name"] !== undefined); - }).length; - - item_name_container = sub. - map(id=>JSON.parse(currenttree).find(d => d["id"] === id)). - filter(n => n["name"] !== "None" && n["name"] !== undefined). - map(n => n["name"]); - preview_containing = item_name_container.slice(0,10).join(", "); - - - - taxa = svg.select(`text[data-id='${entry}']`); - taxa.hover(function (){ - this.node.style.cursor = "pointer"; - this.attr({fill:"#1e90ff"}); - - const box = this.getBBox(); - - const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); - let lines = []; - const temp = JSON.parse(currenttree).find(d => d["id"] === entry); - lines.push("'"+collapsedmap[temp["parent"]]["label"]+"' "+"contains "+item_counter+" taxa:"); - lines.push(" ") - previewlist.forEach(taxa => lines.push("- "+taxa)); - lines.push(" "); - if(item_counter > 10){ - lines.push(" ... ( "+ (item_counter-10)+ " More )"); - }else{ - lines.push(" ... ( 0 More )"); - } - let text = svg.text(box.x + box.width+10, box.y + box.height /2, lines).attr({ - fill: "#171515", - "collapse-hover-id": entry - }); - - text.selectAll("tspan").forEach((tspan, i) =>{ - let dyc; - if(i === 0){ - dyc = 0; - }else{ - dyc = 16; - } - tspan.attr({ - x : box.x + box.width+10, - dy: dyc - }); - }); - const textbox = text.getBBox(); - const desc = svg.rect(textbox.x - 5, textbox.y-3,textbox.width+80, textbox.height+25,5,5).attr({ - strokeWidth: 0.5, - fill: "#bdbdbd", - stroke: "black", - "collapse-hover-id": entry - }); - - g.add(desc); - g.add(text); - - },function (){ - this.node.style.cursor = "default"; - this.attr({fill:"#000000"}); - svg.selectAll(`[collapse-hover-id='${entry}']`).remove(); - }); - }); - } - } @@ -1754,6 +1659,120 @@ $(function () { }); } + if(compactmode === true) { //TODO: FIX WRONG ID OVERRIDE AT COMPACT CALCULATION. + data.forEach(function (item, index, array) { + const r_child_check = data.find(d => d["id"] === item["r_child"]); + const l_child_check = data.find(d => d["id"] === item["l_child"]); + + let right; + let left; + if(r_child_check){ + right = r_child_check["name"]; + }else{ + right = "None"; + } + if(l_child_check){ + left = l_child_check["name"]; + }else{ + left = "None" + } + + const compactlabel = Object.values(collapsedmap).some(v => + ("'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === right || + "'" + v["label"] + "'" === left) && (v["right_id"] || v["left_id"]) + ); + + if (compactlabel) { + const c = Object.entries(collapsedmap).map(([id, v]) => { + if(( "'" +v["label"] + "'" === right + && r_child_check["name"] !== "None" && v["right_id"])) { + return(v["right_id"]); + }else if(( + "'" + v["label"] + "'" === left && l_child_check["name"] !== "None" && v["left_id"])){ + return(v["left_id"]); + } + return null; + }).filter(v => v !== null); + + let sub; + let item_counter; + let item_name_container; + let preview_containing; + let taxa; + console.log(c) + console.log(collapsedmap) + c.forEach(entry => { //TODO: FIX EMPTY TAXA BUG + sub = getTreeCompact(entry,JSON.parse(currenttree) ); + item_counter = sub.filter(id => { + const n = JSON.parse(currenttree).find(d => d["id"] === id); + return (n["name"] !== "None" && n["name"] !== undefined); + }).length; + + item_name_container = sub. + map(id=>JSON.parse(currenttree).find(d => d["id"] === id)). + filter(n => n["name"] !== "None" && n["name"] !== undefined). + map(n => n["name"]); + preview_containing = item_name_container.slice(0,10).join(", "); + + + taxa = svg.select(`text[data-id='${entry}']`); + taxa.hover(function (){ + this.node.style.cursor = "pointer"; + this.attr({fill:"#1e90ff"}); + + const box = this.getBBox(); + + const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); + let lines = []; + const temp = JSON.parse(currenttree).find(d => d["id"] === entry); + lines.push("'"+collapsedmap[temp["parent"]]["label"]+"' "+"contains "+item_counter+" taxa:"); + lines.push(" ") + previewlist.forEach(taxa => lines.push("- "+taxa)); + lines.push(" "); + if(item_counter > 10){ + lines.push(" ... ( "+ (item_counter-10)+ " More )"); + }else{ + lines.push(" ... ( 0 More )"); + } + let text = svg.text(box.x + box.width+10, box.y + box.height /2, lines).attr({ + fill: "#171515", + "collapse-hover-id": entry + }); + + text.selectAll("tspan").forEach((tspan, i) =>{ + let dyc; + if(i === 0){ + dyc = 0; + }else{ + dyc = 16; + } + tspan.attr({ + x : box.x + box.width+10, + dy: dyc + }); + }); + const textbox = text.getBBox(); + const desc = svg.rect(textbox.x - 5, textbox.y-3,textbox.width+80, textbox.height+25,5,5).attr({ + strokeWidth: 0.5, + fill: "#bdbdbd", + stroke: "black", + "collapse-hover-id": entry + }); + + g.add(desc); + g.add(text); + + },function (){ + this.node.style.cursor = "default"; + this.attr({fill:"#000000"}); + svg.selectAll(`[collapse-hover-id='${entry}']`).remove(); + }); + + }); + } + }); + } + // Sets the initial position if (typeof xBefore === "undefined") { From 7d33fdae6f780a647182e793ad23451dba8ff5a9 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 20 Apr 2026 15:55:30 +0200 Subject: [PATCH 34/48] Fixxed inconsitent handling of hovering during compact mode --- matt/static/js.js | 77 ++++++++++++++++------------------------------- 1 file changed, 26 insertions(+), 51 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 54e55fc..2c6771c 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1106,7 +1106,7 @@ $(function () { let nameText = null; if(collapsed_check){ - nameText = svg.text(maxX - offset, circle.attr("data-v"), "'"+collapsedmap[start]["label"]+"'"+ " (" + item_counter+ ")").attr({ + nameText = svg.text(maxX - offset, circle.attr("data-v"), "'"+collapsedmap[childitem]["label"]+"'"+ " (" + item_counter+ ")").attr({ dominantBaseline: 'middle', fontSize: fontSize, fill: "black", @@ -1134,7 +1134,7 @@ $(function () { const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); let lines = []; - lines.push("'"+collapsedmap[start]["label"]+"' "+"contains "+item_counter+" taxa:"); + lines.push("'"+collapsedmap[childitem]["label"]+"' "+"contains "+item_counter+" taxa:"); lines.push(" ") previewlist.forEach(taxa => lines.push("- "+taxa)); lines.push(" "); @@ -1659,63 +1659,39 @@ $(function () { }); } - if(compactmode === true) { //TODO: FIX WRONG ID OVERRIDE AT COMPACT CALCULATION. - data.forEach(function (item, index, array) { - const r_child_check = data.find(d => d["id"] === item["r_child"]); - const l_child_check = data.find(d => d["id"] === item["l_child"]); + if(compactmode === true) { + data.forEach(function (item) { + const compactlabel = Object.values(collapsedmap).some(v => + ("'" + v["label"] + "'" === item["name"]) + ); - let right; - let left; - if(r_child_check){ - right = r_child_check["name"]; - }else{ - right = "None"; - } - if(l_child_check){ - left = l_child_check["name"]; - }else{ - left = "None" - } + if (compactlabel) { + const c = Object.entries(collapsedmap).map(([id, v]) => { + if((v["left_id"]!== null)){ + return v["left_id"]; + } + if((v["right_id"]!== null)) { + return v["right_id"]; + } + return undefined; + }).filter(v => (v !== undefined)); - const compactlabel = Object.values(collapsedmap).some(v => - ("'" + v["label"] + "'" === item["name"] || "'" + v["label"] + "'" === right || - "'" + v["label"] + "'" === left) && (v["right_id"] || v["left_id"]) - ); - - if (compactlabel) { - const c = Object.entries(collapsedmap).map(([id, v]) => { - if(( "'" +v["label"] + "'" === right - && r_child_check["name"] !== "None" && v["right_id"])) { - return(v["right_id"]); - }else if(( - "'" + v["label"] + "'" === left && l_child_check["name"] !== "None" && v["left_id"])){ - return(v["left_id"]); - } - return null; - }).filter(v => v !== null); - - let sub; - let item_counter; - let item_name_container; - let preview_containing; - let taxa; - console.log(c) - console.log(collapsedmap) - c.forEach(entry => { //TODO: FIX EMPTY TAXA BUG - sub = getTreeCompact(entry,JSON.parse(currenttree) ); - item_counter = sub.filter(id => { + + c.forEach(entry => { + const sub = getTreeCompact(entry,JSON.parse(currenttree)); + const item_counter = sub.filter(id => { const n = JSON.parse(currenttree).find(d => d["id"] === id); return (n["name"] !== "None" && n["name"] !== undefined); }).length; - item_name_container = sub. + const item_name_container = sub. map(id=>JSON.parse(currenttree).find(d => d["id"] === id)). filter(n => n["name"] !== "None" && n["name"] !== undefined). map(n => n["name"]); - preview_containing = item_name_container.slice(0,10).join(", "); + const preview_containing = item_name_container.slice(0,10).join(", "); - taxa = svg.select(`text[data-id='${entry}']`); + const taxa = svg.select(`text[data-id='${entry}']`); taxa.hover(function (){ this.node.style.cursor = "pointer"; this.attr({fill:"#1e90ff"}); @@ -1725,7 +1701,7 @@ $(function () { const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); let lines = []; const temp = JSON.parse(currenttree).find(d => d["id"] === entry); - lines.push("'"+collapsedmap[temp["parent"]]["label"]+"' "+"contains "+item_counter+" taxa:"); + lines.push("'"+collapsedmap[temp["id"]]["label"]+"' "+"contains "+item_counter+" taxa:"); lines.push(" ") previewlist.forEach(taxa => lines.push("- "+taxa)); lines.push(" "); @@ -2315,12 +2291,11 @@ $(function () { let newtree = tree.filter(node => !removeablenodes.includes(node["id"])); collapsednodes.map(id => { const neededchange = newtree.find(i => i["id"] === id); - neededchange["name"] = "'"+colmap[neededchange["parent"]]["label"]+"'"; + neededchange["name"] = "'"+colmap[neededchange["id"]]["label"]+"'"; neededchange["l_child"] = ""; neededchange["r_child"] = ""; }); - console.log(newtree) return newtree; } From 5ec6626e1dd6e3a6d21e567723b59910ebc826c8 Mon Sep 17 00:00:00 2001 From: Adrian Date: Tue, 28 Apr 2026 13:39:15 +0200 Subject: [PATCH 35/48] Fixxed minor bug including displaying wrong label text --- matt/static/js.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 2c6771c..22f5ac8 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1178,8 +1178,7 @@ $(function () { svg.selectAll(`[collapse-hover-id='${start}']`).remove(); } ); - //UPDATE NAMETAG IF NAME CHANGED - nameText.click(function () { //TODO: IMPLEMENT FUNCTION SO 2 collapsed tress cannot have same label. + nameText.click(function () { const box = this.getBBox(); const ctm = this.node.getScreenCTM(); const point = svg.node.createSVGPoint(); @@ -1208,8 +1207,8 @@ $(function () { } label_blacklist.push(newtext); - label_blacklist = label_blacklist.filter(x=> x !== collapsedmap[start]["label"]); - collapsedmap[start]["label"] = newtext; + label_blacklist = label_blacklist.filter(x=> x !== collapsedmap[childitem]["label"]); + collapsedmap[childitem]["label"] = newtext; this.attr({ text: "\""+newtext+"\"" + "("+item_counter+")", From 791094d5ceb83cfb170df76a743b57caf0c00cf9 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sat, 9 May 2026 16:12:41 +0200 Subject: [PATCH 36/48] Hotfix Nr 1: Hoverbug leading to crash fixxed. --- matt/static/js.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 22f5ac8..f16879c 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1134,7 +1134,7 @@ $(function () { const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); let lines = []; - lines.push("'"+collapsedmap[childitem]["label"]+"' "+"contains "+item_counter+" taxa:"); + lines.push("This taxa "+"contains "+item_counter+" taxa:"); lines.push(" ") previewlist.forEach(taxa => lines.push("- "+taxa)); lines.push(" "); @@ -1674,7 +1674,7 @@ $(function () { } return undefined; }).filter(v => (v !== undefined)); - + console.log(c) c.forEach(entry => { const sub = getTreeCompact(entry,JSON.parse(currenttree)); @@ -1699,8 +1699,7 @@ $(function () { const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); let lines = []; - const temp = JSON.parse(currenttree).find(d => d["id"] === entry); - lines.push("'"+collapsedmap[temp["id"]]["label"]+"' "+"contains "+item_counter+" taxa:"); + lines.push("This taxa "+"contains "+item_counter+" taxa:"); lines.push(" ") previewlist.forEach(taxa => lines.push("- "+taxa)); lines.push(" "); From 12606408cc32ea50729028496fb81437eff8a697 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sat, 9 May 2026 16:21:15 +0200 Subject: [PATCH 37/48] Hotfix Nr 2: Stack collapse leading to crash fixxed. --- matt/static/js.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index f16879c..63d8bae 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1134,7 +1134,7 @@ $(function () { const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); let lines = []; - lines.push("This taxa "+"contains "+item_counter+" taxa:"); + lines.push("This collapsed taxa "+"contains "+item_counter+" taxa:"); lines.push(" ") previewlist.forEach(taxa => lines.push("- "+taxa)); lines.push(" "); @@ -1691,6 +1691,7 @@ $(function () { const taxa = svg.select(`text[data-id='${entry}']`); + if(taxa === null) return; taxa.hover(function (){ this.node.style.cursor = "pointer"; this.attr({fill:"#1e90ff"}); @@ -1699,7 +1700,7 @@ $(function () { const previewlist = preview_containing.split(",").map(x => x.trim()).slice(0,10); let lines = []; - lines.push("This taxa "+"contains "+item_counter+" taxa:"); + lines.push("This collapsed taxa "+"contains "+item_counter+" taxa:"); lines.push(" ") previewlist.forEach(taxa => lines.push("- "+taxa)); lines.push(" "); From 83a7bed732eaf4bdec6a3ec079b046b62297cc79 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 11 May 2026 22:30:38 +0200 Subject: [PATCH 38/48] Changed text info when trying to snapshot an existing tree. --- matt/static/js.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 63d8bae..2aff8ce 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -503,7 +503,7 @@ $(function () { if (snapshotTrees.find(element => element[0] == trees[counter_of_trees - 1][0])) { // Shows the info modal $("#info-modal-label").text("This snapshot already exists!") - $("#info-modal-body").text("This tree has already been snapshot.") + $("#info-modal-body").text("You already froze this tree topology.") $("#info-modal").modal("show"); return; } @@ -1976,7 +1976,7 @@ $(function () { if (snapshotTrees.find(element => element[0] == trees[counter_of_trees - 1][0])) { // Shows the info modal $("#info-modal-label").text("This snapshot already exists!") - $("#info-modal-body").text("This tree has already been snapshot.") + $("#info-modal-body").text("You already froze this tree topology.") $("#info-modal").modal("show"); return; } @@ -1993,7 +1993,7 @@ $(function () { if (snapshotTrees.find(element => element[0] == trees[counter_of_trees - 1][0])) { // Shows the info modal $("#info-modal-label").text("This snapshot already exists!") - $("#info-modal-body").text("This tree has already been snapshot.") + $("#info-modal-body").text("You already froze this tree topology.") $("#info-modal").modal("show"); return; } From 4f4f313cc80919b1e9132e4796643942ab87f420 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 11 May 2026 23:07:13 +0200 Subject: [PATCH 39/48] Fixxed bug where tree file that should contain lengths didnt contained them. --- matt/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matt/app.py b/matt/app.py index 73506e8..ba2be87 100644 --- a/matt/app.py +++ b/matt/app.py @@ -153,7 +153,7 @@ def download(tree_id): file_without.write(tree_without + "\n") file_without.close() - tree_with = Tree(tree_lengths_json).to_newick() + tree_with = Tree(tree_lengths_json, enable_lengths=True).to_newick() path_with = os.path.join(session["working-directory"], (session["session-name"] + "_" if session["session-name"] else "") + "with_lengths.tree") file_with = open(path_with, "w") From c8e09e9917a8794bfcc7ac2c5bb9a7c288510a44 Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 13 May 2026 15:11:17 +0200 Subject: [PATCH 40/48] Fixxed bug where collapse function was enabled at compact node even tho it shouldnt. --- matt/static/js.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 2aff8ce..48fb18b 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1040,6 +1040,7 @@ $(function () { // COLAPSE if(collapsed_check){ + if(compactmode === true && (collapsedmap[start]["left_id"] || collapsedmap[start]["right_id"])) return; svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "none"}); sub.forEach(child => { @@ -1058,6 +1059,7 @@ $(function () { //EXPAND }else{ + if(compactmode === true && (collapsedmap[start]["left_id"] || collapsedmap[start]["right_id"])) return; svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "inline"}); sub.forEach(child => { @@ -1067,8 +1069,7 @@ $(function () { counter_of_nodes = counter_of_nodes.filter(x=> x !== child); }); - - + draw_collapsed_line(childitem, start, collapsed_check, direct); if((counter_of_nodes.length > 0)) { $("#compact-button").prop("disabled", false); From 1cdbbb3af1c0eb72650a7971cd4aaf2dc6d5b881 Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 14 May 2026 18:25:12 +0200 Subject: [PATCH 41/48] Fixxed bug where collapse function was enabled at compact node even tho it shouldnt. (now complete) --- matt/static/js.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 48fb18b..30c9c5f 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -42,6 +42,7 @@ $(function () { let enableLengths; let alignLabels = true; let collapsedmap = {}; + let compactlist = []; let compactmode = false; let currenttree; let counter_of_nodes = [] @@ -1040,7 +1041,6 @@ $(function () { // COLAPSE if(collapsed_check){ - if(compactmode === true && (collapsedmap[start]["left_id"] || collapsedmap[start]["right_id"])) return; svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "none"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "none"}); sub.forEach(child => { @@ -1059,7 +1059,6 @@ $(function () { //EXPAND }else{ - if(compactmode === true && (collapsedmap[start]["left_id"] || collapsedmap[start]["right_id"])) return; svg.selectAll(`circle[data-id='${childitem}']`).attr({display: "inline"}); minimap.selectAll(`path[data-id='${childitem}']`).attr({display: "inline"}); sub.forEach(child => { @@ -1361,6 +1360,8 @@ $(function () { r_edge.click(function(){ + const check = compactlist.find(n => n === item["r_child"]); + if(compactmode === true && (check !== undefined)) return; document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); collapsedmap[item["id"]]["collapsed-line-Y"] = r_edge.attr("data-v"); collapse(item["r_child"],item["id"], !collapsedmap[item["id"]]["right"], "right"); @@ -1369,6 +1370,8 @@ $(function () { }); l_edge.click(function(){ + const check = compactlist.find(n => n === item["l_child"]); + if(compactmode === true && (check !== undefined)) return; document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); collapsedmap[item["id"]]["collapsed-line-Y"] = l_edge.attr("data-v"); collapse(item["l_child"],item["id"], !collapsedmap[item["id"]]["left"], "left"); @@ -1675,7 +1678,7 @@ $(function () { } return undefined; }).filter(v => (v !== undefined)); - console.log(c) + c.forEach(entry => { const sub = getTreeCompact(entry,JSON.parse(currenttree)); @@ -2287,6 +2290,7 @@ $(function () { const removeablenodes = collapsednodes.map(id => { return getTreeCompact(id, tree); }).flat(); + compactlist = collapsednodes; let newtree = tree.filter(node => !removeablenodes.includes(node["id"])); collapsednodes.map(id => { From 51827368770a8de6a36996b5d1d6b186cb5ec2d5 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 15 May 2026 21:42:51 +0200 Subject: [PATCH 42/48] Fixxed bug where ghost circle for leafes were drawn even tho they shouldnt. --- matt/static/js.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/matt/static/js.js b/matt/static/js.js index 30c9c5f..31c6c38 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -1337,6 +1337,19 @@ $(function () { "data-direct": "left", }); + if(compactmode === true){ + if((collapsedmap[item["id"]] && collapsedmap[item["id"]]["right_id"] === item["r_child"])){ + r_edge.attr({display: "none"}); + }else{ + r_edge.attr({display: "inline"}); + } + if((collapsedmap[item["id"]] && collapsedmap[item["id"]]["left_id"] === item["l_child"])){ + l_edge.attr({display: "none"}); + }else{ + l_edge.attr({display: "inline"}); + } + } + l_edge.hover( function () { this.node.style.cursor = "pointer"; @@ -1382,9 +1395,8 @@ $(function () { g.add(r_edge, l_edge); //Removing false leaves - if((r_child_check["name"] !== "None") && !collapsedmap[item["id"]]["right"]) r_edge.remove(); - if((l_child_check["name"] !== "None") && !collapsedmap[item["id"]]["left"]) l_edge.remove(); - // hover function in compact mode + if((r_child_check["name"] !== "None")) r_edge.remove(); + if((l_child_check["name"] !== "None")) l_edge.remove(); @@ -1583,7 +1595,7 @@ $(function () { Object.keys(collapsedmap).forEach(id => { document.querySelectorAll(".svg-edit-input").forEach(e => e.remove()); const node = data.find(d => d["id"] === id); - if (node === null) return; + if (node === undefined) return; //SIDE SWAP CHECK IF STATEMENTS AFTER BRANCH SWAP @@ -1631,7 +1643,6 @@ $(function () { const children_path = svg.select(`path[data-id='${leftpath_id}']`); collapsedmap[children]["l-d"] = children_path.attr("d"); } - }); } if (collapsedmap[id]["left"] && collapsedmap[id]["left_id"]) { From bb081aa422c9b84e2ac3b79e2d8840fb09447541 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sat, 4 Jul 2026 23:02:48 +0200 Subject: [PATCH 43/48] Updated Email in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3490acd..41403bb 100644 --- a/README.md +++ b/README.md @@ -101,4 +101,4 @@ Any bug reports, comments and suggestions are highly appreciated. Please An Application Note is in the works. Stay tuned. # Contact -For further support or bug reports please contact us via [email](mailto:jeffgower98@gmail.com). +For further support or bug reports please contact us via [email](mailto:jeffgower98@gmail.com) or [email](mailto:faflow456456@gmail.com). From 2cd8e1f7f8e9d240ab2cd90b7e0c4e37eead5b46 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sat, 4 Jul 2026 23:03:40 +0200 Subject: [PATCH 44/48] Updated Email in README.md. Small change --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41403bb..0b3a8a5 100644 --- a/README.md +++ b/README.md @@ -101,4 +101,4 @@ Any bug reports, comments and suggestions are highly appreciated. Please An Application Note is in the works. Stay tuned. # Contact -For further support or bug reports please contact us via [email](mailto:jeffgower98@gmail.com) or [email](mailto:faflow456456@gmail.com). +For further support or bug reports please contact us via [email-Jeff](mailto:jeffgower98@gmail.com) or [email-Adrian](mailto:faflow456456@gmail.com). From 6243d2d2d898ff5b220dc0edc02c15d3bfab1879 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sat, 4 Jul 2026 23:04:07 +0200 Subject: [PATCH 45/48] Updated Email in README.md. Small change [2] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b3a8a5..680778b 100644 --- a/README.md +++ b/README.md @@ -101,4 +101,4 @@ Any bug reports, comments and suggestions are highly appreciated. Please An Application Note is in the works. Stay tuned. # Contact -For further support or bug reports please contact us via [email-Jeff](mailto:jeffgower98@gmail.com) or [email-Adrian](mailto:faflow456456@gmail.com). +For further support or bug reports please contact us via [@Jeff](mailto:jeffgower98@gmail.com) or [@Adrian](mailto:faflow456456@gmail.com). From 91037cfbdb1e82d417e905eabeb75474b6c746e7 Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 9 Jul 2026 13:45:04 +0200 Subject: [PATCH 46/48] Todo message for future updates. --- matt/static/js.js | 1 + 1 file changed, 1 insertion(+) diff --git a/matt/static/js.js b/matt/static/js.js index 31c6c38..644826a 100644 --- a/matt/static/js.js +++ b/matt/static/js.js @@ -701,6 +701,7 @@ $(function () { }); } + //TODO: Rework this. It works but not really smooth. if(typeof data === "undefined") return { calculateCompactTree: calculateCompactTree }; From 6e9595d9d8b3e7bd1d0ba4a6dca15d35164b27d9 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 10 Jul 2026 15:48:46 +0200 Subject: [PATCH 47/48] New version text --- matt/version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matt/version.txt b/matt/version.txt index 90a7f60..7962dcf 100644 --- a/matt/version.txt +++ b/matt/version.txt @@ -1 +1 @@ -1.3.12 +1.3.13 From daa2df72b391298259fc851935f70a78340765a7 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 10 Jul 2026 16:25:40 +0200 Subject: [PATCH 48/48] Update version.txt back to 1.3.12 --- matt/version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matt/version.txt b/matt/version.txt index 7962dcf..90a7f60 100644 --- a/matt/version.txt +++ b/matt/version.txt @@ -1 +1 @@ -1.3.13 +1.3.12