Fossil

Check-in [759fbda4e8]
Login

Check-in [759fbda4e8]

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

Overview
Comment:Minor improvements to the copy-button logic.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 759fbda4e8963d89d157ef45b7329f33fb1c5ec633130ffcd6442d8ecf8f72d6
User & Date: drh 2019-06-07 13:15:02.462
Context
2019-06-08
10:29
Fix a minor fault in the graph drawing javascript. ... (check-in: 3a15daaa3f user: drh tags: trunk)
2019-06-07
13:15
Minor improvements to the copy-button logic. ... (check-in: 759fbda4e8 user: drh tags: trunk)
13:10
Minor name changes to new routines. ... (Closed-Leaf check-in: ef848025da user: drh tags: copybtn.js-tweaks)
13:01
Add a configuration option to cause timeline timestamps to link to the /info page instead of to the /timeline page. ... (check-in: 647424d463 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/copybtn.js.
28
29
30
31
32
33
34

35
36
37
38
39
40
41
  elButton.className = "copy-button";
  if( bFlipped ) elButton.className += " copy-button-flipped";
  elButton.id = "copy-" + idTarget;
  initCopyButton(elButton,idTarget,cchLength);
  return elButton;
}
function initCopyButtonById(idButton,idTarget,cchLength){

  var elButton = document.getElementById(idButton);
  if( elButton ) initCopyButton(elButton,idTarget,cchLength);
  return elButton;
}
function initCopyButton(elButton,idTarget,cchLength){
  elButton.style.transition = "";
  elButton.style.opacity = 1;







>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  elButton.className = "copy-button";
  if( bFlipped ) elButton.className += " copy-button-flipped";
  elButton.id = "copy-" + idTarget;
  initCopyButton(elButton,idTarget,cchLength);
  return elButton;
}
function initCopyButtonById(idButton,idTarget,cchLength){
  idButton = idButton || "copy-" + idTarget;
  var elButton = document.getElementById(idButton);
  if( elButton ) initCopyButton(elButton,idTarget,cchLength);
  return elButton;
}
function initCopyButton(elButton,idTarget,cchLength){
  elButton.style.transition = "";
  elButton.style.opacity = 1;
Changes to src/default_css.txt.
785
786
787
788
789
790
791



  cursor: pointer;
}
.copy-button-flipped {
//Note: .16em is suitable for element grouping.
  margin-left: .16em;
  margin-right: 0;
}










>
>
>
785
786
787
788
789
790
791
792
793
794
  cursor: pointer;
}
.copy-button-flipped {
//Note: .16em is suitable for element grouping.
  margin-left: .16em;
  margin-right: 0;
}
.nobr {
  white-space: nowrap;
}
Changes to src/info.c.
761
762
763
764
765
766
767
768
769
770
771
772

