| | @@ -241,23 +241,17 @@ |
| 241 | 241 | dateFormat = db_get_int("timeline-date-format", 0); |
| 242 | 242 | zDateFmt = P("datefmt"); |
| 243 | 243 | if( zDateFmt ) dateFormat = atoi(zDateFmt); |
| 244 | 244 | if( tmFlags & TIMELINE_GRAPH ){ |
| 245 | 245 | pGraph = graph_init(); |
| 246 | | - /* style is not moved to css, because this is |
| 247 | | - ** a technical div for the timeline graph |
| 248 | | - */ |
| 249 | | - @ <div id="canvas" style="position:relative;height:0px;width:0px;" |
| 250 | | - @ onclick="clickOnGraph(event)"></div> |
| 251 | 246 | } |
| 252 | 247 | db_static_prepare(&qbranch, |
| 253 | 248 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 254 | 249 | TAG_BRANCH |
| 255 | 250 | ); |
| 256 | 251 | |
| 257 | | - @ <table id="timelineTable" class="timelineTable" |
| 258 | | - @ onclick="clickOnGraph(event)"> |
| 252 | + @ <table id="timelineTable" class="timelineTable"> |
| 259 | 253 | blob_zero(&comment); |
| 260 | 254 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 261 | 255 | int rid = db_column_int(pQuery, 0); |
| 262 | 256 | const char *zUuid = db_column_text(pQuery, 1); |
| 263 | 257 | int isLeaf = db_column_int(pQuery, 5); |
| | @@ -391,11 +385,11 @@ |
| 391 | 385 | } |
| 392 | 386 | db_reset(&qparent); |
| 393 | 387 | gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr, |
| 394 | 388 | zUuid, isLeaf); |
| 395 | 389 | db_reset(&qbranch); |
| 396 | | - @ <div id="m%d(gidx)"></div> |
| 390 | + @ <div id="m%d(gidx)" class="tl-nodemark"></div> |
| 397 | 391 | } |
| 398 | 392 | @</td> |
| 399 | 393 | if( zBgClr && zBgClr[0] && rid!=selectedRid ){ |
| 400 | 394 | @ <td class="timelineTableCell" style="background-color: %h(zBgClr);"> |
| 401 | 395 | }else{ |
| | @@ -589,18 +583,11 @@ |
| 589 | 583 | graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0); |
| 590 | 584 | if( pGraph->nErr ){ |
| 591 | 585 | graph_free(pGraph); |
| 592 | 586 | pGraph = 0; |
| 593 | 587 | }else{ |
| 594 | | - int w; |
| 595 | | - /* style is not moved to css, because this is |
| 596 | | - ** a technical div for the timeline graph |
| 597 | | - */ |
| 598 | | - w = pGraph->mxRail*pGraph->iRailPitch + 28; |
| 599 | | - @ <tr class="timelineBottom"><td></td><td> |
| 600 | | - @ <div id="grbtm" style="width:%d(w)px;"></div> |
| 601 | | - @ </td><td></td></tr> |
| 588 | + @ <tr class="timelineBottom"><td></td><td></td><td></td></tr> |
| 602 | 589 | } |
| 603 | 590 | } |
| 604 | 591 | @ </table> |
| 605 | 592 | if( fchngQueryInit ) db_finalize(&fchngQuery); |
| 606 | 593 | timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0); |
| | @@ -648,31 +635,34 @@ |
| 648 | 635 | ){ |
| 649 | 636 | if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){ |
| 650 | 637 | GraphRow *pRow; |
| 651 | 638 | int i; |
| 652 | 639 | char cSep; |
| 653 | | - int mergeOffset; /* Pixel offset from rail to merge riser */ |
| 654 | 640 | int iRailPitch; /* Pixels between consecutive rails */ |
| 655 | 641 | int showArrowheads; /* True to draw arrowheads. False to omit. */ |
| 656 | 642 | int circleNodes; /* True for circle nodes. False for square nodes */ |
| 657 | 643 | int colorGraph; /* Use colors for graph lines */ |
| 658 | 644 | |
| 659 | | - iRailPitch = pGraph->iRailPitch; |
| 645 | + iRailPitch = atoi(PD("railpitch","0")); |
| 660 | 646 | showArrowheads = skin_detail_boolean("timeline-arrowheads"); |
| 661 | 647 | circleNodes = skin_detail_boolean("timeline-circle-nodes"); |
| 662 | 648 | colorGraph = skin_detail_boolean("timeline-color-graph-lines"); |
| 663 | 649 | |
| 664 | | - /* Number of pixels that the thin merge lines are offset from the |
| 665 | | - ** the center of the think rail lines. If zero, then the vertical |
| 666 | | - ** merge lines overlap with the thicker rail lines. |
| 667 | | - */ |
| 668 | | - mergeOffset = iRailPitch>=14 ? 4 : iRailPitch>=13 ? 3 : 0; |
| 669 | | - if( PB("nomo") ) mergeOffset = 0; |
| 670 | | - |
| 671 | | - @ <script> |
| 672 | | - @ var railPitch=%d(iRailPitch); |
| 673 | | - |
| 650 | + @ <script>(function(){ |
| 651 | + @ "use strict"; |
| 652 | + @ var css = ""; |
| 653 | + if( circleNodes ){ |
| 654 | + @ css += ".tl-node, .tl-node:after { border-radius: 50%%; }"; |
| 655 | + } |
| 656 | + if( !showArrowheads ){ |
| 657 | + @ css += ".tl-arrow.u { display: none; }"; |
| 658 | + } |
| 659 | + @ if( css!=="" ){ |
| 660 | + @ var style = document.createElement("style"); |
| 661 | + @ style.textContent = css; |
| 662 | + @ document.querySelector("head").appendChild(style); |
| 663 | + @ } |
| 674 | 664 | /* the rowinfo[] array contains all the information needed to generate |
| 675 | 665 | ** the graph. Each entry contains information for a single row: |
| 676 | 666 | ** |
| 677 | 667 | ** id: The id of the <div> element for the row. This is an integer. |
| 678 | 668 | ** to get an actual id, prepend "m" to the integer. The top node |
| | @@ -680,13 +670,13 @@ |
| 680 | 670 | ** bg: The background color for this row |
| 681 | 671 | ** r: The "rail" that the node for this row sits on. The left-most |
| 682 | 672 | ** rail is 0 and the number increases to the right. |
| 683 | 673 | ** d: True if there is a "descender" - an arrow coming from the bottom |
| 684 | 674 | ** of the page straight up to this node. |
| 685 | | - ** mo: "merge-out". If non-zero, this is one more than the x-coordinate |
| 675 | + ** mo: "merge-out". If non-negative, this is the rail position |
| 686 | 676 | ** for the upward portion of a merge arrow. The merge arrow goes up |
| 687 | | - ** to the row identified by mu:. If this value is zero then |
| 677 | + ** to the row identified by mu:. If this value is negative then |
| 688 | 678 | ** node has no merge children and no merge-out line is drawn. |
| 689 | 679 | ** mu: The id of the row which is the top of the merge-out arrow. |
| 690 | 680 | ** u: Draw a thick child-line out of the top of this node and up to |
| 691 | 681 | ** the node with an id equal to this value. 0 if it is straight to |
| 692 | 682 | ** the top of the page, -1 if there is no thick-line riser. |
| | @@ -693,37 +683,25 @@ |
| 693 | 683 | ** f: 0x01: a leaf node. |
| 694 | 684 | ** au: An array of integers that define thick-line risers for branches. |
| 695 | 685 | ** The integers are in pairs. For each pair, the first integer is |
| 696 | 686 | ** is the rail on which the riser should run and the second integer |
| 697 | 687 | ** is the id of the node upto which the riser should run. |
| 698 | | - ** mi: "merge-in". An array of integer x-coordinates from which |
| 688 | + ** mi: "merge-in". An array of integer rail positions from which |
| 699 | 689 | ** merge arrows should be drawn into this node. If the value is |
| 700 | | - ** negative, then the x-coordinate is the absolute value of mi[] |
| 690 | + ** negative, then the rail position is the absolute value of mi[] |
| 701 | 691 | ** and a thin merge-arrow descender is drawn to the bottom of |
| 702 | 692 | ** the screen. |
| 703 | 693 | ** h: The SHA1 hash of the object being graphed |
| 704 | 694 | */ |
| 705 | 695 | cgi_printf("var rowinfo = [\n"); |
| 706 | 696 | for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ |
| 707 | | - int mo = pRow->mergeOut; |
| 708 | | - if( mo<0 ){ |
| 709 | | - mo = 0; |
| 710 | | - }else{ |
| 711 | | - int x = (mo/4)*iRailPitch; |
| 712 | | - switch( mo&3 ){ |
| 713 | | - case 0: x -= mergeOffset-2; break; |
| 714 | | - case 1: x += 1; break; |
| 715 | | - case 2: x += mergeOffset+1; break; |
| 716 | | - } |
| 717 | | - mo = x; |
| 718 | | - } |
| 719 | 697 | cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:", |
| 720 | 698 | pRow->idx, /* id */ |
| 721 | 699 | pRow->zBgClr, /* bg */ |
| 722 | 700 | pRow->iRail, /* r */ |
| 723 | 701 | pRow->bDescender, /* d */ |
| 724 | | - mo, /* mo */ |
| 702 | + pRow->mergeOut, /* mo */ |
| 725 | 703 | pRow->mergeUpto, /* mu */ |
| 726 | 704 | pRow->aiRiser[pRow->iRail], /* u */ |
| 727 | 705 | pRow->isLeaf ? 1 : 0 /* f */ |
| 728 | 706 | ); |
| 729 | 707 | /* u */ |
| | @@ -743,13 +721,11 @@ |
| 743 | 721 | /* mi */ |
| 744 | 722 | cgi_printf("mi:"); |
| 745 | 723 | cSep = '['; |
| 746 | 724 | for(i=0; i<GR_MAX_RAIL; i++){ |
| 747 | 725 | if( pRow->mergeIn[i] ){ |
| 748 | | - int mi = i*iRailPitch; |
| 749 | | - if( pRow->mergeIn[i]==1 ) mi -= mergeOffset-1; |
| 750 | | - if( pRow->mergeIn[i]==3 ) mi += mergeOffset; |
| 726 | + int mi = i; |
| 751 | 727 | if( pRow->mergeDown & (1<<i) ) mi = -mi; |
| 752 | 728 | cgi_printf("%c%d", cSep, mi); |
| 753 | 729 | cSep = ','; |
| 754 | 730 | } |
| 755 | 731 | } |
| | @@ -756,246 +732,238 @@ |
| 756 | 732 | if( cSep=='[' ) cgi_printf("["); |
| 757 | 733 | cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n"); |
| 758 | 734 | } |
| 759 | 735 | cgi_printf("var nrail = %d\n", pGraph->mxRail+1); |
| 760 | 736 | graph_free(pGraph); |
| 761 | | - @ var cDiv = gebi("canvas"); |
| 762 | | - @ var csty = window.getComputedStyle && window.getComputedStyle(cDiv,null); |
| 763 | | - @ var lineClr = (csty && csty.getPropertyValue('color')) || 'black'; |
| 764 | | - @ var bgClr = (csty && csty.getPropertyValue('background-color')) ||'white'; |
| 765 | | - @ if( bgClr=='transparent' ) bgClr = 'white'; |
| 766 | | - @ var boxColor = lineClr; |
| 767 | | - @ function drawBox(color,x0,y0,x1,y1){ |
| 737 | + @ var canvasDiv; |
| 738 | + @ var railPitch; |
| 739 | + @ var mergeOffset; |
| 740 | + @ var node, arrow, arrowSmall, line, mArrow, mLine, wArrow, wLine; |
| 741 | + @ function initGraph(){ |
| 742 | + @ var parent = gebi("timelineTable").rows[0].cells[1]; |
| 743 | + @ parent.style.verticalAlign = "top"; |
| 744 | + @ canvasDiv = document.createElement("div"); |
| 745 | + @ canvasDiv.className = "tl-canvas"; |
| 746 | + @ canvasDiv.style.position = "absolute"; |
| 747 | + @ parent.appendChild(canvasDiv); |
| 748 | + @ |
| 749 | + @ var elems = {}; |
| 750 | + @ var elemClasses = [ |
| 751 | + @ "rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line", |
| 752 | + @ "arrow merge r", "line merge", "arrow warp", "line warp" |
| 753 | + @ ]; |
| 754 | + @ for( var i=0; i<elemClasses.length; i++ ){ |
| 755 | + @ var cls = elemClasses[i]; |
| 756 | + @ var elem = document.createElement("div"); |
| 757 | + @ elem.className = "tl-" + cls; |
| 758 | + @ if( cls.indexOf("line")==0 ) elem.className += " v"; |
| 759 | + @ canvasDiv.appendChild(elem); |
| 760 | + @ var k = cls.replace(/\s/g, "_"); |
| 761 | + @ var r = elem.getBoundingClientRect(); |
| 762 | + @ var w = Math.round(r.right - r.left); |
| 763 | + @ var h = Math.round(r.bottom - r.top); |
| 764 | + @ elems[k] = {w: w, h: h, cls: cls}; |
| 765 | + @ } |
| 766 | + @ node = elems.node; |
| 767 | + @ arrow = elems.arrow_u; |
| 768 | + @ arrowSmall = elems.arrow_u_sm; |
| 769 | + @ line = elems.line; |
| 770 | + @ mArrow = elems.arrow_merge_r; |
| 771 | + @ mLine = elems.line_merge; |
| 772 | + @ wArrow = elems.arrow_warp; |
| 773 | + @ wLine = elems.line_warp; |
| 774 | + @ |
| 775 | + @ var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1); |
| 776 | + if( iRailPitch ){ |
| 777 | + @ railPitch = %d(iRailPitch); |
| 778 | + }else{ |
| 779 | + @ railPitch = elems.rail.w; |
| 780 | + @ railPitch -= Math.floor((nrail-1)*(railPitch-minRailPitch)/21); |
| 781 | + } |
| 782 | + @ railPitch = Math.max(railPitch, minRailPitch); |
| 783 | + @ |
| 784 | + if( PB("nomo") ){ |
| 785 | + @ mergeOffset = 0; |
| 786 | + }else{ |
| 787 | + @ mergeOffset = railPitch-minRailPitch-mLine.w; |
| 788 | + @ mergeOffset = Math.min(mergeOffset, elems.mergeoffset.w); |
| 789 | + @ mergeOffset = mergeOffset>0 ? mergeOffset + line.w/2 : 0; |
| 790 | + } |
| 791 | + @ |
| 792 | + @ var canvasWidth = (nrail-1)*railPitch + node.w; |
| 793 | + @ canvasDiv.style.width = canvasWidth + "px"; |
| 794 | + @ canvasDiv.style.position = "relative"; |
| 795 | + @ } |
| 796 | + @ function drawBox(cls,color,x0,y0,x1,y1){ |
| 768 | 797 | @ var n = document.createElement("div"); |
| 798 | + @ x0 = Math.floor(x0); |
| 799 | + @ y0 = Math.floor(y0); |
| 800 | + @ x1 = x1 || x1===0 ? Math.floor(x1) : x0; |
| 801 | + @ y1 = y1 || y1===0 ? Math.floor(y1) : y0; |
| 769 | 802 | @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } |
| 770 | 803 | @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } |
| 771 | | - @ var w = x1-x0+1; |
| 772 | | - @ var h = y1-y0+1; |
| 804 | + @ var w = x1-x0; |
| 805 | + @ var h = y1-y0; |
| 773 | 806 | @ n.style.position = "absolute"; |
| 774 | | - @ n.style.overflow = "hidden"; |
| 775 | 807 | @ n.style.left = x0+"px"; |
| 776 | 808 | @ n.style.top = y0+"px"; |
| 777 | | - @ n.style.width = w+"px"; |
| 778 | | - @ n.style.height = h+"px"; |
| 779 | | - @ n.style.backgroundColor = color; |
| 780 | | - @ cDiv.appendChild(n); |
| 809 | + @ if( w ) n.style.width = w+"px"; |
| 810 | + @ if( h ) n.style.height = h+"px"; |
| 811 | + @ if( color ) n.style.backgroundColor = color; |
| 812 | + @ n.className = "tl-"+cls; |
| 813 | + @ canvasDiv.appendChild(n); |
| 781 | 814 | @ return n; |
| 782 | 815 | @ } |
| 783 | | - @ function absoluteY(id){ |
| 784 | | - @ var obj = gebi(id); |
| 785 | | - @ if( !obj ) return; |
| 816 | + @ function absoluteY(obj){ |
| 786 | 817 | @ var top = 0; |
| 787 | 818 | @ if( obj.offsetParent ){ |
| 788 | 819 | @ do{ |
| 789 | 820 | @ top += obj.offsetTop; |
| 790 | 821 | @ }while( obj = obj.offsetParent ); |
| 791 | 822 | @ } |
| 792 | 823 | @ return top; |
| 793 | 824 | @ } |
| 794 | | - @ function absoluteX(id){ |
| 795 | | - @ var obj = gebi(id); |
| 796 | | - @ if( !obj ) return; |
| 797 | | - @ var left = 0; |
| 798 | | - @ if( obj.offsetParent ){ |
| 799 | | - @ do{ |
| 800 | | - @ left += obj.offsetLeft; |
| 801 | | - @ }while( obj = obj.offsetParent ); |
| 802 | | - @ } |
| 803 | | - @ return left; |
| 804 | | - @ } |
| 805 | | - if( showArrowheads ){ |
| 806 | | - @ function drawUpArrow(x,y0,y1,clr){ |
| 807 | | - @ drawBox(clr,x,y0+4,x+1,y1); |
| 808 | | - @ var n = document.createElement("div"), |
| 809 | | - @ l = x-2, |
| 810 | | - @ t = y0; |
| 811 | | - @ n.style.position = "absolute"; |
| 812 | | - @ n.style.left = l+"px"; |
| 813 | | - @ n.style.top = t+"px"; |
| 814 | | - @ n.style.width = 0; |
| 815 | | - @ n.style.height = 0; |
| 816 | | - @ n.style.transform = "scale(.999)"; |
| 817 | | - @ n.style.borderWidth = 0; |
| 818 | | - @ n.style.borderStyle = "solid"; |
| 819 | | - @ n.style.borderColor = "transparent"; |
| 820 | | - @ n.style.borderRightWidth = "3px"; |
| 821 | | - @ n.style.borderBottomColor = clr; |
| 822 | | - @ n.style.borderLeftWidth = "3px"; |
| 823 | | - @ if( y0+10>=y1 ){ |
| 824 | | - @ n.style.borderBottomWidth = "5px"; |
| 825 | | - @ } else { |
| 826 | | - @ n.style.borderBottomWidth = "7px"; |
| 827 | | - @ } |
| 828 | | - @ cDiv.appendChild(n); |
| 829 | | - @ } |
| 830 | | - }else{ |
| 831 | | - @ function drawUpArrow(x,y0,y1,clr){ |
| 832 | | - @ drawBox(clr,x,y0+1,x+1,y1); |
| 833 | | - @ } |
| 834 | | - } |
| 835 | | - @ function drawThinArrow(y,xFrom,xTo){ |
| 836 | | - @ var n = document.createElement("div"), |
| 837 | | - @ t = y-2; |
| 838 | | - @ n.style.position = "absolute"; |
| 839 | | - @ n.style.top = t+"px"; |
| 840 | | - @ n.style.width = 0; |
| 841 | | - @ n.style.height = "1px"; |
| 842 | | - @ n.style.transform = "scale(.999)"; |
| 843 | | - @ n.style.borderWidth = 0; |
| 844 | | - @ n.style.borderStyle = "solid"; |
| 845 | | - @ n.style.borderColor = "transparent"; |
| 846 | | - @ n.style.borderTopWidth = "2px"; |
| 847 | | - @ n.style.borderBottomWidth = "2px"; |
| 848 | | - @ if( xFrom<xTo ){ |
| 849 | | - @ drawBox(lineClr,xFrom,y,xTo-3,y); |
| 850 | | - @ n.style.left = xTo-3+"px"; |
| 851 | | - @ n.style.borderLeftWidth = "3px"; |
| 852 | | - @ n.style.borderLeftColor = lineClr; |
| 853 | | - @ }else{ |
| 854 | | - @ drawBox(lineClr,xTo+3,y,xFrom,y); |
| 855 | | - @ n.style.left = xTo+1+"px"; |
| 856 | | - @ n.style.borderRightWidth = "3px"; |
| 857 | | - @ n.style.borderRightColor = lineClr; |
| 858 | | - @ } |
| 859 | | - @ cDiv.appendChild(n); |
| 860 | | - @ } |
| 861 | | - @ function drawThinLine(x0,y0,x1,y1){ |
| 862 | | - @ drawBox(lineClr,x0,y0,x1,y1); |
| 863 | | - @ } |
| 864 | | - @ function drawNodeBox(color,x0,y0,x1,y1){ |
| 865 | | - @ var n = drawBox(color,x0,y0,x1,y1); |
| 866 | | - @ n.style.cursor = "pointer"; |
| 867 | | - if( circleNodes ){ |
| 868 | | - @ n.style.borderRadius = "6px"; |
| 869 | | - } |
| 870 | | - @ } |
| 871 | | - @ function drawNode(p, left, btm){ |
| 872 | | - @ drawNodeBox(boxColor,p.x-5,p.y-5,p.x+6,p.y+6); |
| 873 | | - @ drawNodeBox(p.bg||bgClr,p.x-4,p.y-4,p.x+5,p.y+5); |
| 874 | | - @ if( p.u>0 ) drawUpArrow(p.x,rowinfo[p.u-1].y+6,p.y-6,p.fg||lineClr); |
| 875 | | - @ if( p.f&1 ) drawNodeBox(boxColor,p.x-1,p.y-1,p.x+2,p.y+2); |
| 876 | | - if( !omitDescenders ){ |
| 877 | | - @ if( p.u==0 ) drawUpArrow(p.x,0,p.y-6,p.fg||lineClr); |
| 878 | | - @ if( p.d ) drawUpArrow(p.x,p.y+6,btm,p.fg||lineClr); |
| 879 | | - } |
| 880 | | - @ if( p.mo>0 ){ |
| 881 | | - @ var x1 = p.mo + left - 1; |
| 882 | | - @ var y1 = p.y-3; |
| 883 | | - @ var x0 = x1>p.x ? p.x+7 : p.x-6; |
| 884 | | - @ var u = rowinfo[p.mu-1]; |
| 885 | | - @ var y0 = u.y+5; |
| 886 | | - @ if( x1>=p.x-5 && x1<=p.x+5 ){ |
| 887 | | - @ y1 = p.y-5; |
| 888 | | - @ }else{ |
| 889 | | - @ drawThinLine(x0,y1,x1,y1); |
| 890 | | - @ } |
| 891 | | - if( mergeOffset==0 ) cgi_printf("if( p.mo!=p.u-1 ) "); |
| 892 | | - @ drawThinLine(x1,y0,x1,y1); |
| 893 | | - @ } |
| 894 | | - @ var n = p.au.length; |
| 895 | | - @ for(var i=0; i<n; i+=2){ |
| 896 | | - @ var x1 = p.au[i]*railPitch + left; |
| 897 | | - @ var x0 = x1>p.x ? p.x+7 : p.x-6; |
| 898 | | - @ var u = rowinfo[p.au[i+1]-1]; |
| 899 | | - @ if(u.id<p.id){ |
| 900 | | - @ drawBox(u.fg||lineClr,x0,p.y,x1+1,p.y+1); |
| 901 | | - @ drawUpArrow(x1,u.y+6,p.y,u.fg||lineClr); |
| 902 | | - @ }else{ |
| 903 | | - @ drawBox("#600000",x0,p.y,x1,p.y+1); |
| 904 | | - @ drawBox("#600000",x1-1,p.y,x1,u.y+1); |
| 905 | | - @ drawBox("#600000",x1,u.y,u.x-10,u.y+1); |
| 906 | | - @ var n = document.createElement("div"), |
| 907 | | - @ t = u.y-2, |
| 908 | | - @ l = u.x-11; |
| 909 | | - @ n.style.position = "absolute"; |
| 910 | | - @ n.style.top = t+"px"; |
| 911 | | - @ n.style.left = l+"px"; |
| 912 | | - @ n.style.width = 0; |
| 913 | | - @ n.style.height = 0; |
| 914 | | - @ n.style.transform = "scale(.999)"; |
| 915 | | - @ n.style.borderWidth = 0; |
| 916 | | - @ n.style.borderStyle = "solid"; |
| 917 | | - @ n.style.borderColor = "transparent"; |
| 918 | | - @ n.style.borderTopWidth = "3px"; |
| 919 | | - @ n.style.borderBottomWidth = "3px"; |
| 920 | | - @ n.style.borderLeftWidth = "7px"; |
| 921 | | - @ n.style.borderLeftColor = "#600000"; |
| 922 | | - @ cDiv.appendChild(n); |
| 923 | | - @ } |
| 924 | | - @ } |
| 925 | | - @ for(var j in p.mi){ |
| 926 | | - @ var y0 = p.y+5; |
| 927 | | - @ var mx = p.mi[j]; |
| 928 | | - @ if( mx<0 ){ |
| 929 | | - @ mx = left-mx; |
| 930 | | - @ drawThinLine(mx,y0,mx,btm); |
| 931 | | - @ }else{ |
| 932 | | - @ mx += left; |
| 933 | | - @ } |
| 934 | | - @ if( mx>p.x ){ |
| 935 | | - @ drawThinArrow(y0,mx,p.x+6); |
| 936 | | - @ }else{ |
| 937 | | - @ drawThinArrow(y0,mx,p.x-5); |
| 938 | | - @ } |
| 939 | | - @ } |
| 940 | | - @ } |
| 941 | | - @ var selBox = null; |
| 942 | | - @ var selRow = null; |
| 943 | | - @ function renderGraph(){ |
| 944 | | - @ var canvasDiv = gebi("canvas"); |
| 945 | | - @ while( canvasDiv.hasChildNodes() ){ |
| 946 | | - @ canvasDiv.removeChild(canvasDiv.firstChild); |
| 947 | | - @ } |
| 948 | | - @ var canvasY = absoluteY("timelineTable"); |
| 949 | | - @ var left = absoluteX("m"+rowinfo[0].id) - absoluteX("canvas") + 15; |
| 950 | | - @ for(var i in rowinfo){ |
| 951 | | - @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 952 | | - @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 953 | | - @ } |
| 954 | | - @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 955 | | - @ for(var i in rowinfo){ |
| 956 | | - @ drawNode(rowinfo[i], left, btm); |
| 957 | | - @ } |
| 958 | | - @ if( selRow!=null ) clickOnRow(selRow); |
| 959 | | - @ } |
| 960 | | - @ function clickOnGraph(event){ |
| 961 | | - @ var x=event.clientX-absoluteX("canvas"); |
| 962 | | - @ var y=event.clientY-absoluteY("canvas"); |
| 963 | | - @ if(window.pageXOffset!=null){ |
| 964 | | - @ x += window.pageXOffset; |
| 965 | | - @ y += window.pageYOffset; |
| 966 | | - @ }else{ |
| 967 | | - @ var d = window.document.documentElement; |
| 968 | | - @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 969 | | - @ x += d.scrollLeft; |
| 970 | | - @ y += d.scrollTop; |
| 971 | | - @ } |
| 972 | | - if( P("clicktest")!=0 ){ |
| 973 | | - @ alert("click at "+x+","+y) |
| 974 | | - } |
| 975 | | - @ for(var i in rowinfo){ |
| 976 | | - @ p = rowinfo[i]; |
| 977 | | - @ if( p.y<y-11 ) continue; |
| 978 | | - @ if( p.y>y+9 ) break; |
| 979 | | - @ if( p.x>x-11 && p.x<x+9 ){ |
| 980 | | - @ clickOnRow(p); |
| 981 | | - @ break; |
| 982 | | - @ } |
| 983 | | - @ } |
| 984 | | - @ } |
| 985 | | - @ function clickOnRow(p){ |
| 986 | | - @ if( selRow==null ){ |
| 987 | | - @ selBox = drawBox("red",p.x-2,p.y-2,p.x+3,p.y+3); |
| 988 | | - if( circleNodes ){ |
| 989 | | - @ selBox.style.borderRadius="6px"; |
| 990 | | - } |
| 991 | | - @ selRow = p; |
| 992 | | - @ }else if( selRow==p ){ |
| 993 | | - @ var canvasDiv = gebi("canvas"); |
| 994 | | - @ canvasDiv.removeChild(selBox); |
| 995 | | - @ selBox = null; |
| 996 | | - @ selRow = null; |
| 825 | + @ function miLineY(p){ |
| 826 | + @ return p.y + node.h - mLine.w - 1; |
| 827 | + @ } |
| 828 | + @ function drawLine(elem,color,x0,y0,x1,y1){ |
| 829 | + @ var cls = elem.cls + " "; |
| 830 | + @ if( x1===null ){ |
| 831 | + @ x1 = x0+elem.w; |
| 832 | + @ cls += "v"; |
| 833 | + @ }else{ |
| 834 | + @ y1 = y0+elem.w; |
| 835 | + @ cls += "h"; |
| 836 | + @ } |
| 837 | + @ drawBox(cls,color,x0,y0,x1,y1); |
| 838 | + @ } |
| 839 | + @ function drawUpArrow(from,to,color){ |
| 840 | + @ var y = to.y + node.h; |
| 841 | + @ var arrowSpace = from.y - y + (!from.id || from.r!=to.r ? node.h/2 : 0); |
| 842 | + @ var arw = arrowSpace < arrow.h*1.5 ? arrowSmall : arrow; |
| 843 | + @ var x = to.x + (node.w-line.w)/2; |
| 844 | + @ var y0 = from.y + node.h/2; |
| 845 | + @ var y1 = Math.ceil(to.y + node.h + arw.h/2); |
| 846 | + @ drawLine(line,color,x,y0,null,y1); |
| 847 | + @ x = to.x + (node.w-arw.w)/2; |
| 848 | + @ var n = drawBox(arw.cls,null,x,y); |
| 849 | + @ n.style.borderBottomColor = color; |
| 850 | + @ } |
| 851 | + @ function drawMergeLine(x0,y0,x1,y1){ |
| 852 | + @ drawLine(mLine,null,x0,y0,x1,y1); |
| 853 | + @ } |
| 854 | + @ function drawMergeArrow(p,rail){ |
| 855 | + @ var x0 = rail*railPitch + node.w/2; |
| 856 | + @ if( rail in mergeLines ){ |
| 857 | + @ x0 += mergeLines[rail]; |
| 858 | + @ if( p.r<rail ) x0 += mLine.w; |
| 859 | + @ }else{ |
| 860 | + @ x0 += (p.r<rail ? -1 : 1)*line.w/2; |
| 861 | + @ } |
| 862 | + @ var x1 = mArrow.w ? mArrow.w/2 : -node.w/2; |
| 863 | + @ x1 = p.x + (p.r<rail ? node.w + Math.ceil(x1) : -x1); |
| 864 | + @ var y = miLineY(p); |
| 865 | + @ drawMergeLine(x0,y,x1,null); |
| 866 | + @ var x = p.x + (p.r<rail ? node.w : -mArrow.w); |
| 867 | + @ var cls = "arrow merge " + (p.r<rail ? "l" : "r"); |
| 868 | + @ drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2); |
| 869 | + @ } |
| 870 | + @ function drawNode(p, btm){ |
| 871 | + @ if( p.u>0 ) drawUpArrow(p,rowinfo[p.u-1],p.fg); |
| 872 | + @ var cls = node.cls; |
| 873 | + @ if( p.mi.length ) cls += " merge"; |
| 874 | + @ if( p.f&1 ) cls += " leaf"; |
| 875 | + @ var n = drawBox(cls,p.bg,p.x,p.y); |
| 876 | + @ n.id = "tln"+p.id; |
| 877 | + @ n.onclick = clickOnNode; |
| 878 | + @ n.style.zIndex = 10; |
| 879 | + if( !omitDescenders ){ |
| 880 | + @ if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg); |
| 881 | + @ if( p.d ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg); |
| 882 | + } |
| 883 | + @ if( p.mo>=0 ){ |
| 884 | + @ var x0 = p.x + node.w/2; |
| 885 | + @ var x1 = p.mo*railPitch + node.w/2; |
| 886 | + @ var u = rowinfo[p.mu-1]; |
| 887 | + @ var y1 = miLineY(u); |
| 888 | + @ if( p.u<0 || p.mo!=p.r ){ |
| 889 | + @ x1 += mergeLines[p.mo] = -mLine.w/2; |
| 890 | + @ var y0 = p.y+2; |
| 891 | + @ if( p.r!=p.mo ) drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null); |
| 892 | + @ drawMergeLine(x1,y0+mLine.w,null,y1); |
| 893 | + @ }else if( mergeOffset ){ |
| 894 | + @ mergeLines[p.mo] = u.r<p.r ? -mergeOffset-mLine.w : mergeOffset; |
| 895 | + @ x1 += mergeLines[p.mo]; |
| 896 | + @ drawMergeLine(x1,p.y+node.h/2,null,y1); |
| 897 | + @ }else{ |
| 898 | + @ delete mergeLines[p.mo]; |
| 899 | + @ } |
| 900 | + @ } |
| 901 | + @ for( var i=0; i<p.au.length; i+=2 ){ |
| 902 | + @ var rail = p.au[i]; |
| 903 | + @ var x0 = p.x + node.w/2; |
| 904 | + @ var x1 = rail*railPitch + (node.w-line.w)/2; |
| 905 | + @ if( x0<x1 ){ |
| 906 | + @ x0 = Math.ceil(x0); |
| 907 | + @ x1 += line.w; |
| 908 | + @ } |
| 909 | + @ var y0 = p.y + (node.h-line.w)/2; |
| 910 | + @ var u = rowinfo[p.au[i+1]-1]; |
| 911 | + @ if( u.id<p.id ){ |
| 912 | + @ drawLine(line,u.fg,x0,y0,x1,null); |
| 913 | + @ drawUpArrow(p,u,u.fg); |
| 914 | + @ }else{ |
| 915 | + @ var y1 = u.y + (node.h-line.w)/2; |
| 916 | + @ drawLine(wLine,u.fg,x0,y0,x1,null); |
| 917 | + @ drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w); |
| 918 | + @ drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null); |
| 919 | + @ var x = u.x-wArrow.w; |
| 920 | + @ var y = u.y+(node.h-wArrow.h)/2; |
| 921 | + @ var n = drawBox(wArrow.cls,null,x,y); |
| 922 | + @ if( u.fg ) n.style.borderLeftColor = u.fg; |
| 923 | + @ } |
| 924 | + @ } |
| 925 | + @ for( var i=0; i<p.mi.length; i++ ){ |
| 926 | + @ var rail = p.mi[i]; |
| 927 | + @ if( rail<0 ){ |
| 928 | + @ rail = -rail; |
| 929 | + @ mergeLines[rail] = -mLine.w/2; |
| 930 | + @ var x = rail*railPitch + (node.w-mLine.w)/2; |
| 931 | + @ drawMergeLine(x,miLineY(p),null,btm); |
| 932 | + @ } |
| 933 | + @ drawMergeArrow(p,rail); |
| 934 | + @ } |
| 935 | + @ } |
| 936 | + @ var mergeLines; |
| 937 | + @ function renderGraph(){ |
| 938 | + @ mergeLines = {}; |
| 939 | + @ canvasDiv.innerHTML = ""; |
| 940 | + @ var canvasY = absoluteY(canvasDiv); |
| 941 | + @ for( var i=0; i<rowinfo.length; i++ ){ |
| 942 | + @ rowinfo[i].y = absoluteY(gebi("m"+rowinfo[i].id)) - canvasY; |
| 943 | + @ rowinfo[i].x = rowinfo[i].r*railPitch; |
| 944 | + @ } |
| 945 | + @ var tlBtm = document.querySelector(".timelineBottom"); |
| 946 | + @ if( tlBtm.offsetHeight<node.h ){ |
| 947 | + @ tlBtm.style.height = node.h + "px"; |
| 948 | + @ } |
| 949 | + @ var btm = absoluteY(tlBtm) - canvasY + tlBtm.offsetHeight; |
| 950 | + @ for( var i=rowinfo.length-1; i>=0; i-- ){ |
| 951 | + @ drawNode(rowinfo[i], btm); |
| 952 | + @ } |
| 953 | + @ } |
| 954 | + @ var selRow; |
| 955 | + @ function clickOnNode(){ |
| 956 | + @ var p = rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-1]; |
| 957 | + @ if( !selRow ){ |
| 958 | + @ selRow = p; |
| 959 | + @ this.className += " sel"; |
| 960 | + @ canvasDiv.className += " sel"; |
| 961 | + @ }else if( selRow==p ){ |
| 962 | + @ selRow = null; |
| 963 | + @ this.className = this.className.replace(" sel", ""); |
| 964 | + @ canvasDiv.className = canvasDiv.className.replace(" sel", ""); |
| 997 | 965 | @ }else{ |
| 998 | 966 | if( fileDiff ){ |
| 999 | 967 | @ location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h+"&sbs=1"; |
| 1000 | 968 | }else{ |
| 1001 | 969 | if( db_get_boolean("show-version-diffs", 0)==0 ){ |
| | @@ -1004,22 +972,23 @@ |
| 1004 | 972 | @ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=1"; |
| 1005 | 973 | } |
| 1006 | 974 | } |
| 1007 | 975 | @ } |
| 1008 | 976 | @ } |
| 1009 | | - @ var lastId = "m"+rowinfo[rowinfo.length-1].id; |
| 977 | + @ var lastRow = gebi("m"+rowinfo[rowinfo.length-1].id); |
| 1010 | 978 | @ var lastY = 0; |
| 1011 | 979 | @ function checkHeight(){ |
| 1012 | | - @ var h = absoluteY(lastId); |
| 980 | + @ var h = absoluteY(lastRow); |
| 1013 | 981 | @ if( h!=lastY ){ |
| 1014 | 982 | @ renderGraph(); |
| 1015 | 983 | @ lastY = h; |
| 1016 | 984 | @ } |
| 1017 | | - @ setTimeout("checkHeight();", 1000); |
| 985 | + @ setTimeout(checkHeight, 1000); |
| 1018 | 986 | @ } |
| 987 | + @ initGraph(); |
| 1019 | 988 | @ checkHeight(); |
| 1020 | | - @ </script> |
| 989 | + @ }())</script> |
| 1021 | 990 | } |
| 1022 | 991 | } |
| 1023 | 992 | |
| 1024 | 993 | /* |
| 1025 | 994 | ** Create a temporary table suitable for storing timeline data. |
| 1026 | 995 | |
| 1027 | 996 | ADDED test/contains-selector.test |