Fossil SCM

Improvements to graph rendering to make it much more configurable using CSS.

drh 2015-05-09 12:42 trunk merge
Commit ca869aabe2a938c620d04402f1cf75943965c16a
--- skins/eagle/css.txt
+++ skins/eagle/css.txt
@@ -168,10 +168,58 @@
168168
}
169169
170170
tr.timelineSelected {
171171
background-color: #7EA2D9;
172172
}
173
+
174
+/* commit node */
175
+.tl-node {
176
+ width: 10px;
177
+ height: 10px;
178
+ border: 1px solid #fff;
179
+ background: #485D7B;
180
+ cursor: pointer;
181
+}
182
+
183
+/* leaf commit marker */
184
+.tl-node.leaf:after {
185
+ content: '';
186
+ position: absolute;
187
+ top: 3px;
188
+ left: 3px;
189
+ width: 4px;
190
+ height: 4px;
191
+ background: #fff;
192
+}
193
+
194
+/* up arrow */
195
+.tl-arrow.u {
196
+ margin-top: -1px;
197
+ border-width: 0 3px;
198
+ border-bottom: 7px solid #fff;
199
+}
200
+
201
+/* small up arrow */
202
+.tl-arrow.u.sm {
203
+ border-bottom: 5px solid #fff;
204
+}
205
+
206
+/* line */
207
+.tl-line {
208
+ background: #fff;
209
+ width: 2px;
210
+}
211
+
212
+/* left merge arrow */
213
+.tl-arrow.merge.l {
214
+ border-right: 3px solid #fff;
215
+}
216
+
217
+/* right merge arrow */
218
+.tl-arrow.merge.r {
219
+ border-left: 3px solid #fff;
220
+}
173221
174222
/* Side-by-side diff */
175223
table.sbsdiff {
176224
background-color: #485D7B;
177225
font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
@@ -264,14 +312,10 @@
264312
/* line numbers in a diff */
265313
span.diffln {
266314
color: white;
267315
}
268316
269
-#canvas {
270
- background-color: #485D7B;
271
-}
272
-
273317
.fileage tr:hover {
274318
background-color: #7EA2D9;
275319
}
276320
277321
.fileage td {
278322
--- skins/eagle/css.txt
+++ skins/eagle/css.txt
@@ -168,10 +168,58 @@
168 }
169
170 tr.timelineSelected {
171 background-color: #7EA2D9;
172 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
174 /* Side-by-side diff */
175 table.sbsdiff {
176 background-color: #485D7B;
177 font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
@@ -264,14 +312,10 @@
264 /* line numbers in a diff */
265 span.diffln {
266 color: white;
267 }
268
269 #canvas {
270 background-color: #485D7B;
271 }
272
273 .fileage tr:hover {
274 background-color: #7EA2D9;
275 }
276
277 .fileage td {
278
--- skins/eagle/css.txt
+++ skins/eagle/css.txt
@@ -168,10 +168,58 @@
168 }
169
170 tr.timelineSelected {
171 background-color: #7EA2D9;
172 }
173
174 /* commit node */
175 .tl-node {
176 width: 10px;
177 height: 10px;
178 border: 1px solid #fff;
179 background: #485D7B;
180 cursor: pointer;
181 }
182
183 /* leaf commit marker */
184 .tl-node.leaf:after {
185 content: '';
186 position: absolute;
187 top: 3px;
188 left: 3px;
189 width: 4px;
190 height: 4px;
191 background: #fff;
192 }
193
194 /* up arrow */
195 .tl-arrow.u {
196 margin-top: -1px;
197 border-width: 0 3px;
198 border-bottom: 7px solid #fff;
199 }
200
201 /* small up arrow */
202 .tl-arrow.u.sm {
203 border-bottom: 5px solid #fff;
204 }
205
206 /* line */
207 .tl-line {
208 background: #fff;
209 width: 2px;
210 }
211
212 /* left merge arrow */
213 .tl-arrow.merge.l {
214 border-right: 3px solid #fff;
215 }
216
217 /* right merge arrow */
218 .tl-arrow.merge.r {
219 border-left: 3px solid #fff;
220 }
221
222 /* Side-by-side diff */
223 table.sbsdiff {
224 background-color: #485D7B;
225 font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
@@ -264,14 +312,10 @@
312 /* line numbers in a diff */
313 span.diffln {
314 color: white;
315 }
316
 
 
 
 
317 .fileage tr:hover {
318 background-color: #7EA2D9;
319 }
320
321 .fileage td {
322
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -700,15 +700,10 @@
700700
701701
/**************************************
702702
* Timeline
703703
*/
704704
705
-#canvas {
706
- color: #000;
707
- background-color: #fff;
708
-}
709
-
710705
div.divider {
711706
color: #ee0;
712707
font-size: 1.2rem;
713708
font-weight: bold;
714709
margin-top: 1rem;
715710
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -700,15 +700,10 @@
700
701 /**************************************
702 * Timeline
703 */
704
705 #canvas {
706 color: #000;
707 background-color: #fff;
708 }
709
710 div.divider {
711 color: #ee0;
712 font-size: 1.2rem;
713 font-weight: bold;
714 margin-top: 1rem;
715
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -700,15 +700,10 @@
700
701 /**************************************
702 * Timeline
703 */
704
 
 
 
 
 
705 div.divider {
706 color: #ee0;
707 font-size: 1.2rem;
708 font-weight: bold;
709 margin-top: 1rem;
710
+2 -7
--- src/finfo.c
+++ src/finfo.c
@@ -403,12 +403,10 @@
403403
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
404404
}
405405
@ <h2>%b(&title)</h2>
406406
blob_reset(&title);
407407
pGraph = graph_init();
408
- @ <div id="canvas" style="position:relative;width:1px;height:1px;"
409
- @ onclick="clickOnGraph(event)"></div>
410408
@ <table id="timelineTable" class="timelineTable">
411409
while( db_step(&q)==SQLITE_ROW ){
412410
const char *zDate = db_column_text(&q, 0);
413411
const char *zCom = db_column_text(&q, 1);
414412
const char *zUser = db_column_text(&q, 2);
@@ -459,11 +457,11 @@
459457
}
460458
memcpy(zTime, &zDate[11], 5);
461459
zTime[5] = 0;
462460
@ <tr><td class="timelineTime">
463461
@ %z(href("%R/timeline?c=%t",zDate))%s(zTime)</a></td>
464
- @ <td class="timelineGraph"><div id="m%d(gidx)"></div></td>
462
+ @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div></td>
465463
if( zBgClr && zBgClr[0] ){
466464
@ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
467465
}else{
468466
@ <td class="timelineTableCell">
469467
}
@@ -536,15 +534,12 @@
536534
graph_finish(pGraph, 1);
537535
if( pGraph->nErr ){
538536
graph_free(pGraph);
539537
pGraph = 0;
540538
}else{
541
- int w = pGraph->mxRail*pGraph->iRailPitch + 28;
542
- @ <tr><td></td><td>
543
- @ <div id="grbtm" style="width:%d(w)px;"></div>
544
- @ </td><td></td></tr>
539
+ @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
545540
}
546541
}
547542
@ </table>
548543
timeline_output_graph_javascript(pGraph, 0, 1);
549544
style_footer();
550545
}
551546
--- src/finfo.c
+++ src/finfo.c
@@ -403,12 +403,10 @@
403 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
404 }
405 @ <h2>%b(&title)</h2>
406 blob_reset(&title);
407 pGraph = graph_init();
408 @ <div id="canvas" style="position:relative;width:1px;height:1px;"
409 @ onclick="clickOnGraph(event)"></div>
410 @ <table id="timelineTable" class="timelineTable">
411 while( db_step(&q)==SQLITE_ROW ){
412 const char *zDate = db_column_text(&q, 0);
413 const char *zCom = db_column_text(&q, 1);
414 const char *zUser = db_column_text(&q, 2);
@@ -459,11 +457,11 @@
459 }
460 memcpy(zTime, &zDate[11], 5);
461 zTime[5] = 0;
462 @ <tr><td class="timelineTime">
463 @ %z(href("%R/timeline?c=%t",zDate))%s(zTime)</a></td>
464 @ <td class="timelineGraph"><div id="m%d(gidx)"></div></td>
465 if( zBgClr && zBgClr[0] ){
466 @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
467 }else{
468 @ <td class="timelineTableCell">
469 }
@@ -536,15 +534,12 @@
536 graph_finish(pGraph, 1);
537 if( pGraph->nErr ){
538 graph_free(pGraph);
539 pGraph = 0;
540 }else{
541 int w = pGraph->mxRail*pGraph->iRailPitch + 28;
542 @ <tr><td></td><td>
543 @ <div id="grbtm" style="width:%d(w)px;"></div>
544 @ </td><td></td></tr>
545 }
546 }
547 @ </table>
548 timeline_output_graph_javascript(pGraph, 0, 1);
549 style_footer();
550 }
551
--- src/finfo.c
+++ src/finfo.c
@@ -403,12 +403,10 @@
403 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
404 }
405 @ <h2>%b(&title)</h2>
406 blob_reset(&title);
407 pGraph = graph_init();
 
 
408 @ <table id="timelineTable" class="timelineTable">
409 while( db_step(&q)==SQLITE_ROW ){
410 const char *zDate = db_column_text(&q, 0);
411 const char *zCom = db_column_text(&q, 1);
412 const char *zUser = db_column_text(&q, 2);
@@ -459,11 +457,11 @@
457 }
458 memcpy(zTime, &zDate[11], 5);
459 zTime[5] = 0;
460 @ <tr><td class="timelineTime">
461 @ %z(href("%R/timeline?c=%t",zDate))%s(zTime)</a></td>
462 @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div></td>
463 if( zBgClr && zBgClr[0] ){
464 @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
465 }else{
466 @ <td class="timelineTableCell">
467 }
@@ -536,15 +534,12 @@
534 graph_finish(pGraph, 1);
535 if( pGraph->nErr ){
536 graph_free(pGraph);
537 pGraph = 0;
538 }else{
539 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
 
 
 
540 }
541 }
542 @ </table>
543 timeline_output_graph_javascript(pGraph, 0, 1);
544 style_footer();
545 }
546
+8 -15
--- src/graph.c
+++ src/graph.c
@@ -47,11 +47,11 @@
4747
u8 isDup; /* True if this is duplicate of a prior entry */
4848
u8 isLeaf; /* True if this is a leaf node */
4949
u8 timeWarp; /* Child is earlier in time */
5050
u8 bDescender; /* True if riser from bottom of graph to here. */
5151
i8 iRail; /* Which rail this check-in appears on. 0-based.*/
52
- i8 mergeOut; /* Merge out on rail mergeOut/4. -1 for none */
52
+ i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */
5353
u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */
5454
int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
5555
int mergeUpto; /* Draw the mergeOut rail up to this level */
5656
u64 mergeDown; /* Draw merge lines up from bottom of graph */
5757
@@ -61,11 +61,10 @@
6161
/* Context while building a graph
6262
*/
6363
struct GraphContext {
6464
int nErr; /* Number of errors encountered */
6565
int mxRail; /* Number of rails required to render the graph */
66
- int iRailPitch; /* Pixels between rail centers */
6766
GraphRow *pFirst; /* First row in the list */
6867
GraphRow *pLast; /* Last row in the list */
6968
int nBranch; /* Number of distinct branches */
7069
char **azBranch; /* Names of the branches */
7170
int nRow; /* Number of rows */
@@ -294,28 +293,27 @@
294293
u = pParent->aiRiser[pParent->iRail];
295294
if( u>=0 && u<pChild->idx ){
296295
/* The thick arrow up to the next primary child of pDesc goes
297296
** further up than the thin merge arrow riser, so draw them both
298297
** on the same rail. */
299
- pParent->mergeOut = pParent->iRail*4;
300
- if( pParent->iRail<pChild->iRail ) pParent->mergeOut += 2;
298
+ pParent->mergeOut = pParent->iRail;
301299
pParent->mergeUpto = pChild->idx;
302300
}else{
303301
/* The thin merge arrow riser is taller than the thick primary
304302
** child riser, so use separate rails. */
305303
int iTarget = pParent->iRail;
306304
pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1,
307
- 0, iTarget)*4 + 1;
305
+ 0, iTarget);
308306
pParent->mergeUpto = pChild->idx;
309
- mask = BIT(pParent->mergeOut/4);
307
+ mask = BIT(pParent->mergeOut);
310308
for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid;
311309
pLoop=pLoop->pNext){
312310
pLoop->railInUse |= mask;
313311
}
314312
}
315313
}
316
- pChild->mergeIn[pParent->mergeOut/4] = (pParent->mergeOut&3)+1;
314
+ pChild->mergeIn[pParent->mergeOut] = 1;
317315
}
318316
319317
/*
320318
** Compute the maximum rail number.
321319
*/
@@ -322,11 +320,11 @@
322320
static void find_max_rail(GraphContext *p){
323321
GraphRow *pRow;
324322
p->mxRail = 0;
325323
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
326324
if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
327
- if( pRow->mergeOut/4>p->mxRail ) p->mxRail = pRow->mergeOut/4;
325
+ if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
328326
while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>(BIT(p->mxRail+1)-1) ){
329327
p->mxRail++;
330328
}
331329
}
332330
}
@@ -540,11 +538,11 @@
540538
if( pDesc==0 ){
541539
/* Merge from a node that is off-screen */
542540
int iMrail = findFreeRail(p, pRow->idx, p->nRow, 0, 0);
543541
if( p->mxRail>=GR_MAX_RAIL ) return;
544542
mask = BIT(iMrail);
545
- pRow->mergeIn[iMrail] = 2;
543
+ pRow->mergeIn[iMrail] = 1;
546544
pRow->mergeDown |= mask;
547545
for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){
548546
pLoop->railInUse |= mask;
549547
}
550548
}else{
@@ -569,11 +567,11 @@
569567
if( !pRow->isDup ) continue;
570568
pRow->iRail = dupRail;
571569
pDesc = hashFind(p, pRow->rid);
572570
assert( pDesc!=0 && pDesc!=pRow );
573571
createMergeRiser(p, pDesc, pRow);
574
- if( pDesc->mergeOut/4>mxRail ) mxRail = pDesc->mergeOut/4;
572
+ if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut;
575573
}
576574
if( dupRail<=mxRail ){
577575
dupRail = mxRail+1;
578576
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
579577
if( pRow->isDup ) pRow->iRail = dupRail;
@@ -584,12 +582,7 @@
584582
585583
/*
586584
** Find the maximum rail number.
587585
*/
588586
find_max_rail(p);
589
- p->iRailPitch = atoi(PD("railpitch","0"));
590
- if( p->iRailPitch<=0 ){
591
- p->iRailPitch = 18 - (p->mxRail/3);
592
- if( p->iRailPitch<11 ) p->iRailPitch = 11;
593
- }
594587
p->nErr = 0;
595588
}
596589
--- src/graph.c
+++ src/graph.c
@@ -47,11 +47,11 @@
47 u8 isDup; /* True if this is duplicate of a prior entry */
48 u8 isLeaf; /* True if this is a leaf node */
49 u8 timeWarp; /* Child is earlier in time */
50 u8 bDescender; /* True if riser from bottom of graph to here. */
51 i8 iRail; /* Which rail this check-in appears on. 0-based.*/
52 i8 mergeOut; /* Merge out on rail mergeOut/4. -1 for none */
53 u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */
54 int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
55 int mergeUpto; /* Draw the mergeOut rail up to this level */
56 u64 mergeDown; /* Draw merge lines up from bottom of graph */
57
@@ -61,11 +61,10 @@
61 /* Context while building a graph
62 */
63 struct GraphContext {
64 int nErr; /* Number of errors encountered */
65 int mxRail; /* Number of rails required to render the graph */
66 int iRailPitch; /* Pixels between rail centers */
67 GraphRow *pFirst; /* First row in the list */
68 GraphRow *pLast; /* Last row in the list */
69 int nBranch; /* Number of distinct branches */
70 char **azBranch; /* Names of the branches */
71 int nRow; /* Number of rows */
@@ -294,28 +293,27 @@
294 u = pParent->aiRiser[pParent->iRail];
295 if( u>=0 && u<pChild->idx ){
296 /* The thick arrow up to the next primary child of pDesc goes
297 ** further up than the thin merge arrow riser, so draw them both
298 ** on the same rail. */
299 pParent->mergeOut = pParent->iRail*4;
300 if( pParent->iRail<pChild->iRail ) pParent->mergeOut += 2;
301 pParent->mergeUpto = pChild->idx;
302 }else{
303 /* The thin merge arrow riser is taller than the thick primary
304 ** child riser, so use separate rails. */
305 int iTarget = pParent->iRail;
306 pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1,
307 0, iTarget)*4 + 1;
308 pParent->mergeUpto = pChild->idx;
309 mask = BIT(pParent->mergeOut/4);
310 for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid;
311 pLoop=pLoop->pNext){
312 pLoop->railInUse |= mask;
313 }
314 }
315 }
316 pChild->mergeIn[pParent->mergeOut/4] = (pParent->mergeOut&3)+1;
317 }
318
319 /*
320 ** Compute the maximum rail number.
321 */
@@ -322,11 +320,11 @@
322 static void find_max_rail(GraphContext *p){
323 GraphRow *pRow;
324 p->mxRail = 0;
325 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
326 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
327 if( pRow->mergeOut/4>p->mxRail ) p->mxRail = pRow->mergeOut/4;
328 while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>(BIT(p->mxRail+1)-1) ){
329 p->mxRail++;
330 }
331 }
332 }
@@ -540,11 +538,11 @@
540 if( pDesc==0 ){
541 /* Merge from a node that is off-screen */
542 int iMrail = findFreeRail(p, pRow->idx, p->nRow, 0, 0);
543 if( p->mxRail>=GR_MAX_RAIL ) return;
544 mask = BIT(iMrail);
545 pRow->mergeIn[iMrail] = 2;
546 pRow->mergeDown |= mask;
547 for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){
548 pLoop->railInUse |= mask;
549 }
550 }else{
@@ -569,11 +567,11 @@
569 if( !pRow->isDup ) continue;
570 pRow->iRail = dupRail;
571 pDesc = hashFind(p, pRow->rid);
572 assert( pDesc!=0 && pDesc!=pRow );
573 createMergeRiser(p, pDesc, pRow);
574 if( pDesc->mergeOut/4>mxRail ) mxRail = pDesc->mergeOut/4;
575 }
576 if( dupRail<=mxRail ){
577 dupRail = mxRail+1;
578 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
579 if( pRow->isDup ) pRow->iRail = dupRail;
@@ -584,12 +582,7 @@
584
585 /*
586 ** Find the maximum rail number.
587 */
588 find_max_rail(p);
589 p->iRailPitch = atoi(PD("railpitch","0"));
590 if( p->iRailPitch<=0 ){
591 p->iRailPitch = 18 - (p->mxRail/3);
592 if( p->iRailPitch<11 ) p->iRailPitch = 11;
593 }
594 p->nErr = 0;
595 }
596
--- src/graph.c
+++ src/graph.c
@@ -47,11 +47,11 @@
47 u8 isDup; /* True if this is duplicate of a prior entry */
48 u8 isLeaf; /* True if this is a leaf node */
49 u8 timeWarp; /* Child is earlier in time */
50 u8 bDescender; /* True if riser from bottom of graph to here. */
51 i8 iRail; /* Which rail this check-in appears on. 0-based.*/
52 i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */
53 u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */
54 int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
55 int mergeUpto; /* Draw the mergeOut rail up to this level */
56 u64 mergeDown; /* Draw merge lines up from bottom of graph */
57
@@ -61,11 +61,10 @@
61 /* Context while building a graph
62 */
63 struct GraphContext {
64 int nErr; /* Number of errors encountered */
65 int mxRail; /* Number of rails required to render the graph */
 
66 GraphRow *pFirst; /* First row in the list */
67 GraphRow *pLast; /* Last row in the list */
68 int nBranch; /* Number of distinct branches */
69 char **azBranch; /* Names of the branches */
70 int nRow; /* Number of rows */
@@ -294,28 +293,27 @@
293 u = pParent->aiRiser[pParent->iRail];
294 if( u>=0 && u<pChild->idx ){
295 /* The thick arrow up to the next primary child of pDesc goes
296 ** further up than the thin merge arrow riser, so draw them both
297 ** on the same rail. */
298 pParent->mergeOut = pParent->iRail;
 
299 pParent->mergeUpto = pChild->idx;
300 }else{
301 /* The thin merge arrow riser is taller than the thick primary
302 ** child riser, so use separate rails. */
303 int iTarget = pParent->iRail;
304 pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1,
305 0, iTarget);
306 pParent->mergeUpto = pChild->idx;
307 mask = BIT(pParent->mergeOut);
308 for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid;
309 pLoop=pLoop->pNext){
310 pLoop->railInUse |= mask;
311 }
312 }
313 }
314 pChild->mergeIn[pParent->mergeOut] = 1;
315 }
316
317 /*
318 ** Compute the maximum rail number.
319 */
@@ -322,11 +320,11 @@
320 static void find_max_rail(GraphContext *p){
321 GraphRow *pRow;
322 p->mxRail = 0;
323 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
324 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
325 if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
326 while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>(BIT(p->mxRail+1)-1) ){
327 p->mxRail++;
328 }
329 }
330 }
@@ -540,11 +538,11 @@
538 if( pDesc==0 ){
539 /* Merge from a node that is off-screen */
540 int iMrail = findFreeRail(p, pRow->idx, p->nRow, 0, 0);
541 if( p->mxRail>=GR_MAX_RAIL ) return;
542 mask = BIT(iMrail);
543 pRow->mergeIn[iMrail] = 1;
544 pRow->mergeDown |= mask;
545 for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){
546 pLoop->railInUse |= mask;
547 }
548 }else{
@@ -569,11 +567,11 @@
567 if( !pRow->isDup ) continue;
568 pRow->iRail = dupRail;
569 pDesc = hashFind(p, pRow->rid);
570 assert( pDesc!=0 && pDesc!=pRow );
571 createMergeRiser(p, pDesc, pRow);
572 if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut;
573 }
574 if( dupRail<=mxRail ){
575 dupRail = mxRail+1;
576 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
577 if( pRow->isDup ) pRow->iRail = dupRail;
@@ -584,12 +582,7 @@
582
583 /*
584 ** Find the maximum rail number.
585 */
586 find_max_rail(p);
 
 
 
 
 
587 p->nErr = 0;
588 }
589
+124 -15
--- src/style.c
+++ src/style.c
@@ -701,15 +701,108 @@
701701
@ vertical-align: top;
702702
@ text-align: right;
703703
@ white-space: nowrap;
704704
},
705705
{ "td.timelineGraph",
706
- "the format for the grap placeholder cells in timelines",
706
+ "the format for the graph placeholder cells in timelines",
707707
@ width: 20px;
708708
@ text-align: left;
709709
@ vertical-align: top;
710710
},
711
+ { ".tl-canvas",
712
+ "timeline graph canvas",
713
+ @ margin: 0 6px 0 10px;
714
+ },
715
+ { ".tl-rail",
716
+ "maximum rail spacing",
717
+ @ width: 18px;
718
+ },
719
+ { ".tl-mergeoffset",
720
+ "maximum spacing between merge risers and primary child risers",
721
+ @ width: 2px;
722
+ },
723
+ { ".tl-nodemark",
724
+ "adjusts the vertical position of graph nodes",
725
+ @ margin-top: 5px;
726
+ },
727
+ { ".tl-node",
728
+ "commit node",
729
+ @ width: 10px;
730
+ @ height: 10px;
731
+ @ border: 1px solid #000;
732
+ @ background: #fff;
733
+ @ cursor: pointer;
734
+ },
735
+ { ".tl-node.leaf:after",
736
+ "leaf commit marker",
737
+ @ content: '';
738
+ @ position: absolute;
739
+ @ top: 3px;
740
+ @ left: 3px;
741
+ @ width: 4px;
742
+ @ height: 4px;
743
+ @ background: #000;
744
+ },
745
+ { ".tl-node.sel:after",
746
+ "selected commit node marker",
747
+ @ content: '';
748
+ @ position: absolute;
749
+ @ top: 2px;
750
+ @ left: 2px;
751
+ @ width: 6px;
752
+ @ height: 6px;
753
+ @ background: red;
754
+ },
755
+ { ".tl-arrow",
756
+ "arrow",
757
+ @ width: 0;
758
+ @ height: 0;
759
+ @ transform: scale(.999);
760
+ @ border: 0 solid transparent;
761
+ },
762
+ { ".tl-arrow.u",
763
+ "up arrow",
764
+ @ margin-top: -1px;
765
+ @ border-width: 0 3px;
766
+ @ border-bottom: 7px solid #000;
767
+ },
768
+ { ".tl-arrow.u.sm",
769
+ "small up arrow",
770
+ @ border-bottom: 5px solid #000;
771
+ },
772
+ { ".tl-line",
773
+ "line",
774
+ @ background: #000;
775
+ @ width: 2px;
776
+ },
777
+ { ".tl-arrow.merge",
778
+ "merge arrow",
779
+ @ height: 1px;
780
+ @ border-width: 2px 0;
781
+ },
782
+ { ".tl-arrow.merge.l",
783
+ "left merge arrow",
784
+ @ border-right: 3px solid #000;
785
+ },
786
+ { ".tl-arrow.merge.r",
787
+ "right merge arrow",
788
+ @ border-left: 3px solid #000;
789
+ },
790
+ { ".tl-line.merge",
791
+ "merge line",
792
+ @ width: 1px;
793
+ },
794
+ { ".tl-arrow.warp",
795
+ "timewarp arrow",
796
+ @ margin-left: 1px;
797
+ @ border-width: 3px 0;
798
+ @ border-left: 7px solid #600000;
799
+ },
800
+ { ".tl-line.warp",
801
+ "timewarp line",
802
+ @ background: #600000;
803
+ },
711804
{ "a.tagLink",
712805
"the format for the tag links",
713806
@
714807
},
715808
{ "span.tagDsp",
@@ -1226,14 +1319,10 @@
12261319
},
12271320
{ "#usetupEditCapability",
12281321
"format for capabilities string, mentioned on the user edit page",
12291322
@ font-weight: bold;
12301323
},
1231
- { "#canvas", "timeline graph node colors",
1232
- @ color: black;
1233
- @ background-color: white;
1234
- },
12351324
{ "table.adminLogTable",
12361325
"Class for the /admin_log table",
12371326
@ text-align: left;
12381327
},
12391328
{ ".adminLogTable .adminTime",
@@ -1336,30 +1425,50 @@
13361425
}
13371426
}
13381427
}
13391428
13401429
/*
1341
-** Search string zHaystack for zNeedle. zNeedle must be an isolated
1342
-** word with space or punctuation on either size.
1430
+** Search string zCss for zSelector.
13431431
**
13441432
** Return true if found. Return false if not found
13451433
*/
1346
-static int containsString(const char *zHaystack, const char *zNeedle){
1434
+static int containsSelector(const char *zCss, const char *zSelector){
13471435
char *z;
13481436
int n;
1437
+ int selectorLen = (int)strlen(zSelector);
13491438
1350
- while( zHaystack[0] ){
1351
- z = strstr(zHaystack, zNeedle);
1439
+ for( z=zCss; *z; z+=selectorLen ){
1440
+ z = strstr(z, zSelector);
13521441
if( z==0 ) return 0;
1353
- n = (int)strlen(zNeedle);
1354
- if( (z==zHaystack || !fossil_isalnum(z[-1])) && !fossil_isalnum(z[n]) ){
1355
- return 1;
1442
+ if( z!=zCss ){
1443
+ for( n=-1; z+n!=zCss && fossil_isspace(z[n]); n--);
1444
+ if( z+n!=zCss && z[n]!=',' && z[n]!= '}' && z[n]!='/' ) continue;
13561445
}
1357
- zHaystack = z + n;
1446
+ for( n=selectorLen; z[n] && fossil_isspace(z[n]); n++ );
1447
+ if( z[n]==',' || z[n]=='{' || z[n]=='/' ) return 1;
13581448
}
13591449
return 0;
13601450
}
1451
+
1452
+/*
1453
+** COMMAND: test-contains-selector
1454
+**
1455
+** Usage: %fossil test-contains-selector FILENAME SELECTOR
1456
+**
1457
+** Determine if the CSS stylesheet FILENAME contains SELECTOR.
1458
+*/
1459
+void contains_selector_cmd(void){
1460
+ int found;
1461
+ char *zSelector;
1462
+ Blob css;
1463
+ if( g.argc!=4 ) usage("FILENAME SELECTOR");
1464
+ blob_read_from_file(&css, g.argv[2]);
1465
+ zSelector = g.argv[3];
1466
+ found = containsSelector(blob_str(&css), zSelector);
1467
+ fossil_print("%s %s\n", zSelector, found ? "found" : "not found");
1468
+ blob_reset(&css);
1469
+}
13611470
13621471
13631472
/*
13641473
** WEBPAGE: style.css
13651474
**
@@ -1373,11 +1482,11 @@
13731482
blob_init(&css,skin_get("css"),-1);
13741483
13751484
/* add special missing definitions */
13761485
for(i=1; cssDefaultList[i].elementClass; i++){
13771486
char *z = blob_str(&css);
1378
- if( !containsString(z, cssDefaultList[i].elementClass) ){
1487
+ if( !containsSelector(z, cssDefaultList[i].elementClass) ){
13791488
blob_appendf(&css, "/* %s */\n%s {\n%s}\n",
13801489
cssDefaultList[i].comment,
13811490
cssDefaultList[i].elementClass,
13821491
cssDefaultList[i].value);
13831492
}
13841493
--- src/style.c
+++ src/style.c
@@ -701,15 +701,108 @@
701 @ vertical-align: top;
702 @ text-align: right;
703 @ white-space: nowrap;
704 },
705 { "td.timelineGraph",
706 "the format for the grap placeholder cells in timelines",
707 @ width: 20px;
708 @ text-align: left;
709 @ vertical-align: top;
710 },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
711 { "a.tagLink",
712 "the format for the tag links",
713 @
714 },
715 { "span.tagDsp",
@@ -1226,14 +1319,10 @@
1226 },
1227 { "#usetupEditCapability",
1228 "format for capabilities string, mentioned on the user edit page",
1229 @ font-weight: bold;
1230 },
1231 { "#canvas", "timeline graph node colors",
1232 @ color: black;
1233 @ background-color: white;
1234 },
1235 { "table.adminLogTable",
1236 "Class for the /admin_log table",
1237 @ text-align: left;
1238 },
1239 { ".adminLogTable .adminTime",
@@ -1336,30 +1425,50 @@
1336 }
1337 }
1338 }
1339
1340 /*
1341 ** Search string zHaystack for zNeedle. zNeedle must be an isolated
1342 ** word with space or punctuation on either size.
1343 **
1344 ** Return true if found. Return false if not found
1345 */
1346 static int containsString(const char *zHaystack, const char *zNeedle){
1347 char *z;
1348 int n;
 
1349
1350 while( zHaystack[0] ){
1351 z = strstr(zHaystack, zNeedle);
1352 if( z==0 ) return 0;
1353 n = (int)strlen(zNeedle);
1354 if( (z==zHaystack || !fossil_isalnum(z[-1])) && !fossil_isalnum(z[n]) ){
1355 return 1;
1356 }
1357 zHaystack = z + n;
 
1358 }
1359 return 0;
1360 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1361
1362
1363 /*
1364 ** WEBPAGE: style.css
1365 **
@@ -1373,11 +1482,11 @@
1373 blob_init(&css,skin_get("css"),-1);
1374
1375 /* add special missing definitions */
1376 for(i=1; cssDefaultList[i].elementClass; i++){
1377 char *z = blob_str(&css);
1378 if( !containsString(z, cssDefaultList[i].elementClass) ){
1379 blob_appendf(&css, "/* %s */\n%s {\n%s}\n",
1380 cssDefaultList[i].comment,
1381 cssDefaultList[i].elementClass,
1382 cssDefaultList[i].value);
1383 }
1384
--- src/style.c
+++ src/style.c
@@ -701,15 +701,108 @@
701 @ vertical-align: top;
702 @ text-align: right;
703 @ white-space: nowrap;
704 },
705 { "td.timelineGraph",
706 "the format for the graph placeholder cells in timelines",
707 @ width: 20px;
708 @ text-align: left;
709 @ vertical-align: top;
710 },
711 { ".tl-canvas",
712 "timeline graph canvas",
713 @ margin: 0 6px 0 10px;
714 },
715 { ".tl-rail",
716 "maximum rail spacing",
717 @ width: 18px;
718 },
719 { ".tl-mergeoffset",
720 "maximum spacing between merge risers and primary child risers",
721 @ width: 2px;
722 },
723 { ".tl-nodemark",
724 "adjusts the vertical position of graph nodes",
725 @ margin-top: 5px;
726 },
727 { ".tl-node",
728 "commit node",
729 @ width: 10px;
730 @ height: 10px;
731 @ border: 1px solid #000;
732 @ background: #fff;
733 @ cursor: pointer;
734 },
735 { ".tl-node.leaf:after",
736 "leaf commit marker",
737 @ content: '';
738 @ position: absolute;
739 @ top: 3px;
740 @ left: 3px;
741 @ width: 4px;
742 @ height: 4px;
743 @ background: #000;
744 },
745 { ".tl-node.sel:after",
746 "selected commit node marker",
747 @ content: '';
748 @ position: absolute;
749 @ top: 2px;
750 @ left: 2px;
751 @ width: 6px;
752 @ height: 6px;
753 @ background: red;
754 },
755 { ".tl-arrow",
756 "arrow",
757 @ width: 0;
758 @ height: 0;
759 @ transform: scale(.999);
760 @ border: 0 solid transparent;
761 },
762 { ".tl-arrow.u",
763 "up arrow",
764 @ margin-top: -1px;
765 @ border-width: 0 3px;
766 @ border-bottom: 7px solid #000;
767 },
768 { ".tl-arrow.u.sm",
769 "small up arrow",
770 @ border-bottom: 5px solid #000;
771 },
772 { ".tl-line",
773 "line",
774 @ background: #000;
775 @ width: 2px;
776 },
777 { ".tl-arrow.merge",
778 "merge arrow",
779 @ height: 1px;
780 @ border-width: 2px 0;
781 },
782 { ".tl-arrow.merge.l",
783 "left merge arrow",
784 @ border-right: 3px solid #000;
785 },
786 { ".tl-arrow.merge.r",
787 "right merge arrow",
788 @ border-left: 3px solid #000;
789 },
790 { ".tl-line.merge",
791 "merge line",
792 @ width: 1px;
793 },
794 { ".tl-arrow.warp",
795 "timewarp arrow",
796 @ margin-left: 1px;
797 @ border-width: 3px 0;
798 @ border-left: 7px solid #600000;
799 },
800 { ".tl-line.warp",
801 "timewarp line",
802 @ background: #600000;
803 },
804 { "a.tagLink",
805 "the format for the tag links",
806 @
807 },
808 { "span.tagDsp",
@@ -1226,14 +1319,10 @@
1319 },
1320 { "#usetupEditCapability",
1321 "format for capabilities string, mentioned on the user edit page",
1322 @ font-weight: bold;
1323 },
 
 
 
 
1324 { "table.adminLogTable",
1325 "Class for the /admin_log table",
1326 @ text-align: left;
1327 },
1328 { ".adminLogTable .adminTime",
@@ -1336,30 +1425,50 @@
1425 }
1426 }
1427 }
1428
1429 /*
1430 ** Search string zCss for zSelector.
 
1431 **
1432 ** Return true if found. Return false if not found
1433 */
1434 static int containsSelector(const char *zCss, const char *zSelector){
1435 char *z;
1436 int n;
1437 int selectorLen = (int)strlen(zSelector);
1438
1439 for( z=zCss; *z; z+=selectorLen ){
1440 z = strstr(z, zSelector);
1441 if( z==0 ) return 0;
1442 if( z!=zCss ){
1443 for( n=-1; z+n!=zCss && fossil_isspace(z[n]); n--);
1444 if( z+n!=zCss && z[n]!=',' && z[n]!= '}' && z[n]!='/' ) continue;
1445 }
1446 for( n=selectorLen; z[n] && fossil_isspace(z[n]); n++ );
1447 if( z[n]==',' || z[n]=='{' || z[n]=='/' ) return 1;
1448 }
1449 return 0;
1450 }
1451
1452 /*
1453 ** COMMAND: test-contains-selector
1454 **
1455 ** Usage: %fossil test-contains-selector FILENAME SELECTOR
1456 **
1457 ** Determine if the CSS stylesheet FILENAME contains SELECTOR.
1458 */
1459 void contains_selector_cmd(void){
1460 int found;
1461 char *zSelector;
1462 Blob css;
1463 if( g.argc!=4 ) usage("FILENAME SELECTOR");
1464 blob_read_from_file(&css, g.argv[2]);
1465 zSelector = g.argv[3];
1466 found = containsSelector(blob_str(&css), zSelector);
1467 fossil_print("%s %s\n", zSelector, found ? "found" : "not found");
1468 blob_reset(&css);
1469 }
1470
1471
1472 /*
1473 ** WEBPAGE: style.css
1474 **
@@ -1373,11 +1482,11 @@
1482 blob_init(&css,skin_get("css"),-1);
1483
1484 /* add special missing definitions */
1485 for(i=1; cssDefaultList[i].elementClass; i++){
1486 char *z = blob_str(&css);
1487 if( !containsSelector(z, cssDefaultList[i].elementClass) ){
1488 blob_appendf(&css, "/* %s */\n%s {\n%s}\n",
1489 cssDefaultList[i].comment,
1490 cssDefaultList[i].elementClass,
1491 cssDefaultList[i].value);
1492 }
1493
+241 -272
--- src/timeline.c
+++ src/timeline.c
@@ -241,23 +241,17 @@
241241
dateFormat = db_get_int("timeline-date-format", 0);
242242
zDateFmt = P("datefmt");
243243
if( zDateFmt ) dateFormat = atoi(zDateFmt);
244244
if( tmFlags & TIMELINE_GRAPH ){
245245
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>
251246
}
252247
db_static_prepare(&qbranch,
253248
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
254249
TAG_BRANCH
255250
);
256251
257
- @ <table id="timelineTable" class="timelineTable"
258
- @ onclick="clickOnGraph(event)">
252
+ @ <table id="timelineTable" class="timelineTable">
259253
blob_zero(&comment);
260254
while( db_step(pQuery)==SQLITE_ROW ){
261255
int rid = db_column_int(pQuery, 0);
262256
const char *zUuid = db_column_text(pQuery, 1);
263257
int isLeaf = db_column_int(pQuery, 5);
@@ -391,11 +385,11 @@
391385
}
392386
db_reset(&qparent);
393387
gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr,
394388
zUuid, isLeaf);
395389
db_reset(&qbranch);
396
- @ <div id="m%d(gidx)"></div>
390
+ @ <div id="m%d(gidx)" class="tl-nodemark"></div>
397391
}
398392
@</td>
399393
if( zBgClr && zBgClr[0] && rid!=selectedRid ){
400394
@ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
401395
}else{
@@ -589,18 +583,11 @@
589583
graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
590584
if( pGraph->nErr ){
591585
graph_free(pGraph);
592586
pGraph = 0;
593587
}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>
602589
}
603590
}
604591
@ </table>
605592
if( fchngQueryInit ) db_finalize(&fchngQuery);
606593
timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);
@@ -648,31 +635,34 @@
648635
){
649636
if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
650637
GraphRow *pRow;
651638
int i;
652639
char cSep;
653
- int mergeOffset; /* Pixel offset from rail to merge riser */
654640
int iRailPitch; /* Pixels between consecutive rails */
655641
int showArrowheads; /* True to draw arrowheads. False to omit. */
656642
int circleNodes; /* True for circle nodes. False for square nodes */
657643
int colorGraph; /* Use colors for graph lines */
658644
659
- iRailPitch = pGraph->iRailPitch;
645
+ iRailPitch = atoi(PD("railpitch","0"));
660646
showArrowheads = skin_detail_boolean("timeline-arrowheads");
661647
circleNodes = skin_detail_boolean("timeline-circle-nodes");
662648
colorGraph = skin_detail_boolean("timeline-color-graph-lines");
663649
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
+ @ }
674664
/* the rowinfo[] array contains all the information needed to generate
675665
** the graph. Each entry contains information for a single row:
676666
**
677667
** id: The id of the <div> element for the row. This is an integer.
678668
** to get an actual id, prepend "m" to the integer. The top node
@@ -680,13 +670,13 @@
680670
** bg: The background color for this row
681671
** r: The "rail" that the node for this row sits on. The left-most
682672
** rail is 0 and the number increases to the right.
683673
** d: True if there is a "descender" - an arrow coming from the bottom
684674
** 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
686676
** 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
688678
** node has no merge children and no merge-out line is drawn.
689679
** mu: The id of the row which is the top of the merge-out arrow.
690680
** u: Draw a thick child-line out of the top of this node and up to
691681
** the node with an id equal to this value. 0 if it is straight to
692682
** the top of the page, -1 if there is no thick-line riser.
@@ -693,37 +683,25 @@
693683
** f: 0x01: a leaf node.
694684
** au: An array of integers that define thick-line risers for branches.
695685
** The integers are in pairs. For each pair, the first integer is
696686
** is the rail on which the riser should run and the second integer
697687
** 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
699689
** 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[]
701691
** and a thin merge-arrow descender is drawn to the bottom of
702692
** the screen.
703693
** h: The SHA1 hash of the object being graphed
704694
*/
705695
cgi_printf("var rowinfo = [\n");
706696
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
- }
719697
cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
720698
pRow->idx, /* id */
721699
pRow->zBgClr, /* bg */
722700
pRow->iRail, /* r */
723701
pRow->bDescender, /* d */
724
- mo, /* mo */
702
+ pRow->mergeOut, /* mo */
725703
pRow->mergeUpto, /* mu */
726704
pRow->aiRiser[pRow->iRail], /* u */
727705
pRow->isLeaf ? 1 : 0 /* f */
728706
);
729707
/* u */
@@ -743,13 +721,11 @@
743721
/* mi */
744722
cgi_printf("mi:");
745723
cSep = '[';
746724
for(i=0; i<GR_MAX_RAIL; i++){
747725
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;
751727
if( pRow->mergeDown & (1<<i) ) mi = -mi;
752728
cgi_printf("%c%d", cSep, mi);
753729
cSep = ',';
754730
}
755731
}
@@ -756,246 +732,238 @@
756732
if( cSep=='[' ) cgi_printf("[");
757733
cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n");
758734
}
759735
cgi_printf("var nrail = %d\n", pGraph->mxRail+1);
760736
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){
768797
@ 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;
769802
@ if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
770803
@ 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;
773806
@ n.style.position = "absolute";
774
- @ n.style.overflow = "hidden";
775807
@ n.style.left = x0+"px";
776808
@ 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);
781814
@ return n;
782815
@ }
783
- @ function absoluteY(id){
784
- @ var obj = gebi(id);
785
- @ if( !obj ) return;
816
+ @ function absoluteY(obj){
786817
@ var top = 0;
787818
@ if( obj.offsetParent ){
788819
@ do{
789820
@ top += obj.offsetTop;
790821
@ }while( obj = obj.offsetParent );
791822
@ }
792823
@ return top;
793824
@ }
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", "");
997965
@ }else{
998966
if( fileDiff ){
999967
@ location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h+"&sbs=1";
1000968
}else{
1001969
if( db_get_boolean("show-version-diffs", 0)==0 ){
@@ -1004,22 +972,23 @@
1004972
@ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=1";
1005973
}
1006974
}
1007975
@ }
1008976
@ }
1009
- @ var lastId = "m"+rowinfo[rowinfo.length-1].id;
977
+ @ var lastRow = gebi("m"+rowinfo[rowinfo.length-1].id);
1010978
@ var lastY = 0;
1011979
@ function checkHeight(){
1012
- @ var h = absoluteY(lastId);
980
+ @ var h = absoluteY(lastRow);
1013981
@ if( h!=lastY ){
1014982
@ renderGraph();
1015983
@ lastY = h;
1016984
@ }
1017
- @ setTimeout("checkHeight();", 1000);
985
+ @ setTimeout(checkHeight, 1000);
1018986
@ }
987
+ @ initGraph();
1019988
@ checkHeight();
1020
- @ </script>
989
+ @ }())</script>
1021990
}
1022991
}
1023992
1024993
/*
1025994
** Create a temporary table suitable for storing timeline data.
1026995
1027996
ADDED test/contains-selector.test
--- src/timeline.c
+++ src/timeline.c
@@ -241,23 +241,17 @@
241 dateFormat = db_get_int("timeline-date-format", 0);
242 zDateFmt = P("datefmt");
243 if( zDateFmt ) dateFormat = atoi(zDateFmt);
244 if( tmFlags & TIMELINE_GRAPH ){
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 }
252 db_static_prepare(&qbranch,
253 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
254 TAG_BRANCH
255 );
256
257 @ <table id="timelineTable" class="timelineTable"
258 @ onclick="clickOnGraph(event)">
259 blob_zero(&comment);
260 while( db_step(pQuery)==SQLITE_ROW ){
261 int rid = db_column_int(pQuery, 0);
262 const char *zUuid = db_column_text(pQuery, 1);
263 int isLeaf = db_column_int(pQuery, 5);
@@ -391,11 +385,11 @@
391 }
392 db_reset(&qparent);
393 gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr,
394 zUuid, isLeaf);
395 db_reset(&qbranch);
396 @ <div id="m%d(gidx)"></div>
397 }
398 @</td>
399 if( zBgClr && zBgClr[0] && rid!=selectedRid ){
400 @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
401 }else{
@@ -589,18 +583,11 @@
589 graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
590 if( pGraph->nErr ){
591 graph_free(pGraph);
592 pGraph = 0;
593 }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>
602 }
603 }
604 @ </table>
605 if( fchngQueryInit ) db_finalize(&fchngQuery);
606 timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);
@@ -648,31 +635,34 @@
648 ){
649 if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
650 GraphRow *pRow;
651 int i;
652 char cSep;
653 int mergeOffset; /* Pixel offset from rail to merge riser */
654 int iRailPitch; /* Pixels between consecutive rails */
655 int showArrowheads; /* True to draw arrowheads. False to omit. */
656 int circleNodes; /* True for circle nodes. False for square nodes */
657 int colorGraph; /* Use colors for graph lines */
658
659 iRailPitch = pGraph->iRailPitch;
660 showArrowheads = skin_detail_boolean("timeline-arrowheads");
661 circleNodes = skin_detail_boolean("timeline-circle-nodes");
662 colorGraph = skin_detail_boolean("timeline-color-graph-lines");
663
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
 
 
 
 
674 /* the rowinfo[] array contains all the information needed to generate
675 ** the graph. Each entry contains information for a single row:
676 **
677 ** id: The id of the <div> element for the row. This is an integer.
678 ** to get an actual id, prepend "m" to the integer. The top node
@@ -680,13 +670,13 @@
680 ** bg: The background color for this row
681 ** r: The "rail" that the node for this row sits on. The left-most
682 ** rail is 0 and the number increases to the right.
683 ** d: True if there is a "descender" - an arrow coming from the bottom
684 ** of the page straight up to this node.
685 ** mo: "merge-out". If non-zero, this is one more than the x-coordinate
686 ** 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
688 ** node has no merge children and no merge-out line is drawn.
689 ** mu: The id of the row which is the top of the merge-out arrow.
690 ** u: Draw a thick child-line out of the top of this node and up to
691 ** the node with an id equal to this value. 0 if it is straight to
692 ** the top of the page, -1 if there is no thick-line riser.
@@ -693,37 +683,25 @@
693 ** f: 0x01: a leaf node.
694 ** au: An array of integers that define thick-line risers for branches.
695 ** The integers are in pairs. For each pair, the first integer is
696 ** is the rail on which the riser should run and the second integer
697 ** is the id of the node upto which the riser should run.
698 ** mi: "merge-in". An array of integer x-coordinates from which
699 ** merge arrows should be drawn into this node. If the value is
700 ** negative, then the x-coordinate is the absolute value of mi[]
701 ** and a thin merge-arrow descender is drawn to the bottom of
702 ** the screen.
703 ** h: The SHA1 hash of the object being graphed
704 */
705 cgi_printf("var rowinfo = [\n");
706 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 cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
720 pRow->idx, /* id */
721 pRow->zBgClr, /* bg */
722 pRow->iRail, /* r */
723 pRow->bDescender, /* d */
724 mo, /* mo */
725 pRow->mergeUpto, /* mu */
726 pRow->aiRiser[pRow->iRail], /* u */
727 pRow->isLeaf ? 1 : 0 /* f */
728 );
729 /* u */
@@ -743,13 +721,11 @@
743 /* mi */
744 cgi_printf("mi:");
745 cSep = '[';
746 for(i=0; i<GR_MAX_RAIL; i++){
747 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;
751 if( pRow->mergeDown & (1<<i) ) mi = -mi;
752 cgi_printf("%c%d", cSep, mi);
753 cSep = ',';
754 }
755 }
@@ -756,246 +732,238 @@
756 if( cSep=='[' ) cgi_printf("[");
757 cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n");
758 }
759 cgi_printf("var nrail = %d\n", pGraph->mxRail+1);
760 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){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
768 @ var n = document.createElement("div");
 
 
 
 
769 @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
770 @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
771 @ var w = x1-x0+1;
772 @ var h = y1-y0+1;
773 @ n.style.position = "absolute";
774 @ n.style.overflow = "hidden";
775 @ n.style.left = x0+"px";
776 @ 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);
 
