script.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /* Copyright © 2019 Inria. All rights reserved. */
  2. function start(svgObject){
  3. const texts = svgObject.getElementsByTagName('text')
  4. for (let text of texts) {
  5. text.setAttribute("style", text.getAttribute("style") + "; pointer-events:none")
  6. }
  7. }
  8. async function createProcesses(svgdoc, req, json = null){
  9. await clear(svgdoc)
  10. clearTable()
  11. if(!json){
  12. response = await fetch("/" + req)
  13. json = await response.json()
  14. }
  15. json.forEach(proc => {
  16. displayProcess(svgdoc, proc, req)
  17. })
  18. createTooltip(svgdoc)
  19. if (/^proc\/\d+$/.test(req)) {
  20. //only show if single PID
  21. displayInfo(svgdoc, json)
  22. //threads auto-enabled for single PID
  23. document.getElementById('threads').checked = true
  24. }
  25. }
  26. async function displayProcess(svgdoc, proc, req = null){
  27. if(!proc.object)
  28. return
  29. //create table except when looking at a single process
  30. if(!/proc\/\d+$/.test(req))
  31. fillTable(svgdoc, proc)
  32. bullet(svgdoc, proc, "")
  33. text(svgdoc, proc, "")
  34. if(proc.threads){
  35. proc.threads.forEach(async thread =>{
  36. if(thread.object){
  37. bullet(svgdoc, thread, "thread")
  38. text(svgdoc, thread, "thread")
  39. }
  40. })
  41. }
  42. }
  43. function bullet(svgdoc, obj, objType){
  44. let bulletCircle = svgdoc.getElementById("bulletCircle" + objType + "_" + obj.object.replace(':','_'))
  45. //if a bullet doesn't already exist, if it does, just add the process id to the bullet attributes
  46. if(!bulletCircle){
  47. let id = obj.object.replace(':','_')+"_rect"
  48. let rect = svgdoc.getElementById(id)
  49. bulletCircle = svgdoc.createElementNS("http://www.w3.org/2000/svg","rect")
  50. //difference thread and process
  51. if(objType === "thread"){
  52. bulletCircle.setAttribute("thread", true)
  53. bulletCircle.setAttribute("height", "15")
  54. bulletCircle.setAttribute("fill", "#7cacf9")
  55. bulletCircle.setAttribute("y", rect.y.animVal.value + 12)
  56. }else{
  57. bulletCircle.setAttribute("y", rect.y.animVal.value - 5)
  58. bulletCircle.setAttribute("height", "15")
  59. bulletCircle.setAttribute("fill", "#FFFFFF")
  60. }
  61. //set the bullet in the middle if the process is on machine_O
  62. if(id == "Machine_0_rect"){
  63. const svg = svgdoc.getElementsByTagName('svg')[0]
  64. bulletCircle.setAttribute("x", svg.width.baseVal.value / 2 - 10)
  65. bulletCircle.setAttribute("y", 5)
  66. if(objType === "thread"){
  67. bulletCircle.setAttribute("x", svg.width.baseVal.value / 2 + 45)
  68. bulletCircle.setAttribute("y", 5)
  69. }
  70. }else{
  71. bulletCircle.setAttribute("x", rect.x.animVal.value-5)
  72. }
  73. bulletCircle.setAttribute("rx", 10)
  74. bulletCircle.setAttribute("ry", 10)
  75. bulletCircle.setAttribute("circle", true)
  76. bulletCircle.setAttribute("stroke-width", "1")
  77. bulletCircle.setAttribute("stroke", "#000000")
  78. bulletCircle.classList.add("tooltip-trigger")
  79. rect.parentNode.appendChild(bulletCircle)
  80. bulletCircle.id = "bulletCircle" + objType + "_" + obj.object.replace(':','_')
  81. let secondId
  82. if(objType === "thread")
  83. secondId = "thread id : " + obj.PID
  84. else
  85. secondId = "process id : " + obj.PID
  86. bulletCircle.setAttribute('secondId', secondId)
  87. }else {
  88. let secondId = bulletCircle.getAttribute('secondId')
  89. if(objType === "thread")
  90. secondId += ';' + "thread id : " + obj.PID
  91. else
  92. secondId += ';' + "process id : " + obj.PID
  93. bulletCircle.setAttribute('secondId', secondId)
  94. }
  95. }
  96. function text(svgdoc, obj, objType){
  97. let id = obj.object.replace(':','_')+"_rect"
  98. let rect = svgdoc.getElementById(id)
  99. let bulletText = svgdoc.createElementNS("http://www.w3.org/2000/svg","text")
  100. let bulletCircle = svgdoc.getElementById("bulletCircle" + objType + "_" + obj.object.replace(':','_'))
  101. //if a bullet doesn't already exist, if it does, increment the text
  102. if(!svgdoc.getElementById("bulletText" + objType + "_" + obj.object.replace(':','_'))){
  103. bulletText.setAttribute("circle", true)
  104. //set the text in the middle if the process is on machine_O
  105. if(id == "Machine_0_rect"){
  106. const svg = svgdoc.getElementsByTagName('svg')[0]
  107. bulletText.setAttribute("x", svg.width.baseVal.value / 2 - 3)
  108. bulletText.setAttribute("y", 15)
  109. if(objType === "thread"){
  110. bulletText.setAttribute("x", svg.width.baseVal.value / 2 + 52)
  111. bulletText.setAttribute("y", 15)
  112. }
  113. }else{
  114. bulletText.setAttribute("x", rect.x.animVal.value+2)
  115. bulletText.setAttribute("y", rect.y.animVal.value+6)
  116. }
  117. //difference thread and process
  118. if(objType === "thread" && id != "Machine_0_rect"){
  119. bulletText.setAttribute("thread", true)
  120. bulletText.setAttribute("y", rect.y.animVal.value+22)
  121. }
  122. bulletText.setAttribute("style", bulletText.getAttribute("style") + "; pointer-events:none")
  123. bulletText.setAttribute("font-size", "10px")
  124. updateBulletText(bulletText, obj.PID, bulletCircle)
  125. bulletText.id = "bulletText" + objType + "_" + obj.object.replace(':','_')
  126. rect.parentNode.appendChild(bulletText)
  127. }else{
  128. let text = svgdoc.getElementById("bulletText" + objType + "_" + obj.object.replace(':','_'))
  129. updateBulletText(text, findPid(text, '+'), bulletCircle)
  130. //set the text in the middle if the process is on machine_O
  131. if(id == "Machine_0_rect"){
  132. if (text.textContent == 10)
  133. text.setAttribute("x", parseInt(text.getAttribute('x')) - 3)
  134. else if(text.textContent == 100)
  135. text.setAttribute("x", parseInt(text.getAttribute('x')) - 4)
  136. return
  137. }
  138. }
  139. }
  140. async function updateBulletText(text, pid, bulletCircle){
  141. text.textContent = await pid
  142. let length = text.getComputedTextLength()
  143. bulletCircle.setAttribute("width", length + 15)
  144. }
  145. function findPid(text, op){
  146. let nb
  147. if(!text.getAttribute('nb')){
  148. nb = "[2]"
  149. text.setAttribute('nb', nb)
  150. }else{
  151. nb = text.getAttribute('nb')
  152. nb = nb.replace('[','')
  153. nb = nb.replace(']','')
  154. if(op == '+')
  155. nb = parseInt(nb) + 1
  156. else{
  157. nb = parseInt(nb) - 1
  158. }
  159. nb = '[' + nb + ']'
  160. text.setAttribute('nb', nb)
  161. }
  162. return nb
  163. }
  164. function clear(svgdoc){
  165. document.getElementById('searchId').value = ''
  166. const info = document.getElementById('info')
  167. info.classList = 'hidden'
  168. const infoNotFound = document.getElementById('info-not-found')
  169. infoNotFound.classList = 'hidden'
  170. svgElements = svgdoc.getElementsByTagName('svg')[0].children
  171. let i = 0;
  172. for(element of svgElements){
  173. if(element.id.includes('bulletCircle'))
  174. i += 2
  175. }
  176. if(i == 0)
  177. return
  178. for(i ; i >= 0 ; i--){
  179. svgElements[svgElements.length - 1].remove()
  180. }
  181. }
  182. function createTooltip(svgdoc){
  183. if(svgdoc.getElementById('tooltip'))
  184. svgdoc.getElementById('tooltip').remove()
  185. //Create tooltip to display process id
  186. const g = svgdoc.createElementNS("http://www.w3.org/2000/svg","g")
  187. const blackRect = svgdoc.createElementNS("http://www.w3.org/2000/svg",'rect')
  188. const whiteRect = svgdoc.createElementNS("http://www.w3.org/2000/svg",'rect')
  189. const tooltip = svgdoc.createElementNS("http://www.w3.org/2000/svg",'text')
  190. blackRect.setAttribute('fill','black')
  191. blackRect.setAttribute('opacity','0.4')
  192. blackRect.setAttribute('rx','2')
  193. blackRect.setAttribute('ry','2')
  194. blackRect.setAttribute('x','2')
  195. blackRect.setAttribute('y','-8')
  196. blackRect.setAttribute('height','11')
  197. blackRect.setAttribute('opacity','0.4')
  198. whiteRect.setAttribute('y','-10')
  199. whiteRect.setAttribute('fill','white')
  200. whiteRect.setAttribute('rx','2')
  201. whiteRect.setAttribute('ry','2')
  202. whiteRect.setAttribute('height','11')
  203. tooltip.setAttribute('x','4')
  204. tooltip.setAttribute('y','6')
  205. g.setAttribute('visibility', 'hidden')
  206. g.id = "tooltip"
  207. g.appendChild(blackRect)
  208. g.appendChild(whiteRect)
  209. g.appendChild(tooltip)
  210. svgdoc.getElementsByTagName('svg')[0].appendChild(g)
  211. let triggers = svgdoc.getElementsByClassName('tooltip-trigger')
  212. for (let i = 0; i < triggers.length; i++) {
  213. triggers[i].onmousemove = showTooltip
  214. triggers[i].onmouseout = hideTooltip
  215. triggers[i].onclick = async function(){
  216. //on click : display process info for the first process in the bullet
  217. procs = triggers[i].getAttribute('secondId').split(";")
  218. let data = new Array()
  219. response = await fetch("/json")
  220. json = await response.json()
  221. await procs.forEach(proc => {
  222. json.forEach(jsonProcess => {
  223. if(proc.substring(13) == jsonProcess.PID)
  224. data.push(jsonProcess)
  225. })
  226. })
  227. let req = data.length == 1 ? "proc/" + data[0].PID : null
  228. createProcesses(svgdoc, req, data)
  229. }
  230. }
  231. function showTooltip(evt) {
  232. const tooltip = svgdoc.getElementById('tooltip')
  233. let tooltipText = tooltip.getElementsByTagName('text')[0]
  234. let tooltipRects = tooltip.getElementsByTagName('rect')
  235. //clear tooltip
  236. while(tooltipText.firstChild){
  237. tooltipText.removeChild(tooltipText.firstChild)
  238. for (let i = 0; i < tooltipRects.length; i++) {
  239. tooltipRects[i].setAttribute("height", parseInt(tooltipRects[i].getAttribute("height")) - 15)
  240. }
  241. }
  242. //display all processes id in the bullet
  243. let text = evt.target.getAttribute('secondId')
  244. let tspan
  245. text = text.split(';')
  246. let length = 0
  247. for (str of text ) {
  248. //create the text element
  249. tspan = svgdoc.createElementNS("http://www.w3.org/2000/svg",'tspan')
  250. if(text.indexOf(str) !== 0)
  251. tspan.setAttribute('dy', '1em')
  252. tspan.setAttribute('x', '0')
  253. tspan.textContent = str
  254. tooltipText.appendChild(tspan)
  255. //find the length of the longest text
  256. if(tspan.getComputedTextLength() > length)
  257. length = tspan.getComputedTextLength()
  258. //ajust the height of the tooltip element ( and its shadow )
  259. for (let i = 0; i < tooltipRects.length; i++) {
  260. tooltipRects[i].setAttribute("height", parseInt(tooltipRects[i].getAttribute("height")) + 15)
  261. }
  262. //display "..." if there is more than 5 processes
  263. if(text.indexOf(str) === 5){
  264. tspan.textContent = '\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0...'
  265. tooltipText.appendChild(tspan)
  266. break
  267. }
  268. }
  269. //set the tooltip ( and its shadow ) width according to the longest element
  270. for (let i = 0; i < tooltipRects.length; i++) {
  271. tooltipRects[i].setAttributeNS(null, "width", length + 8)
  272. }
  273. //display the tooltip at the mouse coordonates
  274. let CTM = svgdoc.getElementsByTagName('svg')[0].getScreenCTM()
  275. let x = (evt.clientX - CTM.e + 6) / CTM.a;
  276. let y = (evt.clientY - CTM.f + 20) / CTM.d;
  277. tooltip.setAttributeNS(null, "transform", "translate(" + x + " " + y + ")");
  278. tooltip.setAttributeNS(null, "visibility", "visible")
  279. }
  280. function hideTooltip() {
  281. const tooltip = svgdoc.getElementById('tooltip')
  282. tooltip.setAttributeNS(null, "visibility", "hidden")
  283. }
  284. }
  285. async function displayInfo(svgdoc, data){
  286. //handle 2 div : one if the process is found, and one if it isn't
  287. const info = document.getElementById('info')
  288. const infoNotFound = document.getElementById('info-not-found')
  289. info.classList = 'hidden'
  290. infoNotFound.classList = 'hidden'
  291. if(!data[0].object){
  292. infoNotFound.classList.toggle('hidden')
  293. return
  294. }
  295. const infos = info.children[0].children
  296. infos[0].innerHTML = 'Process: ' + data[0].PID
  297. infos[1].innerHTML = 'Name: ' + data[0].name
  298. infos[2].innerHTML = 'Object: ' + data[0].object.replace(':',' L#')
  299. if(data[0].threads){
  300. infos[3].innerHTML = 'Threads: ' + (data[0].threads.length - 1)
  301. }else{
  302. infos[3].classList = 'hidden'
  303. }
  304. info.classList.toggle('hidden')
  305. }
  306. function fillTable(svgdoc, proc){
  307. document.getElementById('processTable').classList = "table100 ver2 m-b-110"
  308. const table = document.getElementById('table')
  309. const tr = document.createElement('tr')
  310. const td = document.createElement('td')
  311. const td2 = document.createElement('td')
  312. const td3 = document.createElement('td')
  313. const checkbox = document.getElementById('mainCheck')
  314. tr.classList.add("row100", "head")
  315. td.classList.add("cell100", "column1")
  316. td2.classList.add("cell100", "column1")
  317. td3.classList.add("cell100", "column1")
  318. tr.setAttribute("object", proc.object)
  319. td.innerHTML = proc.PID
  320. td2.innerHTML = proc.name
  321. td3.innerHTML = "<input class='subCheck' checked='true' type='checkbox'>"
  322. td3.children[0].addEventListener('change', displayTableElement)
  323. checkbox.checked = true
  324. checkbox.onchange = changeAll
  325. tr.appendChild(td)
  326. tr.appendChild(td2)
  327. tr.appendChild(td3)
  328. table.appendChild(tr)
  329. //toggle dispay - hide according to checkbox value
  330. function displayTableElement(event){
  331. const tr = event.target.parentElement.parentElement
  332. const tds = tr.children
  333. const proc = { PID : tds[0].innerHTML, name : tds[1].innerHTML, object : tr.getAttribute('object') }
  334. if(event.target.checked == false){
  335. const element = svgdoc.getElementById("bulletCircle" + "_" + proc.object.replace(':','_'))
  336. removeProc(element, proc)
  337. }else{
  338. bullet(svgdoc, proc, "")
  339. text(svgdoc, proc, "")
  340. createTooltip(svgdoc)
  341. }
  342. }
  343. function removeProc(element, proc){
  344. let id = element.id
  345. let text = svgdoc.getElementById(id.replace('bulletCircle', 'bulletText'))
  346. let secondId = element.getAttribute('secondId').replace('process id : ' + proc.PID + ';', '')
  347. if(!text.getAttribute('nb') || text.getAttribute('nb') == '[1]'){
  348. text.remove()
  349. element.remove()
  350. }else{
  351. updateBulletText(text, findPid(text, '-'), element)
  352. }
  353. element.setAttribute('secondId', secondId)
  354. }
  355. //handle click on main checkbox -> set all checkbox value to its value
  356. function changeAll(event){
  357. const checkboxes = document.getElementsByClassName('subCheck')
  358. size = checkboxes.length
  359. for(let i = 0 ; i < size ; i++ ){
  360. if(checkboxes[i].checked != event.target.checked){
  361. checkboxes[i].checked = event.target.checked
  362. displayTableElement({target: checkboxes[i]})
  363. }
  364. }
  365. }
  366. }
  367. function clearProc(){
  368. document.getElementById('searchId').value = ""
  369. const checkboxes = document.getElementsByClassName('checkbox')
  370. for( let i = 0 ; i < checkboxes.length ; i++ ){
  371. checkboxes[i].checked = false
  372. }
  373. let svgObject = document.getElementById('svg-object').contentDocument
  374. clear(svgObject)
  375. }
  376. function clearTable(){
  377. const table = document.getElementById("table");
  378. while (table.firstChild) {
  379. table.removeChild(table.firstChild);
  380. }
  381. document.getElementById("processTable").classList = "table100 ver2 m-b-110 hidden"
  382. }
  383. function handleProcButton(){
  384. let functionIsRunning = false
  385. //use function is running so that the function doesn't run twice if the user double click.
  386. async function addProc(req){
  387. if (!functionIsRunning) {
  388. functionIsRunning = true
  389. if(req == 'bound' || req == 'all'){
  390. clearProc()
  391. clearTable()
  392. }
  393. let svgObject = document.getElementById('svg-object').contentDocument
  394. await createProcesses(svgObject, req)
  395. functionIsRunning = false
  396. }
  397. }
  398. const procButtons = document.getElementsByClassName('procButton')
  399. for(let i = 0 ; i < procButtons.length ; i++){
  400. procButtons[i].addEventListener('click', function(){
  401. addProc(procButtons[i].type)
  402. })
  403. }
  404. }
  405. function handleCheckbox(){
  406. const checkboxes = document.getElementsByClassName('checkbox')
  407. for( let i = 0 ; i < checkboxes.length ; i++ ){
  408. checkboxes[i].checked = false
  409. checkboxes[i].addEventListener('change',function(e){
  410. clearTable()
  411. let svgObject = document.getElementById('svg-object').contentDocument
  412. createProcesses(svgObject, e.target.id)
  413. })
  414. }
  415. }
  416. function procByid(){
  417. let svgObject = document.getElementById('svg-object').contentDocument
  418. let id = document.getElementById('searchId').value
  419. clearProc()
  420. createProcesses(svgObject, 'proc/'+id)
  421. }
  422. //time out so that the svg has the time to load
  423. setTimeout(function() {
  424. handleCheckbox()
  425. handleProcButton()
  426. const input = document.getElementById('searchId')
  427. input.value = ''
  428. input.addEventListener('keyup',function(event){
  429. if(event.keyCode === 13){
  430. event.preventDefault()
  431. procByid()
  432. }
  433. })
  434. let svgObject = document.getElementById('svg-object').contentDocument
  435. let svgElement = svgObject.getElementById('Machine_0_rect')
  436. if(svgObject.getElementById('Machine_0_rect')){
  437. createProcesses(svgObject, 'bound')
  438. start(svgObject)
  439. }else{
  440. const h1 = document.createElement('h1')
  441. h1.innerHTML = "Your svg file doesn't have the good format to load javascript"
  442. document.body.appendChild(h1)
  443. }
  444. }, 200)