773
774
775
776
777
778
779
    db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag "
                   " WHERE rid=%d AND tagtype>0 "
                   "   AND tag.tagid=tagxref.tagid "
                   "   AND +tag.tagname GLOB 'sym-*'", rid);
    while( db_step(&q2)==SQLITE_ROW ){
      const char *zTagName = db_column_text(&q2, 0);
      if( fossil_strcmp(zTagName,zBrName)==0 ){
        @  | <span class="copy-button" id="copy-name-br"
        @      data-copytarget="name-br" data-copylength="0">
        @  </span><span id="name-br"><!--
        @  -->%z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</a>
        @  </span>

        if( wiki_tagid2("branch",zTagName)!=0 ){
          blob_appendf(&wiki_read_links, " | %z%h</a>",
              href("%R/wiki?name=branch/%h",zTagName), zTagName);
        }else if( g.perm.Write && g.perm.WrWiki ){
          blob_appendf(&wiki_add_links, " | %z%h</a>",
              href("%R/wikiedit?name=branch/%h",zTagName), zTagName);
        }







|
<
|
|
<
>







761
762
763
764
765
766
767
768

769
770

771
772
773
774
775
776
777
778
    db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag "
                   " WHERE rid=%d AND tagtype>0 "
                   "   AND tag.tagid=tagxref.tagid "
                   "   AND +tag.tagname GLOB 'sym-*'", rid);
    while( db_step(&q2)==SQLITE_ROW ){
      const char *zTagName = db_column_text(&q2, 0);
      if( fossil_strcmp(zTagName,zBrName)==0 ){
        cgi_printf(" | ");

        style_copy_button(1, "name-br", 0, 0, "%z%h</a>",
          href("%R/timeline?r=%T&unhide",zTagName), zTagName);

        cgi_printf("\n");
        if( wiki_tagid2("branch",zTagName)!=0 ){
          blob_appendf(&wiki_read_links, " | %z%h</a>",
              href("%R/wiki?name=branch/%h",zTagName), zTagName);
        }else if( g.perm.Write && g.perm.WrWiki ){
          blob_appendf(&wiki_add_links, " | %z%h</a>",
              href("%R/wikiedit?name=branch/%h",zTagName), zTagName);
        }
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
    @     %z(href("%R/tree?ci=%!S",zUuid))files</a>
    @   | %z(href("%R/fileage?name=%!S",zUuid))file ages</a>
    @   | %z(href("%R/tree?nofiles&type=tree&ci=%!S",zUuid))folders</a>
    @   </td>
    @ </tr>

    @ <tr><th>%s(hname_alg(nUuid)):</th><td>
    @ <span class="copy-button" id="copy-hash-ci"
    @   data-copytarget="hash-ci" data-copylength="%d(hash_digits(1))">
    @ </span><span id="hash-ci">%.32s(zUuid)<wbr>%s(zUuid+32)</span>
    if( g.perm.Setup ){
      @ (Record ID: %d(rid))
    }
    @ </td></tr>
    @ <tr><th>User&nbsp;&amp;&nbsp;Date:</th><td>
    hyperlink_to_user(zUser,zDate," on ");
    hyperlink_to_date(zDate, "</td></tr>");
    if( zEComment ){
      @ <tr><th>Original&nbsp;Comment:</th>







|
<
<

|







795
796
797
798
799
800
801
802


803
804
805
806
807
808
809
810
811
    @     %z(href("%R/tree?ci=%!S",zUuid))files</a>
    @   | %z(href("%R/fileage?name=%!S",zUuid))file ages</a>
    @   | %z(href("%R/tree?nofiles&type=tree&ci=%!S",zUuid))folders</a>
    @   </td>
    @ </tr>

    @ <tr><th>%s(hname_alg(nUuid)):</th><td>
    style_copy_button(1, "hash-ci", 0, 2, "%.32s<wbr>%s", zUuid, zUuid+32);


    if( g.perm.Setup ){
      @  (Record ID: %d(rid))
    }
    @ </td></tr>
    @ <tr><th>User&nbsp;&amp;&nbsp;Date:</th><td>
    hyperlink_to_user(zUser,zDate," on ");
    hyperlink_to_date(zDate, "</td></tr>");
    if( zEComment ){
      @ <tr><th>Original&nbsp;Comment:</th>
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
      style_submenu_element("Unshun", "%s/shun?accept=%s&sub=1#delshun",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
    }
  }
  style_header("Hex Artifact Content");
  style_copy_button();
  zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
  @ <h2>Artifact
  @ <span class="copy-button" id="copy-hash-ar"
  @   data-copytarget="hash-ar" data-copylength="%d(hash_digits(1))">
  if( g.perm.Setup ){
    @ </span><span id="hash-ar">%s(zUuid)</span> (%d(rid)):</h2>
  }else{
    @ </span><span id="hash-ar">%s(zUuid)</span>:</h2>
  }
  blob_zero(&downloadName);
  if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
  object_description(rid, objdescFlags, &downloadName);
  style_submenu_element("Download", "%s/raw/%T?name=%s",
        g.zTop, blob_str(&downloadName), zUuid);
  @ <hr />







<


|
<

|

|







1914
1915
1916
1917
1918
1919
1920

1921
1922
1923

1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
      style_submenu_element("Unshun", "%s/shun?accept=%s&sub=1#delshun",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
    }
  }
  style_header("Hex Artifact Content");

  zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
  @ <h2>Artifact
  style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);

  if( g.perm.Setup ){
    @  (%d(rid)):</h2>
  }else{
    @ :</h2>
  }
  blob_zero(&downloadName);
  if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
  object_description(rid, objdescFlags, &downloadName);
  style_submenu_element("Download", "%s/raw/%T?name=%s",
        g.zTop, blob_str(&downloadName), zUuid);
  @ <hr />
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
    objdescFlags |= OBJDESC_DETAIL;
  }
  zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( isFile ){
    @ <h2>Latest version of file '%h(zName)':</h2>
    style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
  }else{
    style_copy_button();
    @ <h2>Artifact
    @ <span class="copy-button" id="copy-hash-ar"
    @   data-copytarget="hash-ar" data-copylength="%d(hash_digits(1))">
    if( g.perm.Setup ){
      @ </span><span id="hash-ar">%s(zUuid)</span> (%d(rid)):</h2>
    }else{
      @ </span><span id="hash-ar">%s(zUuid)</span>:</h2>
    }
  }
  blob_zero(&downloadName);
  asText = P("txt")!=0;
  if( asText ) objdescFlags &= ~OBJDESC_BASE;
  objType = object_description(rid, objdescFlags, &downloadName);
  if( !descOnly && P("download")!=0 ){







<

|
<

|

|







2187
2188
2189
2190
2191
2192
2193

2194
2195

2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
    objdescFlags |= OBJDESC_DETAIL;
  }
  zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( isFile ){
    @ <h2>Latest version of file '%h(zName)':</h2>
    style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
  }else{

    @ <h2>Artifact
    style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);

    if( g.perm.Setup ){
      @  (%d(rid)):</h2>
    }else{
      @ :</h2>
    }
  }
  blob_zero(&downloadName);
  asText = P("txt")!=0;
  if( asText ) objdescFlags &= ~OBJDESC_BASE;
  objType = object_description(rid, objdescFlags, &downloadName);
  if( !descOnly && P("download")!=0 ){
Changes to src/style.c.
364
365
366
367
368
369
370




















































































371
372
373
374
375
376
377
static void image_url_var(const char *zImageName){
  char *zVarPrefix = mprintf("%s_image", zImageName);
  char *zConfigName = mprintf("%s-image", zImageName);
  url_var(zVarPrefix, zConfigName, zImageName);
  free(zVarPrefix);
  free(zConfigName);
}





















































































/*
** Return a random nonce that is stored in static space.  For a particular
** run, the same nonce is always returned.
*/
char *style_nonce(void){
  static char zNonce[52];







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







364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
static void image_url_var(const char *zImageName){
  char *zVarPrefix = mprintf("%s_image", zImageName);
  char *zConfigName = mprintf("%s-image", zImageName);
  url_var(zVarPrefix, zConfigName, zImageName);
  free(zVarPrefix);
  free(zConfigName);
}

/*
** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
** Javascript module, and generates HTML elements with the following IDs:
**
**    TARGETID:       The <span> wrapper around TEXT.
**    copy-TARGETID:  The <span> for the copy button.
**
** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT.
**
** The COPYLENGTH argument defines the length of the substring of TEXT copied to
** clipboard:
**
**    <= 0:   No limit (default if the argument is omitted).
**    >= 3:   Truncate TEXT after COPYLENGTH (single-byte) characters.
**       1:   Use the "hash-digits" setting as the limit.
**       2:   Use the length appropriate for URLs as the limit (defined at
**            compile-time by FOSSIL_HASH_DIGITS_URL, defaults to 16).
*/
char *style_copy_button(
  int bOutputCGI,         /* Don't return result, but send to cgi_printf(). */
  const char *zTargetId,  /* The TARGETID argument. */
  int bFlipped,           /* The FLIPPED argument. */
  int cchLength,          /* The COPYLENGTH argument. */
  const char *zTextFmt,   /* Formatting of the TEXT argument (htmlized). */
  ...                     /* Formatting parameters of the TEXT argument. */
){
  va_list ap;
  char *zText;
  char *zResult = 0;
  va_start(ap,zTextFmt);
  zText = vmprintf(zTextFmt/*works-like:?*/,ap);
  va_end(ap);
  if( cchLength==1 ) cchLength = hash_digits(0);
  else if( cchLength==2 ) cchLength = hash_digits(1);
  if( !bFlipped ){
    const char *zBtnFmt =
      "<span class=\"nobr\">"
      "<span "
      "class=\"copy-button\" "
      "id=\"copy-%h\" "
      "data-copytarget=\"%h\" "
      "data-copylength=\"%d\">"
      "</span>"
      "<span id=\"%h\">"
      "%s"
      "</span>"
      "</span>";
    if( bOutputCGI ){
      cgi_printf(
                  zBtnFmt/*works-like:"%h%h%d%h%s"*/,
                  zTargetId,zTargetId,cchLength,zTargetId,zText);
    }else{
      zResult = mprintf(
                  zBtnFmt/*works-like:"%h%h%d%h%s"*/,
                  zTargetId,zTargetId,cchLength,zTargetId,zText);
    }
  }else{
    const char *zBtnFmt =
      "<span class=\"nobr\">"
      "<span id=\"%h\">"
      "%s"
      "</span>"
      "<span "
      "class=\"copy-button copy-button-flipped\" "
      "id=\"copy-%h\" "
      "data-copytarget=\"%h\" "
      "data-copylength=\"%d\">"
      "</span>"
      "</span>";
    if( bOutputCGI ){
      cgi_printf(
                  zBtnFmt/*works-like:"%h%s%h%h%d"*/,
                  zTargetId,zText,zTargetId,zTargetId,cchLength);
    }else{
      zResult = mprintf(
                  zBtnFmt/*works-like:"%h%s%h%h%d"*/,
                  zTargetId,zText,zTargetId,zTargetId,cchLength);
    }
  }
  free(zText);
  style_copybutton_control();
  return zResult;
}

/*
** Return a random nonce that is stored in static space.  For a particular
** run, the same nonce is always returned.
*/
char *style_nonce(void){
  static char zNonce[52];
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
void style_graph_generator(void){
  needGraphJs = 1;
}

/*
** Indicate that the copy button javascript is needed.
*/
void style_copy_button(void){
  needCopyBtnJs = 1;
}

/*
** Generate code to load a single javascript file
*/
void style_load_one_js_file(const char *zFile){







|







625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
void style_graph_generator(void){
  needGraphJs = 1;
}

/*
** Indicate that the copy button javascript is needed.
*/
void style_copybutton_control(void){
  needCopyBtnJs = 1;
}

/*
** Generate code to load a single javascript file
*/
void style_load_one_js_file(const char *zFile){
Changes to src/th_main.c.
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049

1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
  if( argc!=4 && argc!=5 ){
    return Th_WrongNumArgs(interp,
                           "copybtn TARGETID FLIPPED TEXT ?COPYLENGTH?");
  }
  if( enableOutput ){
    int flipped = 0;
    int copylength = 0;
    char *zTargetId, *zText, *zResult;
    if( Th_ToInt(interp, argv[2], argl[2], &flipped) ) return TH_ERROR;
    if( argc==5 ){
      if( Th_ToInt(interp, argv[4], argl[4], &copylength) ) return TH_ERROR;
    }
    if( copylength==1 ) copylength = hash_digits(0);
    else if( copylength==2 ) copylength = hash_digits(1);
    zTargetId = htmlize((char*)argv[1], argl[1]);
    zText = htmlize((char*)argv[3], argl[3]);
    if( !flipped ){
      zResult = mprintf(
                  "<span "
                  "class=\"copy-button\" "
                  "id=\"copy-%s\" "
                  "data-copytarget=\"%s\" "
                  "data-copylength=\"%d\">"
                  "</span>"
                  "<span id=\"%s\">"
                  "%s"
                  "</span>",

                  zTargetId, zTargetId, copylength, zTargetId, zText);
    }else{
      zResult = mprintf(
                  "<span id=\"%s\">"
                  "%s"
                  "</span>"
                  "<span "
                  "class=\"copy-button copy-button-flipped\" "
                  "id=\"copy-%s\" "
                  "data-copytarget=\"%s\" "
                  "data-copylength=\"%d\">"
                  "</span>",
                  zTargetId, zText, zTargetId, zTargetId, copylength);
    }
    free(zTargetId);
    free(zText);
    style_copy_button();
    sendText(zResult, -1, 0);
    free(zResult);
  }
  return TH_OK;
}

/*







|




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







1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034





1035









1036
1037
















1038
1039
1040
1041
1042
1043
1044
  if( argc!=4 && argc!=5 ){
    return Th_WrongNumArgs(interp,
                           "copybtn TARGETID FLIPPED TEXT ?COPYLENGTH?");
  }
  if( enableOutput ){
    int flipped = 0;
    int copylength = 0;
    char *zResult;
    if( Th_ToInt(interp, argv[2], argl[2], &flipped) ) return TH_ERROR;
    if( argc==5 ){
      if( Th_ToInt(interp, argv[4], argl[4], &copylength) ) return TH_ERROR;
    }





    zResult = style_copy_button(









                /*bOutputCGI==*/0, /*TARGETID==*/(char*)argv[1],
                flipped, copylength, "%h", /*TEXT==*/(char*)argv[3]);
















    sendText(zResult, -1, 0);
    free(zResult);
  }
  return TH_OK;
}

/*
Changes to src/timeline.c.
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
      if( k ) cgi_printf("],");
      cgi_printf("\"br\":\"%j\",", pRow->zBranch ? pRow->zBranch : "");
      cgi_printf("\"h\":\"%!S\"}%s",
                 pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
    }
    @ }</script>
    style_graph_generator();
    style_copy_button(); /* Dependency: graph.js requires copybtn.js. */
    graph_free(pGraph);
  }
}

/*
** Create a temporary table suitable for storing timeline data.
*/







|







1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
      if( k ) cgi_printf("],");
      cgi_printf("\"br\":\"%j\",", pRow->zBranch ? pRow->zBranch : "");
      cgi_printf("\"h\":\"%!S\"}%s",
                 pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
    }
    @ }</script>
    style_graph_generator();
    style_copybutton_control(); /* Dependency: graph.js requires copybtn.js. */
    graph_free(pGraph);
  }
}

/*
** Create a temporary table suitable for storing timeline data.
*/