Fossil SCM
Revamp the Copy Buttons for a more responsive user experience. See the [branch/copybtn.js-responsive | wiki page linked to this branch] for more details.
Commit
32c3a210c80b33bcec0605fb082d7ed95316d2602037a0b31c49ff5dd1353ef2
Parent
c6265bb3a7eb867…
10 files changed
+19
-22
+21
-8
+9
-11
+4
-8
+1
-2
+3
-4
-1
+1
-1
+2
-2
+20
-16
+19
-22
| --- src/copybtn.js | ||
| +++ src/copybtn.js | ||
| @@ -1,32 +1,34 @@ | ||
| 1 | 1 | /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts |
| 2 | 2 | ** thereof) of the target elements to the clipboard. |
| 3 | 3 | ** |
| 4 | -** Newly created buttons are <span> elements with an SVG background icon, | |
| 5 | -** defined by the "copy-button" class in the default CSS style sheet, and are | |
| 6 | -** assigned the element ID "copy-<idTarget>". | |
| 7 | -** | |
| 8 | -** To simplify customization, the only properties modified for HTML-defined | |
| 9 | -** buttons are the "onclick" handler, and the "transition" and "opacity" styles | |
| 10 | -** (used for animation). | |
| 4 | +** Newly created buttons are <button> elements plus a nested <span> element with | |
| 5 | +** an SVG background icon, defined by the "copy-button" class in the default CSS | |
| 6 | +** style sheet, and are assigned the element ID "copy-<idTarget>". | |
| 11 | 7 | ** |
| 12 | 8 | ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(), |
| 13 | 9 | ** needs to be called to attach the "onclick" handler (done automatically from |
| 14 | -** a handler attached to the "DOMContentLoaded" event). | |
| 10 | +** a handler attached to the "DOMContentLoaded" event). These functions create | |
| 11 | +** the nested <span> element if the <button> element has no child nodes. Using | |
| 12 | +** static HTML for the <span> element ensures the buttons are visible if there | |
| 13 | +** are script errors, which may be useful for Fossil JS hackers (as good parts | |
| 14 | +** of the Fossil web UI come down on JS errors, anyway). | |
| 15 | 15 | ** |
| 16 | 16 | ** The initialization functions do not overwrite the "data-copytarget" and |
| 17 | 17 | ** "data-copylength" attributes with empty or null values for <idTarget> and |
| 18 | 18 | ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the |
| 19 | 19 | ** previous copy length limit. |
| 20 | 20 | ** |
| 21 | 21 | ** HTML snippet for statically created buttons: |
| 22 | 22 | ** |
| 23 | -** <span class="copy-button" id="copy-<idTarget>" | |
| 24 | -** data-copytarget="<idTarget>" data-copylength="<cchLength>"></span> | |
| 23 | +** <button class="copy-button" id="copy-<idTarget>" | |
| 24 | +** data-copytarget="<idTarget>" data-copylength="<cchLength>"> | |
| 25 | +** <span></span> | |
| 26 | +** </button> | |
| 25 | 27 | */ |
| 26 | 28 | function makeCopyButton(idTarget,bFlipped,cchLength){ |
| 27 | - var elButton = document.createElement("span"); | |
| 29 | + var elButton = document.createElement("button"); | |
| 28 | 30 | elButton.className = "copy-button"; |
| 29 | 31 | if( bFlipped ) elButton.className += " copy-button-flipped"; |
| 30 | 32 | elButton.id = "copy-" + idTarget; |
| 31 | 33 | initCopyButton(elButton,idTarget,cchLength); |
| 32 | 34 | return elButton; |
| @@ -36,15 +38,18 @@ | ||
| 36 | 38 | var elButton = document.getElementById(idButton); |
| 37 | 39 | if( elButton ) initCopyButton(elButton,idTarget,cchLength); |
| 38 | 40 | return elButton; |
| 39 | 41 | } |
| 40 | 42 | function initCopyButton(elButton,idTarget,cchLength){ |
| 41 | - elButton.style.transition = ""; | |
| 42 | - elButton.style.opacity = 1; | |
| 43 | 43 | if( idTarget ) elButton.setAttribute("data-copytarget",idTarget); |
| 44 | 44 | if( cchLength ) elButton.setAttribute("data-copylength",cchLength); |
| 45 | 45 | elButton.onclick = clickCopyButton; |
| 46 | + /* Make sure the <button> contains a single nested <span>. */ | |
| 47 | + if( elButton.childElementCount!=1 || elButton.firstChild.tagName!="SPAN" ){ | |
| 48 | + while( elButton.firstChild ) elButton.removeChild(elButton.lastChild); | |
| 49 | + elButton.appendChild(document.createElement("span")); | |
| 50 | + } | |
| 46 | 51 | return elButton; |
| 47 | 52 | } |
| 48 | 53 | setTimeout(function(){ |
| 49 | 54 | var elButtons = document.getElementsByClassName("copy-button"); |
| 50 | 55 | for ( var i=0; i<elButtons.length; i++ ){ |
| @@ -53,14 +58,11 @@ | ||
| 53 | 58 | },1); |
| 54 | 59 | /* The onclick handler for the "Copy Button". */ |
| 55 | 60 | function clickCopyButton(e){ |
| 56 | 61 | e.preventDefault(); /* Mandatory for <a> and <button>. */ |
| 57 | 62 | e.stopPropagation(); |
| 58 | - if( this.getAttribute("data-copylocked") ) return; | |
| 59 | - this.setAttribute("data-copylocked","1"); | |
| 60 | - this.style.transition = "opacity 400ms ease-in-out"; | |
| 61 | - this.style.opacity = 0; | |
| 63 | + if( this.disabled ) return; /* This check is probably redundant. */ | |
| 62 | 64 | var idTarget = this.getAttribute("data-copytarget"); |
| 63 | 65 | var elTarget = document.getElementById(idTarget); |
| 64 | 66 | if( elTarget ){ |
| 65 | 67 | var text = elTarget.innerText.replace(/^\s+|\s+$/g,""); |
| 66 | 68 | var cchLength = parseInt(this.getAttribute("data-copylength")); |
| @@ -67,15 +69,10 @@ | ||
| 67 | 69 | if( !isNaN(cchLength) && cchLength>0 ){ |
| 68 | 70 | text = text.slice(0,cchLength); /* Assume single-byte chars. */ |
| 69 | 71 | } |
| 70 | 72 | copyTextToClipboard(text); |
| 71 | 73 | } |
| 72 | - setTimeout(function(){ | |
| 73 | - this.style.transition = ""; | |
| 74 | - this.style.opacity = 1; | |
| 75 | - this.removeAttribute("data-copylocked"); | |
| 76 | - }.bind(this),400); | |
| 77 | 74 | } |
| 78 | 75 | /* Create a temporary <textarea> element and copy the contents to clipboard. */ |
| 79 | 76 | function copyTextToClipboard(text){ |
| 80 | 77 | if( window.clipboardData && window.clipboardData.setData ){ |
| 81 | 78 | window.clipboardData.setData("Text",text); |
| 82 | 79 |
| --- src/copybtn.js | |
| +++ src/copybtn.js | |
| @@ -1,32 +1,34 @@ | |
| 1 | /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts |
| 2 | ** thereof) of the target elements to the clipboard. |
| 3 | ** |
| 4 | ** Newly created buttons are <span> elements with an SVG background icon, |
| 5 | ** defined by the "copy-button" class in the default CSS style sheet, and are |
| 6 | ** assigned the element ID "copy-<idTarget>". |
| 7 | ** |
| 8 | ** To simplify customization, the only properties modified for HTML-defined |
| 9 | ** buttons are the "onclick" handler, and the "transition" and "opacity" styles |
| 10 | ** (used for animation). |
| 11 | ** |
| 12 | ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(), |
| 13 | ** needs to be called to attach the "onclick" handler (done automatically from |
| 14 | ** a handler attached to the "DOMContentLoaded" event). |
| 15 | ** |
| 16 | ** The initialization functions do not overwrite the "data-copytarget" and |
| 17 | ** "data-copylength" attributes with empty or null values for <idTarget> and |
| 18 | ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the |
| 19 | ** previous copy length limit. |
| 20 | ** |
| 21 | ** HTML snippet for statically created buttons: |
| 22 | ** |
| 23 | ** <span class="copy-button" id="copy-<idTarget>" |
| 24 | ** data-copytarget="<idTarget>" data-copylength="<cchLength>"></span> |
| 25 | */ |
| 26 | function makeCopyButton(idTarget,bFlipped,cchLength){ |
| 27 | var elButton = document.createElement("span"); |
| 28 | elButton.className = "copy-button"; |
| 29 | if( bFlipped ) elButton.className += " copy-button-flipped"; |
| 30 | elButton.id = "copy-" + idTarget; |
| 31 | initCopyButton(elButton,idTarget,cchLength); |
| 32 | return elButton; |
| @@ -36,15 +38,18 @@ | |
| 36 | var elButton = document.getElementById(idButton); |
| 37 | if( elButton ) initCopyButton(elButton,idTarget,cchLength); |
| 38 | return elButton; |
| 39 | } |
| 40 | function initCopyButton(elButton,idTarget,cchLength){ |
| 41 | elButton.style.transition = ""; |
| 42 | elButton.style.opacity = 1; |
| 43 | if( idTarget ) elButton.setAttribute("data-copytarget",idTarget); |
| 44 | if( cchLength ) elButton.setAttribute("data-copylength",cchLength); |
| 45 | elButton.onclick = clickCopyButton; |
| 46 | return elButton; |
| 47 | } |
| 48 | setTimeout(function(){ |
| 49 | var elButtons = document.getElementsByClassName("copy-button"); |
| 50 | for ( var i=0; i<elButtons.length; i++ ){ |
| @@ -53,14 +58,11 @@ | |
| 53 | },1); |
| 54 | /* The onclick handler for the "Copy Button". */ |
| 55 | function clickCopyButton(e){ |
| 56 | e.preventDefault(); /* Mandatory for <a> and <button>. */ |
| 57 | e.stopPropagation(); |
| 58 | if( this.getAttribute("data-copylocked") ) return; |
| 59 | this.setAttribute("data-copylocked","1"); |
| 60 | this.style.transition = "opacity 400ms ease-in-out"; |
| 61 | this.style.opacity = 0; |
| 62 | var idTarget = this.getAttribute("data-copytarget"); |
| 63 | var elTarget = document.getElementById(idTarget); |
| 64 | if( elTarget ){ |
| 65 | var text = elTarget.innerText.replace(/^\s+|\s+$/g,""); |
| 66 | var cchLength = parseInt(this.getAttribute("data-copylength")); |
| @@ -67,15 +69,10 @@ | |
| 67 | if( !isNaN(cchLength) && cchLength>0 ){ |
| 68 | text = text.slice(0,cchLength); /* Assume single-byte chars. */ |
| 69 | } |
| 70 | copyTextToClipboard(text); |
| 71 | } |
| 72 | setTimeout(function(){ |
| 73 | this.style.transition = ""; |
| 74 | this.style.opacity = 1; |
| 75 | this.removeAttribute("data-copylocked"); |
| 76 | }.bind(this),400); |
| 77 | } |
| 78 | /* Create a temporary <textarea> element and copy the contents to clipboard. */ |
| 79 | function copyTextToClipboard(text){ |
| 80 | if( window.clipboardData && window.clipboardData.setData ){ |
| 81 | window.clipboardData.setData("Text",text); |
| 82 |
| --- src/copybtn.js | |
| +++ src/copybtn.js | |
| @@ -1,32 +1,34 @@ | |
| 1 | /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts |
| 2 | ** thereof) of the target elements to the clipboard. |
| 3 | ** |
| 4 | ** Newly created buttons are <button> elements plus a nested <span> element with |
| 5 | ** an SVG background icon, defined by the "copy-button" class in the default CSS |
| 6 | ** style sheet, and are assigned the element ID "copy-<idTarget>". |
| 7 | ** |
| 8 | ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(), |
| 9 | ** needs to be called to attach the "onclick" handler (done automatically from |
| 10 | ** a handler attached to the "DOMContentLoaded" event). These functions create |
| 11 | ** the nested <span> element if the <button> element has no child nodes. Using |
| 12 | ** static HTML for the <span> element ensures the buttons are visible if there |
| 13 | ** are script errors, which may be useful for Fossil JS hackers (as good parts |
| 14 | ** of the Fossil web UI come down on JS errors, anyway). |
| 15 | ** |
| 16 | ** The initialization functions do not overwrite the "data-copytarget" and |
| 17 | ** "data-copylength" attributes with empty or null values for <idTarget> and |
| 18 | ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the |
| 19 | ** previous copy length limit. |
| 20 | ** |
| 21 | ** HTML snippet for statically created buttons: |
| 22 | ** |
| 23 | ** <button class="copy-button" id="copy-<idTarget>" |
| 24 | ** data-copytarget="<idTarget>" data-copylength="<cchLength>"> |
| 25 | ** <span></span> |
| 26 | ** </button> |
| 27 | */ |
| 28 | function makeCopyButton(idTarget,bFlipped,cchLength){ |
| 29 | var elButton = document.createElement("button"); |
| 30 | elButton.className = "copy-button"; |
| 31 | if( bFlipped ) elButton.className += " copy-button-flipped"; |
| 32 | elButton.id = "copy-" + idTarget; |
| 33 | initCopyButton(elButton,idTarget,cchLength); |
| 34 | return elButton; |
| @@ -36,15 +38,18 @@ | |
| 38 | var elButton = document.getElementById(idButton); |
| 39 | if( elButton ) initCopyButton(elButton,idTarget,cchLength); |
| 40 | return elButton; |
| 41 | } |
| 42 | function initCopyButton(elButton,idTarget,cchLength){ |
| 43 | if( idTarget ) elButton.setAttribute("data-copytarget",idTarget); |
| 44 | if( cchLength ) elButton.setAttribute("data-copylength",cchLength); |
| 45 | elButton.onclick = clickCopyButton; |
| 46 | /* Make sure the <button> contains a single nested <span>. */ |
| 47 | if( elButton.childElementCount!=1 || elButton.firstChild.tagName!="SPAN" ){ |
| 48 | while( elButton.firstChild ) elButton.removeChild(elButton.lastChild); |
| 49 | elButton.appendChild(document.createElement("span")); |
| 50 | } |
| 51 | return elButton; |
| 52 | } |
| 53 | setTimeout(function(){ |
| 54 | var elButtons = document.getElementsByClassName("copy-button"); |
| 55 | for ( var i=0; i<elButtons.length; i++ ){ |
| @@ -53,14 +58,11 @@ | |
| 58 | },1); |
| 59 | /* The onclick handler for the "Copy Button". */ |
| 60 | function clickCopyButton(e){ |
| 61 | e.preventDefault(); /* Mandatory for <a> and <button>. */ |
| 62 | e.stopPropagation(); |
| 63 | if( this.disabled ) return; /* This check is probably redundant. */ |
| 64 | var idTarget = this.getAttribute("data-copytarget"); |
| 65 | var elTarget = document.getElementById(idTarget); |
| 66 | if( elTarget ){ |
| 67 | var text = elTarget.innerText.replace(/^\s+|\s+$/g,""); |
| 68 | var cchLength = parseInt(this.getAttribute("data-copylength")); |
| @@ -67,15 +69,10 @@ | |
| 69 | if( !isNaN(cchLength) && cchLength>0 ){ |
| 70 | text = text.slice(0,cchLength); /* Assume single-byte chars. */ |
| 71 | } |
| 72 | copyTextToClipboard(text); |
| 73 | } |
| 74 | } |
| 75 | /* Create a temporary <textarea> element and copy the contents to clipboard. */ |
| 76 | function copyTextToClipboard(text){ |
| 77 | if( window.clipboardData && window.clipboardData.setData ){ |
| 78 | window.clipboardData.setData("Text",text); |
| 79 |
+21
-8
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -1136,19 +1136,34 @@ | ||
| 1136 | 1136 | white-space: nowrap; |
| 1137 | 1137 | } |
| 1138 | 1138 | label[for] { |
| 1139 | 1139 | cursor: pointer; |
| 1140 | 1140 | } |
| 1141 | -.copy-button { | |
| 1142 | - display: inline-block; | |
| 1141 | +button.copy-button { | |
| 1143 | 1142 | width: 14px; |
| 1144 | 1143 | height: 14px; |
| 1145 | 1144 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1146 | 1145 | margin: -2px .24em 0 0; |
| 1147 | 1146 | padding: 0; |
| 1148 | 1147 | border: 0; |
| 1148 | + background: none; | |
| 1149 | + font-size: inherit; /* Required for horizontal spacing. */ | |
| 1149 | 1150 | vertical-align: middle; |
| 1151 | + user-select: none; | |
| 1152 | + cursor: pointer; | |
| 1153 | +} | |
| 1154 | +button.copy-button-flipped { | |
| 1155 | +/*Note: .16em is suitable for element grouping.*/ | |
| 1156 | + margin: -2px 0 0 .16em; | |
| 1157 | +} | |
| 1158 | +button.copy-button span { | |
| 1159 | + display: block; | |
| 1160 | + width: 100%; | |
| 1161 | + height: 100%; | |
| 1162 | + margin: 0; | |
| 1163 | + padding: 0; | |
| 1164 | + border: 0; | |
| 1150 | 1165 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1151 | 1166 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1152 | 1167 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1153 | 1168 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1154 | 1169 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1159,19 +1174,17 @@ | ||
| 1159 | 1174 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1160 | 1175 | background-repeat: no-repeat; |
| 1161 | 1176 | background-position: center; |
| 1162 | 1177 | cursor: pointer; |
| 1163 | 1178 | } |
| 1164 | -.copy-button.disabled { | |
| 1179 | +button.copy-button:enabled:active span { | |
| 1180 | + background-size: 90%; | |
| 1181 | +} | |
| 1182 | +button.copy-button:disabled span { | |
| 1165 | 1183 | filter: grayscale(1); |
| 1166 | 1184 | opacity: 0.4; |
| 1167 | 1185 | } |
| 1168 | -.copy-button-flipped { | |
| 1169 | -/*Note: .16em is suitable for element grouping.*/ | |
| 1170 | - margin-left: .16em; | |
| 1171 | - margin-right: 0; | |
| 1172 | -} | |
| 1173 | 1186 | .nobr { |
| 1174 | 1187 | white-space: nowrap; |
| 1175 | 1188 | } |
| 1176 | 1189 | .accordion { |
| 1177 | 1190 | cursor: pointer; |
| 1178 | 1191 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1136,19 +1136,34 @@ | |
| 1136 | white-space: nowrap; |
| 1137 | } |
| 1138 | label[for] { |
| 1139 | cursor: pointer; |
| 1140 | } |
| 1141 | .copy-button { |
| 1142 | display: inline-block; |
| 1143 | width: 14px; |
| 1144 | height: 14px; |
| 1145 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1146 | margin: -2px .24em 0 0; |
| 1147 | padding: 0; |
| 1148 | border: 0; |
| 1149 | vertical-align: middle; |
| 1150 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1151 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1152 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1153 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1154 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1159,19 +1174,17 @@ | |
| 1159 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1160 | background-repeat: no-repeat; |
| 1161 | background-position: center; |
| 1162 | cursor: pointer; |
| 1163 | } |
| 1164 | .copy-button.disabled { |
| 1165 | filter: grayscale(1); |
| 1166 | opacity: 0.4; |
| 1167 | } |
| 1168 | .copy-button-flipped { |
| 1169 | /*Note: .16em is suitable for element grouping.*/ |
| 1170 | margin-left: .16em; |
| 1171 | margin-right: 0; |
| 1172 | } |
| 1173 | .nobr { |
| 1174 | white-space: nowrap; |
| 1175 | } |
| 1176 | .accordion { |
| 1177 | cursor: pointer; |
| 1178 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1136,19 +1136,34 @@ | |
| 1136 | white-space: nowrap; |
| 1137 | } |
| 1138 | label[for] { |
| 1139 | cursor: pointer; |
| 1140 | } |
| 1141 | button.copy-button { |
| 1142 | width: 14px; |
| 1143 | height: 14px; |
| 1144 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1145 | margin: -2px .24em 0 0; |
| 1146 | padding: 0; |
| 1147 | border: 0; |
| 1148 | background: none; |
| 1149 | font-size: inherit; /* Required for horizontal spacing. */ |
| 1150 | vertical-align: middle; |
| 1151 | user-select: none; |
| 1152 | cursor: pointer; |
| 1153 | } |
| 1154 | button.copy-button-flipped { |
| 1155 | /*Note: .16em is suitable for element grouping.*/ |
| 1156 | margin: -2px 0 0 .16em; |
| 1157 | } |
| 1158 | button.copy-button span { |
| 1159 | display: block; |
| 1160 | width: 100%; |
| 1161 | height: 100%; |
| 1162 | margin: 0; |
| 1163 | padding: 0; |
| 1164 | border: 0; |
| 1165 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1166 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1167 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1168 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1169 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1159,19 +1174,17 @@ | |
| 1174 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1175 | background-repeat: no-repeat; |
| 1176 | background-position: center; |
| 1177 | cursor: pointer; |
| 1178 | } |
| 1179 | button.copy-button:enabled:active span { |
| 1180 | background-size: 90%; |
| 1181 | } |
| 1182 | button.copy-button:disabled span { |
| 1183 | filter: grayscale(1); |
| 1184 | opacity: 0.4; |
| 1185 | } |
| 1186 | .nobr { |
| 1187 | white-space: nowrap; |
| 1188 | } |
| 1189 | .accordion { |
| 1190 | cursor: pointer; |
| 1191 |
+9
-11
| --- src/fossil.copybutton.js | ||
| +++ src/fossil.copybutton.js | ||
| @@ -42,13 +42,11 @@ | ||
| 42 | 42 | |
| 43 | 43 | .oncopy: an optional callback function which is added as an event |
| 44 | 44 | listener for the 'text-copied' event (see below). There is |
| 45 | 45 | functionally no difference from setting this option or adding a |
| 46 | 46 | 'text-copied' event listener to the element, and this option is |
| 47 | - considered to be a convenience form of that. For the sake of | |
| 48 | - framework-level consistency, the default value is a callback | |
| 49 | - which passes the copy button to fossil.dom.flashOnce(). | |
| 47 | + considered to be a convenience form of that. | |
| 50 | 48 | |
| 51 | 49 | Note that this function's own defaultOptions object holds default |
| 52 | 50 | values for some options. Any changes made to that object affect |
| 53 | 51 | any future calls to this function. |
| 54 | 52 | |
| @@ -62,25 +60,21 @@ | ||
| 62 | 60 | member is an object with a "text" property holding the copied |
| 63 | 61 | text. Other properties may be added in the future. The event is |
| 64 | 62 | not fired if copying to the clipboard fails (e.g. is not |
| 65 | 63 | available in the current environment). |
| 66 | 64 | |
| 67 | - As a special case, the copy button's click handler is suppressed | |
| 68 | - (becomes a no-op) for as long as the element has the CSS class | |
| 69 | - "disabled". This allows elements which cannot be disabled via | |
| 70 | - HTML attributes, e.g. a SPAN, to act as a copy button while still | |
| 71 | - providing a way to disable them. | |
| 65 | + The copy button's click handler is suppressed (becomes a no-op) | |
| 66 | + for as long as the element has the "disabled" attribute. | |
| 72 | 67 | |
| 73 | 68 | Returns the copy-initialized element. |
| 74 | 69 | |
| 75 | 70 | Example: |
| 76 | 71 | |
| 77 | 72 | const button = fossil.copyButton('#my-copy-button', { |
| 78 | 73 | copyFromId: 'some-other-element-id' |
| 79 | 74 | }); |
| 80 | 75 | button.addEventListener('text-copied',function(ev){ |
| 81 | - fossil.dom.flashOnce(ev.target); | |
| 82 | 76 | console.debug("Copied text:",ev.detail.text); |
| 83 | 77 | }); |
| 84 | 78 | */ |
| 85 | 79 | F.copyButton = function f(e, opt){ |
| 86 | 80 | if('string'===typeof e){ |
| @@ -103,11 +97,11 @@ | ||
| 103 | 97 | e.addEventListener( |
| 104 | 98 | 'click', |
| 105 | 99 | function(ev){ |
| 106 | 100 | ev.preventDefault(); |
| 107 | 101 | ev.stopPropagation(); |
| 108 | - if(e.classList.contains('disabled')) return; | |
| 102 | + if(e.disabled) return; /* This check is probably redundant. */ | |
| 109 | 103 | const txt = extract.call(opt); |
| 110 | 104 | if(txt && D.copyTextToClipboard(txt)){ |
| 111 | 105 | e.dispatchEvent(new CustomEvent('text-copied',{ |
| 112 | 106 | detail: {text: txt} |
| 113 | 107 | })); |
| @@ -116,15 +110,19 @@ | ||
| 116 | 110 | false |
| 117 | 111 | ); |
| 118 | 112 | if('function' === typeof opt.oncopy){ |
| 119 | 113 | e.addEventListener('text-copied', opt.oncopy, false); |
| 120 | 114 | } |
| 115 | + /* Make sure the <button> contains a single nested <span>. */ | |
| 116 | + if(e.childElementCount!=1 || e.firstChild.tagName!='SPAN'){ | |
| 117 | + D.append(D.clearElement(e), D.span()); | |
| 118 | + } | |
| 121 | 119 | return e; |
| 122 | 120 | }; |
| 123 | 121 | |
| 124 | 122 | F.copyButton.defaultOptions = { |
| 125 | 123 | cssClass: 'copy-button', |
| 126 | - oncopy: D.flashOnce.eventHandler, | |
| 124 | + oncopy: undefined, | |
| 127 | 125 | style: {/*properties copied as-is into element.style*/} |
| 128 | 126 | }; |
| 129 | 127 | |
| 130 | 128 | })(window.fossil); |
| 131 | 129 |
| --- src/fossil.copybutton.js | |
| +++ src/fossil.copybutton.js | |
| @@ -42,13 +42,11 @@ | |
| 42 | |
| 43 | .oncopy: an optional callback function which is added as an event |
| 44 | listener for the 'text-copied' event (see below). There is |
| 45 | functionally no difference from setting this option or adding a |
| 46 | 'text-copied' event listener to the element, and this option is |
| 47 | considered to be a convenience form of that. For the sake of |
| 48 | framework-level consistency, the default value is a callback |
| 49 | which passes the copy button to fossil.dom.flashOnce(). |
| 50 | |
| 51 | Note that this function's own defaultOptions object holds default |
| 52 | values for some options. Any changes made to that object affect |
| 53 | any future calls to this function. |
| 54 | |
| @@ -62,25 +60,21 @@ | |
| 62 | member is an object with a "text" property holding the copied |
| 63 | text. Other properties may be added in the future. The event is |
| 64 | not fired if copying to the clipboard fails (e.g. is not |
| 65 | available in the current environment). |
| 66 | |
| 67 | As a special case, the copy button's click handler is suppressed |
| 68 | (becomes a no-op) for as long as the element has the CSS class |
| 69 | "disabled". This allows elements which cannot be disabled via |
| 70 | HTML attributes, e.g. a SPAN, to act as a copy button while still |
| 71 | providing a way to disable them. |
| 72 | |
| 73 | Returns the copy-initialized element. |
| 74 | |
| 75 | Example: |
| 76 | |
| 77 | const button = fossil.copyButton('#my-copy-button', { |
| 78 | copyFromId: 'some-other-element-id' |
| 79 | }); |
| 80 | button.addEventListener('text-copied',function(ev){ |
| 81 | fossil.dom.flashOnce(ev.target); |
| 82 | console.debug("Copied text:",ev.detail.text); |
| 83 | }); |
| 84 | */ |
| 85 | F.copyButton = function f(e, opt){ |
| 86 | if('string'===typeof e){ |
| @@ -103,11 +97,11 @@ | |
| 103 | e.addEventListener( |
| 104 | 'click', |
| 105 | function(ev){ |
| 106 | ev.preventDefault(); |
| 107 | ev.stopPropagation(); |
| 108 | if(e.classList.contains('disabled')) return; |
| 109 | const txt = extract.call(opt); |
| 110 | if(txt && D.copyTextToClipboard(txt)){ |
| 111 | e.dispatchEvent(new CustomEvent('text-copied',{ |
| 112 | detail: {text: txt} |
| 113 | })); |
| @@ -116,15 +110,19 @@ | |
| 116 | false |
| 117 | ); |
| 118 | if('function' === typeof opt.oncopy){ |
| 119 | e.addEventListener('text-copied', opt.oncopy, false); |
| 120 | } |
| 121 | return e; |
| 122 | }; |
| 123 | |
| 124 | F.copyButton.defaultOptions = { |
| 125 | cssClass: 'copy-button', |
| 126 | oncopy: D.flashOnce.eventHandler, |
| 127 | style: {/*properties copied as-is into element.style*/} |
| 128 | }; |
| 129 | |
| 130 | })(window.fossil); |
| 131 |
| --- src/fossil.copybutton.js | |
| +++ src/fossil.copybutton.js | |
| @@ -42,13 +42,11 @@ | |
| 42 | |
| 43 | .oncopy: an optional callback function which is added as an event |
| 44 | listener for the 'text-copied' event (see below). There is |
| 45 | functionally no difference from setting this option or adding a |
| 46 | 'text-copied' event listener to the element, and this option is |
| 47 | considered to be a convenience form of that. |
| 48 | |
| 49 | Note that this function's own defaultOptions object holds default |
| 50 | values for some options. Any changes made to that object affect |
| 51 | any future calls to this function. |
| 52 | |
| @@ -62,25 +60,21 @@ | |
| 60 | member is an object with a "text" property holding the copied |
| 61 | text. Other properties may be added in the future. The event is |
| 62 | not fired if copying to the clipboard fails (e.g. is not |
| 63 | available in the current environment). |
| 64 | |
| 65 | The copy button's click handler is suppressed (becomes a no-op) |
| 66 | for as long as the element has the "disabled" attribute. |
| 67 | |
| 68 | Returns the copy-initialized element. |
| 69 | |
| 70 | Example: |
| 71 | |
| 72 | const button = fossil.copyButton('#my-copy-button', { |
| 73 | copyFromId: 'some-other-element-id' |
| 74 | }); |
| 75 | button.addEventListener('text-copied',function(ev){ |
| 76 | console.debug("Copied text:",ev.detail.text); |
| 77 | }); |
| 78 | */ |
| 79 | F.copyButton = function f(e, opt){ |
| 80 | if('string'===typeof e){ |
| @@ -103,11 +97,11 @@ | |
| 97 | e.addEventListener( |
| 98 | 'click', |
| 99 | function(ev){ |
| 100 | ev.preventDefault(); |
| 101 | ev.stopPropagation(); |
| 102 | if(e.disabled) return; /* This check is probably redundant. */ |
| 103 | const txt = extract.call(opt); |
| 104 | if(txt && D.copyTextToClipboard(txt)){ |
| 105 | e.dispatchEvent(new CustomEvent('text-copied',{ |
| 106 | detail: {text: txt} |
| 107 | })); |
| @@ -116,15 +110,19 @@ | |
| 110 | false |
| 111 | ); |
| 112 | if('function' === typeof opt.oncopy){ |
| 113 | e.addEventListener('text-copied', opt.oncopy, false); |
| 114 | } |
| 115 | /* Make sure the <button> contains a single nested <span>. */ |
| 116 | if(e.childElementCount!=1 || e.firstChild.tagName!='SPAN'){ |
| 117 | D.append(D.clearElement(e), D.span()); |
| 118 | } |
| 119 | return e; |
| 120 | }; |
| 121 | |
| 122 | F.copyButton.defaultOptions = { |
| 123 | cssClass: 'copy-button', |
| 124 | oncopy: undefined, |
| 125 | style: {/*properties copied as-is into element.style*/} |
| 126 | }; |
| 127 | |
| 128 | })(window.fossil); |
| 129 |
+4
-8
| --- src/fossil.numbered-lines.js | ||
| +++ src/fossil.numbered-lines.js | ||
| @@ -23,13 +23,10 @@ | ||
| 23 | 23 | .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ |
| 24 | 24 | .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */ |
| 25 | 25 | .replace('?&','?'); |
| 26 | 26 | const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 }; |
| 27 | 27 | const lineTip = new F.PopupWidget({ |
| 28 | - style: { | |
| 29 | - cursor: 'pointer' | |
| 30 | - }, | |
| 31 | 28 | refresh: function(){ |
| 32 | 29 | const link = this.state.link; |
| 33 | 30 | D.clearElement(link); |
| 34 | 31 | if(lineState.start){ |
| 35 | 32 | const ls = [lineState.start]; |
| @@ -48,23 +45,22 @@ | ||
| 48 | 45 | D.append(link, "No lines selected."); |
| 49 | 46 | } |
| 50 | 47 | }, |
| 51 | 48 | init: function(){ |
| 52 | 49 | const e = this.e; |
| 53 | - const btnCopy = D.span(), | |
| 54 | - link = D.span(); | |
| 50 | + const btnCopy = D.attr(D.button(), 'id', 'linenum-copy-button'); | |
| 51 | + link = D.label('linenum-copy-button'); | |
| 55 | 52 | this.state = {link}; |
| 56 | 53 | F.copyButton(btnCopy,{ |
| 57 | 54 | copyFromElement: link, |
| 58 | 55 | extractText: ()=>link.dataset.url, |
| 59 | 56 | oncopy: (ev)=>{ |
| 60 | - D.flashOnce(ev.target, undefined, ()=>lineTip.hide()); | |
| 57 | + setTimeout(()=>lineTip.hide(), 400); | |
| 61 | 58 | // arguably too snazzy: F.toast.message("Copied link to clipboard."); |
| 62 | 59 | } |
| 63 | 60 | }); |
| 64 | - this.e.addEventListener('click', ()=>btnCopy.click(), false); | |
| 65 | - D.append(this.e, btnCopy, link) | |
| 61 | + D.append(this.e, btnCopy, link); | |
| 66 | 62 | } |
| 67 | 63 | }); |
| 68 | 64 | |
| 69 | 65 | tbl.addEventListener('click', ()=>lineTip.hide(), true); |
| 70 | 66 | |
| 71 | 67 |
| --- src/fossil.numbered-lines.js | |
| +++ src/fossil.numbered-lines.js | |
| @@ -23,13 +23,10 @@ | |
| 23 | .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ |
| 24 | .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */ |
| 25 | .replace('?&','?'); |
| 26 | const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 }; |
| 27 | const lineTip = new F.PopupWidget({ |
| 28 | style: { |
| 29 | cursor: 'pointer' |
| 30 | }, |
| 31 | refresh: function(){ |
| 32 | const link = this.state.link; |
| 33 | D.clearElement(link); |
| 34 | if(lineState.start){ |
| 35 | const ls = [lineState.start]; |
| @@ -48,23 +45,22 @@ | |
| 48 | D.append(link, "No lines selected."); |
| 49 | } |
| 50 | }, |
| 51 | init: function(){ |
| 52 | const e = this.e; |
| 53 | const btnCopy = D.span(), |
| 54 | link = D.span(); |
| 55 | this.state = {link}; |
| 56 | F.copyButton(btnCopy,{ |
| 57 | copyFromElement: link, |
| 58 | extractText: ()=>link.dataset.url, |
| 59 | oncopy: (ev)=>{ |
| 60 | D.flashOnce(ev.target, undefined, ()=>lineTip.hide()); |
| 61 | // arguably too snazzy: F.toast.message("Copied link to clipboard."); |
| 62 | } |
| 63 | }); |
| 64 | this.e.addEventListener('click', ()=>btnCopy.click(), false); |
| 65 | D.append(this.e, btnCopy, link) |
| 66 | } |
| 67 | }); |
| 68 | |
| 69 | tbl.addEventListener('click', ()=>lineTip.hide(), true); |
| 70 | |
| 71 |
| --- src/fossil.numbered-lines.js | |
| +++ src/fossil.numbered-lines.js | |
| @@ -23,13 +23,10 @@ | |
| 23 | .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ |
| 24 | .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */ |
| 25 | .replace('?&','?'); |
| 26 | const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 }; |
| 27 | const lineTip = new F.PopupWidget({ |
| 28 | refresh: function(){ |
| 29 | const link = this.state.link; |
| 30 | D.clearElement(link); |
| 31 | if(lineState.start){ |
| 32 | const ls = [lineState.start]; |
| @@ -48,23 +45,22 @@ | |
| 45 | D.append(link, "No lines selected."); |
| 46 | } |
| 47 | }, |
| 48 | init: function(){ |
| 49 | const e = this.e; |
| 50 | const btnCopy = D.attr(D.button(), 'id', 'linenum-copy-button'); |
| 51 | link = D.label('linenum-copy-button'); |
| 52 | this.state = {link}; |
| 53 | F.copyButton(btnCopy,{ |
| 54 | copyFromElement: link, |
| 55 | extractText: ()=>link.dataset.url, |
| 56 | oncopy: (ev)=>{ |
| 57 | setTimeout(()=>lineTip.hide(), 400); |
| 58 | // arguably too snazzy: F.toast.message("Copied link to clipboard."); |
| 59 | } |
| 60 | }); |
| 61 | D.append(this.e, btnCopy, link); |
| 62 | } |
| 63 | }); |
| 64 | |
| 65 | tbl.addEventListener('click', ()=>lineTip.hide(), true); |
| 66 | |
| 67 |
+1
-2
| --- src/fossil.page.chat.js | ||
| +++ src/fossil.page.chat.js | ||
| @@ -901,14 +901,13 @@ | ||
| 901 | 901 | const cpId = 'copy-to-clipboard-'+id; |
| 902 | 902 | /* ^^^ copy button element ID, needed for LABEL element |
| 903 | 903 | pairing. Recall that we destroy all child elements of |
| 904 | 904 | `content` each time we hit this block, so we can reuse |
| 905 | 905 | that element ID on subsequent toggles. */ |
| 906 | - const btnCp = D.attr(D.addClass(D.span(),'copy-button'), 'id', cpId); | |
| 906 | + const btnCp = D.attr(D.addClass(D.button(),'copy-button'), 'id', cpId); | |
| 907 | 907 | F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw}); |
| 908 | 908 | const lblCp = D.label(cpId, "Copy unformatted text"); |
| 909 | - lblCp.addEventListener('click',()=>btnCp.click(), false); | |
| 910 | 909 | D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp)); |
| 911 | 910 | } |
| 912 | 911 | delete e.$isToggling; |
| 913 | 912 | D.append(content, child); |
| 914 | 913 | return; |
| 915 | 914 |
| --- src/fossil.page.chat.js | |
| +++ src/fossil.page.chat.js | |
| @@ -901,14 +901,13 @@ | |
| 901 | const cpId = 'copy-to-clipboard-'+id; |
| 902 | /* ^^^ copy button element ID, needed for LABEL element |
| 903 | pairing. Recall that we destroy all child elements of |
| 904 | `content` each time we hit this block, so we can reuse |
| 905 | that element ID on subsequent toggles. */ |
| 906 | const btnCp = D.attr(D.addClass(D.span(),'copy-button'), 'id', cpId); |
| 907 | F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw}); |
| 908 | const lblCp = D.label(cpId, "Copy unformatted text"); |
| 909 | lblCp.addEventListener('click',()=>btnCp.click(), false); |
| 910 | D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp)); |
| 911 | } |
| 912 | delete e.$isToggling; |
| 913 | D.append(content, child); |
| 914 | return; |
| 915 |
| --- src/fossil.page.chat.js | |
| +++ src/fossil.page.chat.js | |
| @@ -901,14 +901,13 @@ | |
| 901 | const cpId = 'copy-to-clipboard-'+id; |
| 902 | /* ^^^ copy button element ID, needed for LABEL element |
| 903 | pairing. Recall that we destroy all child elements of |
| 904 | `content` each time we hit this block, so we can reuse |
| 905 | that element ID on subsequent toggles. */ |
| 906 | const btnCp = D.attr(D.addClass(D.button(),'copy-button'), 'id', cpId); |
| 907 | F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw}); |
| 908 | const lblCp = D.label(cpId, "Copy unformatted text"); |
| 909 | D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp)); |
| 910 | } |
| 911 | delete e.$isToggling; |
| 912 | D.append(content, child); |
| 913 | return; |
| 914 |
+3
-4
| --- src/fossil.page.pikchrshow.js | ||
| +++ src/fossil.page.pikchrshow.js | ||
| @@ -47,11 +47,11 @@ | ||
| 47 | 47 | document.body.classList.add('pikchrshow'); |
| 48 | 48 | P.e = { /* various DOM elements we work with... */ |
| 49 | 49 | previewTarget: E('#pikchrshow-output'), |
| 50 | 50 | previewLegend: E('#pikchrshow-output-wrapper > legend'), |
| 51 | 51 | previewCopyButton: D.attr( |
| 52 | - D.addClass(D.span(),'copy-button'), | |
| 52 | + D.addClass(D.button(),'copy-button'), | |
| 53 | 53 | 'id','preview-copy-button' |
| 54 | 54 | ), |
| 55 | 55 | previewModeLabel: D.label('preview-copy-button'), |
| 56 | 56 | btnSubmit: E('#pikchr-submit-preview'), |
| 57 | 57 | btnStash: E('#pikchr-stash'), |
| @@ -119,11 +119,10 @@ | ||
| 119 | 119 | }, false); |
| 120 | 120 | |
| 121 | 121 | //////////////////////////////////////////////////////////// |
| 122 | 122 | // Setup clipboard-copy of markup/SVG... |
| 123 | 123 | F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText}); |
| 124 | - P.e.previewModeLabel.addEventListener('click', ()=>P.e.previewCopyButton.click(), false); | |
| 125 | 124 | |
| 126 | 125 | //////////////////////////////////////////////////////////// |
| 127 | 126 | // Set up dark mode simulator... |
| 128 | 127 | P.e.cbDarkMode.addEventListener('change', function(ev){ |
| 129 | 128 | if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode'); |
| @@ -348,11 +347,11 @@ | ||
| 348 | 347 | D.addClass(preTgt, 'error'); |
| 349 | 348 | this.e.previewModeLabel.innerText = "Error"; |
| 350 | 349 | return; |
| 351 | 350 | } |
| 352 | 351 | D.removeClass(preTgt, 'error'); |
| 353 | - D.removeClass(this.e.previewCopyButton, 'disabled'); | |
| 352 | + this.e.previewCopyButton.disabled = false; | |
| 354 | 353 | D.removeClass(this.e.markupAlignWrapper, 'hidden'); |
| 355 | 354 | D.enable(this.e.previewModeToggle, this.e.markupAlignRadios); |
| 356 | 355 | let label, svg; |
| 357 | 356 | switch(this.previewMode){ |
| 358 | 357 | case 0: |
| @@ -427,11 +426,11 @@ | ||
| 427 | 426 | P.renderPreview(); |
| 428 | 427 | }; |
| 429 | 428 | } |
| 430 | 429 | D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios); |
| 431 | 430 | D.addClass(this.e.markupAlignWrapper, 'hidden'); |
| 432 | - D.addClass(this.e.previewCopyButton, 'disabled'); | |
| 431 | + this.e.previewCopyButton.disabled = true; | |
| 433 | 432 | const content = this.e.taContent.value.trim(); |
| 434 | 433 | this.response.raw = this.response.rawSvg = undefined; |
| 435 | 434 | this.response.inputText = content; |
| 436 | 435 | const sampleScript = fp.$_sampleScript; |
| 437 | 436 | delete fp.$_sampleScript; |
| 438 | 437 |
| --- src/fossil.page.pikchrshow.js | |
| +++ src/fossil.page.pikchrshow.js | |
| @@ -47,11 +47,11 @@ | |
| 47 | document.body.classList.add('pikchrshow'); |
| 48 | P.e = { /* various DOM elements we work with... */ |
| 49 | previewTarget: E('#pikchrshow-output'), |
| 50 | previewLegend: E('#pikchrshow-output-wrapper > legend'), |
| 51 | previewCopyButton: D.attr( |
| 52 | D.addClass(D.span(),'copy-button'), |
| 53 | 'id','preview-copy-button' |
| 54 | ), |
| 55 | previewModeLabel: D.label('preview-copy-button'), |
| 56 | btnSubmit: E('#pikchr-submit-preview'), |
| 57 | btnStash: E('#pikchr-stash'), |
| @@ -119,11 +119,10 @@ | |
| 119 | }, false); |
| 120 | |
| 121 | //////////////////////////////////////////////////////////// |
| 122 | // Setup clipboard-copy of markup/SVG... |
| 123 | F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText}); |
| 124 | P.e.previewModeLabel.addEventListener('click', ()=>P.e.previewCopyButton.click(), false); |
| 125 | |
| 126 | //////////////////////////////////////////////////////////// |
| 127 | // Set up dark mode simulator... |
| 128 | P.e.cbDarkMode.addEventListener('change', function(ev){ |
| 129 | if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode'); |
| @@ -348,11 +347,11 @@ | |
| 348 | D.addClass(preTgt, 'error'); |
| 349 | this.e.previewModeLabel.innerText = "Error"; |
| 350 | return; |
| 351 | } |
| 352 | D.removeClass(preTgt, 'error'); |
| 353 | D.removeClass(this.e.previewCopyButton, 'disabled'); |
| 354 | D.removeClass(this.e.markupAlignWrapper, 'hidden'); |
| 355 | D.enable(this.e.previewModeToggle, this.e.markupAlignRadios); |
| 356 | let label, svg; |
| 357 | switch(this.previewMode){ |
| 358 | case 0: |
| @@ -427,11 +426,11 @@ | |
| 427 | P.renderPreview(); |
| 428 | }; |
| 429 | } |
| 430 | D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios); |
| 431 | D.addClass(this.e.markupAlignWrapper, 'hidden'); |
| 432 | D.addClass(this.e.previewCopyButton, 'disabled'); |
| 433 | const content = this.e.taContent.value.trim(); |
| 434 | this.response.raw = this.response.rawSvg = undefined; |
| 435 | this.response.inputText = content; |
| 436 | const sampleScript = fp.$_sampleScript; |
| 437 | delete fp.$_sampleScript; |
| 438 |
| --- src/fossil.page.pikchrshow.js | |
| +++ src/fossil.page.pikchrshow.js | |
| @@ -47,11 +47,11 @@ | |
| 47 | document.body.classList.add('pikchrshow'); |
| 48 | P.e = { /* various DOM elements we work with... */ |
| 49 | previewTarget: E('#pikchrshow-output'), |
| 50 | previewLegend: E('#pikchrshow-output-wrapper > legend'), |
| 51 | previewCopyButton: D.attr( |
| 52 | D.addClass(D.button(),'copy-button'), |
| 53 | 'id','preview-copy-button' |
| 54 | ), |
| 55 | previewModeLabel: D.label('preview-copy-button'), |
| 56 | btnSubmit: E('#pikchr-submit-preview'), |
| 57 | btnStash: E('#pikchr-stash'), |
| @@ -119,11 +119,10 @@ | |
| 119 | }, false); |
| 120 | |
| 121 | //////////////////////////////////////////////////////////// |
| 122 | // Setup clipboard-copy of markup/SVG... |
| 123 | F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText}); |
| 124 | |
| 125 | //////////////////////////////////////////////////////////// |
| 126 | // Set up dark mode simulator... |
| 127 | P.e.cbDarkMode.addEventListener('change', function(ev){ |
| 128 | if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode'); |
| @@ -348,11 +347,11 @@ | |
| 347 | D.addClass(preTgt, 'error'); |
| 348 | this.e.previewModeLabel.innerText = "Error"; |
| 349 | return; |
| 350 | } |
| 351 | D.removeClass(preTgt, 'error'); |
| 352 | this.e.previewCopyButton.disabled = false; |
| 353 | D.removeClass(this.e.markupAlignWrapper, 'hidden'); |
| 354 | D.enable(this.e.previewModeToggle, this.e.markupAlignRadios); |
| 355 | let label, svg; |
| 356 | switch(this.previewMode){ |
| 357 | case 0: |
| @@ -427,11 +426,11 @@ | |
| 426 | P.renderPreview(); |
| 427 | }; |
| 428 | } |
| 429 | D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios); |
| 430 | D.addClass(this.e.markupAlignWrapper, 'hidden'); |
| 431 | this.e.previewCopyButton.disabled = true; |
| 432 | const content = this.e.taContent.value.trim(); |
| 433 | this.response.raw = this.response.rawSvg = undefined; |
| 434 | this.response.inputText = content; |
| 435 | const sampleScript = fp.$_sampleScript; |
| 436 | delete fp.$_sampleScript; |
| 437 |
| --- src/fossil.page.pikchrshowasm.js | ||
| +++ src/fossil.page.pikchrshowasm.js | ||
| @@ -312,11 +312,10 @@ | ||
| 312 | 312 | if(this.e.pikOut.dataset.pikchr){ |
| 313 | 313 | this.render(this.e.pikOut.dataset.pikchr); |
| 314 | 314 | } |
| 315 | 315 | }.bind(PS)); |
| 316 | 316 | F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText}); |
| 317 | - PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false); | |
| 318 | 317 | |
| 319 | 318 | PS.addMsgHandler('working',function f(ev){ |
| 320 | 319 | switch(ev.data){ |
| 321 | 320 | case 'start': /* See notes in preStartWork(). */; return; |
| 322 | 321 | case 'end': |
| 323 | 322 |
| --- src/fossil.page.pikchrshowasm.js | |
| +++ src/fossil.page.pikchrshowasm.js | |
| @@ -312,11 +312,10 @@ | |
| 312 | if(this.e.pikOut.dataset.pikchr){ |
| 313 | this.render(this.e.pikOut.dataset.pikchr); |
| 314 | } |
| 315 | }.bind(PS)); |
| 316 | F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText}); |
| 317 | PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false); |
| 318 | |
| 319 | PS.addMsgHandler('working',function f(ev){ |
| 320 | switch(ev.data){ |
| 321 | case 'start': /* See notes in preStartWork(). */; return; |
| 322 | case 'end': |
| 323 |
| --- src/fossil.page.pikchrshowasm.js | |
| +++ src/fossil.page.pikchrshowasm.js | |
| @@ -312,11 +312,10 @@ | |
| 312 | if(this.e.pikOut.dataset.pikchr){ |
| 313 | this.render(this.e.pikOut.dataset.pikchr); |
| 314 | } |
| 315 | }.bind(PS)); |
| 316 | F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText}); |
| 317 | |
| 318 | PS.addMsgHandler('working',function f(ev){ |
| 319 | switch(ev.data){ |
| 320 | case 'start': /* See notes in preStartWork(). */; return; |
| 321 | case 'end': |
| 322 |
+1
-1
| --- src/fossil.page.wikiedit.js | ||
| +++ src/fossil.page.wikiedit.js | ||
| @@ -1234,11 +1234,11 @@ | ||
| 1234 | 1234 | encodeURIComponent(a.filename) |
| 1235 | 1235 | ].join(''), |
| 1236 | 1236 | "raw/"+a.src |
| 1237 | 1237 | ].forEach(function(url){ |
| 1238 | 1238 | const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url); |
| 1239 | - const urlCopy = D.span(); | |
| 1239 | + const urlCopy = D.button(); | |
| 1240 | 1240 | const li = D.li(ul); |
| 1241 | 1241 | D.append(li, urlCopy, " ", imgUrl); |
| 1242 | 1242 | F.copyButton(urlCopy, {copyFromElement: imgUrl}); |
| 1243 | 1243 | }); |
| 1244 | 1244 | }); |
| 1245 | 1245 |
| --- src/fossil.page.wikiedit.js | |
| +++ src/fossil.page.wikiedit.js | |
| @@ -1234,11 +1234,11 @@ | |
| 1234 | encodeURIComponent(a.filename) |
| 1235 | ].join(''), |
| 1236 | "raw/"+a.src |
| 1237 | ].forEach(function(url){ |
| 1238 | const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url); |
| 1239 | const urlCopy = D.span(); |
| 1240 | const li = D.li(ul); |
| 1241 | D.append(li, urlCopy, " ", imgUrl); |
| 1242 | F.copyButton(urlCopy, {copyFromElement: imgUrl}); |
| 1243 | }); |
| 1244 | }); |
| 1245 |
| --- src/fossil.page.wikiedit.js | |
| +++ src/fossil.page.wikiedit.js | |
| @@ -1234,11 +1234,11 @@ | |
| 1234 | encodeURIComponent(a.filename) |
| 1235 | ].join(''), |
| 1236 | "raw/"+a.src |
| 1237 | ].forEach(function(url){ |
| 1238 | const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url); |
| 1239 | const urlCopy = D.button(); |
| 1240 | const li = D.li(ul); |
| 1241 | D.append(li, urlCopy, " ", imgUrl); |
| 1242 | F.copyButton(urlCopy, {copyFromElement: imgUrl}); |
| 1243 | }); |
| 1244 | }); |
| 1245 |
+2
-2
| --- src/pikchrshow.c | ||
| +++ src/pikchrshow.c | ||
| @@ -462,12 +462,12 @@ | ||
| 462 | 462 | } CX("</fieldset><!-- .zone-wrapper.input -->"); |
| 463 | 463 | CX("<fieldset class='zone-wrapper output'>"); { |
| 464 | 464 | CX("<legend><div class='button-bar'>"); |
| 465 | 465 | CX("<button id='btn-render-mode'>Render Mode</button> "); |
| 466 | 466 | CX("<span style='white-space:nowrap'>" |
| 467 | - "<span id='preview-copy-button' " | |
| 468 | - "title='Tap to copy to clipboard.'></span>" | |
| 467 | + "<button id='preview-copy-button' " | |
| 468 | + "title='Tap to copy to clipboard.'><span></span></button>" | |
| 469 | 469 | "<label for='preview-copy-button' " |
| 470 | 470 | "title='Tap to copy to clipboard.'></label>" |
| 471 | 471 | "</span>"); |
| 472 | 472 | CX("</div></legend>"); |
| 473 | 473 | CX("<div id='pikchr-output-wrapper'>"); |
| 474 | 474 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -462,12 +462,12 @@ | |
| 462 | } CX("</fieldset><!-- .zone-wrapper.input -->"); |
| 463 | CX("<fieldset class='zone-wrapper output'>"); { |
| 464 | CX("<legend><div class='button-bar'>"); |
| 465 | CX("<button id='btn-render-mode'>Render Mode</button> "); |
| 466 | CX("<span style='white-space:nowrap'>" |
| 467 | "<span id='preview-copy-button' " |
| 468 | "title='Tap to copy to clipboard.'></span>" |
| 469 | "<label for='preview-copy-button' " |
| 470 | "title='Tap to copy to clipboard.'></label>" |
| 471 | "</span>"); |
| 472 | CX("</div></legend>"); |
| 473 | CX("<div id='pikchr-output-wrapper'>"); |
| 474 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -462,12 +462,12 @@ | |
| 462 | } CX("</fieldset><!-- .zone-wrapper.input -->"); |
| 463 | CX("<fieldset class='zone-wrapper output'>"); { |
| 464 | CX("<legend><div class='button-bar'>"); |
| 465 | CX("<button id='btn-render-mode'>Render Mode</button> "); |
| 466 | CX("<span style='white-space:nowrap'>" |
| 467 | "<button id='preview-copy-button' " |
| 468 | "title='Tap to copy to clipboard.'><span></span></button>" |
| 469 | "<label for='preview-copy-button' " |
| 470 | "title='Tap to copy to clipboard.'></label>" |
| 471 | "</span>"); |
| 472 | CX("</div></legend>"); |
| 473 | CX("<div id='pikchr-output-wrapper'>"); |
| 474 |
+20
-16
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -478,11 +478,11 @@ | ||
| 478 | 478 | /* |
| 479 | 479 | ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js |
| 480 | 480 | ** Javascript module, and generates HTML elements with the following IDs: |
| 481 | 481 | ** |
| 482 | 482 | ** TARGETID: The <span> wrapper around TEXT. |
| 483 | -** copy-TARGETID: The <span> for the copy button. | |
| 483 | +** copy-TARGETID: The <button> for the copy button. | |
| 484 | 484 | ** |
| 485 | 485 | ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT. |
| 486 | 486 | ** |
| 487 | 487 | ** The COPYLENGTH argument defines the length of the substring of TEXT copied to |
| 488 | 488 | ** clipboard: |
| @@ -510,18 +510,20 @@ | ||
| 510 | 510 | if( cchLength==1 ) cchLength = hash_digits(0); |
| 511 | 511 | else if( cchLength==2 ) cchLength = hash_digits(1); |
| 512 | 512 | if( !bFlipped ){ |
| 513 | 513 | const char *zBtnFmt = |
| 514 | 514 | "<span class=\"nobr\">" |
| 515 | - "<span " | |
| 516 | - "class=\"copy-button\" " | |
| 517 | - "id=\"copy-%h\" " | |
| 518 | - "data-copytarget=\"%h\" " | |
| 519 | - "data-copylength=\"%d\">" | |
| 520 | - "</span>" | |
| 515 | + "<button " | |
| 516 | + "class=\"copy-button\" " | |
| 517 | + "id=\"copy-%h\" " | |
| 518 | + "data-copytarget=\"%h\" " | |
| 519 | + "data-copylength=\"%d\">" | |
| 520 | + "<span>" | |
| 521 | + "</span>" | |
| 522 | + "</button>" | |
| 521 | 523 | "<span id=\"%h\">" |
| 522 | - "%s" | |
| 524 | + "%s" | |
| 523 | 525 | "</span>" |
| 524 | 526 | "</span>"; |
| 525 | 527 | if( bOutputCGI ){ |
| 526 | 528 | cgi_printf( |
| 527 | 529 | zBtnFmt/*works-like:"%h%h%d%h%s"*/, |
| @@ -533,18 +535,20 @@ | ||
| 533 | 535 | } |
| 534 | 536 | }else{ |
| 535 | 537 | const char *zBtnFmt = |
| 536 | 538 | "<span class=\"nobr\">" |
| 537 | 539 | "<span id=\"%h\">" |
| 538 | - "%s" | |
| 539 | - "</span>" | |
| 540 | - "<span " | |
| 541 | - "class=\"copy-button copy-button-flipped\" " | |
| 542 | - "id=\"copy-%h\" " | |
| 543 | - "data-copytarget=\"%h\" " | |
| 544 | - "data-copylength=\"%d\">" | |
| 545 | - "</span>" | |
| 540 | + "%s" | |
| 541 | + "</span>" | |
| 542 | + "<button " | |
| 543 | + "class=\"copy-button copy-button-flipped\" " | |
| 544 | + "id=\"copy-%h\" " | |
| 545 | + "data-copytarget=\"%h\" " | |
| 546 | + "data-copylength=\"%d\">" | |
| 547 | + "<span>" | |
| 548 | + "</span>" | |
| 549 | + "</button>" | |
| 546 | 550 | "</span>"; |
| 547 | 551 | if( bOutputCGI ){ |
| 548 | 552 | cgi_printf( |
| 549 | 553 | zBtnFmt/*works-like:"%h%s%h%h%d"*/, |
| 550 | 554 | zTargetId,zText,zTargetId,zTargetId,cchLength); |
| 551 | 555 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -478,11 +478,11 @@ | |
| 478 | /* |
| 479 | ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js |
| 480 | ** Javascript module, and generates HTML elements with the following IDs: |
| 481 | ** |
| 482 | ** TARGETID: The <span> wrapper around TEXT. |
| 483 | ** copy-TARGETID: The <span> for the copy button. |
| 484 | ** |
| 485 | ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT. |
| 486 | ** |
| 487 | ** The COPYLENGTH argument defines the length of the substring of TEXT copied to |
| 488 | ** clipboard: |
| @@ -510,18 +510,20 @@ | |
| 510 | if( cchLength==1 ) cchLength = hash_digits(0); |
| 511 | else if( cchLength==2 ) cchLength = hash_digits(1); |
| 512 | if( !bFlipped ){ |
| 513 | const char *zBtnFmt = |
| 514 | "<span class=\"nobr\">" |
| 515 | "<span " |
| 516 | "class=\"copy-button\" " |
| 517 | "id=\"copy-%h\" " |
| 518 | "data-copytarget=\"%h\" " |
| 519 | "data-copylength=\"%d\">" |
| 520 | "</span>" |
| 521 | "<span id=\"%h\">" |
| 522 | "%s" |
| 523 | "</span>" |
| 524 | "</span>"; |
| 525 | if( bOutputCGI ){ |
| 526 | cgi_printf( |
| 527 | zBtnFmt/*works-like:"%h%h%d%h%s"*/, |
| @@ -533,18 +535,20 @@ | |
| 533 | } |
| 534 | }else{ |
| 535 | const char *zBtnFmt = |
| 536 | "<span class=\"nobr\">" |
| 537 | "<span id=\"%h\">" |
| 538 | "%s" |
| 539 | "</span>" |
| 540 | "<span " |
| 541 | "class=\"copy-button copy-button-flipped\" " |
| 542 | "id=\"copy-%h\" " |
| 543 | "data-copytarget=\"%h\" " |
| 544 | "data-copylength=\"%d\">" |
| 545 | "</span>" |
| 546 | "</span>"; |
| 547 | if( bOutputCGI ){ |
| 548 | cgi_printf( |
| 549 | zBtnFmt/*works-like:"%h%s%h%h%d"*/, |
| 550 | zTargetId,zText,zTargetId,zTargetId,cchLength); |
| 551 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -478,11 +478,11 @@ | |
| 478 | /* |
| 479 | ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js |
| 480 | ** Javascript module, and generates HTML elements with the following IDs: |
| 481 | ** |
| 482 | ** TARGETID: The <span> wrapper around TEXT. |
| 483 | ** copy-TARGETID: The <button> for the copy button. |
| 484 | ** |
| 485 | ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT. |
| 486 | ** |
| 487 | ** The COPYLENGTH argument defines the length of the substring of TEXT copied to |
| 488 | ** clipboard: |
| @@ -510,18 +510,20 @@ | |
| 510 | if( cchLength==1 ) cchLength = hash_digits(0); |
| 511 | else if( cchLength==2 ) cchLength = hash_digits(1); |
| 512 | if( !bFlipped ){ |
| 513 | const char *zBtnFmt = |
| 514 | "<span class=\"nobr\">" |
| 515 | "<button " |
| 516 | "class=\"copy-button\" " |
| 517 | "id=\"copy-%h\" " |
| 518 | "data-copytarget=\"%h\" " |
| 519 | "data-copylength=\"%d\">" |
| 520 | "<span>" |
| 521 | "</span>" |
| 522 | "</button>" |
| 523 | "<span id=\"%h\">" |
| 524 | "%s" |
| 525 | "</span>" |
| 526 | "</span>"; |
| 527 | if( bOutputCGI ){ |
| 528 | cgi_printf( |
| 529 | zBtnFmt/*works-like:"%h%h%d%h%s"*/, |
| @@ -533,18 +535,20 @@ | |
| 535 | } |
| 536 | }else{ |
| 537 | const char *zBtnFmt = |
| 538 | "<span class=\"nobr\">" |
| 539 | "<span id=\"%h\">" |
| 540 | "%s" |
| 541 | "</span>" |
| 542 | "<button " |
| 543 | "class=\"copy-button copy-button-flipped\" " |
| 544 | "id=\"copy-%h\" " |
| 545 | "data-copytarget=\"%h\" " |
| 546 | "data-copylength=\"%d\">" |
| 547 | "<span>" |
| 548 | "</span>" |
| 549 | "</button>" |
| 550 | "</span>"; |
| 551 | if( bOutputCGI ){ |
| 552 | cgi_printf( |
| 553 | zBtnFmt/*works-like:"%h%s%h%h%d"*/, |
| 554 | zTargetId,zText,zTargetId,zTargetId,cchLength); |
| 555 |