Fossil

Check-in [fb9b3707]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Expose timeline circles, arrowheads, and branch rail colors as settings
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | differentiate-timeline
Files: files | file ages | folders
SHA1:fb9b3707b854c2a59e2fd6650bd1b8b58c526047
User & Date: jmoger 2015-03-19 23:57:21
Context
2015-03-26
13:23
Merge updates from trunk Closed-Leaf check-in: f8832feb user: jmoger tags: differentiate-timeline
2015-03-19
23:57
Expose timeline circles, arrowheads, and branch rail colors as settings check-in: fb9b3707 user: jmoger tags: differentiate-timeline
19:12
Fix off-by-one with top-of-page descending rails check-in: 550f245a user: jmoger tags: differentiate-timeline
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to skins/xekri/css.txt.

647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664


/**************************************
 * Timeline
 */

#canvas {
  color: darkGray;
  border-color: black;
  outline-color: white;
  background-color: #0a0;
}

div.divider {
  color: #ee0;
  font-size: 1.2rem;
  font-weight: bold;
  margin-top: 1rem;







|
|
|
|







647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664


/**************************************
 * Timeline
 */

#canvas {
  color: darkGray;         /* default rail/line color */
  border-color: black;     /* default branch node/box color */
  outline-color: white;    /* leaf indicator color */
  background-color: #0a0;  /* node/box border color */
}

div.divider {
  color: #ee0;
  font-size: 1.2rem;
  font-weight: bold;
  margin-top: 1rem;

Changes to src/configure.c.

91
92
93
94
95
96
97



98
99
100
101
102
103
104
  { "logo-mimetype",          CONFIGSET_SKIN },
  { "logo-image",             CONFIGSET_SKIN },
  { "background-mimetype",    CONFIGSET_SKIN },
  { "background-image",       CONFIGSET_SKIN },
  { "timeline-block-markup",  CONFIGSET_SKIN },
  { "timeline-max-comment",   CONFIGSET_SKIN },
  { "timeline-plaintext",     CONFIGSET_SKIN },



  { "adunit",                 CONFIGSET_SKIN },
  { "adunit-omit-if-admin",   CONFIGSET_SKIN },
  { "adunit-omit-if-user",    CONFIGSET_SKIN },
  { "white-foreground",       CONFIGSET_SKIN },
#ifdef FOSSIL_ENABLE_TH1_DOCS
  { "th1-docs",               CONFIGSET_TH1 },
#endif







>
>
>







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  { "logo-mimetype",          CONFIGSET_SKIN },
  { "logo-image",             CONFIGSET_SKIN },
  { "background-mimetype",    CONFIGSET_SKIN },
  { "background-image",       CONFIGSET_SKIN },
  { "timeline-block-markup",  CONFIGSET_SKIN },
  { "timeline-max-comment",   CONFIGSET_SKIN },
  { "timeline-plaintext",     CONFIGSET_SKIN },
  { "timeline-rail-arrows",   CONFIGSET_SKIN },
  { "timeline-rail-circles",  CONFIGSET_SKIN },
  { "timeline-rail-colors",   CONFIGSET_SKIN },
  { "adunit",                 CONFIGSET_SKIN },
  { "adunit-omit-if-admin",   CONFIGSET_SKIN },
  { "adunit-omit-if-user",    CONFIGSET_SKIN },
  { "white-foreground",       CONFIGSET_SKIN },
#ifdef FOSSIL_ENABLE_TH1_DOCS
  { "th1-docs",               CONFIGSET_TH1 },
#endif

Changes to src/finfo.c.

305
306
307
308
309
310
311



312
313
314
315
316
317
318
...
540
541
542
543
544
545
546
547

548
549
  Blob sql;
  HQuery url;
  GraphContext *pGraph;
  int brBg = P("brbg")!=0;
  int uBg = P("ubg")!=0;
  int fDebug = atoi(PD("debug","0"));
  int fShowId = P("showid")!=0;




  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_header("File History");
  login_anonymous_available();
  url_initialize(&url, "finfo");
  if( brBg ) url_add_parameter(&url, "brbg", 0);
................................................................................
      int w = pGraph->mxRail*pGraph->iRailPitch + 28;
      @ <tr><td></td><td>
      @ <div id="grbtm" style="width:%d(w)px;"></div>
      @     </td><td></td></tr>
    }
  }
  @ </table>
  timeline_output_graph_javascript(pGraph, 0, 1);

  style_footer();
}







