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

Overview
Comment:Anti-robot defenses are now CSP-safe.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | main.js
Files: files | file ages | folders
SHA3-256:4f2c9841323513544e41ab779c759b81488c7498d4dbaa106309bb2a8597d1ec
User & Date: drh 2017-12-05 01:05:25
Context
2017-12-05
01:24
Remove unnecessary "antibot" class names from anchors. Closed-Leaf check-in: 52a47db1 user: drh tags: main.js
01:05
Anti-robot defenses are now CSP-safe. check-in: 4f2c9841 user: drh tags: main.js
2017-12-04
21:08
Attempt to add a separate JS file and source it just prior to </body> check-in: 487aa43f user: drh tags: main.js
Changes

Changes to src/codecheck1.c.

310
311
312
313
314
315
316

317
318
319
320
321
322
323
  unsigned fmtFlags;     /* Processing flags */
} aFmtFunc[] = {
  { "admin_log",               1, 0 },
  { "blob_append_sql",         2, FMT_NO_S },
  { "blob_appendf",            2, 0 },
  { "cgi_panic",               1, 0 },
  { "cgi_redirectf",           1, 0 },

  { "db_blob",                 2, FMT_NO_S },
  { "db_double",               2, FMT_NO_S },
  { "db_err",                  1, 0 },
  { "db_exists",               1, FMT_NO_S },
  { "db_int",                  2, FMT_NO_S },
  { "db_int64",                2, FMT_NO_S },
  { "db_multi_exec",           1, FMT_NO_S },







>







310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
  unsigned fmtFlags;     /* Processing flags */
} aFmtFunc[] = {
  { "admin_log",               1, 0 },
  { "blob_append_sql",         2, FMT_NO_S },
  { "blob_appendf",            2, 0 },
  { "cgi_panic",               1, 0 },
  { "cgi_redirectf",           1, 0 },
  { "chref",                   2, 0 },
  { "db_blob",                 2, FMT_NO_S },
  { "db_double",               2, FMT_NO_S },
  { "db_err",                  1, 0 },
  { "db_exists",               1, FMT_NO_S },
  { "db_int",                  2, FMT_NO_S },
  { "db_int64",                2, FMT_NO_S },
  { "db_multi_exec",           1, FMT_NO_S },

Changes to src/info.c.

796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
    verboseFlag = !verboseFlag;
    zPage = "ci";
    zPageHide = "vinfo";
  }
  diffFlags = construct_diff_flags(verboseFlag, sideBySide);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  if( verboseFlag ){
    @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName))
    @ Hide&nbsp;Diffs</a>
    if( sideBySide ){
      @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW))
      @ Unified&nbsp;Diffs</a>
    }else{
      @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW))
      @ Side-by-Side&nbsp;Diffs</a>
    }
    if( *zW ){
      @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
      @ Show&nbsp;Whitespace&nbsp;Changes</a>
    }else{
      @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
      @ Ignore&nbsp;Whitespace</a>
    }
  }else{
    @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName))
    @ Show&nbsp;Unified&nbsp;Diffs</a>
    @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName))
    @ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
  }
  if( zParent ){
    @ %z(xhref("class='button'","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
    @ Patch</a>
  }
  if( g.perm.Admin ){
    @ %z(xhref("class='button'","%R/mlink?ci=%!S",zUuid))MLink Table</a>
  }
  @</div>
  if( pRe ){
    @ <p><b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b></p>
  }
  db_prepare(&q3,







|


|


|



|


|



|

|



|



|







796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
    verboseFlag = !verboseFlag;
    zPage = "ci";
    zPageHide = "vinfo";
  }
  diffFlags = construct_diff_flags(verboseFlag, sideBySide);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  if( verboseFlag ){
    @ %z(chref("button","%R/%s/%T",zPageHide,zName))
    @ Hide&nbsp;Diffs</a>
    if( sideBySide ){
      @ %z(chref("button","%R/%s/%T?sbs=0%s",zPage,zName,zW))
      @ Unified&nbsp;Diffs</a>
    }else{
      @ %z(chref("button","%R/%s/%T?sbs=1%s",zPage,zName,zW))
      @ Side-by-Side&nbsp;Diffs</a>
    }
    if( *zW ){
      @ %z(chref("button","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
      @ Show&nbsp;Whitespace&nbsp;Changes</a>
    }else{
      @ %z(chref("button","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
      @ Ignore&nbsp;Whitespace</a>
    }
  }else{
    @ %z(chref("button","%R/%s/%T?sbs=0",zPage,zName))
    @ Show&nbsp;Unified&nbsp;Diffs</a>
    @ %z(chref("button","%R/%s/%T?sbs=1",zPage,zName))
    @ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
  }
  if( zParent ){
    @ %z(chref("button","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
    @ Patch</a>
  }
  if( g.perm.Admin ){
    @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</a>
  }
  @</div>
  if( pRe ){
    @ <p><b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b></p>
  }
  db_prepare(&q3,

Changes to src/main.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33



/* This script is sourced just prior to the </body> in every Fossil webpage */
var pageDataObj = document.getElementById('page-data');
var links = new Array();
if( pageDataObj ){
  var pageData = JSON.parse(pageDataObj.textContent || pageDataObj.innerText);
  var r;
  for(r in pageData){
    switch(r.op){
      "setAllHrefs": {
        links = r.links;
        if(r.mouseMove){
          document.getElementsByTagName("body")[0].onmousemove=function(){
            setTimeout("setAllHrefs();",r.nDelay);
          }
        }else{
          setTimeout("setAllHrefs();",r.nDelay);
        }
        break;
      }
      "no-op": {
        alert('finished processing page-data');
        break;
      }
    }
  }
}
function setAllHrefs(){
  var x;
  for(x in links){
    var y = document.getElementById(x.id);
    if(y) y.href=x.href
  }
}




|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* This script is sourced just prior to the </body> in every Fossil webpage */
var x = document.getElementById("page-data");
var jx = x.textContent || x.innerText;
var g = JSON.parse(jx);

/* As an anti-robot defense, <a> elements are initially coded with the
** href= set to the honeypot, and <form> elements are initialized with
** action= set to the login page.  The real values for href= and action=
** are held in data-href= and data-action=.  The following code moves
** data-href= into href= and data-action= into action= for all
** <a> and <form> elements, after delay and maybe also after mouse
** movement is seen.
*/
function setAllHrefs(){
  var anchors = document.getElementsByTagName("a");
  for(var i=0; i<anchors.length; i++){
    var j = anchors[i];
    if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
  }
  var forms = document.getElementsByTagName("form");
  for(var i=0; i<forms.length; i++){
    var j = forms[i];
    if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
  }
}
if(g.antibot.enable){
  var isOperaMini =
       Object.prototype.toString.call(window.operamini)==="[object OperaMini]";
  if(g.antibot.mouseover && !isOperaMini){
    document.getElementByTagName("body")[0].onmousemove=function(){
      setTimeout("setAllHrefs();",g.antibot.delay);
    }
  }else{
    setTimeout("setAllHrefs();",g.antibot.delay);
  }
}

Changes to src/style.c.

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
...
129
130
131
132
133
134
135


136
137

138
139
140
141
142
143
144
...
148
149
150
151
152
153
154
155
156
157
158

159
160










161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
...
574
575
576
577
578
579
580

581
582
583
584
585
586
587
...
724
725
726
727
728
729
730

731
732
733

734


















735



736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
static int sideboxUsed = 0;

/*
** Ad-unit styles.
*/
static unsigned adUnitFlags = 0;

/*
** Page data JSON
*/
static Blob pageDataJson = BLOB_INITIALIZER;


/*
** List of hyperlinks and forms that need to be resolved by javascript in
** the footer.
*/
char **aHref = 0;
int nHref = 0;
int nHrefAlloc = 0;
char **aFormAction = 0;
int nFormAction = 0;

/*
** Generate and return a anchor tag like this:
**
**        <a href="URL">
**  or    <a id="ID">
**
................................................................................
**
**      @ %z(href("%R/artifact/%s",zUuid))%h(zFN)</a>
**
** Note %z format.  The string returned by this function is always
** obtained from fossil_malloc() so rendering it with %z will reclaim
** that memory space.
**


** There are two versions of this routine: href() does a plain hyperlink
** and xhref() adds extra attribute text.

**
** g.perm.Hyperlink is true if the user has the Hyperlink (h) property.
** Most logged in users should have this property, since we can assume
** that a logged in user is not a bot.  Only "nobody" lacks g.perm.Hyperlink,
** typically.
*/
char *xhref(const char *zExtra, const char *zFormat, ...){
................................................................................
  zUrl = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
    char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
    fossil_free(zUrl);
    return zHUrl;
  }
  if( nHref>=nHrefAlloc ){
    nHrefAlloc = nHrefAlloc*2 + 10;
    aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
  }

  aHref[nHref++] = zUrl;
  return mprintf("<a %s id='a%d' href='%R/honeypot'>", zExtra, nHref);










}
char *href(const char *zFormat, ...){
  char *zUrl;
  va_list ap;
  va_start(ap, zFormat);
  zUrl = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
    char *zHUrl = mprintf("<a href=\"%h\">", zUrl);
    fossil_free(zUrl);
    return zHUrl;
  }
  if( nHref>=nHrefAlloc ){
    nHrefAlloc = nHrefAlloc*2 + 10;
    aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
  }
  aHref[nHref++] = zUrl;
  return mprintf("<a id='a%d' href='%R/honeypot'>", nHref);
}

/*
** Generate <form method="post" action=ARG>.  The ARG value is inserted
** by javascript.
*/
void form_begin(const char *zOtherArgs, const char *zAction, ...){
................................................................................
  if( zOtherArgs==0 ) zOtherArgs = "";
  va_start(ap, zAction);
  zLink = vmprintf(zAction, ap);
  va_end(ap);
  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
    @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
  }else{
    int n;
    aFormAction = fossil_realloc(aFormAction, (nFormAction+1)*sizeof(char*));
    aFormAction[nFormAction++] = zLink;
    n = nFormAction;
    @ <form id="form%d(n)" method="POST" action='%R/login' %s(zOtherArgs)>
  }
}

/*
** Append page-data JSON
*/
void style_pagedata_appendf(const char *zFormat, ...){
  va_list ap;
  if( blob_size(&pageDataJson)==0 ){
    blob_append(&pageDataJson, "[", 1);
  }
  va_start(ap, zFormat);
  blob_vappendf(&pageDataJson, zFormat, ap);
  va_end(ap);
}

/*
** Generate javascript that will set the href= attribute on all anchors.
*/
void style_resolve_href(void){
  int i;
  int nDelay = db_get_int("auto-hyperlink-delay",10);
  if( !g.perm.Hyperlink ) return;
  if( nHref==0 && nFormAction==0 ) return;
  @ <script>
  @ function setAllHrefs(){
  if( g.javascriptHyperlink ){
    for(i=0; i<nHref; i++){
      @ gebi("a%d(i+1)").href="%s(aHref[i])";
    }
  }
  for(i=0; i<nFormAction; i++){
    @ gebi("form%d(i+1)").action="%s(aFormAction[i])";
  }
  @ }
  if( sqlite3_strglob("*Opera Mini/[1-9]*", PD("HTTP_USER_AGENT",""))==0 ){
    /* Special case for Opera Mini, which executes JS server-side */
    @ var isOperaMini = Object.prototype.toString.call(window.operamini)
    @                   === "[object OperaMini]";
    @ if( isOperaMini ){
    @   setTimeout("setAllHrefs();",%d(nDelay));
    @ }
  }else if( db_get_boolean("auto-hyperlink-ishuman",0) && g.isHuman ){
    /* Active hyperlinks after a delay */
    @ setTimeout("setAllHrefs();",%d(nDelay));
  }else if( db_get_boolean("auto-hyperlink-mouseover",0) ){
    /* Require mouse movement before starting the teim that will
    ** activating hyperlinks */
    @ document.getElementsByTagName("body")[0].onmousemove=function(){
    @   setTimeout("setAllHrefs();",%d(nDelay));
    @   this.onmousemove = null;
    @ }
  }else{
    /* Active hyperlinks after a delay */
    @ setTimeout("setAllHrefs();",%d(nDelay));
  }
  @ </script>
}

/*
** Add a new element to the submenu
*/
void style_submenu_element(
  const char *zLabel,
  const char *zLink,
  ...
................................................................................
/*
** Draw the footer at the bottom of the page.
*/
void style_footer(void){
  const char *zFooter;
  const char *zAd = 0;
  unsigned int mAdFlags = 0;


  if( !headerHasBeenGenerated ) return;

  /* Go back and put the submenu at the top of the page.  We delay the
  ** creation of the submenu until the end so that we can add elements
  ** to the submenu while generating page text.
  */
................................................................................
    ** the additional clear/both is needed to extend the content
    ** part to the end of an optional sidebox.
    */
    @ <div class="endContent"></div>
  }
  @ </div>


  /* Set the href= field on hyperlinks.  Do this before the footer since
  ** the footer will be generating </html> */
  style_resolve_href();




















  zFooter = skin_get("footer");



  if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
  Th_Render(zFooter);
  if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);

  /* Render trace log if TH1 tracing is enabled. */
  if( g.thTrace ){
    cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
    cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
    cgi_append_content("</span>\n", -1);
  }

  /* Add document end mark if it was not in the footer */
  if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){
    style_pagedata_appendf("{'op':'no-op'}]");
    @ <script type='application/json' id='page-data'>
    @ %s(blob_str(&pageDataJson))
    @ </script>
    @ <script src='%s(g.zBaseURL)/main.js' type='application/javascript'>\
    @ <script>
    @ </body>
    @ </html>
  }
}

/*
** Begin a side-box on the right-hand side of a page.  The title and







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
|
|
>







 







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












|
|
<
<
<
<







 







|
<
<
<
|



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>







 







>



>

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

>
>
>













<
<
<
<

|







81
82
83
84
85
86
87















88
89
90
91
92
93
94
...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
136
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172




173
174
175
176
177
178
179
...
182
183
184
185
186
187
188
189



190
191
192
193
























































194
195
196
197
198
199
200
...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
...
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707




708
709
710
711
712
713
714
715
716
static int sideboxUsed = 0;

/*
** Ad-unit styles.
*/
static unsigned adUnitFlags = 0;

















/*
** Generate and return a anchor tag like this:
**
**        <a href="URL">
**  or    <a id="ID">
**
................................................................................
**
**      @ %z(href("%R/artifact/%s",zUuid))%h(zFN)</a>
**
** Note %z format.  The string returned by this function is always
** obtained from fossil_malloc() so rendering it with %z will reclaim
** that memory space.
**
** There are three versions of this routine:
**
**    (1)   href() does a plain hyperlink
**    (2)   xhref() adds extra attribute text
**    (3)   chref() adds a class name
**
** g.perm.Hyperlink is true if the user has the Hyperlink (h) property.
** Most logged in users should have this property, since we can assume
** that a logged in user is not a bot.  Only "nobody" lacks g.perm.Hyperlink,
** typically.
*/
char *xhref(const char *zExtra, const char *zFormat, ...){
................................................................................
  zUrl = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
    char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
    fossil_free(zUrl);
    return zHUrl;
  }
  return mprintf("<a %s class='antibot' data-href='%z' href='%R/honeypot'>",
                  zExtra, zUrl);

}
char *chref(const char *zExtra, const char *zFormat, ...){
  char *zUrl;
  va_list ap;
  va_start(ap, zFormat);
  zUrl = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
    char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
    fossil_free(zUrl);
    return zHUrl;
  }
  return mprintf("<a class='antibot %s' data-href='%z' href='%R/honeypot'>",
                 zExtra, zUrl);
}
char *href(const char *zFormat, ...){
  char *zUrl;
  va_list ap;
  va_start(ap, zFormat);
  zUrl = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
    char *zHUrl = mprintf("<a href=\"%h\">", zUrl);
    fossil_free(zUrl);
    return zHUrl;
  }
  return mprintf("<a class='antibot' data-href='%s' href='%R/honeypot'>",
                  zUrl);




}

/*
** Generate <form method="post" action=ARG>.  The ARG value is inserted
** by javascript.
*/
void form_begin(const char *zOtherArgs, const char *zAction, ...){
................................................................................
  if( zOtherArgs==0 ) zOtherArgs = "";
  va_start(ap, zAction);
  zLink = vmprintf(zAction, ap);
  va_end(ap);
  if( g.perm.Hyperlink && !g.javascriptHyperlink ){
    @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
  }else{
    @ <form method="POST" data-action='%s(zLink)' action='%R/login' \



    @ %s(zOtherArgs)>
  }
}

























































/*
** Add a new element to the submenu
*/
void style_submenu_element(
  const char *zLabel,
  const char *zLink,
  ...
................................................................................
/*
** Draw the footer at the bottom of the page.
*/
void style_footer(void){
  const char *zFooter;
  const char *zAd = 0;
  unsigned int mAdFlags = 0;
  int bMouseover = 0;            /* Active hyperlinks after mouseover */

  if( !headerHasBeenGenerated ) return;

  /* Go back and put the submenu at the top of the page.  We delay the
  ** creation of the submenu until the end so that we can add elements
  ** to the submenu while generating page text.
  */
................................................................................
    ** the additional clear/both is needed to extend the content
    ** part to the end of an optional sidebox.
    */
    @ <div class="endContent"></div>
  }
  @ </div>

#if 0
  /* Set the href= field on hyperlinks.  Do this before the footer since
  ** the footer will be generating </html> */
  style_resolve_href();
#endif

  /* Load up the page data */
  @ <script id='page-data' type='application/json'>
  if( !g.javascriptHyperlink ){
    @ {"antibot":{"enable":0},
  }else{
    int nDelay = db_get_int("auto-hyperlink-delay",0);
    int bMouseover;
    bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0))
                 && db_get_boolean("auto-hyperlink-mouseover",0);
    @ {"antibot":
    @   {"enable":1,
    @    "delay":%d(nDelay),
    @    "mouseover":%d(bMouseover)},
  }
  @ "noop":0}
  @ </script>


  zFooter = skin_get("footer");
  if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){
    @ <script src='%s(g.zBaseURL)/main.js' type='application/javascript'>\
  }
  if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
  Th_Render(zFooter);
  if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);

  /* Render trace log if TH1 tracing is enabled. */
  if( g.thTrace ){
    cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
    cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
    cgi_append_content("</span>\n", -1);
  }

  /* Add document end mark if it was not in the footer */
  if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){




    @ <script src='%s(g.zBaseURL)/main.js' type='application/javascript'>\
    @ </script>
    @ </body>
    @ </html>
  }
}

