Fossil SCM

Move the "Copy Button" functionality to a separate Javascript module, to be loaded and used independently from the timeline graph module.

florian 2019-05-29 14:02 tooltip-copyhash
Commit f6fcbf292b9560d47c2eebd845bac5a6b0e0efd0c566975129ee113dd6fe64a7
--- a/src/copybtn.js
+++ b/src/copybtn.js
@@ -0,0 +1,20 @@
1
+/* Create (if necessary) and initialize a "Copy Text" button <idButton> linked
2
+** to <idTarget>idButton" data-copytarget="idTarget"></span>
3
+**
4
+** Note: <idTarget> can be set statically or dynamically, this function does not
5
+** overwrite attributes with empty values.Button,idTarget){!elButton ){
6
+ elButton.id = idButton;
7
+ }
8
+Text" button;var textAreatextAreatextArea.style.top = 0;
9
+ textArea.style.left = 0;
10
+ textArea.style.width = '2em';
11
+ textArea.style.height = '2em';
12
+ textArea.style.padding = 0;
13
+ textArea.style.border = 'none';
14
+ textArea.style.outline = 'none';
15
+ textArea.style.boxShadow = 'none';
16
+ textArea.style.background = 'transparent';
17
+ textArea.value = text;
18
+textArea);
19
+ textArea.focus();
20
+
--- a/src/copybtn.js
+++ b/src/copybtn.js
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/copybtn.js
+++ b/src/copybtn.js
@@ -0,0 +1,20 @@
1 /* Create (if necessary) and initialize a "Copy Text" button <idButton> linked
2 ** to <idTarget>idButton" data-copytarget="idTarget"></span>
3 **
4 ** Note: <idTarget> can be set statically or dynamically, this function does not
5 ** overwrite attributes with empty values.Button,idTarget){!elButton ){
6 elButton.id = idButton;
7 }
8 Text" button;var textAreatextAreatextArea.style.top = 0;
9 textArea.style.left = 0;
10 textArea.style.width = '2em';
11 textArea.style.height = '2em';
12 textArea.style.padding = 0;
13 textArea.style.border = 'none';
14 textArea.style.outline = 'none';
15 textArea.style.boxShadow = 'none';
16 textArea.style.background = 'transparent';
17 textArea.value = text;
18 textArea);
19 textArea.focus();
20
-70
--- src/graph.js
+++ src/graph.js
@@ -746,75 +746,5 @@
746746
var txJson = dataObj.textContent || dataObj.innerText;
747747
var tx = JSON.parse(txJson);
748748
TimelineGraph(tx);
749749
}
750750
}())
751
-
752
-/* Create (if necessary) and initialize a "Copy Text" button <idButton> linked
753
-** to the target element <idTarget>.
754
-**
755
-** HTML snippet for statically created buttons:
756
-** <span class="copy-button" id="idButton" data-copytarget="idTarget"></span>
757
-**
758
-** Note: <idTarget> can be set statically or dynamically, this function does not
759
-** overwrite "data-copytarget" attributes with empty values.
760
-*/
761
-function makeCopyButton(idButton,idTarget){
762
- var elButton = document.getElementById(idButton);
763
- if( !elButton ){
764
- elButton = document.createElement("span");
765
- elButton.className = "copy-button";
766
- elButton.id = idButton;
767
- }
768
- elButton.style.transition = "";
769
- elButton.style.opacity = 1;
770
- if( idTarget ) elButton.setAttribute("data-copytarget",idTarget);
771
- elButton.onclick = clickCopyButton;
772
- return elButton;
773
-}
774
-/* The onclick handler for the "Copy Text" button. */
775
-var lockCopyText = false;
776
-function clickCopyButton(e){
777
- e.preventDefault(); /* Mandatory for <a> and <button>. */
778
- e.stopPropagation();
779
- if( lockCopyText ) return;
780
- lockCopyText = true;
781
- this.style.transition = "opacity 400ms ease-in-out";
782
- this.style.opacity = 0;
783
- var idTarget = this.getAttribute("data-copytarget");
784
- var elTarget = document.getElementById(idTarget);
785
- if( elTarget ){
786
- var text = elTarget.innerText;
787
- copyTextToClipboard(text);
788
- }
789
- setTimeout(function(id){
790
- var elButton = document.getElementById(id);
791
- if( elButton ){
792
- elButton.style.transition = "";
793
- elButton.style.opacity = 1;
794
- }
795
- lockCopyText = false;
796
- }.bind(null,this.id),400);
797
-}
798
-/* Create a temporary <textarea> element and copy the contents to clipboard. */
799
-function copyTextToClipboard(text){
800
- var textArea = document.createElement("textarea");
801
- textArea.style.position = 'fixed';
802
- textArea.style.top = 0;
803
- textArea.style.left = 0;
804
- textArea.style.width = '2em';
805
- textArea.style.height = '2em';
806
- textArea.style.padding = 0;
807
- textArea.style.border = 'none';
808
- textArea.style.outline = 'none';
809
- textArea.style.boxShadow = 'none';
810
- textArea.style.background = 'transparent';
811
- textArea.value = text;
812
- document.body.appendChild(textArea);
813
- textArea.focus();
814
- textArea.select();
815
- try{
816
- document.execCommand('copy');
817
- }catch(err){
818
- }
819
- document.body.removeChild(textArea);
820
-}
821751
--- src/graph.js
+++ src/graph.js
@@ -746,75 +746,5 @@
746 var txJson = dataObj.textContent || dataObj.innerText;
747 var tx = JSON.parse(txJson);
748 TimelineGraph(tx);
749 }
750 }())
751
752 /* Create (if necessary) and initialize a "Copy Text" button <idButton> linked
753 ** to the target element <idTarget>.
754 **
755 ** HTML snippet for statically created buttons:
756 ** <span class="copy-button" id="idButton" data-copytarget="idTarget"></span>
757 **
758 ** Note: <idTarget> can be set statically or dynamically, this function does not
759 ** overwrite "data-copytarget" attributes with empty values.
760 */
761 function makeCopyButton(idButton,idTarget){
762 var elButton = document.getElementById(idButton);
763 if( !elButton ){
764 elButton = document.createElement("span");
765 elButton.className = "copy-button";
766 elButton.id = idButton;
767 }
768 elButton.style.transition = "";
769 elButton.style.opacity = 1;
770 if( idTarget ) elButton.setAttribute("data-copytarget",idTarget);
771 elButton.onclick = clickCopyButton;
772 return elButton;
773 }
774 /* The onclick handler for the "Copy Text" button. */
775 var lockCopyText = false;
776 function clickCopyButton(e){
777 e.preventDefault(); /* Mandatory for <a> and <button>. */
778 e.stopPropagation();
779 if( lockCopyText ) return;
780 lockCopyText = true;
781 this.style.transition = "opacity 400ms ease-in-out";
782 this.style.opacity = 0;
783 var idTarget = this.getAttribute("data-copytarget");
784 var elTarget = document.getElementById(idTarget);
785 if( elTarget ){
786 var text = elTarget.innerText;
787 copyTextToClipboard(text);
788 }
789 setTimeout(function(id){
790 var elButton = document.getElementById(id);
791 if( elButton ){
792 elButton.style.transition = "";
793 elButton.style.opacity = 1;
794 }
795 lockCopyText = false;
796 }.bind(null,this.id),400);
797 }
798 /* Create a temporary <textarea> element and copy the contents to clipboard. */
799 function copyTextToClipboard(text){
800 var textArea = document.createElement("textarea");
801 textArea.style.position = 'fixed';
802 textArea.style.top = 0;
803 textArea.style.left = 0;
804 textArea.style.width = '2em';
805 textArea.style.height = '2em';
806 textArea.style.padding = 0;
807 textArea.style.border = 'none';
808 textArea.style.outline = 'none';
809 textArea.style.boxShadow = 'none';
810 textArea.style.background = 'transparent';
811 textArea.value = text;
812 document.body.appendChild(textArea);
813 textArea.focus();
814 textArea.select();
815 try{
816 document.execCommand('copy');
817 }catch(err){
818 }
819 document.body.removeChild(textArea);
820 }
821
--- src/graph.js
+++ src/graph.js
@@ -746,75 +746,5 @@
746 var txJson = dataObj.textContent || dataObj.innerText;
747 var tx = JSON.parse(txJson);
748 TimelineGraph(tx);
749 }
750 }())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
751
--- src/main.mk
+++ src/main.mk
@@ -210,10 +210,11 @@
210210
$(SRCDIR)/../skins/xekri/css.txt \
211211
$(SRCDIR)/../skins/xekri/details.txt \
212212
$(SRCDIR)/../skins/xekri/footer.txt \
213213
$(SRCDIR)/../skins/xekri/header.txt \
214214
$(SRCDIR)/ci_edit.js \
215
+ $(SRCDIR)/copybtn.js \
215216
$(SRCDIR)/diff.tcl \
216217
$(SRCDIR)/forum.js \
217218
$(SRCDIR)/graph.js \
218219
$(SRCDIR)/href.js \
219220
$(SRCDIR)/login.js \
220221
--- src/main.mk
+++ src/main.mk
@@ -210,10 +210,11 @@
210 $(SRCDIR)/../skins/xekri/css.txt \
211 $(SRCDIR)/../skins/xekri/details.txt \
212 $(SRCDIR)/../skins/xekri/footer.txt \
213 $(SRCDIR)/../skins/xekri/header.txt \
214 $(SRCDIR)/ci_edit.js \
 
