diff --git a/html/table.html b/html/table.html index 1f84939..28afd1a 100644 --- a/html/table.html +++ b/html/table.html @@ -1679,7 +1679,7 @@ class SelectedNodes { static nodes = new Map(); static #circle_pos = 1; - static #name_pos = 3; + static #name_pos = 0; static #circle_size_selected = LEAF_NODE_SIZE * 1.5; static #circle_size_default = LEAF_NODE_SIZE; static #default_color = "#999"; @@ -3557,9 +3557,34 @@ css_export.appendChild(link_style_sheet); svg.insertBefore(css_export, svg.firstChild); + + let longest_label = 10 + if(ORIGINAL_DATA){ + let leaves = svg.querySelectorAll(".leaf-node") + leaves.forEach((ele) => { + let [element, circle, text] = SelectedNodes.getNodeData(ele) + let input_text = text.textContent; + if(ORIGINAL_DATA.has(input_text) && input_text){ + let row = ORIGINAL_DATA.get(input_text) + let new_label = `${input_text}\t${row.slice(1, -1).join('\t')}` + text.textContent = new_label + let text_length = new_label.length; + if(text_length > longest_label){ + longest_label = text_length; + } + } + }) + } + svg.style.overflow = "visible" const tree_svg = svg.querySelector("#TreeSVG"); - const offset_safety_factor = 20; - tree_svg.style.marginLeft = legend_width + offset_safety_factor; + tree_svg.style.overflow = "visible" + tree_svg.style.paddingLeft = "4em" + tree_svg.style.height = "auto" + tree_svg.style.width = "auto" + tree_svg.width = tree_svg.width.baseVal.value + (longest_label*4); + const attr = tree_svg.getAttributeNode("height") + tree_svg.removeAttributeNode(attr); + tree_svg.setAttribute("viewBox", `${0} -${tree_svg.viewBox.baseVal.height} ${tree_svg.width.baseVal.value + (longest_label*4)} ${tree_svg.viewBox.baseVal.height + (longest_label*4)}`); const serializer = new window.XMLSerializer; const xml_string = serializer.serializeToString(svg); @@ -3572,6 +3597,7 @@ let downloadLink = document.createElement("a"); downloadLink.download = 'tree_snapshot_'+date.getDate()+'-'+ (date.getMonth()+1)+'-'+date.getFullYear()+'_'+date.getHours()+'h.svg'; + let svg = document.getElementById("TreeData").cloneNode(true) //remove duplicated leaf nodes text duplication for more accurate searches svg.querySelectorAll('g .tree-node').forEach(node => { @@ -3580,8 +3606,15 @@ text_nodes_leafs[0].remove() } }) - svg.querySelector("#TreeSVG").style.overflow = "visible" - const blob = serialize2svg(svg)[0]; + + const tree_svg = svg.querySelector("#TreeSVG"); + const [minX, minY, width, height] = tree_svg.getAttribute("viewBox").split(/[\s,]+/).map(Number); + svg.style.height = `${height*1.15}px` + svg.style.width = `${width*1.05}px` + + svg.style.overflow = "visible"; + + const blob = serialize2svg(svg)[0]; downloadLink.href = window.URL.createObjectURL(blob); downloadLink.click(); //Trigger a click on the element downloadLink.remove(); @@ -3589,72 +3622,69 @@ /*Adapted from https://gist.github.com/tatsuyasusukida/1261585e3422da5645a1cbb9cf8813d6 and https://zooper.pages.dev/articles/how-to-convert-a-svg-to-png-using-canvas*/ export_tree_to_png = function(){ + let svg = document.getElementById("TreeSVG").cloneNode(true); + let remove_svg = false; + if(ORIGINAL_DATA){ + // SVG value needs to be in DOM to be rendered in the canvas, + // but we also need ta copy to update the text attributes in the tree + // or else the displayed tree is modified + // Copies are only needed if metadata is included as well + let leaves = svg.querySelectorAll(".leaf-node") + let longest_label = 10 + svg = svg.cloneNode(true); + svg.style.zIndex = '-1'; + document.getElementById("TreeData").appendChild(svg); + remove_svg = true; + leaves.forEach((ele) => { + let [element, circle, text] = SelectedNodes.getNodeData(ele) + let input_text = text.textContent; + if(ORIGINAL_DATA.has(input_text) && input_text){ + let row = ORIGINAL_DATA.get(input_text) + // End is not included in the slice, as it is simply a bool added on the program to track changed values + let new_label = `${input_text}\t${row.slice(1, -1).join('\t')}` + text.textContent = new_label + let text_length = new_label.length; + if(text_length > longest_label){ + longest_label = text_length; + } + } + }) + } + // Get the view box required for setting the correct output size + const viewbox = svg.getAttribute("viewBox"); + const [minX, minY, width, height] = viewbox.split(/[\s,]+/).map(Number); + let svg_width = width; + let svg_height = height; + + //extract SVG element + const svgData = new XMLSerializer().serializeToString(svg); + + //create canvas to the size of the SVG + let canvas = document.createElement('canvas') + canvas.width = svg_width+svg_width*0.05; + canvas.height = svg_height+svg_width*0.15; - let svg = document.getElementById("TreeSVG").cloneNode(true); - - let {x, y, width, height} = svg.viewBox.baseVal; - let svg_width = width; - let svg_height = height; - let modified_width = svg_width + (svg_width * 0.15); - let modified_height = svg_height + (svg_height * 0.15); - - // Change the width of the svg element allowing for more of the viewbox to show up - svg.setAttribute("width", modified_width) - - /* - Create a temporary svg expanding increasing the size of the final - canvas the elelment is rendered in. Having this seperate wrapper - also allows for seperate modifications of the PNG before export allowing - for translationst to be performed. - */ - let wrapping_svg = d3.select("body") - .append("svg") - .attr("id", "temporary_svg") - .attr("height", modified_height) - .attr("width", modified_width) - .append('g') - .attr("id", "translation_element") - .attr("height", modified_height) - .attr("width", modified_width) - .append(() => { - return svg - }) - - - //extract SVG element - const svgData = new XMLSerializer().serializeToString(document.getElementById("temporary_svg")); - - //create canvas to the size of the SVG - let canvas = document.createElement('canvas') - canvas.width = modified_width; - canvas.height = modified_height; - - //create SVG image element - let img = new Image(); - img.src = "data:image/svg+xml;base64," + btoa(svgData); - - img.onload = function () { //must be inside this image onload event function due to async image load nature - //render SVG image onto canvas element - let ctx = canvas.getContext('2d'); - ctx.fillStyle = "white"; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - ctx.drawImage(img, 0, 0, modified_width, modified_height); - - //create PNG image from the canvas - let pngUrl = canvas.toDataURL('image/png').replace('image/png', 'octet/stream'); - //create download link - let downloadLink = document.createElement("a"); - downloadLink.download = 'tree_snapshot.png'; - downloadLink.href = pngUrl; - downloadLink.click(); - downloadLink.remove(); - } - - // Remove temporary elements - document.getElementById("temporary_svg").remove(); - - + //create SVG image element + let img = new Image(); + img.src = "data:image/svg+xml;base64," + btoa(svgData); + + img.onload = function () { //must be inside this image onload event function due to async image load nature + //render SVG image onto canvas element + let ctx = canvas.getContext('2d'); + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0); + //create PNG image from the canvas + let pngUrl = canvas.toDataURL('image/png').replace('image/png', 'octet/stream'); + //create download link + let downloadLink = document.createElement("a"); + downloadLink.download = 'tree_snapshot.png'; + downloadLink.href = pngUrl; + downloadLink.click(); + downloadLink.remove(); + canvas.remove(); + if(remove_svg) svg.remove(); + } } tree_full_screen_mode = function(){