Fossil SCM

Replace the in-line graph-drawing javascript with a separate graph drawing module, "graph.js".

drh 2017-12-05 20:16 trunk
Commit 32a2fff8509116848f97ccfd525f61be449b02ed2b7c4b190699773a25cb55a0
+2 -3
--- src/finfo.c
+++ src/finfo.c
@@ -312,11 +312,10 @@
312312
int brBg = P("brbg")!=0;
313313
int uBg = P("ubg")!=0;
314314
int fDebug = atoi(PD("debug","0"));
315315
int fShowId = P("showid")!=0;
316316
Stmt qparent;
317
- int iTableId = timeline_tableid();
318317
int tmFlags = 0; /* Viewing mode */
319318
const char *zStyle; /* Viewing mode name */
320319
321320
login_check_credentials();
322321
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -447,11 +446,11 @@
447446
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
448447
}
449448
@ <h2>%b(&title)</h2>
450449
blob_reset(&title);
451450
pGraph = graph_init();
452
- @ <table id="timelineTable%d(iTableId)" class="timelineTable">
451
+ @ <table id="timelineTable" class="timelineTable">
453452
if( baseCheckin ){
454453
db_prepare(&qparent,
455454
"SELECT DISTINCT pid FROM mlink"
456455
" WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid"
457456
" AND pmid IN (SELECT rid FROM ancestor)"
@@ -636,11 +635,11 @@
636635
}else{
637636
@ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
638637
}
639638
}
640639
@ </table>
641
- timeline_output_graph_javascript(pGraph, 0, iTableId, 1);
640
+ timeline_output_graph_javascript(pGraph, 0, 1);
642641
style_footer();
643642
}
644643
645644
/*
646645
** WEBPAGE: mlink
647646
648647
ADDED src/graph.js
--- src/finfo.c
+++ src/finfo.c
@@ -312,11 +312,10 @@
312 int brBg = P("brbg")!=0;
313 int uBg = P("ubg")!=0;
314 int fDebug = atoi(PD("debug","0"));
315 int fShowId = P("showid")!=0;
316 Stmt qparent;
317 int iTableId = timeline_tableid();
318 int tmFlags = 0; /* Viewing mode */
319 const char *zStyle; /* Viewing mode name */
320
321 login_check_credentials();
322 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -447,11 +446,11 @@
447 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
448 }
449 @ <h2>%b(&title)</h2>
450 blob_reset(&title);
451 pGraph = graph_init();
452 @ <table id="timelineTable%d(iTableId)" class="timelineTable">
453 if( baseCheckin ){
454 db_prepare(&qparent,
455 "SELECT DISTINCT pid FROM mlink"
456 " WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid"
457 " AND pmid IN (SELECT rid FROM ancestor)"
@@ -636,11 +635,11 @@
636 }else{
637 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
638 }
639 }
640 @ </table>
641 timeline_output_graph_javascript(pGraph, 0, iTableId, 1);
642 style_footer();
643 }
644
645 /*
646 ** WEBPAGE: mlink
647
648 DDED src/graph.js
--- src/finfo.c
+++ src/finfo.c
@@ -312,11 +312,10 @@
312 int brBg = P("brbg")!=0;
313 int uBg = P("ubg")!=0;
314 int fDebug = atoi(PD("debug","0"));
315 int fShowId = P("showid")!=0;
316 Stmt qparent;
 
317 int tmFlags = 0; /* Viewing mode */
318 const char *zStyle; /* Viewing mode name */
319
320 login_check_credentials();
321 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -447,11 +446,11 @@
446 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
447 }
448 @ <h2>%b(&title)</h2>
449 blob_reset(&title);
450 pGraph = graph_init();
451 @ <table id="timelineTable" class="timelineTable">
452 if( baseCheckin ){
453 db_prepare(&qparent,
454 "SELECT DISTINCT pid FROM mlink"
455 " WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid"
456 " AND pmid IN (SELECT rid FROM ancestor)"
@@ -636,11 +635,11 @@
635 }else{
636 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
637 }
638 }
639 @ </table>
640 timeline_output_graph_javascript(pGraph, 0, 1);
641 style_footer();
642 }
643
644 /*
645 ** WEBPAGE: mlink
646
647 DDED src/graph.js
+13
--- a/src/graph.js
+++ b/src/graph.js
@@ -0,0 +1,13 @@
1
+isdeclutter(){
2
+ changeDisplay('clutter','none');
3
+ changeDisplay('anticlutter','inline');reclutter(){
4
+ changeDisplay('clutter','inline');
5
+ changeDisplay('anticlutter','none');id){+"&sbs=1";
6
+ }else{
7
+/*
8
+** if( db_get_boolean("show-version-diffs", 0)==0 ){
9
+** declutter(){
10
+ changeDisplay('clutter','none');
11
+ changeDisplay('anticlutter','inline');reclutter(){
12
+ changeDisplay('clutter','inline');
13
+ changeDisplay('anticlutter','none');id){splay('clu
--- a/src/graph.js
+++ b/src/graph.js
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/graph.js
+++ b/src/graph.js
@@ -0,0 +1,13 @@
1 isdeclutter(){
2 changeDisplay('clutter','none');
3 changeDisplay('anticlutter','inline');reclutter(){
4 changeDisplay('clutter','inline');
5 changeDisplay('anticlutter','none');id){+"&sbs=1";
6 }else{
7 /*
8 ** if( db_get_boolean("show-version-diffs", 0)==0 ){
9 ** declutter(){
10 changeDisplay('clutter','none');
11 changeDisplay('anticlutter','inline');reclutter(){
12 changeDisplay('clutter','inline');
13 changeDisplay('anticlutter','none');id){splay('clu
+11
--- src/href.js
+++ src/href.js
@@ -3,10 +3,21 @@
33
** action= set to the login page. The real values for href= and action=
44
** are held in data-href= and data-action=. The following code moves
55
** data-href= into href= and data-action= into action= for all
66
** <a> and <form> elements, after delay and maybe also after mouse
77
** movement is seen.
8
+**
9
+** Before sourcing this script, create a separate <script> element
10
+** (with type='application/json' to avoid Content Security Policy issues)
11
+** containing:
12
+**
13
+** {"delay":MILLISECONDS, "mouseover":BOOLEAN}
14
+**
15
+** The <script> must have an id='href-data'. DELAY is the number
16
+** milliseconds delay prior to populating href= and action=. If the
17
+** mouseover boolean is true, then the timer does not start until a
18
+** mouse motion event occurs over top of the document.
819
*/
920
function setAllHrefs(){
1021
var anchors = document.getElementsByTagName("a");
1122
for(var i=0; i<anchors.length; i++){
1223
var j = anchors[i];
1324
--- src/href.js
+++ src/href.js
@@ -3,10 +3,21 @@
3 ** action= set to the login page. The real values for href= and action=
4 ** are held in data-href= and data-action=. The following code moves
5 ** data-href= into href= and data-action= into action= for all
6 ** <a> and <form> elements, after delay and maybe also after mouse
7 ** movement is seen.
 
 
 
 
 
 
 
 
 
 
 
8 */
9 function setAllHrefs(){
10 var anchors = document.getElementsByTagName("a");
11 for(var i=0; i<anchors.length; i++){
12 var j = anchors[i];
13
--- src/href.js
+++ src/href.js
@@ -3,10 +3,21 @@
3 ** action= set to the login page. The real values for href= and action=
4 ** are held in data-href= and data-action=. The following code moves
5 ** data-href= into href= and data-action= into action= for all
6 ** <a> and <form> elements, after delay and maybe also after mouse
7 ** movement is seen.
8 **
9 ** Before sourcing this script, create a separate <script> element
10 ** (with type='application/json' to avoid Content Security Policy issues)
11 ** containing:
12 **
13 ** {"delay":MILLISECONDS, "mouseover":BOOLEAN}
14 **
15 ** The <script> must have an id='href-data'. DELAY is the number
16 ** milliseconds delay prior to populating href= and action=. If the
17 ** mouseover boolean is true, then the timer does not start until a
18 ** mouse motion event occurs over top of the document.
19 */
20 function setAllHrefs(){
21 var anchors = document.getElementsByTagName("a");
22 for(var i=0; i<anchors.length; i++){
23 var j = anchors[i];
24
--- src/main.mk
+++ src/main.mk
@@ -194,10 +194,11 @@
194194
$(SRCDIR)/../skins/xekri/css.txt \
195195
$(SRCDIR)/../skins/xekri/details.txt \
196196
$(SRCDIR)/../skins/xekri/footer.txt \
197197
$(SRCDIR)/../skins/xekri/header.txt \
198198
$(SRCDIR)/diff.tcl \
199
+ $(SRCDIR)/graph.js \
199200
$(SRCDIR)/href.js \
200201
$(SRCDIR)/markdown.md \
201202
$(SRCDIR)/wiki.wiki
202203
203204
TRANS_SRC = \
204205
--- src/main.mk
+++ src/main.mk
@@ -194,10 +194,11 @@
194 $(SRCDIR)/../skins/xekri/css.txt \
195 $(SRCDIR)/../skins/xekri/details.txt \
196 $(SRCDIR)/../skins/xekri/footer.txt \
197 $(SRCDIR)/../skins/xekri/header.txt \
198 $(SRCDIR)/diff.tcl \
 
199 $(SRCDIR)/href.js \
200 $(SRCDIR)/markdown.md \
201 $(SRCDIR)/wiki.wiki
202
203 TRANS_SRC = \
204
--- src/main.mk
+++ src/main.mk
@@ -194,10 +194,11 @@
194 $(SRCDIR)/../skins/xekri/css.txt \
195 $(SRCDIR)/../skins/xekri/details.txt \
196 $(SRCDIR)/../skins/xekri/footer.txt \
197 $(SRCDIR)/../skins/xekri/header.txt \
198 $(SRCDIR)/diff.tcl \
199 $(SRCDIR)/graph.js \
200 $(SRCDIR)/href.js \
201 $(SRCDIR)/markdown.md \
202 $(SRCDIR)/wiki.wiki
203
204 TRANS_SRC = \
205
+2 -2
--- src/style.c
+++ src/style.c
@@ -516,11 +516,11 @@
516516
}
517517
518518
/*
519519
** Generate code to load a single javascript file
520520
*/
521
-static void style_load_one_js_file(const char *zFile){
521
+void style_load_one_js_file(const char *zFile){
522522
@ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script>
523523
}
524524
525525
/*
526526
** Generate code to load all required javascript files.
@@ -863,11 +863,11 @@
863863
const char *zName = P("name");
864864
const char *zTxt = 0;
865865
if( zName ) zTxt = builtin_text(zName);
866866
if( zTxt==0 ){
867867
cgi_set_status(404, "Not Found");
868
- @ File \"%h(zName)\" not found
868
+ @ File "%h(zName)" not found
869869
return;
870870
}
871871
if( sqlite3_strglob("*.js", zName)==0 ){
872872
cgi_set_content_type("application/javascript");
873873
}else{
874874
--- src/style.c
+++ src/style.c
@@ -516,11 +516,11 @@
516 }
517
518 /*
519 ** Generate code to load a single javascript file
520 */
521 static void style_load_one_js_file(const char *zFile){
522 @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script>
523 }
524
525 /*
526 ** Generate code to load all required javascript files.
@@ -863,11 +863,11 @@
863 const char *zName = P("name");
864 const char *zTxt = 0;
865 if( zName ) zTxt = builtin_text(zName);
866 if( zTxt==0 ){
867 cgi_set_status(404, "Not Found");
868 @ File \"%h(zName)\" not found
869 return;
870 }
871 if( sqlite3_strglob("*.js", zName)==0 ){
872 cgi_set_content_type("application/javascript");
873 }else{
874
--- src/style.c
+++ src/style.c
@@ -516,11 +516,11 @@
516 }
517
518 /*
519 ** Generate code to load a single javascript file
520 */
521 void style_load_one_js_file(const char *zFile){
522 @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script>
523 }
524
525 /*
526 ** Generate code to load all required javascript files.
@@ -863,11 +863,11 @@
863 const char *zName = P("name");
864 const char *zTxt = 0;
865 if( zName ) zTxt = builtin_text(zName);
866 if( zTxt==0 ){
867 cgi_set_status(404, "Not Found");
868 @ File "%h(zName)" not found
869 return;
870 }
871 if( sqlite3_strglob("*.js", zName)==0 ){
872 cgi_set_content_type("application/javascript");
873 }else{
874
+34 -332
--- src/timeline.c
+++ src/timeline.c
@@ -255,11 +255,10 @@
255255
int vid = 0; /* Current checkout version */
256256
int dateFormat = 0; /* 0: HH:MM (default) */
257257
int bCommentGitStyle = 0; /* Only show comments through first blank line */
258258
const char *zStyle; /* Sub-name for classes for the style */
259259
const char *zDateFmt;
260
- int iTableId = timeline_tableid();
261260
262261
if( fossil_strcmp(g.zIpAddr, "127.0.0.1")==0 && db_open_local(0) ){
263262
vid = db_lget_int("checkout", 0);
264263
}
265264
zPrevDate[0] = 0;
@@ -286,11 +285,11 @@
286285
db_static_prepare(&qbranch,
287286
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
288287
TAG_BRANCH
289288
);
290289
291
- @ <table id="timelineTable%d(iTableId)" class="timelineTable">
290
+ @ <table id="timelineTable" class="timelineTable">
292291
blob_zero(&comment);
293292
while( db_step(pQuery)==SQLITE_ROW ){
294293
int rid = db_column_int(pQuery, 0);
295294
const char *zUuid = db_column_text(pQuery, 1);
296295
int isLeaf = db_column_int(pQuery, 5);
@@ -723,11 +722,11 @@
723722
@ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
724723
}
725724
}
726725
@ </table>
727726
if( fchngQueryInit ) db_finalize(&fchngQuery);
728
- timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, iTableId, 0);
727
+ timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);
729728
}
730729
731730
/*
732731
** Change the RGB background color given in the argument in a foreground
733732
** color with the same hue.
@@ -764,11 +763,10 @@
764763
** graph.
765764
*/
766765
void timeline_output_graph_javascript(
767766
GraphContext *pGraph, /* The graph to be displayed */
768767
int omitDescenders, /* True to omit descenders */
769
- int iTableId, /* Identifier for the timelineTable */
770768
int fileDiff /* True for file diff. False for check-in diff */
771769
){
772770
if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
773771
GraphRow *pRow;
774772
int i;
@@ -781,25 +779,25 @@
781779
782780
iRailPitch = atoi(PD("railpitch","0"));
783781
showArrowheads = skin_detail_boolean("timeline-arrowheads");
784782
circleNodes = skin_detail_boolean("timeline-circle-nodes");
785783
colorGraph = skin_detail_boolean("timeline-color-graph-lines");
786
-
787
- @ <script>
788
- @ "use strict";
789
- @ var css = "";
790
- if( circleNodes ){
791
- @ css += ".tl-node, .tl-node:after { border-radius: 50%%; }";
792
- }
793
- if( !showArrowheads ){
794
- @ css += ".tl-arrow.u { display: none; }";
795
- }
796
- @ if( css!=="" ){
797
- @ var style = document.createElement("style");
798
- @ style.textContent = css;
799
- @ document.querySelector("head").appendChild(style);
800
- @ }
784
+ iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0;
785
+
786
+ @ <script id='timeline-data' type='application/json'>{
787
+ @ "circleNodes": %d(circleNodes),
788
+ @ "showArrowheads": %d(showArrowheads),
789
+ @ "iRailPitch": %d(iRailPitch),
790
+ @ "colorGraph": %d(colorGraph),
791
+ @ "nomo": %d(PB("nomo")),
792
+ @ "iTopRow": %d(iTopRow),
793
+ @ "omitDescenders": %d(omitDescenders),
794
+ @ "fileDiff": %d(fileDiff),
795
+ @ "nrail": %d(pGraph->mxRail+1),
796
+ @ "baseUrl": "%R",
797
+ @ "rowinfo": [
798
+
801799
/* the rowinfo[] array contains all the information needed to generate
802800
** the graph. Each entry contains information for a single row:
803801
**
804802
** id: The id of the <div> element for the row. This is an integer.
805803
** to get an actual id, prepend "m" to the integer. The top node
@@ -827,24 +825,20 @@
827825
** negative, then the rail position is the absolute value of mi[]
828826
** and a thin merge-arrow descender is drawn to the bottom of
829827
** the screen.
830828
** h: The artifact hash of the object being graphed
831829
*/
832
- iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0;
833
- cgi_printf("var rowinfo = [\n");
834830
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
835
- cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
836
- pRow->idx, /* id */
837
- pRow->zBgClr, /* bg */
838
- pRow->iRail, /* r */
839
- pRow->bDescender, /* d */
840
- pRow->mergeOut, /* mo */
841
- pRow->mergeUpto, /* mu */
842
- pRow->aiRiser[pRow->iRail], /* u */
843
- pRow->isLeaf ? 1 : 0 /* f */
844
- );
845
- /* au */
831
+ cgi_printf("{\"id\":%d,", pRow->idx);
832
+ cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
833
+ cgi_printf("\"r\":%d,", pRow->iRail);
834
+ cgi_printf("\"d\":%d,", pRow->bDescender);
835
+ cgi_printf("\"mo\":%d,", pRow->mergeOut);
836
+ cgi_printf("\"mu\":%d,", pRow->mergeUpto);
837
+ cgi_printf("\"u\":%d,", pRow->aiRiser[pRow->iRail]);
838
+ cgi_printf("\"f\":%d,", pRow->isLeaf ? 1 : 0);
839
+ cgi_printf("\"au\":");
846840
cSep = '[';
847841
for(i=0; i<GR_MAX_RAIL; i++){
848842
if( i==pRow->iRail ) continue;
849843
if( pRow->aiRiser[i]>0 ){
850844
cgi_printf("%c%d,%d", cSep, i, pRow->aiRiser[i]);
@@ -852,14 +846,14 @@
852846
}
853847
}
854848
if( cSep=='[' ) cgi_printf("[");
855849
cgi_printf("],");
856850
if( colorGraph && pRow->zBgClr[0]=='#' ){
857
- cgi_printf("fg:\"%s\",", bg_to_fg(pRow->zBgClr));
851
+ cgi_printf("\"fg\":\"%s\",", bg_to_fg(pRow->zBgClr));
858852
}
859853
/* mi */
860
- cgi_printf("mi:");
854
+ cgi_printf("\"mi\":");
861855
cSep = '[';
862856
for(i=0; i<GR_MAX_RAIL; i++){
863857
if( pRow->mergeIn[i] ){
864858
int mi = i;
865859
if( (pRow->mergeDown >> i) & 1 ) mi = -mi;
@@ -866,308 +860,16 @@
866860
cgi_printf("%c%d", cSep, mi);
867861
cSep = ',';
868862
}
869863
}
870864
if( cSep=='[' ) cgi_printf("[");
871
- cgi_printf("],h:\"%!S\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n");
872
- }
873
- cgi_printf("var nrail = %d\n", pGraph->mxRail+1);
874
- graph_free(pGraph);
875
- @ var canvasDiv;
876
- @ var railPitch;
877
- @ var mergeOffset;
878
- @ var node, arrow, arrowSmall, line, mArrow, mLine, wArrow, wLine;
879
- @ function initGraph(){
880
- @ var parent = gebi("timelineTable%d(iTableId)").rows[0].cells[1];
881
- @ parent.style.verticalAlign = "top";
882
- @ canvasDiv = document.createElement("div");
883
- @ canvasDiv.className = "tl-canvas";
884
- @ canvasDiv.style.position = "absolute";
885
- @ parent.appendChild(canvasDiv);
886
- @
887
- @ var elems = {};
888
- @ var elemClasses = [
889
- @ "rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line",
890
- @ "arrow merge r", "line merge", "arrow warp", "line warp"
891
- @ ];
892
- @ for( var i=0; i<elemClasses.length; i++ ){
893
- @ var cls = elemClasses[i];
894
- @ var elem = document.createElement("div");
895
- @ elem.className = "tl-" + cls;
896
- @ if( cls.indexOf("line")==0 ) elem.className += " v";
897
- @ canvasDiv.appendChild(elem);
898
- @ var k = cls.replace(/\s/g, "_");
899
- @ var r = elem.getBoundingClientRect();
900
- @ var w = Math.round(r.right - r.left);
901
- @ var h = Math.round(r.bottom - r.top);
902
- @ elems[k] = {w: w, h: h, cls: cls};
903
- @ }
904
- @ node = elems.node;
905
- @ arrow = elems.arrow_u;
906
- @ arrowSmall = elems.arrow_u_sm;
907
- @ line = elems.line;
908
- @ mArrow = elems.arrow_merge_r;
909
- @ mLine = elems.line_merge;
910
- @ wArrow = elems.arrow_warp;
911
- @ wLine = elems.line_warp;
912
- @
913
- @ var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1);
914
- if( iRailPitch ){
915
- @ railPitch = %d(iRailPitch);
916
- }else{
917
- @ railPitch = elems.rail.w;
918
- @ railPitch -= Math.floor((nrail-1)*(railPitch-minRailPitch)/21);
919
- }
920
- @ railPitch = Math.max(railPitch, minRailPitch);
921
- @
922
- if( PB("nomo") ){
923
- @ mergeOffset = 0;
924
- }else{
925
- @ mergeOffset = railPitch-minRailPitch-mLine.w;
926
- @ mergeOffset = Math.min(mergeOffset, elems.mergeoffset.w);
927
- @ mergeOffset = mergeOffset>0 ? mergeOffset + line.w/2 : 0;
928
- }
929
- @
930
- @ var canvasWidth = (nrail-1)*railPitch + node.w;
931
- @ canvasDiv.style.width = canvasWidth + "px";
932
- @ canvasDiv.style.position = "relative";
933
- @ }
934
- @ function drawBox(cls,color,x0,y0,x1,y1){
935
- @ var n = document.createElement("div");
936
- @ x0 = Math.floor(x0);
937
- @ y0 = Math.floor(y0);
938
- @ x1 = x1 || x1===0 ? Math.floor(x1) : x0;
939
- @ y1 = y1 || y1===0 ? Math.floor(y1) : y0;
940
- @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
941
- @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
942
- @ var w = x1-x0;
943
- @ var h = y1-y0;
944
- @ n.style.position = "absolute";
945
- @ n.style.left = x0+"px";
946
- @ n.style.top = y0+"px";
947
- @ if( w ) n.style.width = w+"px";
948
- @ if( h ) n.style.height = h+"px";
949
- @ if( color ) n.style.backgroundColor = color;
950
- @ n.className = "tl-"+cls;
951
- @ canvasDiv.appendChild(n);
952
- @ return n;
953
- @ }
954
- @ function absoluteY(obj){
955
- @ var top = 0;
956
- @ if( obj.offsetParent ){
957
- @ do{
958
- @ top += obj.offsetTop;
959
- @ }while( obj = obj.offsetParent );
960
- @ }
961
- @ return top;
962
- @ }
963
- @ function miLineY(p){
964
- @ return p.y + node.h - mLine.w - 1;
965
- @ }
966
- @ function drawLine(elem,color,x0,y0,x1,y1){
967
- @ var cls = elem.cls + " ";
968
- @ if( x1===null ){
969
- @ x1 = x0+elem.w;
970
- @ cls += "v";
971
- @ }else{
972
- @ y1 = y0+elem.w;
973
- @ cls += "h";
974
- @ }
975
- @ drawBox(cls,color,x0,y0,x1,y1);
976
- @ }
977
- @ function drawUpArrow(from,to,color){
978
- @ var y = to.y + node.h;
979
- @ var arrowSpace = from.y - y + (!from.id || from.r!=to.r ? node.h/2 : 0);
980
- @ var arw = arrowSpace < arrow.h*1.5 ? arrowSmall : arrow;
981
- @ var x = to.x + (node.w-line.w)/2;
982
- @ var y0 = from.y + node.h/2;
983
- @ var y1 = Math.ceil(to.y + node.h + arw.h/2);
984
- @ drawLine(line,color,x,y0,null,y1);
985
- @ x = to.x + (node.w-arw.w)/2;
986
- @ var n = drawBox(arw.cls,null,x,y);
987
- @ n.style.borderBottomColor = color;
988
- @ }
989
- @ function drawMergeLine(x0,y0,x1,y1){
990
- @ drawLine(mLine,null,x0,y0,x1,y1);
991
- @ }
992
- @ function drawMergeArrow(p,rail){
993
- @ var x0 = rail*railPitch + node.w/2;
994
- @ if( rail in mergeLines ){
995
- @ x0 += mergeLines[rail];
996
- @ if( p.r<rail ) x0 += mLine.w;
997
- @ }else{
998
- @ x0 += (p.r<rail ? -1 : 1)*line.w/2;
999
- @ }
1000
- @ var x1 = mArrow.w ? mArrow.w/2 : -node.w/2;
1001
- @ x1 = p.x + (p.r<rail ? node.w + Math.ceil(x1) : -x1);
1002
- @ var y = miLineY(p);
1003
- @ drawMergeLine(x0,y,x1,null);
1004
- @ var x = p.x + (p.r<rail ? node.w : -mArrow.w);
1005
- @ var cls = "arrow merge " + (p.r<rail ? "l" : "r");
1006
- @ drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
1007
- @ }
1008
- @ function drawNode(p, btm){
1009
- @ if( p.u>0 ) drawUpArrow(p,rowinfo[p.u-%d(iTopRow)],p.fg);
1010
- @ var cls = node.cls;
1011
- @ if( p.mi.length ) cls += " merge";
1012
- @ if( p.f&1 ) cls += " leaf";
1013
- @ var n = drawBox(cls,p.bg,p.x,p.y);
1014
- @ n.id = "tln"+p.id;
1015
- @ n.onclick = clickOnNode;
1016
- @ n.style.zIndex = 10;
1017
- if( !omitDescenders ){
1018
- @ if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
1019
- @ if( p.d ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
1020
- }
1021
- @ if( p.mo>=0 ){
1022
- @ var x0 = p.x + node.w/2;
1023
- @ var x1 = p.mo*railPitch + node.w/2;
1024
- @ var u = rowinfo[p.mu-%d(iTopRow)];
1025
- @ var y1 = miLineY(u);
1026
- @ if( p.u<0 || p.mo!=p.r ){
1027
- @ x1 += mergeLines[p.mo] = -mLine.w/2;
1028
- @ var y0 = p.y+2;
1029
- @ if( p.r!=p.mo ) drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
1030
- @ drawMergeLine(x1,y0+mLine.w,null,y1);
1031
- @ }else if( mergeOffset ){
1032
- @ mergeLines[p.mo] = u.r<p.r ? -mergeOffset-mLine.w : mergeOffset;
1033
- @ x1 += mergeLines[p.mo];
1034
- @ drawMergeLine(x1,p.y+node.h/2,null,y1);
1035
- @ }else{
1036
- @ delete mergeLines[p.mo];
1037
- @ }
1038
- @ }
1039
- @ for( var i=0; i<p.au.length; i+=2 ){
1040
- @ var rail = p.au[i];
1041
- @ var x0 = p.x + node.w/2;
1042
- @ var x1 = rail*railPitch + (node.w-line.w)/2;
1043
- @ if( x0<x1 ){
1044
- @ x0 = Math.ceil(x0);
1045
- @ x1 += line.w;
1046
- @ }
1047
- @ var y0 = p.y + (node.h-line.w)/2;
1048
- @ var u = rowinfo[p.au[i+1]-%d(iTopRow)];
1049
- @ if( u.id<p.id ){
1050
- @ drawLine(line,u.fg,x0,y0,x1,null);
1051
- @ drawUpArrow(p,u,u.fg);
1052
- @ }else{
1053
- @ var y1 = u.y + (node.h-line.w)/2;
1054
- @ drawLine(wLine,u.fg,x0,y0,x1,null);
1055
- @ drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w);
1056
- @ drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null);
1057
- @ var x = u.x-wArrow.w;
1058
- @ var y = u.y+(node.h-wArrow.h)/2;
1059
- @ var n = drawBox(wArrow.cls,null,x,y);
1060
- @ if( u.fg ) n.style.borderLeftColor = u.fg;
1061
- @ }
1062
- @ }
1063
- @ for( var i=0; i<p.mi.length; i++ ){
1064
- @ var rail = p.mi[i];
1065
- @ if( rail<0 ){
1066
- @ rail = -rail;
1067
- @ mergeLines[rail] = -mLine.w/2;
1068
- @ var x = rail*railPitch + (node.w-mLine.w)/2;
1069
- @ drawMergeLine(x,miLineY(p),null,btm);
1070
- @ }
1071
- @ drawMergeArrow(p,rail);
1072
- @ }
1073
- @ }
1074
- @ var mergeLines;
1075
- @ function renderGraph(){
1076
- @ mergeLines = {};
1077
- @ canvasDiv.innerHTML = "";
1078
- @ var canvasY = absoluteY(canvasDiv);
1079
- @ for( var i=0; i<rowinfo.length; i++ ){
1080
- @ rowinfo[i].y = absoluteY(gebi("m"+rowinfo[i].id)) - canvasY;
1081
- @ rowinfo[i].x = rowinfo[i].r*railPitch;
1082
- @ }
1083
- @ var tlBtm = document.querySelector(".timelineBottom");
1084
- @ if( tlBtm.offsetHeight<node.h ){
1085
- @ tlBtm.style.height = node.h + "px";
1086
- @ }
1087
- @ var btm = absoluteY(tlBtm) - canvasY + tlBtm.offsetHeight;
1088
- @ for( var i=rowinfo.length-1; i>=0; i-- ){
1089
- @ drawNode(rowinfo[i], btm);
1090
- @ }
1091
- @ }
1092
- @ var selRow;
1093
- @ function clickOnNode(){
1094
- @ var p = rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-%d(iTopRow)];
1095
- @ if( !selRow ){
1096
- @ selRow = p;
1097
- @ this.className += " sel";
1098
- @ canvasDiv.className += " sel";
1099
- @ }else if( selRow==p ){
1100
- @ selRow = null;
1101
- @ this.className = this.className.replace(" sel", "");
1102
- @ canvasDiv.className = canvasDiv.className.replace(" sel", "");
1103
- @ }else{
1104
- if( fileDiff ){
1105
- @ location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h+"&sbs=1";
1106
- }else{
1107
- if( db_get_boolean("show-version-diffs", 0)==0 ){
1108
- @ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=0";
1109
- }else{
1110
- @ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=1";
1111
- }
1112
- }
1113
- @ }
1114
- @ }
1115
- @ function changeDisplay(selector,value){
1116
- @ var x = document.getElementsByClassName(selector);
1117
- @ var n = x.length;
1118
- @ for(var i=0; i<n; i++) {x[i].style.display = value;}
1119
- @ }
1120
- @ function declutter(){
1121
- @ changeDisplay('clutter','none');
1122
- @ changeDisplay('anticlutter','inline');
1123
- @ checkHeight();
1124
- @ }
1125
- @ function reclutter(){
1126
- @ changeDisplay('clutter','inline');
1127
- @ changeDisplay('anticlutter','none');
1128
- @ checkHeight();
1129
- @ }
1130
- @ function changeDisplayById(id,value){
1131
- @ var x = document.getElementById(id);
1132
- @ if(x) x.style.display=value;
1133
- @ }
1134
- @ function toggleDetail(id){
1135
- @ var x = gebi("detail-"+id);
1136
- @ if( x.style.display=="inline" ){
1137
- @ x.style.display="none";
1138
- @ changeDisplayById("ellipsis-"+id,"inline");
1139
- @ changeDisplayById("links-"+id,"none");
1140
- @ }else{
1141
- @ x.style.display="inline";
1142
- @ changeDisplayById("ellipsis-"+id,"none");
1143
- @ changeDisplayById("links-"+id,"inline");
1144
- @ }
1145
- @ checkHeight();
1146
- @ }
1147
- @ function scrollToSelected(){
1148
- @ var x = document.getElementsByClassName('timelineSelected');
1149
- @ if(x[0]){
1150
- @ var h = window.innerHeight;
1151
- @ var y = absoluteY(x[0]) - h/2;
1152
- @ if( y>0 ) window.scrollTo(0, y);
1153
- @ }
1154
- @ }
1155
- @ var lastRow = gebi("m"+rowinfo[rowinfo.length-1].id);
1156
- @ var lastY = 0;
1157
- @ function checkHeight(){
1158
- @ var h = absoluteY(lastRow);
1159
- @ if( h!=lastY ){
1160
- @ renderGraph();
1161
- @ lastY = h;
1162
- @ }
1163
- @ setTimeout(checkHeight, 1000);
1164
- @ }
1165
- @ initGraph();
1166
- @ checkHeight();
1167
- @ scrollToSelected();
1168
- @ </script>
865
+ cgi_printf("],\"h\":\"%!S\"}%s",
866
+ pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
867
+ }
868
+ @ }</script>
869
+ style_load_one_js_file("graph.js");
870
+ graph_free(pGraph);
1169871
}
1170872
}
1171873
1172874
/*
1173875
** Create a temporary table suitable for storing timeline data.
1174876
--- src/timeline.c
+++ src/timeline.c
@@ -255,11 +255,10 @@
255 int vid = 0; /* Current checkout version */
256 int dateFormat = 0; /* 0: HH:MM (default) */
257 int bCommentGitStyle = 0; /* Only show comments through first blank line */
258 const char *zStyle; /* Sub-name for classes for the style */
259 const char *zDateFmt;
260 int iTableId = timeline_tableid();
261
262 if( fossil_strcmp(g.zIpAddr, "127.0.0.1")==0 && db_open_local(0) ){
263 vid = db_lget_int("checkout", 0);
264 }
265 zPrevDate[0] = 0;
@@ -286,11 +285,11 @@
286 db_static_prepare(&qbranch,
287 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
288 TAG_BRANCH
289 );
290
291 @ <table id="timelineTable%d(iTableId)" class="timelineTable">
292 blob_zero(&comment);
293 while( db_step(pQuery)==SQLITE_ROW ){
294 int rid = db_column_int(pQuery, 0);
295 const char *zUuid = db_column_text(pQuery, 1);
296 int isLeaf = db_column_int(pQuery, 5);
@@ -723,11 +722,11 @@
723 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
724 }
725 }
726 @ </table>
727 if( fchngQueryInit ) db_finalize(&fchngQuery);
728 timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, iTableId, 0);
729 }
730
731 /*
732 ** Change the RGB background color given in the argument in a foreground
733 ** color with the same hue.
@@ -764,11 +763,10 @@
764 ** graph.
765 */
766 void timeline_output_graph_javascript(
767 GraphContext *pGraph, /* The graph to be displayed */
768 int omitDescenders, /* True to omit descenders */
769 int iTableId, /* Identifier for the timelineTable */
770 int fileDiff /* True for file diff. False for check-in diff */
771 ){
772 if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
773 GraphRow *pRow;
774 int i;
@@ -781,25 +779,25 @@
781
782 iRailPitch = atoi(PD("railpitch","0"));
783 showArrowheads = skin_detail_boolean("timeline-arrowheads");
784 circleNodes = skin_detail_boolean("timeline-circle-nodes");
785 colorGraph = skin_detail_boolean("timeline-color-graph-lines");
786
787 @ <script>
788 @ "use strict";
789 @ var css = "";
790 if( circleNodes ){
791 @ css += ".tl-node, .tl-node:after { border-radius: 50%%; }";
792 }
793 if( !showArrowheads ){
794 @ css += ".tl-arrow.u { display: none; }";
795 }
796 @ if( css!=="" ){
797 @ var style = document.createElement("style");
798 @ style.textContent = css;
799 @ document.querySelector("head").appendChild(style);
800 @ }
801 /* the rowinfo[] array contains all the information needed to generate
802 ** the graph. Each entry contains information for a single row:
803 **
804 ** id: The id of the <div> element for the row. This is an integer.
805 ** to get an actual id, prepend "m" to the integer. The top node
@@ -827,24 +825,20 @@
827 ** negative, then the rail position is the absolute value of mi[]
828 ** and a thin merge-arrow descender is drawn to the bottom of
829 ** the screen.
830 ** h: The artifact hash of the object being graphed
831 */
832 iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0;
833 cgi_printf("var rowinfo = [\n");
834 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
835 cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
836 pRow->idx, /* id */
837 pRow->zBgClr, /* bg */
838 pRow->iRail, /* r */
839 pRow->bDescender, /* d */
840 pRow->mergeOut, /* mo */
841 pRow->mergeUpto, /* mu */
842 pRow->aiRiser[pRow->iRail], /* u */
843 pRow->isLeaf ? 1 : 0 /* f */
844 );
845 /* au */
846 cSep = '[';
847 for(i=0; i<GR_MAX_RAIL; i++){
848 if( i==pRow->iRail ) continue;
849 if( pRow->aiRiser[i]>0 ){
850 cgi_printf("%c%d,%d", cSep, i, pRow->aiRiser[i]);
@@ -852,14 +846,14 @@
852 }
853 }
854 if( cSep=='[' ) cgi_printf("[");
855 cgi_printf("],");
856 if( colorGraph && pRow->zBgClr[0]=='#' ){
857 cgi_printf("fg:\"%s\",", bg_to_fg(pRow->zBgClr));
858 }
859 /* mi */
860 cgi_printf("mi:");
861 cSep = '[';
862 for(i=0; i<GR_MAX_RAIL; i++){
863 if( pRow->mergeIn[i] ){
864 int mi = i;
865 if( (pRow->mergeDown >> i) & 1 ) mi = -mi;
@@ -866,308 +860,16 @@
866 cgi_printf("%c%d", cSep, mi);
867 cSep = ',';
868 }
869 }
870 if( cSep=='[' ) cgi_printf("[");
871 cgi_printf("],h:\"%!S\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n");
872 }
873 cgi_printf("var nrail = %d\n", pGraph->mxRail+1);
874 graph_free(pGraph);
875 @ var canvasDiv;
876 @ var railPitch;
877 @ var mergeOffset;
878 @ var node, arrow, arrowSmall, line, mArrow, mLine, wArrow, wLine;
879 @ function initGraph(){
880 @ var parent = gebi("timelineTable%d(iTableId)").rows[0].cells[1];
881 @ parent.style.verticalAlign = "top";
882 @ canvasDiv = document.createElement("div");
883 @ canvasDiv.className = "tl-canvas";
884 @ canvasDiv.style.position = "absolute";
885 @ parent.appendChild(canvasDiv);
886 @
887 @ var elems = {};
888 @ var elemClasses = [
889 @ "rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line",
890 @ "arrow merge r", "line merge", "arrow warp", "line warp"
891 @ ];
892 @ for( var i=0; i<elemClasses.length; i++ ){
893 @ var cls = elemClasses[i];
894 @ var elem = document.createElement("div");
895 @ elem.className = "tl-" + cls;
896 @ if( cls.indexOf("line")==0 ) elem.className += " v";
897 @ canvasDiv.appendChild(elem);
898 @ var k = cls.replace(/\s/g, "_");
899 @ var r = elem.getBoundingClientRect();
900 @ var w = Math.round(r.right - r.left);
901 @ var h = Math.round(r.bottom - r.top);
902 @ elems[k] = {w: w, h: h, cls: cls};
903 @ }
904 @ node = elems.node;
905 @ arrow = elems.arrow_u;
906 @ arrowSmall = elems.arrow_u_sm;
907 @ line = elems.line;
908 @ mArrow = elems.arrow_merge_r;
909 @ mLine = elems.line_merge;
910 @ wArrow = elems.arrow_warp;
911 @ wLine = elems.line_warp;
912 @
913 @ var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1);
914 if( iRailPitch ){
915 @ railPitch = %d(iRailPitch);
916 }else{
917 @ railPitch = elems.rail.w;
918 @ railPitch -= Math.floor((nrail-1)*(railPitch-minRailPitch)/21);
919 }
920 @ railPitch = Math.max(railPitch, minRailPitch);
921 @
922 if( PB("nomo") ){
923 @ mergeOffset = 0;
924 }else{
925 @ mergeOffset = railPitch-minRailPitch-mLine.w;
926 @ mergeOffset = Math.min(mergeOffset, elems.mergeoffset.w);
927 @ mergeOffset = mergeOffset>0 ? mergeOffset + line.w/2 : 0;
928 }
929 @
930 @ var canvasWidth = (nrail-1)*railPitch + node.w;
931 @ canvasDiv.style.width = canvasWidth + "px";
932 @ canvasDiv.style.position = "relative";
933 @ }
934 @ function drawBox(cls,color,x0,y0,x1,y1){
935 @ var n = document.createElement("div");
936 @ x0 = Math.floor(x0);
937 @ y0 = Math.floor(y0);
938 @ x1 = x1 || x1===0 ? Math.floor(x1) : x0;
939 @ y1 = y1 || y1===0 ? Math.floor(y1) : y0;
940 @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
941 @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
942 @ var w = x1-x0;
943 @ var h = y1-y0;
944 @ n.style.position = "absolute";
945 @ n.style.left = x0+"px";
946 @ n.style.top = y0+"px";
947 @ if( w ) n.style.width = w+"px";
948 @ if( h ) n.style.height = h+"px";
949 @ if( color ) n.style.backgroundColor = color;
950 @ n.className = "tl-"+cls;
951 @ canvasDiv.appendChild(n);
952 @ return n;
953 @ }
954 @ function absoluteY(obj){
955 @ var top = 0;
956 @ if( obj.offsetParent ){
957 @ do{
958 @ top += obj.offsetTop;
959 @ }while( obj = obj.offsetParent );
960 @ }
961 @ return top;
962 @ }
963 @ function miLineY(p){
964 @ return p.y + node.h - mLine.w - 1;
965 @ }
966 @ function drawLine(elem,color,x0,y0,x1,y1){
967 @ var cls = elem.cls + " ";
968 @ if( x1===null ){
969 @ x1 = x0+elem.w;
970 @ cls += "v";
971 @ }else{
972 @ y1 = y0+elem.w;
973 @ cls += "h";
974 @ }
975 @ drawBox(cls,color,x0,y0,x1,y1);
976 @ }
977 @ function drawUpArrow(from,to,color){
978 @ var y = to.y + node.h;
979 @ var arrowSpace = from.y - y + (!from.id || from.r!=to.r ? node.h/2 : 0);
980 @ var arw = arrowSpace < arrow.h*1.5 ? arrowSmall : arrow;
981 @ var x = to.x + (node.w-line.w)/2;
982 @ var y0 = from.y + node.h/2;
983 @ var y1 = Math.ceil(to.y + node.h + arw.h/2);
984 @ drawLine(line,color,x,y0,null,y1);
985 @ x = to.x + (node.w-arw.w)/2;
986 @ var n = drawBox(arw.cls,null,x,y);
987 @ n.style.borderBottomColor = color;
988 @ }
989 @ function drawMergeLine(x0,y0,x1,y1){
990 @ drawLine(mLine,null,x0,y0,x1,y1);
991 @ }
992 @ function drawMergeArrow(p,rail){
993 @ var x0 = rail*railPitch + node.w/2;
994 @ if( rail in mergeLines ){
995 @ x0 += mergeLines[rail];
996 @ if( p.r<rail ) x0 += mLine.w;
997 @ }else{
998 @ x0 += (p.r<rail ? -1 : 1)*line.w/2;
999 @ }
1000 @ var x1 = mArrow.w ? mArrow.w/2 : -node.w/2;
1001 @ x1 = p.x + (p.r<rail ? node.w + Math.ceil(x1) : -x1);
1002 @ var y = miLineY(p);
1003 @ drawMergeLine(x0,y,x1,null);
1004 @ var x = p.x + (p.r<rail ? node.w : -mArrow.w);
1005 @ var cls = "arrow merge " + (p.r<rail ? "l" : "r");
1006 @ drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
1007 @ }
1008 @ function drawNode(p, btm){
1009 @ if( p.u>0 ) drawUpArrow(p,rowinfo[p.u-%d(iTopRow)],p.fg);
1010 @ var cls = node.cls;
1011 @ if( p.mi.length ) cls += " merge";
1012 @ if( p.f&1 ) cls += " leaf";
1013 @ var n = drawBox(cls,p.bg,p.x,p.y);
1014 @ n.id = "tln"+p.id;
1015 @ n.onclick = clickOnNode;
1016 @ n.style.zIndex = 10;
1017 if( !omitDescenders ){
1018 @ if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
1019 @ if( p.d ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
1020 }
1021 @ if( p.mo>=0 ){
1022 @ var x0 = p.x + node.w/2;
1023 @ var x1 = p.mo*railPitch + node.w/2;
1024 @ var u = rowinfo[p.mu-%d(iTopRow)];
1025 @ var y1 = miLineY(u);
1026 @ if( p.u<0 || p.mo!=p.r ){
1027 @ x1 += mergeLines[p.mo] = -mLine.w/2;
1028 @ var y0 = p.y+2;
1029 @ if( p.r!=p.mo ) drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
1030 @ drawMergeLine(x1,y0+mLine.w,null,y1);
1031 @ }else if( mergeOffset ){
1032 @ mergeLines[p.mo] = u.r<p.r ? -mergeOffset-mLine.w : mergeOffset;
1033 @ x1 += mergeLines[p.mo];
1034 @ drawMergeLine(x1,p.y+node.h/2,null,y1);
1035 @ }else{
1036 @ delete mergeLines[p.mo];
1037 @ }
1038 @ }
1039 @ for( var i=0; i<p.au.length; i+=2 ){
1040 @ var rail = p.au[i];
1041 @ var x0 = p.x + node.w/2;
1042 @ var x1 = rail*railPitch + (node.w-line.w)/2;
1043 @ if( x0<x1 ){
1044 @ x0 = Math.ceil(x0);
1045 @ x1 += line.w;
1046 @ }
1047 @ var y0 = p.y + (node.h-line.w)/2;
1048 @ var u = rowinfo[p.au[i+1]-%d(iTopRow)];
1049 @ if( u.id<p.id ){
1050 @ drawLine(line,u.fg,x0,y0,x1,null);
1051 @ drawUpArrow(p,u,u.fg);
1052 @ }else{
1053 @ var y1 = u.y + (node.h-line.w)/2;
1054 @ drawLine(wLine,u.fg,x0,y0,x1,null);
1055 @ drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w);
1056 @ drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null);
1057 @ var x = u.x-wArrow.w;
1058 @ var y = u.y+(node.h-wArrow.h)/2;
1059 @ var n = drawBox(wArrow.cls,null,x,y);
1060 @ if( u.fg ) n.style.borderLeftColor = u.fg;
1061 @ }
1062 @ }
1063 @ for( var i=0; i<p.mi.length; i++ ){
1064 @ var rail = p.mi[i];
1065 @ if( rail<0 ){
1066 @ rail = -rail;
1067 @ mergeLines[rail] = -mLine.w/2;
1068 @ var x = rail*railPitch + (node.w-mLine.w)/2;
1069 @ drawMergeLine(x,miLineY(p),null,btm);
1070 @ }
1071 @ drawMergeArrow(p,rail);
1072 @ }
1073 @ }
1074 @ var mergeLines;
1075 @ function renderGraph(){
1076 @ mergeLines = {};
1077 @ canvasDiv.innerHTML = "";
1078 @ var canvasY = absoluteY(canvasDiv);
1079 @ for( var i=0; i<rowinfo.length; i++ ){
1080 @ rowinfo[i].y = absoluteY(gebi("m"+rowinfo[i].id)) - canvasY;
1081 @ rowinfo[i].x = rowinfo[i].r*railPitch;
1082 @ }
1083 @ var tlBtm = document.querySelector(".timelineBottom");
1084 @ if( tlBtm.offsetHeight<node.h ){
1085 @ tlBtm.style.height = node.h + "px";
1086 @ }
1087 @ var btm = absoluteY(tlBtm) - canvasY + tlBtm.offsetHeight;
1088 @ for( var i=rowinfo.length-1; i>=0; i-- ){
1089 @ drawNode(rowinfo[i], btm);
1090 @ }
1091 @ }
1092 @ var selRow;
1093 @ function clickOnNode(){
1094 @ var p = rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-%d(iTopRow)];
1095 @ if( !selRow ){
1096 @ selRow = p;
1097 @ this.className += " sel";
1098 @ canvasDiv.className += " sel";
1099 @ }else if( selRow==p ){
1100 @ selRow = null;
1101 @ this.className = this.className.replace(" sel", "");
1102 @ canvasDiv.className = canvasDiv.className.replace(" sel", "");
1103 @ }else{
1104 if( fileDiff ){
1105 @ location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h+"&sbs=1";
1106 }else{
1107 if( db_get_boolean("show-version-diffs", 0)==0 ){
1108 @ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=0";
1109 }else{
1110 @ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=1";
1111 }
1112 }
1113 @ }
1114 @ }
1115 @ function changeDisplay(selector,value){
1116 @ var x = document.getElementsByClassName(selector);
1117 @ var n = x.length;
1118 @ for(var i=0; i<n; i++) {x[i].style.display = value;}
1119 @ }
1120 @ function declutter(){
1121 @ changeDisplay('clutter','none');
1122 @ changeDisplay('anticlutter','inline');
1123 @ checkHeight();
1124 @ }
1125 @ function reclutter(){
1126 @ changeDisplay('clutter','inline');
1127 @ changeDisplay('anticlutter','none');
1128 @ checkHeight();
1129 @ }
1130 @ function changeDisplayById(id,value){
1131 @ var x = document.getElementById(id);
1132 @ if(x) x.style.display=value;
1133 @ }
1134 @ function toggleDetail(id){
1135 @ var x = gebi("detail-"+id);
1136 @ if( x.style.display=="inline" ){
1137 @ x.style.display="none";
1138 @ changeDisplayById("ellipsis-"+id,"inline");
1139 @ changeDisplayById("links-"+id,"none");
1140 @ }else{
1141 @ x.style.display="inline";
1142 @ changeDisplayById("ellipsis-"+id,"none");
1143 @ changeDisplayById("links-"+id,"inline");
1144 @ }
1145 @ checkHeight();
1146 @ }
1147 @ function scrollToSelected(){
1148 @ var x = document.getElementsByClassName('timelineSelected');
1149 @ if(x[0]){
1150 @ var h = window.innerHeight;
1151 @ var y = absoluteY(x[0]) - h/2;
1152 @ if( y>0 ) window.scrollTo(0, y);
1153 @ }
1154 @ }
1155 @ var lastRow = gebi("m"+rowinfo[rowinfo.length-1].id);
1156 @ var lastY = 0;
1157 @ function checkHeight(){
1158 @ var h = absoluteY(lastRow);
1159 @ if( h!=lastY ){
1160 @ renderGraph();
1161 @ lastY = h;
1162 @ }
1163 @ setTimeout(checkHeight, 1000);
1164 @ }
1165 @ initGraph();
1166 @ checkHeight();
1167 @ scrollToSelected();
1168 @ </script>
1169 }
1170 }
1171
1172 /*
1173 ** Create a temporary table suitable for storing timeline data.
1174
--- src/timeline.c
+++ src/timeline.c
@@ -255,11 +255,10 @@
255 int vid = 0; /* Current checkout version */
256 int dateFormat = 0; /* 0: HH:MM (default) */
257 int bCommentGitStyle = 0; /* Only show comments through first blank line */
258 const char *zStyle; /* Sub-name for classes for the style */
259 const char *zDateFmt;
 