>
>
>







 







|
>


305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
...
543
544
545
546
547
548
549
550
551
552
553
  Blob sql;
  HQuery url;
  GraphContext *pGraph;
  int brBg = P("brbg")!=0;
  int uBg = P("ubg")!=0;
  int fDebug = atoi(PD("debug","0"));
  int fShowId = P("showid")!=0;
  int showRailArrows = db_get_boolean("timeline-rail-arrows", 1);
  int showRailCircles = db_get_boolean("timeline-rail-circles", 0);
  int showRailColors = db_get_boolean("timeline-rail-colors", 0);

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_header("File History");
  login_anonymous_available();
  url_initialize(&url, "finfo");
  if( brBg ) url_add_parameter(&url, "brbg", 0);
................................................................................
      int w = pGraph->mxRail*pGraph->iRailPitch + 28;
      @ <tr><td></td><td>
      @ <div id="grbtm" style="width:%d(w)px;"></div>
      @     </td><td></td></tr>
    }
  }
  @ </table>
  timeline_output_graph_javascript(pGraph, 0, 1, showRailArrows,
    showRailCircles, showRailColors);
  style_footer();
}

Changes to src/json_config.c.

61
62
63
64
65
66
67



68
69
70
71
72
73
74
{ "logo-mimetype",          CONFIGSET_SKIN },
{ "logo-image",             CONFIGSET_SKIN },
{ "background-mimetype",    CONFIGSET_SKIN },
{ "background-image",       CONFIGSET_SKIN },
{ "timeline-block-markup",  CONFIGSET_SKIN },
{ "timeline-max-comment",   CONFIGSET_SKIN },
{ "timeline-plaintext",     CONFIGSET_SKIN },



{ "adunit",                 CONFIGSET_SKIN },
{ "adunit-omit-if-admin",   CONFIGSET_SKIN },
{ "adunit-omit-if-user",    CONFIGSET_SKIN },
{ "white-foreground",       CONFIGSET_SKIN },

{ "project-name",           CONFIGSET_PROJ },
{ "short-project-name",     CONFIGSET_PROJ },







>
>
>







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
{ "logo-mimetype",          CONFIGSET_SKIN },
{ "logo-image",             CONFIGSET_SKIN },
{ "background-mimetype",    CONFIGSET_SKIN },
{ "background-image",       CONFIGSET_SKIN },
{ "timeline-block-markup",  CONFIGSET_SKIN },
{ "timeline-max-comment",   CONFIGSET_SKIN },
{ "timeline-plaintext",     CONFIGSET_SKIN },
{ "timeline-rail-arrows",   CONFIGSET_SKIN },
{ "timeline-rail-circles",  CONFIGSET_SKIN },
{ "timeline-rail-colors",   CONFIGSET_SKIN },
{ "adunit",                 CONFIGSET_SKIN },
{ "adunit-omit-if-admin",   CONFIGSET_SKIN },
{ "adunit-omit-if-user",    CONFIGSET_SKIN },
{ "white-foreground",       CONFIGSET_SKIN },

{ "project-name",           CONFIGSET_PROJ },
{ "short-project-name",     CONFIGSET_PROJ },

Changes to src/setup.c.

1337
1338
1339
1340
1341
1342
1343



