/*
** Begin a side-box on the right-hand side of a page.  The title and

Changes to src/tag.c.

667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
    " AND tagname GLOB 'sym-*'"
    " ORDER BY tagname"
  );
  @ <ul>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    if( g.perm.Hyperlink ){
      @ <li>%z(xhref("class='taglink'","%R/timeline?t=%T&n=200",zName))
      @ %h(zName)</a></li>
    }else{
      @ <li><span class="tagDsp">%h(zName)</span></li>
    }
  }
  @ </ul>
  db_finalize(&q);







|







667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
    " AND tagname GLOB 'sym-*'"
    " ORDER BY tagname"
  );
  @ <ul>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    if( g.perm.Hyperlink ){
      @ <li>%z(chref("taglink","%R/timeline?t=%T&n=200",zName))
      @ %h(zName)</a></li>
    }else{
      @ <li><span class="tagDsp">%h(zName)</span></li>
    }
  }
  @ </ul>
  db_finalize(&q);

Changes to src/timeline.c.

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
....
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
}

/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
  if( g.perm.Hyperlink ){
    @ %z(xhref("class='timelineHistLink'","%R/info/%!S",zUuid))[%S(zUuid)]</a>
  }else{
    @ <span class="timelineHistDsp">[%S(zUuid)]</span>
  }
}

/*
** Generate a hyperlink to a date & time.
................................................................................
    if( zType[0]=='e' && tagid ){
      char *zId;
      zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
                        tagid);
      zDateLink = href("%R/technote/%s",zId);
      free(zId);
    }else if( zUuid ){
      zDateLink = xhref("class='timelineHistLink'", "%R/info/%!S", zUuid);
    }else{
      zDateLink = mprintf("<a>");
    }
    /* WAS: zDateLink = href("%R/timeline?c=%!S&unhide", zUuid); */
    @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
    @ <td class="timelineGraph">
    if( tmFlags & TIMELINE_UCOLOR )  zBgClr = zUser ? hash_color(zUser) : 0;