215 $(SRCDIR)/diff.tcl \
216 $(SRCDIR)/forum.js \
217 $(SRCDIR)/graph.js \
218 $(SRCDIR)/href.js \
219 $(SRCDIR)/login.js \
220
--- src/main.mk
+++ src/main.mk
@@ -210,10 +210,11 @@
210 $(SRCDIR)/../skins/xekri/css.txt \
211 $(SRCDIR)/../skins/xekri/details.txt \
212 $(SRCDIR)/../skins/xekri/footer.txt \
213 $(SRCDIR)/../skins/xekri/header.txt \
214 $(SRCDIR)/ci_edit.js \
215 $(SRCDIR)/copybtn.js \
216 $(SRCDIR)/diff.tcl \
217 $(SRCDIR)/forum.js \
218 $(SRCDIR)/graph.js \
219 $(SRCDIR)/href.js \
220 $(SRCDIR)/login.js \
221
+15 -4
--- src/style.c
+++ src/style.c
@@ -82,13 +82,14 @@
8282
static unsigned adUnitFlags = 0;
8383
8484
/*
8585
** Flags for various javascript files needed prior to </body>
8686
*/
87
-static int needHrefJs = 0; /* href.js */
88
-static int needSortJs = 0; /* sorttable.js */
89
-static int needGraphJs = 0; /* graph.js */
87
+static int needHrefJs = 0; /* href.js */
88
+static int needSortJs = 0; /* sorttable.js */
89
+static int needGraphJs = 0; /* graph.js */
90
+static int needCopyBtnJs = 0; /* copybtn.js */
9091
9192
/*
9293
** Extra JS added to the end of the file.
9394
*/
9495
static Blob blobOnLoad = BLOB_INITIALIZER;
@@ -533,15 +534,22 @@
533534
void style_table_sorter(void){
534535
needSortJs = 1;
535536
}
536537
537538
/*
538
-** Indicate that the table-sorting javascript is needed.
539
+** Indicate that the timeline graph javascript is needed.
539540
*/
540541
void style_graph_generator(void){
541542
needGraphJs = 1;
542543
}
544
+
545
+/*
546
+** Indicate that the copy button javascript is needed.
547
+*/
548
+void style_copy_button(void){
549
+ needCopyBtnJs = 1;
550
+}
543551
544552
/*
545553
** Generate code to load a single javascript file
546554
*/
547555
void style_load_one_js_file(const char *zFile){
@@ -591,10 +599,13 @@
591599
cgi_append_content(builtin_text("sorttable.js"),-1);
592600
}
593601
if( needGraphJs ){
594602
cgi_append_content(builtin_text("graph.js"),-1);
595603
}
604
+ if( needCopyBtnJs ){
605
+ cgi_append_content(builtin_text("copybtn.js"),-1);
606
+ }
596607
for(i=0; i<nJsToLoad; i++){
597608
cgi_append_content(builtin_text(azJsToLoad[i]),-1);
598609
}
599610
if( blob_size(&blobOnLoad)>0 ){
600611
@ window.onload = function(){
601612
--- src/style.c
+++ src/style.c
@@ -82,13 +82,14 @@
82 static unsigned adUnitFlags = 0;
83
84 /*
85 ** Flags for various javascript files needed prior to </body>
86 */
87 static int needHrefJs = 0; /* href.js */
88 static int needSortJs = 0; /* sorttable.js */
89 static int needGraphJs = 0; /* graph.js */
 
90
91 /*
92 ** Extra JS added to the end of the file.
93 */
94 static Blob blobOnLoad = BLOB_INITIALIZER;
@@ -533,15 +534,22 @@
533 void style_table_sorter(void){
534 needSortJs = 1;
535 }
536
537 /*
538 ** Indicate that the table-sorting javascript is needed.
539 */
540 void style_graph_generator(void){
541 needGraphJs = 1;
542 }
 
 
 
 
 
 
 
543
544 /*
545 ** Generate code to load a single javascript file
546 */
547 void style_load_one_js_file(const char *zFile){
@@ -591,10 +599,13 @@
591 cgi_append_content(builtin_text("sorttable.js"),-1);
592 }
593 if( needGraphJs ){
594 cgi_append_content(builtin_text("graph.js"),-1);
595 }
 
 
 
596 for(i=0; i<nJsToLoad; i++){
597 cgi_append_content(builtin_text(azJsToLoad[i]),-1);
598 }
599 if( blob_size(&blobOnLoad)>0 ){
600 @ window.onload = function(){
601
--- src/style.c
+++ src/style.c
@@ -82,13 +82,14 @@
82 static unsigned adUnitFlags = 0;
83
84 /*
85 ** Flags for various javascript files needed prior to </body>
86 */
87 static int needHrefJs = 0; /* href.js */
88 static int needSortJs = 0; /* sorttable.js */
89 static int needGraphJs = 0; /* graph.js */
90 static int needCopyBtnJs = 0; /* copybtn.js */
91
92 /*
93 ** Extra JS added to the end of the file.
94 */
95 static Blob blobOnLoad = BLOB_INITIALIZER;
@@ -533,15 +534,22 @@
534 void style_table_sorter(void){
535 needSortJs = 1;
536 }
537
538 /*
539 ** Indicate that the timeline graph javascript is needed.
540 */
541 void style_graph_generator(void){
542 needGraphJs = 1;
543 }
544
545 /*
546 ** Indicate that the copy button javascript is needed.
547 */
548 void style_copy_button(void){
549 needCopyBtnJs = 1;
550 }
551
552 /*
553 ** Generate code to load a single javascript file
554 */
555 void style_load_one_js_file(const char *zFile){
@@ -591,10 +599,13 @@
599 cgi_append_content(builtin_text("sorttable.js"),-1);
600 }
601 if( needGraphJs ){
602 cgi_append_content(builtin_text("graph.js"),-1);
603 }
604 if( needCopyBtnJs ){
605 cgi_append_content(builtin_text("copybtn.js"),-1);
606 }
607 for(i=0; i<nJsToLoad; i++){
608 cgi_append_content(builtin_text(azJsToLoad[i]),-1);
609 }
610 if( blob_size(&blobOnLoad)>0 ){
611 @ window.onload = function(){
612
--- src/timeline.c
+++ src/timeline.c
@@ -991,10 +991,11 @@
991991
cgi_printf("\"h\":\"%!S\"}%s",
992992
pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
993993
}
994994
@ }</script>
995995
style_graph_generator();
996
+ style_copy_button(); /* Dependency: graph.js requires copybtn.js. */
996997
graph_free(pGraph);
997998
}
998999
}
9991000
10001001
/*
10011002
--- src/timeline.c
+++ src/timeline.c
@@ -991,10 +991,11 @@
991 cgi_printf("\"h\":\"%!S\"}%s",
992 pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
993 }
994 @ }</script>
995 style_graph_generator();
 
996 graph_free(pGraph);
997 }
998 }
999
1000 /*
1001
--- src/timeline.c
+++ src/timeline.c
@@ -991,10 +991,11 @@
991 cgi_printf("\"h\":\"%!S\"}%s",
992 pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
993 }
994 @ }</script>
995 style_graph_generator();
996 style_copy_button(); /* Dependency: graph.js requires copybtn.js. */
997 graph_free(pGraph);
998 }
999 }
1000
1001 /*
1002
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -632,10 +632,11 @@
632632
$(SRCDIR)/../skins/xekri/css.txt \
633633
$(SRCDIR)/../skins/xekri/details.txt \
634634
$(SRCDIR)/../skins/xekri/footer.txt \
635635
$(SRCDIR)/../skins/xekri/header.txt \
636636
$(SRCDIR)/ci_edit.js \
637
+ $(SRCDIR)/copybtn.js \
637638
$(SRCDIR)/diff.tcl \
638639
$(SRCDIR)/forum.js \
639640
$(SRCDIR)/graph.js \
640641
$(SRCDIR)/href.js \
641642
$(SRCDIR)/login.js \
642643
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -632,10 +632,11 @@
632 $(SRCDIR)/../skins/xekri/css.txt \
633 $(SRCDIR)/../skins/xekri/details.txt \
634 $(SRCDIR)/../skins/xekri/footer.txt \
635 $(SRCDIR)/../skins/xekri/header.txt \
636 $(SRCDIR)/ci_edit.js \
 
637 $(SRCDIR)/diff.tcl \
638 $(SRCDIR)/forum.js \
639 $(SRCDIR)/graph.js \
640 $(SRCDIR)/href.js \
641 $(SRCDIR)/login.js \
642
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -632,10 +632,11 @@
632 $(SRCDIR)/../skins/xekri/css.txt \
633 $(SRCDIR)/../skins/xekri/details.txt \
634 $(SRCDIR)/../skins/xekri/footer.txt \
635 $(SRCDIR)/../skins/xekri/header.txt \
636 $(SRCDIR)/ci_edit.js \
637 $(SRCDIR)/copybtn.js \
638 $(SRCDIR)/diff.tcl \
639 $(SRCDIR)/forum.js \
640 $(SRCDIR)/graph.js \
641 $(SRCDIR)/href.js \
642 $(SRCDIR)/login.js \
643
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -632,10 +632,11 @@
632632
$(SRCDIR)/../skins/xekri/css.txt \
633633
$(SRCDIR)/../skins/xekri/details.txt \
634634
$(SRCDIR)/../skins/xekri/footer.txt \
635635
$(SRCDIR)/../skins/xekri/header.txt \
636636
$(SRCDIR)/ci_edit.js \
637
+ $(SRCDIR)/copybtn.js \
637638
$(SRCDIR)/diff.tcl \
638639
$(SRCDIR)/forum.js \
639640
$(SRCDIR)/graph.js \
640641
$(SRCDIR)/href.js \
641642
$(SRCDIR)/login.js \
642643
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -632,10 +632,11 @@
632 $(SRCDIR)/../skins/xekri/css.txt \
633 $(SRCDIR)/../skins/xekri/details.txt \
634 $(SRCDIR)/../skins/xekri/footer.txt \
635 $(SRCDIR)/../skins/xekri/header.txt \
636 $(SRCDIR)/ci_edit.js \
 
637 $(SRCDIR)/diff.tcl \
638 $(SRCDIR)/forum.js \
639 $(SRCDIR)/graph.js \
640 $(SRCDIR)/href.js \
641 $(SRCDIR)/login.js \
642
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -632,10 +632,11 @@
632 $(SRCDIR)/../skins/xekri/css.txt \
633 $(SRCDIR)/../skins/xekri/details.txt \
634 $(SRCDIR)/../skins/xekri/footer.txt \
635 $(SRCDIR)/../skins/xekri/header.txt \
636 $(SRCDIR)/ci_edit.js \
637 $(SRCDIR)/copybtn.js \
638 $(SRCDIR)/diff.tcl \
639 $(SRCDIR)/forum.js \
640 $(SRCDIR)/graph.js \
641 $(SRCDIR)/href.js \
642 $(SRCDIR)/login.js \
643
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -535,10 +535,11 @@
535535
$(SRCDIR)\..\skins\xekri\css.txt \
536536
$(SRCDIR)\..\skins\xekri\details.txt \
537537
$(SRCDIR)\..\skins\xekri\footer.txt \
538538
$(SRCDIR)\..\skins\xekri\header.txt \
539539
$(SRCDIR)\ci_edit.js \
540
+ $(SRCDIR)\copybtn.js \
540541
$(SRCDIR)\diff.tcl \
541542
$(SRCDIR)\forum.js \
542543
$(SRCDIR)\graph.js \
543544
$(SRCDIR)\href.js \
544545
$(SRCDIR)\login.js \
545546
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -535,10 +535,11 @@
535 $(SRCDIR)\..\skins\xekri\css.txt \
536 $(SRCDIR)\..\skins\xekri\details.txt \
537 $(SRCDIR)\..\skins\xekri\footer.txt \
538 $(SRCDIR)\..\skins\xekri\header.txt \
539 $(SRCDIR)\ci_edit.js \
 
540 $(SRCDIR)\diff.tcl \
541 $(SRCDIR)\forum.js \
542 $(SRCDIR)\graph.js \
543 $(SRCDIR)\href.js \
544 $(SRCDIR)\login.js \
545
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -535,10 +535,11 @@
535 $(SRCDIR)\..\skins\xekri\css.txt \
536 $(SRCDIR)\..\skins\xekri\details.txt \
537 $(SRCDIR)\..\skins\xekri\footer.txt \
538 $(SRCDIR)\..\skins\xekri\header.txt \
539 $(SRCDIR)\ci_edit.js \
540 $(SRCDIR)\copybtn.js \
541 $(SRCDIR)\diff.tcl \
542 $(SRCDIR)\forum.js \
543 $(SRCDIR)\graph.js \
544 $(SRCDIR)\href.js \
545 $(SRCDIR)\login.js \
546

Keyboard Shortcuts

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