var amendCssOnce = 1; function amendCss(circleNodes,showArrowheads){ if( !amendCssOnce ) return; var css = ""; if( circleNodes ){ css += ".tl-node, .tl-node:after { border-radius: 50%; }"; } if( !showArrowheads ){ css += ".tl-arrow.u { display: none; }"; } if( css!=="" ){ var style = document.createElement("style"); style.textContent = css; document.querySelector("head").appendChild(style); } amendCssOnce = 0; } var tooltipObj = document.createElement("span"); tooltipObj.className = "tl-tooltip"; tooltipObj.style.display = "none"; document.getElementsByClassName("content")[0].appendChild(tooltipObj); tooltipObj.onmouseenter = function(){ stopCloseTimer(); stopDwellTimer(); tooltipInfo.ixHover = tooltipInfo.ixActive; } tooltipObj.onmouseleave = function(){ if (tooltipInfo.ixActive != -1) resumeCloseTimer(); }; window.tooltipInfo = { dwellTimeout: 250, closeTimeout: 3000, hashDigits: 16, idTimer: 0, idTimerClose: 0, ixHover: -1, ixActive: -1, nodeHover: null, idNodeActive: 0, posX: 0, posY: 0 }; function onKeyDown(event){ var key = event.which || event.keyCode; if( key==27 ){ event.stopPropagation(); hideGraphTooltip(); } } function hideGraphTooltip(){ document.removeEventListener('keydown',onKeyDown,true); stopCloseTimer(); tooltipObj.style.display = "none"; tooltipInfo.ixActive = -1; tooltipInfo.idNodeActive = 0; } document.body.onunload = hideGraphTooltip function stopDwellTimer(){ if(tooltipInfo.idTimer!=0){ clearTimeout(tooltipInfo.idTimer); tooltipInfo.idTimer = 0; } } function resumeCloseTimer(){ if(tooltipInfo.idTimerClose==0 && tooltipInfo.closeTimeout>0) { tooltipInfo.idTimerClose = setTimeout(function(){ tooltipInfo.idTimerClose = 0; hideGraphTooltip(); },tooltipInfo.closeTimeout); } } function stopCloseTimer(){ if(tooltipInfo.idTimerClose!=0){ clearTimeout(tooltipInfo.idTimerClose); tooltipInfo.idTimerClose = 0; } } function TimelineGraph(tx){ var topObj = document.getElementById("timelineTable"+tx.iTableId); amendCss(tx.circleNodes, tx.showArrowheads); tooltipInfo.dwellTimeout = tx.dwellTimeout tooltipInfo.closeTimeout = tx.closeTimeout tooltipInfo.hashDigits = tx.hashDigits topObj.onclick = clickOnGraph topObj.ondblclick = dblclickOnGraph topObj.onmousemove = function(e) { var ix = findTxIndex(e); topObj.style.cursor = (ix<0) ? "" : "pointer" mouseOverGraph(e,ix,null); }; topObj.onmouseleave = function(e) { if(e.relatedTarget && e.relatedTarget != tooltipObj){ tooltipInfo.ixHover = -1; hideGraphTooltip(); stopDwellTimer(); stopCloseTimer(); } }; function mouseOverNode(e){ e.stopPropagation() mouseOverGraph(e,-2,this) } function mouseOverGraph(e,ix,node){ stopDwellTimer(); var ownTooltip = (ix>=0 && ix==tooltipInfo.ixActive) || (ix==-2 && tooltipInfo.idNodeActive==node.id); if(ownTooltip) stopCloseTimer(); else resumeCloseTimer(); tooltipInfo.ixHover = ix; tooltipInfo.nodeHover = node; tooltipInfo.posX = e.clientX; tooltipInfo.posY = e.clientY; if(ix!=-1 && !ownTooltip && tooltipInfo.dwellTimeout>0){ tooltipInfo.idTimer = setTimeout(function(){ tooltipInfo.idTimer = 0; stopCloseTimer(); showGraphTooltip(); },tooltipInfo.dwellTimeout); } } var canvasDiv; var railPitch; var mergeOffset; var node, arrow, arrowSmall, line, mArrow, mLine, wArrow, wLine; function initGraph(){ var parent = topObj.rows[0].cells[1]; parent.style.verticalAlign = "top"; canvasDiv = document.createElement("div"); canvasDiv.className = "tl-canvas"; canvasDiv.style.position = "absolute"; parent.appendChild(canvasDiv); var elems = {}; var elemClasses = [ "rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line", "arrow merge r", "line merge", "arrow warp", "line warp", "line cherrypick", "line dotted" ]; for( var i=0; i0 ){ railPitch = tx.iRailPitch; }else{ railPitch = elems.rail.w; railPitch -= Math.floor((tx.nrail-1)*(railPitch-minRailPitch)/21); } railPitch = Math.max(railPitch, minRailPitch); } if( tx.nomo ){ mergeOffset = 0; }else{ mergeOffset = railPitch-minRailPitch-mLine.w; mergeOffset = Math.min(mergeOffset, elems.mergeoffset.w); mergeOffset = mergeOffset>0 ? mergeOffset + line.w/2 : 0; } var canvasWidth = (tx.nrail-1)*railPitch + node.w; canvasDiv.style.width = canvasWidth + "px"; canvasDiv.style.position = "relative"; } function drawBox(cls,color,x0,y0,x1,y1){ var n = document.createElement("div"); x0 = Math.floor(x0); y0 = Math.floor(y0); x1 = x1 || x1===0 ? Math.floor(x1) : x0; y1 = y1 || y1===0 ? Math.floor(y1) : y0; if( x0>x1 ){ var t=x0; x0=x1; x1=t; } if( y0>y1 ){ var t=y0; y0=y1; y1=t; } var w = x1-x0; var h = y1-y0; n.style.position = "absolute"; n.style.left = x0+"px"; n.style.top = y0+"px"; if( w ) n.style.width = w+"px"; if( h ) n.style.height = h+"px"; if( color ) n.style.backgroundColor = color; n.className = "tl-"+cls; canvasDiv.appendChild(n); return n; } function absoluteY(obj){ var y = 0; do{ y += obj.offsetTop; }while( obj = obj.offsetParent ); return y; } function absoluteX(obj){ var x = 0; do{ x += obj.offsetLeft; }while( obj = obj.offsetParent ); return x; } function miLineY(p){ return p.y + node.h - mLine.w - 1; } function drawLine(elem,color,x0,y0,x1,y1){ var cls = elem.cls + " "; if( x1===null ){ x1 = x0+elem.w; cls += "v"; }else{ y1 = y0+elem.w; cls += "h"; } return drawBox(cls,color,x0,y0,x1,y1); } function drawUpArrow(from,to,color,id){ var y = to.y + node.h; var arrowSpace = from.y - y + (!from.id || from.r!=to.r ? node.h/2 : 0); var arw = arrowSpace < arrow.h*1.5 ? arrowSmall : arrow; var x = to.x + (node.w-line.w)/2; var y0 = from.y + node.h/2; var y1 = Math.ceil(to.y + node.h + arw.h/2); var n = drawLine(line,color,x,y0,null,y1); addToolTip(n,id) x = to.x + (node.w-arw.w)/2; n = drawBox(arw.cls,null,x,y); if(color) n.style.borderBottomColor = color; addToolTip(n,id) } function drawDotted(from,to,color,id){ var x = to.x + (node.w-line.w)/2; var y0 = from.y + node.h/2; var y1 = Math.ceil(to.y + node.h); var n = drawLine(dotLine,null,x,y0,null,y1) if( color ) n.style.borderColor = color addToolTip(n,id) } function addToolTip(n,id){ if( id ) n.setAttribute("data-ix",id-tx.iTopRow) } function drawMergeLine(x0,y0,x1,y1){ drawLine(mLine,null,x0,y0,x1,y1); } function drawCherrypickLine(x0,y0,x1,y1){ drawLine(cpLine,null,x0,y0,x1,y1); } function drawMergeArrow(p,rail,isCP){ var x0 = rail*railPitch + node.w/2; if( rail in mergeLines ){ x0 += mergeLines[rail]; if( p.r0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg,p.id); if( p.sb>0 ) drawDotted(p,tx.rowinfo[p.sb-tx.iTopRow],p.fg,p.id); var cls = node.cls; if( p.hasOwnProperty('mi') && p.mi.length ) cls += " merge"; if( p.f&2 ) cls += " closed-leaf"; else if( p.f&1 ) cls += " leaf"; var n = drawBox(cls,p.bg,p.x,p.y); n.id = "tln"+p.id; n.onclick = clickOnNode; n.ondblclick = dblclickOnNode; n.onmousemove = mouseOverNode; n.style.zIndex = 10; if( p.f&2 ){ var pt1 = 0; var pt2 = 100; if( tx.circleNodes ){ pt1 = 14; pt2 = 86; } n.innerHTML = "" + `" + ""; } if( !tx.omitDescenders ){ if( p.u==0 ){ if( p.hasOwnProperty('mo') && p.r==p.mo ){ var ix = p.hasOwnProperty('cu') ? p.cu : p.mu; var top = tx.rowinfo[ix-tx.iTopRow] drawUpArrow(p,{x: p.x, y: top.y-node.h}, p.fg, p.id); }else if( p.y>100 ){ drawUpArrow(p,{x: p.x, y: p.y-50}, p.fg, p.id); }else{ drawUpArrow(p,{x: p.x, y: 0},p.fg, p.id); } } if( p.hasOwnProperty('d') ){ if( p.y + 150 >= btm ){ drawUpArrow({x: p.x, y: btm - node.h/2},p,p.fg,p.id); }else{ drawUpArrow({x: p.x, y: p.y+50},p,p.fg,p.id); drawDotted({x: p.x, y: p.y+63},{x: p.x, y: p.y+50-node.h/2},p.fg,p.id); } } } if( p.hasOwnProperty('mo') ){ var x0 = p.x + node.w/2; var x1 = p.mo*railPitch + node.w/2; var u = tx.rowinfo[p.mu-tx.iTopRow]; var mtop = u; if( p.hasOwnProperty('cu') ){ mtop = tx.rowinfo[p.cu-tx.iTopRow]; } var y1 = miLineY(u); if( p.u<=0 || p.mo!=p.r ){ if( p.u==0 && p.mo==p.r ){ mergeLines[p.mo] = mtop.r=0; i-- ){ drawNode(tx.rowinfo[i], btm); } } var selRow; function clickOnNode(e){ hideGraphTooltip() var p = tx.rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-tx.iTopRow]; if( !selRow ){ selRow = p; this.className += " sel"; canvasDiv.className += " sel"; }else if( selRow==p ){ selRow = null; this.className = this.className.replace(" sel", ""); canvasDiv.className = canvasDiv.className.replace(" sel", ""); }else{ if( tx.fileDiff ){ location.href=tx.baseUrl + "/fdiff?v1="+selRow.h+"&v2="+p.h; }else{ var href = tx.baseUrl + "/vdiff?from="+selRow.h+"&to="+p.h; let params = (new URL(document.location)).searchParams; if(params && typeof params === "object"){ let glob = params.get("chng"); if( !glob ){ glob = params.get("glob"); } if( glob ){ href += "&glob=" + glob; } } location.href = href; } } e.stopPropagation() } function dblclickOnNode(e){ var p = tx.rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-tx.iTopRow]; window.location.href = tx.baseUrl+"/info/"+p.h e.stopPropagation() } function findTxIndex(e){ if( !tx.rowinfo ) return -1; var x = e.clientX + window.pageXOffset - absoluteX(canvasDiv); var y = e.clientY + window.pageYOffset - absoluteY(canvasDiv); var aNode = canvasDiv.childNodes var nNode = aNode.length; var i; for(i=0;in.offsetLeft+n.offsetWidth+5 ) continue; if( yn.offsetTop+n.offsetHeight ) continue; return n.getAttribute("data-ix") } return -1 } function branchHyperlink(ix){ var br = tx.rowinfo[ix].br var dest = tx.baseUrl + "/timeline?r=" + encodeURIComponent(br) dest += tx.fileDiff ? "&m&cf=" : "&m&c=" dest += encodeURIComponent(tx.rowinfo[ix].h) return dest } function clickOnGraph(e){ stopCloseTimer(); stopDwellTimer(); tooltipInfo.ixHover = findTxIndex(e); tooltipInfo.posX = e.clientX; tooltipInfo.posY = e.clientY; showGraphTooltip(); } function showGraphTooltip(){ var html = null var ix = -1 if( tooltipInfo.ixHover==-2 ){ ix = parseInt(tooltipInfo.nodeHover.id.match(/\d+$/)[0],10)-tx.iTopRow var h = tx.rowinfo[ix].h var dest = tx.baseUrl + "/info/" + h h = h.slice(0,tooltipInfo.hashDigits); if( tx.fileDiff ){ html = "artifact "+h+"" }else{ html = "check-in "+h+"" } tooltipInfo.ixActive = -2; tooltipInfo.idNodeActive = tooltipInfo.nodeHover.id; }else if( tooltipInfo.ixHover>=0 ){ ix = tooltipInfo.ixHover var br = tx.rowinfo[ix].br var dest = branchHyperlink(ix) var hbr = br.replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); html = "branch "+hbr+"" tooltipInfo.ixActive = ix; tooltipInfo.idNodeActive = 0; } if( html ){ var s = getComputedStyle(document.body) if( tx.rowinfo[ix].bg.length ){ tooltipObj.style.backgroundColor = tx.rowinfo[ix].bg }else{ tooltipObj.style.backgroundColor = s.getPropertyValue('background-color') } tooltipObj.style.borderColor = tooltipObj.style.color = s.getPropertyValue('color') tooltipObj.style.visibility = "hidden" tooltipObj.innerHTML = html tooltipObj.insertBefore(makeCopyButton("tooltip-link",0,0), tooltipObj.childNodes[1]); tooltipObj.style.display = "inline" tooltipObj.style.position = "absolute" var x = tooltipInfo.posX + 4 + window.pageXOffset - absoluteX(tooltipObj.offsetParent) tooltipObj.style.left = x+"px" var y = tooltipInfo.posY + window.pageYOffset - tooltipObj.clientHeight - 4 - absoluteY(tooltipObj.offsetParent) tooltipObj.style.top = y+"px" tooltipObj.style.visibility = "visible" document.addEventListener('keydown',onKeyDown,true); }else{ hideGraphTooltip() } } function dblclickOnGraph(e){ var ix = findTxIndex(e); hideGraphTooltip() if( ix>=0 ){ var dest = branchHyperlink(ix) window.location.href = dest } } function changeDisplay(selector,value){ var x = document.getElementsByClassName(selector); var n = x.length; for(var i=0; i0 ) window.scrollTo(0, y); } } if( tx.rowinfo ){ var lastRow = document.getElementById("m"+tx.rowinfo[tx.rowinfo.length-1].id); var lastY = 0; function checkHeight(){ var h = absoluteY(lastRow); if( h!=lastY ){ renderGraph(); lastY = h; } setTimeout(checkHeight, 1000); } initGraph(); checkHeight(); }else{ function checkHeight(){} } if( tx.scrollToSelect ){ scrollToSelected(); } var lx = topObj.getElementsByClassName('timelineEllipsis'); var i; for(i=0; i