Fossil

Check-in [b0795ff6]
Login

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

Overview
Comment:Introduce the "copy-button" CSS class with the SVG icon as the background image, to simplify the Javascript part.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tooltip-copyhash
Files: files | file ages | folders
SHA3-256: b0795ff6209e474be9d0fc14ef68df1ae9be1cc13568c097b1f3c1915bf7b5ab
User & Date: florian 2019-05-29 12:39:00
Wiki:tooltip-copyhash
Context
2019-05-29
12:47
Give a visual feedback when the copy icon is clicked. check-in: 3783706f user: florian tags: tooltip-copyhash
12:39
Introduce the "copy-button" CSS class with the SVG icon as the background image, to simplify the Javascript part. check-in: b0795ff6 user: florian tags: tooltip-copyhash
2019-05-28
12:32
Cherry-pick [ac199e7a8a]: Explicitly query the client mouse coordinates, to fix the positioning of tooltips for nodes in IE. check-in: 3b5e74c4 user: florian tags: tooltip-copyhash
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/default_css.txt.

765
766
767
768
769
770
771














}
.capsumWrite {
  background-color: #ffb;
}
label {
  white-space: nowrap;
}





















>
>
>
>
>
>
>
>
>
>
>
>
>
>
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
}
.capsumWrite {
  background-color: #ffb;
}
label {
  white-space: nowrap;
}
.copy-button {
  display: inline-block;
  width: 14px;
  height: 16px;
  margin: 0;
  padding: 0;
  border: 0;
  vertical-align: middle;
//Note: the mkcss utility does not support line breaks in data URIs.
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 16'%3E%3Cpath style='fill: black; opacity:0' d='M 14 16 H 0 V 0 h 14 v 16 z'/%3E%3Cpath style='fill:rgb(240,240,240)' d='M 1 0 h 6.6 l 2 2 h 1 l 3.4 3.4 v 8.6 h -10 v -2 h -3 z'/%3E%3Cpath style='fill:rgb(64,64,64)' d='M 2 1 h 5 l 3 3 v 7 h -8 z'/%3E%3Cpath style='fill:rgb(248,248,248)' d='M 3 2 h 3.6 l 2.4 2.4 v 5.6 h -6 z'/%3E%3Cpath style='fill:rgb(80,128,208)' d='M 4 5 h 4 v 1 h -4 z m 0 2 h 4 v 1 h -4 z'/%3E%3Cpath style='fill:rgb(64,64,64)' d='M 5 3 h 5 l 3 3 v 7 h -8 z'/%3E%3Cpath style='fill:rgb(248,248,248)' d='M 10 4.4 v 1.6 h 1.6 z m -4 -0.6 h 3 v 3 h -3 z m 0 3 h 6 v 5.4 h -6 z'/%3E%3Cpath style='fill:rgb(80,128,208)' d='M 7 8 h 4 v 1 h -4 z m 0 2 h 4 v 1 h -4 z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  cursor: pointer;
}

Changes to src/graph.js.

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
...
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
...
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
...
749
750
751
752
753
754
755
756
757
758

759








760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
  dwellTimeout: 250,  /* The tooltip dwell timeout. */
  closeTimeout: 3000, /* The tooltip close timeout. */
  idTimer: 0,         /* The tooltip dwell timer id. */
  idTimerClose: 0,    /* The tooltip close timer id. */
  ixHover: -1,        /* The id of the element with the mouse. */
  ixActive: -1,       /* The id of the element with the tooltip. */
  nodeHover: null,    /* Graph node under mouse when ixHover==-2 */
  imgCopy: null,      /* The image for the "Copy Hash" icon. */
  posX: 0, posY: 0    /* The last mouse position. */
};