260
261 if( fossil_strcmp(g.zIpAddr, "127.0.0.1")==0 && db_open_local(0) ){
262 vid = db_lget_int("checkout", 0);
263 }
264 zPrevDate[0] = 0;
@@ -286,11 +285,11 @@
285 db_static_prepare(&qbranch,
286 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
287 TAG_BRANCH
288 );
289
290 @ <table id="timelineTable" class="timelineTable">
291 blob_zero(&comment);
292 while( db_step(pQuery)==SQLITE_ROW ){
293 int rid = db_column_int(pQuery, 0);
294 const char *zUuid = db_column_text(pQuery, 1);
295 int isLeaf = db_column_int(pQuery, 5);
@@ -723,11 +722,11 @@
722 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
723 }
724 }
725 @ </table>
726 if( fchngQueryInit ) db_finalize(&fchngQuery);
727 timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);
728 }
729
730 /*
731 ** Change the RGB background color given in the argument in a foreground
732 ** color with the same hue.
@@ -764,11 +763,10 @@
763 ** graph.
764 */
765 void timeline_output_graph_javascript(
766 GraphContext *pGraph, /* The graph to be displayed */
767 int omitDescenders, /* True to omit descenders */
 
768 int fileDiff /* True for file diff. False for check-in diff */
769 ){
770 if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
771 GraphRow *pRow;
772 int i;
@@ -781,25 +779,25 @@
779
780 iRailPitch = atoi(PD("railpitch","0"));
781 showArrowheads = skin_detail_boolean("timeline-arrowheads");
782 circleNodes = skin_detail_boolean("timeline-circle-nodes");
783 colorGraph = skin_detail_boolean("timeline-color-graph-lines");
784 iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0;
785
786 @ <script id='timeline-data' type='application/json'>{
787 @ "circleNodes": %d(circleNodes),
788 @ "showArrowheads": %d(showArrowheads),
789 @ "iRailPitch": %d(iRailPitch),
790 @ "colorGraph": %d(colorGraph),
791 @ "nomo": %d(PB("nomo")),
792 @ "iTopRow": %d(iTopRow),
793 @ "omitDescenders": %d(omitDescenders),
794 @ "fileDiff": %d(fileDiff),
795 @ "nrail": %d(pGraph->mxRail+1),
796 @ "baseUrl": "%R",
797 @ "rowinfo": [
798
799 /* the rowinfo[] array contains all the information needed to generate
800 ** the graph. Each entry contains information for a single row:
801 **
802 ** id: The id of the <div> element for the row. This is an integer.
803 ** to get an actual id, prepend "m" to the integer. The top node
@@ -827,24 +825,20 @@
825 ** negative, then the rail position is the absolute value of mi[]
826 ** and a thin merge-arrow descender is drawn to the bottom of
827 ** the screen.
828 ** h: The artifact hash of the object being graphed
829 */
 
 
830 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
831 cgi_printf("{\"id\":%d,", pRow->idx);
832 cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
833 cgi_printf("\"r\":%d,", pRow->iRail);
834 cgi_printf("\"d\":%d,", pRow->bDescender);
835 cgi_printf("\"mo\":%d,", pRow->mergeOut);
836 cgi_printf("\"mu\":%d,", pRow->mergeUpto);
837 cgi_printf("\"u\":%d,", pRow->aiRiser[pRow->iRail]);
838 cgi_printf("\"f\":%d,", pRow->isLeaf ? 1 : 0);
839 cgi_printf("\"au\":");
 
 
840 cSep = '[';
841 for(i=0; i<GR_MAX_RAIL; i++){
842 if( i==pRow->iRail ) continue;
843 if( pRow->aiRiser[i]>0 ){
844 cgi_printf("%c%d,%d", cSep, i, pRow->aiRiser[i]);
@@ -852,14 +846,14 @@
846 }
847 }
848 if( cSep=='[' ) cgi_printf("[");
849 cgi_printf("],");
850 if( colorGraph && pRow->zBgClr[0]=='#' ){
851 cgi_printf("\"fg\":\"%s\",", bg_to_fg(pRow->zBgClr));
852 }
853 /* mi */
854 cgi_printf("\"mi\":");
855 cSep = '[';
856 for(i=0; i<GR_MAX_RAIL; i++){
857 if( pRow->mergeIn[i] ){
858 int mi = i;
859 if( (pRow->mergeDown >> i) & 1 ) mi = -mi;
@@ -866,308 +860,16 @@
860 cgi_printf("%c%d", cSep, mi);
861 cSep = ',';
862 }
863 }
864 if( cSep=='[' ) cgi_printf("[");
865 cgi_printf("],\"h\":\"%!S\"}%s",
866 pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
867 }
868 @ }</script>
869 style_load_one_js_file("graph.js");
870 graph_free(pGraph);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
871 }
872 }
873
874 /*
875 ** Create a temporary table suitable for storing timeline data.
876
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -603,10 +603,11 @@
603603
$(SRCDIR)/../skins/xekri/css.txt \
604604
$(SRCDIR)/../skins/xekri/details.txt \
605605
$(SRCDIR)/../skins/xekri/footer.txt \
606606
$(SRCDIR)/../skins/xekri/header.txt \
607607
$(SRCDIR)/diff.tcl \
608
+ $(SRCDIR)/graph.js \
608609
$(SRCDIR)/href.js \
609610
$(SRCDIR)/markdown.md \
610611
$(SRCDIR)/wiki.wiki
611612
612613
TRANS_SRC = \
613614
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -603,10 +603,11 @@
603 $(SRCDIR)/../skins/xekri/css.txt \
604 $(SRCDIR)/../skins/xekri/details.txt \
605 $(SRCDIR)/../skins/xekri/footer.txt \
606 $(SRCDIR)/../skins/xekri/header.txt \
607 $(SRCDIR)/diff.tcl \
 
608 $(SRCDIR)/href.js \
609 $(SRCDIR)/markdown.md \
610 $(SRCDIR)/wiki.wiki
611
612 TRANS_SRC = \
613
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -603,10 +603,11 @@
603 $(SRCDIR)/../skins/xekri/css.txt \
604 $(SRCDIR)/../skins/xekri/details.txt \
605 $(SRCDIR)/../skins/xekri/footer.txt \
606 $(SRCDIR)/../skins/xekri/header.txt \
607 $(SRCDIR)/diff.tcl \
608 $(SRCDIR)/graph.js \
609 $(SRCDIR)/href.js \
610 $(SRCDIR)/markdown.md \
611 $(SRCDIR)/wiki.wiki
612
613 TRANS_SRC = \
614
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -529,10 +529,11 @@
529529
$(SRCDIR)\../skins/xekri/css.txt \
530530
$(SRCDIR)\../skins/xekri/details.txt \
531531
$(SRCDIR)\../skins/xekri/footer.txt \
532532
$(SRCDIR)\../skins/xekri/header.txt \
533533
$(SRCDIR)\diff.tcl \
534
+ $(SRCDIR)\graph.js \
534535
$(SRCDIR)\href.js \
535536
$(SRCDIR)\markdown.md \
536537
$(SRCDIR)\wiki.wiki
537538
538539
OBJ = $(OX)\add$O \
539540
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -529,10 +529,11 @@
529 $(SRCDIR)\../skins/xekri/css.txt \
530 $(SRCDIR)\../skins/xekri/details.txt \
531 $(SRCDIR)\../skins/xekri/footer.txt \
532 $(SRCDIR)\../skins/xekri/header.txt \
533 $(SRCDIR)\diff.tcl \
 
534 $(SRCDIR)\href.js \
535 $(SRCDIR)\markdown.md \
536 $(SRCDIR)\wiki.wiki
537
538 OBJ = $(OX)\add$O \
539
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -529,10 +529,11 @@
529 $(SRCDIR)\../skins/xekri/css.txt \
530 $(SRCDIR)\../skins/xekri/details.txt \
531 $(SRCDIR)\../skins/xekri/footer.txt \
532 $(SRCDIR)\../skins/xekri/header.txt \
533 $(SRCDIR)\diff.tcl \
534 $(SRCDIR)\graph.js \
535 $(SRCDIR)\href.js \
536 $(SRCDIR)\markdown.md \
537 $(SRCDIR)\wiki.wiki
538
539 OBJ = $(OX)\add$O \
540

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button