................................................................................

  /* Report any errors. */
  if( zError ){
    @ <p class="generalError">%h(zError)</p>
  }

  if( zNewerButton ){
    @ %z(xhref("class='button'","%z",zNewerButton))More&nbsp;&uarr;</a>
  }
  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
  db_finalize(&q);
  if( zOlderButton ){
    @ %z(xhref("class='button'","%z",zOlderButton))More&nbsp;&darr;</a>
  }
  style_footer();
}

/*
** The input query q selects various records.  Print a human-readable
** summary of those records.







|







 







|







 







|




|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
....
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
}

/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
  if( g.perm.Hyperlink ){
    @ %z(chref("timelineHistLink","%R/info/%!S",zUuid))[%S(zUuid)]</a>
  }else{
    @ <span class="timelineHistDsp">[%S(zUuid)]</span>
  }
}

/*
** Generate a hyperlink to a date & time.
................................................................................
    if( zType[0]=='e' && tagid ){
      char *zId;
      zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
                        tagid);
      zDateLink = href("%R/technote/%s",zId);
      free(zId);
    }else if( zUuid ){
      zDateLink = chref("timelineHistLink", "%R/info/%!S", zUuid);
    }else{
      zDateLink = mprintf("<a>");
    }
    /* WAS: zDateLink = href("%R/timeline?c=%!S&unhide", zUuid); */
    @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
    @ <td class="timelineGraph">
    if( tmFlags & TIMELINE_UCOLOR )  zBgClr = zUser ? hash_color(zUser) : 0;
................................................................................

  /* Report any errors. */
  if( zError ){
    @ <p class="generalError">%h(zError)</p>
  }

  if( zNewerButton ){
    @ %z(chref("button","%z",zNewerButton))More&nbsp;&uarr;</a>
  }
  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
  db_finalize(&q);
  if( zOlderButton ){
    @ %z(chref("button","%z",zOlderButton))More&nbsp;&darr;</a>
  }
  style_footer();
}

/*
** The input query q selects various records.  Print a human-readable
** summary of those records.