781 @ return n;
782 @ }
783 @ function absoluteY(id){
784 @ var obj = gebi(id);
785 @ if( !obj ) return;
786 @ var top = 0;
787 @ if( obj.offsetParent ){
788 @ do{
789 @ top += obj.offsetTop;
790 @ }while( obj = obj.offsetParent );
791 @ }
792 @ return top;
793 @ }
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;
997 @ }else{
998 if( fileDiff ){
999 @ location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h+"&sbs=1";
1000 }else{
1001 if( db_get_boolean("show-version-diffs", 0)==0 ){
@@ -1004,22 +972,23 @@
1004 @ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=1";
1005 }
1006 }
1007 @ }
1008 @ }
1009 @ var lastId = "m"+rowinfo[rowinfo.length-1].id;
1010 @ var lastY = 0;
1011 @ function checkHeight(){
1012 @ var h = absoluteY(lastId);
1013 @ if( h!=lastY ){
1014 @ renderGraph();
1015 @ lastY = h;
1016 @ }
1017 @ setTimeout("checkHeight();", 1000);
1018 @ }
 
1019 @ checkHeight();
1020 @ </script>
1021 }
1022 }
1023
1024 /*
1025 ** Create a temporary table suitable for storing timeline data.
1026
1027 DDED test/contains-selector.test
--- src/timeline.c
+++ src/timeline.c
@@ -241,23 +241,17 @@
241 dateFormat = db_get_int("timeline-date-format", 0);
242 zDateFmt = P("datefmt");
243 if( zDateFmt ) dateFormat = atoi(zDateFmt);
244 if( tmFlags & TIMELINE_GRAPH ){
245 pGraph = graph_init();
 
 
 
 
 
246 }
247 db_static_prepare(&qbranch,
248 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
249 TAG_BRANCH
250 );
251
252 @ <table id="timelineTable" class="timelineTable">
 
253 blob_zero(&comment);
254 while( db_step(pQuery)==SQLITE_ROW ){
255 int rid = db_column_int(pQuery, 0);
256 const char *zUuid = db_column_text(pQuery, 1);
257 int isLeaf = db_column_int(pQuery, 5);
@@ -391,11 +385,11 @@
385 }
386 db_reset(&qparent);
387 gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr,
388 zUuid, isLeaf);
389 db_reset(&qbranch);
390 @ <div id="m%d(gidx)" class="tl-nodemark"></div>
391 }
392 @</td>
393 if( zBgClr && zBgClr[0] && rid!=selectedRid ){
394 @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
395 }else{
@@ -589,18 +583,11 @@
583 graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
584 if( pGraph->nErr ){
585 graph_free(pGraph);
586 pGraph = 0;
587 }else{
588 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
 
 
 
 
 
 
 
589 }
590 }
591 @ </table>
592 if( fchngQueryInit ) db_finalize(&fchngQuery);
593 timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);
@@ -648,31 +635,34 @@
635 ){
636 if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
637 GraphRow *pRow;
638 int i;
639 char cSep;
 
640 int iRailPitch; /* Pixels between consecutive rails */
641 int showArrowheads; /* True to draw arrowheads. False to omit. */
642 int circleNodes; /* True for circle nodes. False for square nodes */
643 int colorGraph; /* Use colors for graph lines */
644
645 iRailPitch = atoi(PD("railpitch","0"));
646 showArrowheads = skin_detail_boolean("timeline-arrowheads");
647 circleNodes = skin_detail_boolean("timeline-circle-nodes");
648 colorGraph = skin_detail_boolean("timeline-color-graph-lines");
649
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 @ }
664 /* the rowinfo[] array contains all the information needed to generate
665 ** the graph. Each entry contains information for a single row:
666 **
667 ** id: The id of the <div> element for the row. This is an integer.
668 ** to get an actual id, prepend "m" to the integer. The top node
@@ -680,13 +670,13 @@
670 ** bg: The background color for this row
671 ** r: The "rail" that the node for this row sits on. The left-most
672 ** rail is 0 and the number increases to the right.
673 ** d: True if there is a "descender" - an arrow coming from the bottom
674 ** of the page straight up to this node.
675 ** mo: "merge-out". If non-negative, this is the rail position
676 ** for the upward portion of a merge arrow. The merge arrow goes up
677 ** to the row identified by mu:. If this value is negative then
678 ** node has no merge children and no merge-out line is drawn.
679 ** mu: The id of the row which is the top of the merge-out arrow.
680 ** u: Draw a thick child-line out of the top of this node and up to
681 ** the node with an id equal to this value. 0 if it is straight to
682 ** the top of the page, -1 if there is no thick-line riser.
@@ -693,37 +683,25 @@
683 ** f: 0x01: a leaf node.
684 ** au: An array of integers that define thick-line risers for branches.
685 ** The integers are in pairs. For each pair, the first integer is
686 ** is the rail on which the riser should run and the second integer
687 ** is the id of the node upto which the riser should run.
688 ** mi: "merge-in". An array of integer rail positions from which
689 ** merge arrows should be drawn into this node. If the value is
690 ** negative, then the rail position is the absolute value of mi[]
691 ** and a thin merge-arrow descender is drawn to the bottom of
692 ** the screen.
693 ** h: The SHA1 hash of the object being graphed
694 */
695 cgi_printf("var rowinfo = [\n");
696 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
 
 
 
 
 
 
 
 
 
 
 
 
697 cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
698 pRow->idx, /* id */
699 pRow->zBgClr, /* bg */
700 pRow->iRail, /* r */
701 pRow->bDescender, /* d */
702 pRow->mergeOut, /* mo */
703 pRow->mergeUpto, /* mu */
704 pRow->aiRiser[pRow->iRail], /* u */
705 pRow->isLeaf ? 1 : 0 /* f */
706 );
707 /* u */
@@ -743,13 +721,11 @@
721 /* mi */
722 cgi_printf("mi:");
723 cSep = '[';
724 for(i=0; i<GR_MAX_RAIL; i++){
725 if( pRow->mergeIn[i] ){
726 int mi = i;
 
 
727 if( pRow->mergeDown & (1<<i) ) mi = -mi;
728 cgi_printf("%c%d", cSep, mi);
729 cSep = ',';
730 }
731 }
@@ -756,246 +732,238 @@
732 if( cSep=='[' ) cgi_printf("[");
733 cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n");
734 }
735 cgi_printf("var nrail = %d\n", pGraph->mxRail+1);
736 graph_free(pGraph);
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){
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;
802 @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
803 @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
804 @ var w = x1-x0;
805 @ var h = y1-y0;
806 @ n.style.position = "absolute";
 
807 @ n.style.left = x0+"px";
808 @ n.style.top = y0+"px";
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);
814 @ return n;
815 @ }
816 @ function absoluteY(obj){
 
 
817 @ var top = 0;
818 @ if( obj.offsetParent ){
819 @ do{
820 @ top += obj.offsetTop;
821 @ }while( obj = obj.offsetParent );
822 @ }
823 @ return top;
824 @ }
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", "");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
965 @ }else{
966 if( fileDiff ){
967 @ location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h+"&sbs=1";
968 }else{
969 if( db_get_boolean("show-version-diffs", 0)==0 ){
@@ -1004,22 +972,23 @@
972 @ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=1";
973 }
974 }
975 @ }
976 @ }
977 @ var lastRow = gebi("m"+rowinfo[rowinfo.length-1].id);
978 @ var lastY = 0;
979 @ function checkHeight(){
980 @ var h = absoluteY(lastRow);
981 @ if( h!=lastY ){
982 @ renderGraph();
983 @ lastY = h;
984 @ }
985 @ setTimeout(checkHeight, 1000);
986 @ }
987 @ initGraph();
988 @ checkHeight();
989 @ }())</script>
990 }
991 }
992
993 /*
994 ** Create a temporary table suitable for storing timeline data.
995
996 DDED test/contains-selector.test
--- a/test/contains-selector.test
+++ b/test/contains-selector.test
@@ -0,0 +1,43 @@
1
+#
2
+# Copyright (c) 2015 D. Richard Hipp
3
+#
4
+# This program is free software; you can redistribute it and/or
5
+# modify it under the terms of the Simplified BSD License (also
6
+# known as the "2-Clause License" or "FreeBSD License".)
7
+#
8
+# This program is distributed in the hope that it will be useful,
9
+# but without any warranty; without even the implied warranty of
10
+# merchantability or fitness for a particular purpose.
11
+#
12
+# Author contact information:
13
+# [email protected]
14
+# http://www.hwaci.com/drh/
15
+#
16
+############################################################################
17
+#
18
+# Test containsSelector() proc contains-selector {testId css selectorResultMap} {
19
+ set css [string trim $css]
20
+ set filename [file join $::tempPath compare-selector.css]
21
+ set fh [open $filename w]
22
+ puts -nonewline $fh $css
23
+ close $fh
24
+ foreach {selector found} $selectorResultMap {
25
+ set expected "$selector [expr {$found ? "found" : "not found"}]"
26
+ set result [fossil test-contains-selector $filename $selector]
27
+ test "contains-selector $testId $selector" {$result eq $expected}
28
+ }
29
+ file delete $filename
30
+}
31
+
32
+contains-selector 1 {
33
+ .a.b {}
34
+ .c .de {}
35
+ /* comment */
36
+ .c .d, .e /* comment */ {}
37
+} {
38
+ .a 0
39
+ .b 0
40
+ .a.b 1
41
+ .c 0
42
+ .d 0
43
+ {.c
--- a/test/contains-selector.test
+++ b/test/contains-selector.test
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/contains-selector.test
+++ b/test/contains-selector.test
@@ -0,0 +1,43 @@
1 #
2 # Copyright (c) 2015 D. Richard Hipp
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the Simplified BSD License (also
6 # known as the "2-Clause License" or "FreeBSD License".)
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but without any warranty; without even the implied warranty of
10 # merchantability or fitness for a particular purpose.
11 #
12 # Author contact information:
13 # [email protected]
14 # http://www.hwaci.com/drh/
15 #
16 ############################################################################
17 #
18 # Test containsSelector() proc contains-selector {testId css selectorResultMap} {
19 set css [string trim $css]
20 set filename [file join $::tempPath compare-selector.css]
21 set fh [open $filename w]
22 puts -nonewline $fh $css
23 close $fh
24 foreach {selector found} $selectorResultMap {
25 set expected "$selector [expr {$found ? "found" : "not found"}]"
26 set result [fossil test-contains-selector $filename $selector]
27 test "contains-selector $testId $selector" {$result eq $expected}
28 }
29 file delete $filename
30 }
31
32 contains-selector 1 {
33 .a.b {}
34 .c .de {}
35 /* comment */
36 .c .d, .e /* comment */ {}
37 } {
38 .a 0
39 .b 0
40 .a.b 1
41 .c 0
42 .d 0
43 {.c

Keyboard Shortcuts

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