/* Functions used to control the tooltip popup and its timer */
function hideGraphTooltip(){
  stopCloseTimer();
  tooltipObj.style.display = "none";
................................................................................
    var html = null
    var ix = -1
    if( tooltipInfo.ixHover==-2 ){
      ix = parseInt(tooltipInfo.nodeHover.id.match(/\d+$/)[0],10)-tx.iTopRow
      var h = tx.rowinfo[ix].h
      var dest = tx.baseUrl + "/info/" + h
      if( tx.fileDiff ){
        html = "artifact <a id=\"copyhash\" href=\""+dest+"\">"+h+"</a>"
      }else{
        html = "check-in <a id=\"copyhash\" href=\""+dest+"\">"+h+"</a>"
      }
      tooltipInfo.ixActive = -2;
    }else if( tooltipInfo.ixHover>=0 ){
      ix = tooltipInfo.ixHover
      var br = tx.rowinfo[ix].br
      var dest = branchHyperlink(ix)
      var hbr = br.replace(/&/g, "&amp;")
         .replace(/</g, "&lt;")
         .replace(/>/g, "&gt;")
         .replace(/"/g, "&quot;")
         .replace(/'/g, "&#039;");
      html = "branch <a id=\"copyhash\" href=\""+dest+"\">"+hbr+"</a>"
      tooltipInfo.ixActive = ix;
    }
    if( html ){
      /* Setup while hidden, to ensure proper dimensions. */
      var s = getComputedStyle(document.body)
      if( tx.rowinfo[ix].bg.length ){
        tooltipObj.style.backgroundColor = tx.rowinfo[ix].bg
................................................................................
      }else{
        tooltipObj.style.backgroundColor = s.getPropertyValue('background-color')
      }
      tooltipObj.style.borderColor =
         tooltipObj.style.color = s.getPropertyValue('color')
      tooltipObj.style.visibility = "hidden"
      tooltipObj.innerHTML = html
      /* The "Copy Hash" icon is not added via tooltipObj.innerHTML, to allow
      ** for the image to be cached during the lifetime of the current page. */
      tooltipObj.appendChild(document.createTextNode(' '));
      tooltipInfo.imgCopy = createCopyHashImg(tooltipInfo.imgCopy,"copyhash");
      tooltipObj.appendChild(tooltipInfo.imgCopy);
      tooltipObj.style.display = "inline"
      tooltipObj.style.position = "absolute"
      var x = tooltipInfo.posX + 4 + window.pageXOffset
                   - absoluteX(tooltipObj.offsetParent)
      tooltipObj.style.left = x+"px"
      var y = tooltipInfo.posY + window.pageYOffset
                   - tooltipObj.clientHeight - 4
................................................................................
    if(!dataObj) break;
    var txJson = dataObj.textContent || dataObj.innerText;
    var tx = JSON.parse(txJson);
    TimelineGraph(tx);
  }
}())

/* Create the image for the "Copy Hash" icon. */
function createCopyHashImg(imgRecycled,idCopyTarget){
  var img = imgRecycled;

  if( img==null ){








    img = document.createElement("img");
    img.src =
"data:image/svg+xml,"+
"%3Csvg xmlns='http:"+"/"+"/www.w3.org/2000/svg' viewBox='0 0 14 16'%3E"+
"%3Cpath style='fill: black; opacity:0' d='M 14 16 H 0 V 0 h 14 v 16 z'/%3E"+
"%3Cpath style='fill:rgb(240,240,240)' d='M 1 0 h 6.6 l 2 2 h 1 l 3.4 3.4 v "+
"8.6 h -10 v -2 h -3 z'/%3E%3Cpath style='fill:rgb(64,64,64)' d='M 2 1 h 5 l "+
"3 3 v 7 h -8 z'/%3E%3Cpath style='fill:rgb(248,248,248)' d='M 3 2 h 3.6 l "+
"2.4 2.4 v 5.6 h -6 z'/%3E%3Cpath style='fill:rgb(80,128,208)' d='M 4 5 h 4 v "+
"1 h -4 z m 0 2 h 4 v 1 h -4 z'/%3E%3Cpath style='fill:rgb(64,64,64)' d='M 5 "+
"3 h 5 l 3 3 v 7 h -8 z'/%3E%3Cpath style='fill:rgb(248,248,248)' d='M 10 4.4 "+
"v 1.6 h 1.6 z m -4 -0.6 h 3 v 3 h -3 z m 0 3 h 6 v 5.4 h -6 z'/%3E%3Cpath "+
"style= 'fill:rgb(80,128,208)' d='M 7 8 h 4 v 1 h -4 z m 0 2 h 4 v 1 h -4 "+
"z'/%3E%3C/svg%3E";
    img.width = 14;
    img.height = 16;
  }
  img.style.verticalAlign = "middle";
  img.style.cursor = "pointer";
  img.setAttribute("data-copytarget",idCopyTarget);
  img.onclick = clickCopyHash;
  return img;
}
/* The onclick handler for the "Copy Hash" icon on the tooltip. */
var lockCopyHash = false;
function clickCopyHash(e){
  //e.preventDefault();
  e.stopPropagation();
  if( lockCopyHash ) return;
  lockCopyHash = true;
  var idCopyTarget = this.getAttribute("data-copytarget");
  var elCopyTarget = document.getElementById(idCopyTarget);
  if( elCopyTarget ){
    var hash = elCopyTarget.innerText;
    copyTextToClipboard(hash);
  }
  lockCopyHash = false;
}
/* Create a temporary <textarea> element and copy the contents to clipboard. */
function copyTextToClipboard(text){
  var textArea = document.createElement("textarea");
  textArea.style.position = 'fixed';
  textArea.style.top = 0;
  textArea.style.left = 0;







<







 







|

|











|







 







<
<

<
|







 







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

<
<
|
|
|

|
|
|
|

|
|
|
|
|
|
|

|







100
101
102
103
104
105
106

107
108
109
110
111
112
113
...
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
...
622
623
624
625
626
627
628


629

630
631
632
633
634
635
636
637
...
745
746
747
748
749
750
751
752
753

754
755
756
757
758
759
760
761
762
763
764








765
766





767


768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
  dwellTimeout: 250,  /* The tooltip dwell timeout. */
  closeTimeout: 3000, /* The tooltip close timeout. */
  idTimer: 0,         /* The tooltip dwell timer id. */
  idTimerClose: 0,    /* The tooltip close timer id. */
  ixHover: -1,        /* The id of the element with the mouse. */
  ixActive: -1,       /* The id of the element with the tooltip. */
  nodeHover: null,    /* Graph node under mouse when ixHover==-2 */

  posX: 0, posY: 0    /* The last mouse position. */
};

/* Functions used to control the tooltip popup and its timer */
function hideGraphTooltip(){
  stopCloseTimer();
  tooltipObj.style.display = "none";
................................................................................
    var html = null
    var ix = -1
    if( tooltipInfo.ixHover==-2 ){
      ix = parseInt(tooltipInfo.nodeHover.id.match(/\d+$/)[0],10)-tx.iTopRow
      var h = tx.rowinfo[ix].h
      var dest = tx.baseUrl + "/info/" + h
      if( tx.fileDiff ){
        html = "artifact <a id=\"tooltip-link\" href=\""+dest+"\">"+h+"</a>"
      }else{
        html = "check-in <a id=\"tooltip-link\" href=\""+dest+"\">"+h+"</a>"
      }
      tooltipInfo.ixActive = -2;
    }else if( tooltipInfo.ixHover>=0 ){
      ix = tooltipInfo.ixHover
      var br = tx.rowinfo[ix].br
      var dest = branchHyperlink(ix)
      var hbr = br.replace(/&/g, "&amp;")
         .replace(/</g, "&lt;")
         .replace(/>/g, "&gt;")
         .replace(/"/g, "&quot;")
         .replace(/'/g, "&#039;");
      html = "branch <a id=\"tooltip-link\" href=\""+dest+"\">"+hbr+"</a>"
      tooltipInfo.ixActive = ix;
    }
    if( html ){
      /* Setup while hidden, to ensure proper dimensions. */
      var s = getComputedStyle(document.body)
      if( tx.rowinfo[ix].bg.length ){
        tooltipObj.style.backgroundColor = tx.rowinfo[ix].bg
................................................................................
      }else{
        tooltipObj.style.backgroundColor = s.getPropertyValue('background-color')
      }
      tooltipObj.style.borderColor =
         tooltipObj.style.color = s.getPropertyValue('color')
      tooltipObj.style.visibility = "hidden"
      tooltipObj.innerHTML = html


      tooltipObj.appendChild(document.createTextNode(' '));

      tooltipObj.appendChild(makeCopyButton(null,"tooltip-link"));
      tooltipObj.style.display = "inline"
      tooltipObj.style.position = "absolute"
      var x = tooltipInfo.posX + 4 + window.pageXOffset
                   - absoluteX(tooltipObj.offsetParent)
      tooltipObj.style.left = x+"px"
      var y = tooltipInfo.posY + window.pageYOffset
                   - tooltipObj.clientHeight - 4
................................................................................
    if(!dataObj) break;
    var txJson = dataObj.textContent || dataObj.innerText;
    var tx = JSON.parse(txJson);
    TimelineGraph(tx);
  }
}())

/* Create (if necessary) and initialize a "Copy Text" button <idButton> linked
** to the target element <idTarget>.

**
** HTML snippet for statically created buttons:
**    <span class="copy-button" id="idButton" data-copytarget="idTarget"></span>
**
** Note: <idTarget> can be set statically or dynamically, this function does not
** overwrite "data-copytarget" attributes with empty values.
*/
function makeCopyButton(idButton,idTarget){
  var button = document.getElementById(idButton);
  if( !button ){
    button = document.createElement("span");








    button.className = "copy-button";
    button.id = idButton;





  }


  if( idTarget ) button.setAttribute("data-copytarget",idTarget);
  button.onclick = clickCopyButton;
  return button;
}
/* The onclick handler for the "Copy Text" button. */
var lockCopyText = false;
function clickCopyButton(e){
  e.preventDefault();   /* Mandatory for <a> and <button>. */
  e.stopPropagation();
  if( lockCopyText ) return;
  lockCopyText = true;
  var idTarget = this.getAttribute("data-copytarget");
  var elTarget = document.getElementById(idTarget);
  if( elTarget ){
    var text = elTarget.innerText;
    copyTextToClipboard(text);
  }
  lockCopyText = false;
}
/* Create a temporary <textarea> element and copy the contents to clipboard. */
function copyTextToClipboard(text){
  var textArea = document.createElement("textarea");
  textArea.style.position = 'fixed';
  textArea.style.top = 0;
  textArea.style.left = 0;