/* Copyright © 2019 Inria. All rights reserved. */ function start(svgObject){ const texts = svgObject.getElementsByTagName('text') for (let text of texts) { text.setAttribute("style", text.getAttribute("style") + "; pointer-events:none") } } async function createProcesses(svgdoc, req, json = null){ await clear(svgdoc) clearTable() if(!json){ response = await fetch("/" + req) json = await response.json() } json.forEach(proc => { displayProcess(svgdoc, proc, req) }) createTooltip(svgdoc) if (/^proc\/\d+$/.test(req)) { //only show if single PID displayInfo(svgdoc, json) //threads auto-enabled for single PID document.getElementById('threads').checked = true } } async function displayProcess(svgdoc, proc, req = null){ if(!proc.object) return //create table except when looking at a single process if(!/proc\/\d+$/.test(req)) fillTable(svgdoc, proc) bullet(svgdoc, proc, "") text(svgdoc, proc, "") if(proc.threads){ proc.threads.forEach(async thread =>{ if(thread.object){ bullet(svgdoc, thread, "thread") text(svgdoc, thread, "thread") } }) } } function bullet(svgdoc, obj, objType){ let bulletCircle = svgdoc.getElementById("bulletCircle" + objType + "_" + obj.object.replace(':','_')) //if a bullet doesn't already exist, if it does, just add the process id to the bullet attributes if(!bulletCircle){ let id = obj.object.replace(':','_')+"_rect" let rect = svgdoc.getElementById(id) bulletCircle = svgdoc.createElementNS("http://www.w3.org/2000/svg","rect") //difference thread and process if(objType === "thread"){ bulletCircle.setAttribute("thread", true) bulletCircle.setAttribute("height", "15") bulletCircle.setAttribute("fill", "#7cacf9") bulletCircle.setAttribute("y", rect.y.animVal.value + 12) }else{ bulletCircle.setAttribute("y", rect.y.animVal.value - 5) bulletCircle.setAttribute("height", "15") bulletCircle.setAttribute("fill", "#FFFFFF") } //set the bullet in the middle if the process is on machine_O if(id == "Machine_0_rect"){ const svg = svgdoc.getElementsByTagName('svg')[0] bulletCircle.setAttribute("x", svg.width.baseVal.value / 2 - 10) bulletCircle.setAttribute("y", 5) if(objType === "thread"){ bulletCircle.setAttribute("x", svg.width.baseVal.value / 2 + 45) bulletCircle.setAttribute("y", 5) } }else{ bulletCircle.setAttribute("x", rect.x.animVal.value-5) } bulletCircle.setAttribute("rx", 10) bulletCircle.setAttribute("ry", 10) bulletCircle.setAttribute("circle", true) bulletCircle.setAttribute("stroke-width", "1") bulletCircle.setAttribute("stroke", "#000000") bulletCircle.classList.add("tooltip-trigger") rect.parentNode.appendChild(bulletCircle) bulletCircle.id = "bulletCircle" + objType + "_" + obj.object.replace(':','_') let secondId if(objType === "thread") secondId = "thread id : " + obj.PID else secondId = "process id : " + obj.PID bulletCircle.setAttribute('secondId', secondId) }else { let secondId = bulletCircle.getAttribute('secondId') if(objType === "thread") secondId += ';' + "thread id : " + obj.PID else secondId += ';' + "process id : " + obj.PID bulletCircle.setAttribute('secondId', secondId) } } function text(svgdoc, obj, objType){ let id = obj.object.replace(':','_')+"_rect" let rect = svgdoc.getElementById(id) let bulletText = svgdoc.createElementNS("http://www.w3.org/2000/svg","text") let bulletCircle = svgdoc.getElementById("bulletCircle" + objType + "_" + obj.object.replace(':','_')) //if a bullet doesn't already exist, if it does, increment the text if(!svgdoc.getElementById("bulletText" + objType + "_" + obj.object.replace(':','_'))){ bulletText.setAttribute("circle", true) //set the text in the middle if the process is on machine_O if(id == "Machine_0_rect"){ const svg = svgdoc.getElementsByTagName('svg')[0] bulletText.setAttribute("x", svg.width.baseVal.value / 2 - 3) bulletText.setAttribute("y", 15) if(objType === "thread"){ bulletText.setAttribute("x", svg.width.baseVal.value / 2 + 52) bulletText.setAttribute("y", 15) } }else{ bulletText.setAttribute("x", rect.x.animVal.value+2) bulletText.setAttribute("y", rect.y.animVal.value+6) } //difference thread and process if(objType === "thread" && id != "Machine_0_rect"){ bulletText.setAttribute("thread", true) bulletText.setAttribute("y", rect.y.animVal.value+22) } bulletText.setAttribute("style", bulletText.getAttribute("style") + "; pointer-events:none") bulletText.setAttribute("font-size", "10px") updateBulletText(bulletText, obj.PID, bulletCircle) bulletText.id = "bulletText" + objType + "_" + obj.object.replace(':','_') rect.parentNode.appendChild(bulletText) }else{ let text = svgdoc.getElementById("bulletText" + objType + "_" + obj.object.replace(':','_')) updateBulletText(text, findPid(text, '+'), bulletCircle) //set the text in the middle if the process is on machine_O if(id == "Machine_0_rect"){ if (text.textContent == 10) text.setAttribute("x", parseInt(text.getAttribute('x')) - 3) else if(text.textContent == 100) text.setAttribute("x", parseInt(text.getAttribute('x')) - 4) return } } } async function updateBulletText(text, pid, bulletCircle){ text.textContent = await pid let length = text.getComputedTextLength() bulletCircle.setAttribute("width", length + 15) } function findPid(text, op){ let nb if(!text.getAttribute('nb')){ nb = "[2]" text.setAttribute('nb', nb) }else{ nb = text.getAttribute('nb') nb = nb.replace('[','') nb = nb.replace(']','') if(op == '+') nb = parseInt(nb) + 1 else{ nb = parseInt(nb) - 1 } nb = '[' + nb + ']' text.setAttribute('nb', nb) } return nb } function clear(svgdoc){ document.getElementById('searchId').value = '' const info = document.getElementById('info') info.classList = 'hidden' const infoNotFound = document.getElementById('info-not-found') infoNotFound.classList = 'hidden' svgElements = svgdoc.getElementsByTagName('svg')[0].children let i = 0; for(element of svgElements){ if(element.id.includes('bulletCircle')) i += 2 } if(i == 0) return for(i ; i >= 0 ; i--){ svgElements[svgElements.length - 1].remove() } } function createTooltip(svgdoc){ if(svgdoc.getElementById('tooltip')) svgdoc.getElementById('tooltip').remove() //Create tooltip to display process id const g = svgdoc.createElementNS("http://www.w3.org/2000/svg","g") const blackRect = svgdoc.createElementNS("http://www.w3.org/2000/svg",'rect') const whiteRect = svgdoc.createElementNS("http://www.w3.org/2000/svg",'rect') const tooltip = svgdoc.createElementNS("http://www.w3.org/2000/svg",'text') blackRect.setAttribute('fill','black') blackRect.setAttribute('opacity','0.4') blackRect.setAttribute('rx','2') blackRect.setAttribute('ry','2') blackRect.setAttribute('x','2') blackRect.setAttribute('y','-8') blackRect.setAttribute('height','11') blackRect.setAttribute('opacity','0.4') whiteRect.setAttribute('y','-10') whiteRect.setAttribute('fill','white') whiteRect.setAttribute('rx','2') whiteRect.setAttribute('ry','2') whiteRect.setAttribute('height','11') tooltip.setAttribute('x','4') tooltip.setAttribute('y','6') g.setAttribute('visibility', 'hidden') g.id = "tooltip" g.appendChild(blackRect) g.appendChild(whiteRect) g.appendChild(tooltip) svgdoc.getElementsByTagName('svg')[0].appendChild(g) let triggers = svgdoc.getElementsByClassName('tooltip-trigger') for (let i = 0; i < triggers.length; i++) { triggers[i].onmousemove = showTooltip triggers[i].onmouseout = hideTooltip triggers[i].onclick = async function(){ //on click : display process info for the first process in the bullet procs = triggers[i].getAttribute('secondId').split(";") let data = new Array() response = await fetch("/json") json = await response.json() await procs.forEach(proc => { json.forEach(jsonProcess => { if(proc.substring(13) == jsonProcess.PID) data.push(jsonProcess) }) }) let req = data.length == 1 ? "proc/" + data[0].PID : null createProcesses(svgdoc, req, data) } } function showTooltip(evt) { const tooltip = svgdoc.getElementById('tooltip') let tooltipText = tooltip.getElementsByTagName('text')[0] let tooltipRects = tooltip.getElementsByTagName('rect') //clear tooltip while(tooltipText.firstChild){ tooltipText.removeChild(tooltipText.firstChild) for (let i = 0; i < tooltipRects.length; i++) { tooltipRects[i].setAttribute("height", parseInt(tooltipRects[i].getAttribute("height")) - 15) } } //display all processes id in the bullet let text = evt.target.getAttribute('secondId') let tspan text = text.split(';') let length = 0 for (str of text ) { //create the text element tspan = svgdoc.createElementNS("http://www.w3.org/2000/svg",'tspan') if(text.indexOf(str) !== 0) tspan.setAttribute('dy', '1em') tspan.setAttribute('x', '0') tspan.textContent = str tooltipText.appendChild(tspan) //find the length of the longest text if(tspan.getComputedTextLength() > length) length = tspan.getComputedTextLength() //ajust the height of the tooltip element ( and its shadow ) for (let i = 0; i < tooltipRects.length; i++) { tooltipRects[i].setAttribute("height", parseInt(tooltipRects[i].getAttribute("height")) + 15) } //display "..." if there is more than 5 processes if(text.indexOf(str) === 5){ tspan.textContent = '\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0...' tooltipText.appendChild(tspan) break } } //set the tooltip ( and its shadow ) width according to the longest element for (let i = 0; i < tooltipRects.length; i++) { tooltipRects[i].setAttributeNS(null, "width", length + 8) } //display the tooltip at the mouse coordonates let CTM = svgdoc.getElementsByTagName('svg')[0].getScreenCTM() let x = (evt.clientX - CTM.e + 6) / CTM.a; let y = (evt.clientY - CTM.f + 20) / CTM.d; tooltip.setAttributeNS(null, "transform", "translate(" + x + " " + y + ")"); tooltip.setAttributeNS(null, "visibility", "visible") } function hideTooltip() { const tooltip = svgdoc.getElementById('tooltip') tooltip.setAttributeNS(null, "visibility", "hidden") } } async function displayInfo(svgdoc, data){ //handle 2 div : one if the process is found, and one if it isn't const info = document.getElementById('info') const infoNotFound = document.getElementById('info-not-found') info.classList = 'hidden' infoNotFound.classList = 'hidden' if(!data[0].object){ infoNotFound.classList.toggle('hidden') return } const infos = info.children[0].children infos[0].innerHTML = 'Process: ' + data[0].PID infos[1].innerHTML = 'Name: ' + data[0].name infos[2].innerHTML = 'Object: ' + data[0].object.replace(':',' L#') if(data[0].threads){ infos[3].innerHTML = 'Threads: ' + (data[0].threads.length - 1) }else{ infos[3].classList = 'hidden' } info.classList.toggle('hidden') } function fillTable(svgdoc, proc){ document.getElementById('processTable').classList = "table100 ver2 m-b-110" const table = document.getElementById('table') const tr = document.createElement('tr') const td = document.createElement('td') const td2 = document.createElement('td') const td3 = document.createElement('td') const checkbox = document.getElementById('mainCheck') tr.classList.add("row100", "head") td.classList.add("cell100", "column1") td2.classList.add("cell100", "column1") td3.classList.add("cell100", "column1") tr.setAttribute("object", proc.object) td.innerHTML = proc.PID td2.innerHTML = proc.name td3.innerHTML = "" td3.children[0].addEventListener('change', displayTableElement) checkbox.checked = true checkbox.onchange = changeAll tr.appendChild(td) tr.appendChild(td2) tr.appendChild(td3) table.appendChild(tr) //toggle dispay - hide according to checkbox value function displayTableElement(event){ const tr = event.target.parentElement.parentElement const tds = tr.children const proc = { PID : tds[0].innerHTML, name : tds[1].innerHTML, object : tr.getAttribute('object') } if(event.target.checked == false){ const element = svgdoc.getElementById("bulletCircle" + "_" + proc.object.replace(':','_')) removeProc(element, proc) }else{ bullet(svgdoc, proc, "") text(svgdoc, proc, "") createTooltip(svgdoc) } } function removeProc(element, proc){ let id = element.id let text = svgdoc.getElementById(id.replace('bulletCircle', 'bulletText')) let secondId = element.getAttribute('secondId').replace('process id : ' + proc.PID + ';', '') if(!text.getAttribute('nb') || text.getAttribute('nb') == '[1]'){ text.remove() element.remove() }else{ updateBulletText(text, findPid(text, '-'), element) } element.setAttribute('secondId', secondId) } //handle click on main checkbox -> set all checkbox value to its value function changeAll(event){ const checkboxes = document.getElementsByClassName('subCheck') size = checkboxes.length for(let i = 0 ; i < size ; i++ ){ if(checkboxes[i].checked != event.target.checked){ checkboxes[i].checked = event.target.checked displayTableElement({target: checkboxes[i]}) } } } } function clearProc(){ document.getElementById('searchId').value = "" const checkboxes = document.getElementsByClassName('checkbox') for( let i = 0 ; i < checkboxes.length ; i++ ){ checkboxes[i].checked = false } let svgObject = document.getElementById('svg-object').contentDocument clear(svgObject) } function clearTable(){ const table = document.getElementById("table"); while (table.firstChild) { table.removeChild(table.firstChild); } document.getElementById("processTable").classList = "table100 ver2 m-b-110 hidden" } function handleProcButton(){ let functionIsRunning = false //use function is running so that the function doesn't run twice if the user double click. async function addProc(req){ if (!functionIsRunning) { functionIsRunning = true if(req == 'bound' || req == 'all'){ clearProc() clearTable() } let svgObject = document.getElementById('svg-object').contentDocument await createProcesses(svgObject, req) functionIsRunning = false } } const procButtons = document.getElementsByClassName('procButton') for(let i = 0 ; i < procButtons.length ; i++){ procButtons[i].addEventListener('click', function(){ addProc(procButtons[i].type) }) } } function handleCheckbox(){ const checkboxes = document.getElementsByClassName('checkbox') for( let i = 0 ; i < checkboxes.length ; i++ ){ checkboxes[i].checked = false checkboxes[i].addEventListener('change',function(e){ clearTable() let svgObject = document.getElementById('svg-object').contentDocument createProcesses(svgObject, e.target.id) }) } } function procByid(){ let svgObject = document.getElementById('svg-object').contentDocument let id = document.getElementById('searchId').value clearProc() createProcesses(svgObject, 'proc/'+id) } //time out so that the svg has the time to load setTimeout(function() { handleCheckbox() handleProcButton() const input = document.getElementById('searchId') input.value = '' input.addEventListener('keyup',function(event){ if(event.keyCode === 13){ event.preventDefault() procByid() } }) let svgObject = document.getElementById('svg-object').contentDocument let svgElement = svgObject.getElementById('Machine_0_rect') if(svgObject.getElementById('Machine_0_rect')){ createProcesses(svgObject, 'bound') start(svgObject) }else{ const h1 = document.createElement('h1') h1.innerHTML = "Your svg file doesn't have the good format to load javascript" document.body.appendChild(h1) } }, 200)