1344
1345
1346
1347
1348
1349
1350
  @ <hr />
  onoff_attribute("Plaintext comments on timelines",
                  "timeline-plaintext", "tpt", 0, 0);
  @ <p>In timeline displays, check-in comments are displayed literally,
  @ without any wiki or HTML interpretation.  (Note: Use CSS to change
  @ display formatting features such as fonts and line-wrapping behavior.)</p>




















  @ <hr />
  onoff_attribute("Use Universal Coordinated Time (UTC)",
                  "timeline-utc", "utc", 1, 0);
  @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
  @ Zulu) instead of in local time.  On this server, local time is currently
  tmDiff = db_double(0.0, "SELECT julianday('now')");
  tmDiff = db_double(0.0,







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
  @ <hr />
  onoff_attribute("Plaintext comments on timelines",
                  "timeline-plaintext", "tpt", 0, 0);
  @ <p>In timeline displays, check-in comments are displayed literally,
  @ without any wiki or HTML interpretation.  (Note: Use CSS to change
  @ display formatting features such as fonts and line-wrapping behavior.)</p>

  @ <hr />
  onoff_attribute("Show branch rail arrowheads",
                  "timeline-rail-arrows", "tra", 1, 0);
  @ <p>The rail (line) that connects check-ins may optionally display an
  @ arrowhead indicating the linear progression of changes.</p>

  @ <hr />
  onoff_attribute("Show check-in timeline nodes as circles",
                  "timeline-rail-circles", "trc", 0, 0);
  @ <p>By default, Fossil will display check-nodes as squares. Alternatively you
  @ may prefer to display them as circles.</p>

  @ <hr />
  onoff_attribute("Use branch background color for branch rails",
                  "timeline-rail-colors", "trl", 0, 0);
  @ <p>Fossil will display your branch rails using the color defined for the
  @ branch background.  (Note: Branch background colors may not have enough
  @ contrast to be easily distinguishable from the timeline background.</p>

  @ <hr />
  onoff_attribute("Use Universal Coordinated Time (UTC)",
                  "timeline-utc", "utc", 1, 0);
  @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
  @ Zulu) instead of in local time.  On this server, local time is currently
  tmDiff = db_double(0.0, "SELECT julianday('now')");
  tmDiff = db_double(0.0,

Changes to src/style.c.

1225
1226
1227
1228
1229
1230
1231
1232
1233


1234
1235
1236
1237
1238
1239
1240
    @ /* Use default */
  },
  { "#usetupEditCapability",
    "format for capabilities string, mentioned on the user edit page",
    @ font-weight: bold;
  },
  { "#canvas", "timeline graph node colors",
    @ color: black;
    @ background-color: white;


  },
  { "table.adminLogTable",
    "Class for the /admin_log table",
    @ text-align: left;
  },
  { ".adminLogTable .adminTime",
    "Class for the /admin_log table",







|
|
>
>







1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
    @ /* Use default */
  },
  { "#usetupEditCapability",
    "format for capabilities string, mentioned on the user edit page",
    @ font-weight: bold;
  },
  { "#canvas", "timeline graph node colors",
    @ color: black;             /* default rail/line color */
    @ background-color: white;  /* default branch node/box color */
    @ border-color: black;      /* leaf indicator color */
    @ outline-color: black;     /* node/box border color */
  },
  { "table.adminLogTable",
    "Class for the /admin_log table",
    @ text-align: left;
  },
  { ".adminLogTable .adminTime",
    "Class for the /admin_log table",

Changes to src/timeline.c.

215
216
217
218
219
220
221



222
223
224
225
226
227
228
...
234
235
236
237
238
239
240



241
242
243
244
245
246
247
...
592
593
594
595
596
597
598
599

600
601
602
603
604
605
606
607
608
609



610
611
612
613
614
615
616
...
749
750
751
752
753
754
755
756








757
758

759























760
761
762
763
764
765
766
...
786
787
788
789
790
791
792

793

794
795
796
797
798
799
800
...
823
824
825
826
827
828
829

830
831
832
833
834
835
836
837
...
912
913
914
915
916
917
918
919

920

921
922
923
924
925
926
927
  Stmt *pQuery,          /* Query to implement the timeline */
  int tmFlags,           /* Flags controlling display behavior */
  const char *zThisUser, /* Suppress links to this user */
  const char *zThisTag,  /* Suppress links to this tag */
  int selectedRid,       /* Highlight the line with this RID value */
  void (*xExtra)(int)    /* Routine to call on each line of display */
){



  int mxWikiLen;
  Blob comment;
  int prevTagid = 0;
  int suppressCnt = 0;
  char zPrevDate[20];
  GraphContext *pGraph = 0;
  int prevWasDivider = 0;     /* True if previous output row was <hr> */
................................................................................
  int dateFormat = 0;         /* 0: HH:MM (default) */
  const char *zDateFmt;

  if( fossil_strcmp(g.zIpAddr, "127.0.0.1")==0 && db_open_local(0) ){
    vid = db_lget_int("checkout", 0);
  }
  zPrevDate[0] = 0;



  mxWikiLen = db_get_int("timeline-max-comment", 0);
  dateFormat = db_get_int("timeline-date-format", 0);
  zDateFmt = P("datefmt");
  if( zDateFmt ) dateFormat = atoi(zDateFmt);
  if( tmFlags & TIMELINE_GRAPH ){
    pGraph = graph_init();
    /* style is not moved to css, because this is
................................................................................
      @ <tr><td></td><td>
      @ <div id="grbtm" style="width:%d(w)px;"></div>
      @ </td><td></td></tr>
    }
  }
  @ </table>
  if( fchngQueryInit ) db_finalize(&fchngQuery);
  timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);

}

/*
** Generate all of the necessary javascript to generate a timeline
** graph.
*/
void timeline_output_graph_javascript(
  GraphContext *pGraph,     /* The graph to be displayed */
  int omitDescenders,       /* True to omit descenders */
  int fileDiff              /* True for file diff.  False for check-in diff */



){
  if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
    GraphRow *pRow;
    int i;
    char cSep;
    int mergeOffset;     /* Pixel offset from rail to merge riser */
    int iRailPitch;      /* Pixels between consecutive rails */
................................................................................
    @   var left = 0;
    @   if( obj.offsetParent ){
    @     do{
    @       left += obj.offsetLeft;
    @     }while( obj = obj.offsetParent );
    @   }
    @   return left;
    @ }








    @ function drawRail(x,y0,y1,clr){
    @   if ( bgClr == clr ) clr = lineClr;

    @   drawBox(clr||lineClr,x,y0+1,x+1,y1);























    @ }
    @ function drawThinArrow(y,xFrom,xTo){
    @   var n = document.createElement("div"),
    @       t = y-2;
    @   n.style.position = "absolute";
    @   n.style.top = t+"px";
    @   n.style.width = 0;
................................................................................
    @ }
    @ function drawThinLine(x0,y0,x1,y1){
    @   drawBox(lineClr,x0,y0,x1,y1);
    @ }
    @ function drawNodeBox(color,x0,y0,x1,y1){
    @   var n = drawBox(color,x0,y0,x1,y1);
    @   n.style.cursor = "pointer";

    @  	n.style.borderRadius = "6px";

    @ }
    @ function drawNode(p, left, btm){
    @   /* Current CheckIn node */
    @   drawNodeBox(boxColor,p.x-5,p.y-5,p.x+6,p.y+6);
    @   drawNodeBox(p.bg||bgClr,p.x-4,p.y-4,p.x+5,p.y+5);
    @   /* Leaf indicator for CheckIn node */
    @   if( p.f&1 ) drawNodeBox(leafColor,p.x-1,p.y-1,p.x+2,p.y+2);
................................................................................
    @   var n = p.au.length;
    @   for(var i=0; i<n; i+=2){
    @     var x1 = p.au[i]*railPitch + left;
    @     var x0 = x1>p.x ? p.x+7 : p.x-6;
    @     var u = rowinfo[p.au[i+1]-1];
    @     if(u.id<p.id){
    @       /* Branch rail */

    @       drawBox(u.bg,x0,p.y,x1+1,p.y+1);
    @       drawRail(x1, u.y+6, p.y, u.bg);
    @     }else{
    @       /* Timewarp rail */
    @       drawBox("#600000",x0,p.y,x1,p.y+1);
    @       drawBox("#600000",x1-1,p.y,x1,u.y+1);
    @       drawBox("#600000",x1,u.y,u.x-10,u.y+1);
    @       var n = document.createElement("div"),
................................................................................
    @       clickOnRow(p);
    @       break;
    @     }
    @   }
    @ }
    @ function clickOnRow(p){
    @   if( selRow==null ){
    @     selBox = drawBox("orange",p.x-2,p.y-2,p.x+3,p.y+3);

    @     selBox.style.borderRadius="6px";

    @     selRow = p;
    @   }else if( selRow==p ){
    @     var canvasDiv = gebi("canvas");
    @     canvasDiv.removeChild(selBox);
    @     selBox = null;
    @     selRow = null;
    @   }else{







>
>
>







 







>
>
>







 







|
>









|
>
>
>







 








>
>
>
>
>
>
>
>

<
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
|
>







 







>
|







 







|
>
|
>







215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
...
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
...
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775

776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
...
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
...
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
...
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
  Stmt *pQuery,          /* Query to implement the timeline */
  int tmFlags,           /* Flags controlling display behavior */
  const char *zThisUser, /* Suppress links to this user */
  const char *zThisTag,  /* Suppress links to this tag */
  int selectedRid,       /* Highlight the line with this RID value */
  void (*xExtra)(int)    /* Routine to call on each line of display */
){
  int showRailArrows;
  int showRailCircles;
  int showRailColors;
  int mxWikiLen;
  Blob comment;
  int prevTagid = 0;
  int suppressCnt = 0;
  char zPrevDate[20];
  GraphContext *pGraph = 0;
  int prevWasDivider = 0;     /* True if previous output row was <hr> */
................................................................................
  int dateFormat = 0;         /* 0: HH:MM (default) */
  const char *zDateFmt;

  if( fossil_strcmp(g.zIpAddr, "127.0.0.1")==0 && db_open_local(0) ){
    vid = db_lget_int("checkout", 0);
  }
  zPrevDate[0] = 0;
  showRailArrows = db_get_boolean("timeline-rail-arrows", 1);
  showRailCircles = db_get_boolean("timeline-rail-circles", 0);
  showRailColors = db_get_boolean("timeline-rail-colors", 0);
  mxWikiLen = db_get_int("timeline-max-comment", 0);
  dateFormat = db_get_int("timeline-date-format", 0);
  zDateFmt = P("datefmt");
  if( zDateFmt ) dateFormat = atoi(zDateFmt);
  if( tmFlags & TIMELINE_GRAPH ){
    pGraph = graph_init();
    /* style is not moved to css, because this is
................................................................................
      @ <tr><td></td><td>
      @ <div id="grbtm" style="width:%d(w)px;"></div>
      @ </td><td></td></tr>
    }
  }
  @ </table>
  if( fchngQueryInit ) db_finalize(&fchngQuery);
  timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0,
    showRailArrows, showRailCircles, showRailColors);
}

/*
** Generate all of the necessary javascript to generate a timeline
** graph.
*/
void timeline_output_graph_javascript(
  GraphContext *pGraph,     /* The graph to be displayed */
  int omitDescenders,       /* True to omit descenders */
  int fileDiff,             /* True for file diff.  False for check-in diff */
  int showRailArrows,       /* True to render rail arrow heads */
  int showRailCircles,      /* True to render circles instead of squares */
  int showRailColors        /* True to color rails by the branch background */
){
  if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
    GraphRow *pRow;
    int i;
    char cSep;
    int mergeOffset;     /* Pixel offset from rail to merge riser */
    int iRailPitch;      /* Pixels between consecutive rails */
................................................................................
    @   var left = 0;
    @   if( obj.offsetParent ){
    @     do{
    @       left += obj.offsetLeft;
    @     }while( obj = obj.offsetParent );
    @   }
    @   return left;
    @ }
    @ function getRailColor(clr){
    @   var railClr = lineClr;
    if( showRailColors ) {
      @   if ( bgClr == clr ) railClr = lineClr;
      @   railClr = clr||railClr;
    }
    @   return railClr;
    @ }
    @ function drawRail(x,y0,y1,clr){

    @   var railClr = getRailColor(clr);
    @   drawBox(railClr,x,y0+1,x+1,y1);
    @   var n = document.createElement("div"),
    @       l = x-2,
    @       t = y0;
    if( showRailArrows ){
      @   n.style.position = "absolute";
      @   n.style.left = l+"px";
      @   n.style.top = t+"px";
      @   n.style.width = 0;
      @   n.style.height = 0;
      @   n.style.transform = "scale(.999)";
      @   n.style.borderWidth = 0;
      @   n.style.borderStyle = "solid";
      @   n.style.borderColor = "transparent";
      @   n.style.borderRightWidth = "3px";
      @   n.style.borderBottomColor = railClr;
      @   n.style.borderLeftWidth = "3px";
      @   if( y0+10>=y1 ){
      @     n.style.borderBottomWidth = "5px";
      @   } else {
      @     n.style.borderBottomWidth = "7px";
      @   }
      @   cDiv.appendChild(n);
    }
    @ }
    @ function drawThinArrow(y,xFrom,xTo){
    @   var n = document.createElement("div"),
    @       t = y-2;
    @   n.style.position = "absolute";
    @   n.style.top = t+"px";
    @   n.style.width = 0;
................................................................................
    @ }
    @ function drawThinLine(x0,y0,x1,y1){
    @   drawBox(lineClr,x0,y0,x1,y1);
    @ }
    @ function drawNodeBox(color,x0,y0,x1,y1){
    @   var n = drawBox(color,x0,y0,x1,y1);
    @   n.style.cursor = "pointer";
    if( showRailCircles ){
      @   n.style.borderRadius = "6px";
    }
    @ }
    @ function drawNode(p, left, btm){
    @   /* Current CheckIn node */
    @   drawNodeBox(boxColor,p.x-5,p.y-5,p.x+6,p.y+6);
    @   drawNodeBox(p.bg||bgClr,p.x-4,p.y-4,p.x+5,p.y+5);
    @   /* Leaf indicator for CheckIn node */
    @   if( p.f&1 ) drawNodeBox(leafColor,p.x-1,p.y-1,p.x+2,p.y+2);
................................................................................
    @   var n = p.au.length;
    @   for(var i=0; i<n; i+=2){
    @     var x1 = p.au[i]*railPitch + left;
    @     var x0 = x1>p.x ? p.x+7 : p.x-6;
    @     var u = rowinfo[p.au[i+1]-1];
    @     if(u.id<p.id){
    @       /* Branch rail */
    @       var railClr = getRailColor(u.bg);
    @       drawBox(railClr,x0,p.y,x1+1,p.y+1);
    @       drawRail(x1, u.y+6, p.y, u.bg);
    @     }else{
    @       /* Timewarp rail */
    @       drawBox("#600000",x0,p.y,x1,p.y+1);
    @       drawBox("#600000",x1-1,p.y,x1,u.y+1);
    @       drawBox("#600000",x1,u.y,u.x-10,u.y+1);
    @       var n = document.createElement("div"),
................................................................................
    @       clickOnRow(p);
    @       break;
    @     }
    @   }
    @ }
    @ function clickOnRow(p){
    @   if( selRow==null ){
    @     selBox = drawBox("red",p.x-2,p.y-2,p.x+3,p.y+3);
    if( showRailCircles ){
      @   selBox.style.borderRadius="6px";
    }
    @     selRow = p;
    @   }else if( selRow==p ){
    @     var canvasDiv = gebi("canvas");
    @     canvasDiv.removeChild(selBox);
    @     selBox = null;
    @     selRow = null;
    @   }else{