Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch copybtn.js-responsive Excluding Merge-Ins
This is equivalent to a diff from c6265bb3 to 2bc2f724
2025-08-13
| ||
15:48 | Allow the mimetype query parameter for non-CGI content in /ext. ... (Leaf check-in: 639b96b9 user: drh tags: trunk) | |
2025-08-12
| ||
15:27 | Use equal horizontal spacing for normal and "flipped" Copy Buttons (where the latter are positioned after the text to be copied). The idea is for the buttons to be tied to "their" text without spaces in between, resulting in a somewhat narrower spacing to emphasize the connection, but to have normal HTML whitespace on the other side. ... (Leaf check-in: 2bc2f724 user: florian tags: copybtn.js-responsive) | |
15:20 | Add some higher-specificity CSS declarations to prevent dark-mode skins from overriding the relevant styles of the Copy Button layout, so users don't need to sync their skin customizations with the changes on this branch. ... (check-in: b7f2c9f3 user: florian tags: copybtn.js-responsive) | |
15:04 | Revamp the Copy Buttons for a more responsive user experience. See the wiki page linked to this branch for more details. ... (check-in: 32c3a210 user: florian tags: copybtn.js-responsive) | |
2025-08-10
| ||
10:28 | Raise an error when trying to insert an unversioned file if the file size would cause the database row to exceed SQLITE_LIMIT_LENGTH. ... (check-in: c6265bb3 user: drh tags: trunk) | |
2025-08-07
| ||
19:46 | Add an assert() in a block which cannot happen. It survives 'reconstruct', so we can probably remove the block, but leaving it around for a while seems prudent. ... (check-in: 7d4af37f user: stephan tags: trunk) | |
Changes to src/copybtn.js.
1 2 3 | /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts ** thereof) of the target elements to the clipboard. ** | | | | < < < < | > > > > | | > > | < < > > > > > | < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts ** thereof) of the target elements to the clipboard. ** ** Newly created buttons are <button> elements plus a nested <span> element with ** an SVG background icon, defined by the "copy-button" class in the default CSS ** style sheet, and are assigned the element ID "copy-<idTarget>". ** ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(), ** needs to be called to attach the "onclick" handler (done automatically from ** a handler attached to the "DOMContentLoaded" event). These functions create ** the nested <span> element if the <button> element has no child nodes. Using ** static HTML for the <span> element ensures the buttons are visible if there ** are script errors, which may be useful for Fossil JS hackers (as good parts ** of the Fossil web UI come down on JS errors, anyway). ** ** The initialization functions do not overwrite the "data-copytarget" and ** "data-copylength" attributes with empty or null values for <idTarget> and ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the ** previous copy length limit. ** ** HTML snippet for statically created buttons: ** ** <button class="copy-button" id="copy-<idTarget>" ** data-copytarget="<idTarget>" data-copylength="<cchLength>"> ** <span></span> ** </button> */ function makeCopyButton(idTarget,bFlipped,cchLength){ var elButton = document.createElement("button"); 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){ if( idTarget ) elButton.setAttribute("data-copytarget",idTarget); if( cchLength ) elButton.setAttribute("data-copylength",cchLength); elButton.onclick = clickCopyButton; /* Make sure the <button> contains a single nested <span>. */ if( elButton.childElementCount!=1 || elButton.firstChild.tagName!="SPAN" ){ while( elButton.firstChild ) elButton.removeChild(elButton.lastChild); elButton.appendChild(document.createElement("span")); } return elButton; } setTimeout(function(){ var elButtons = document.getElementsByClassName("copy-button"); for ( var i=0; i<elButtons.length; i++ ){ initCopyButton(elButtons[i],0,0); } },1); /* The onclick handler for the "Copy Button". */ function clickCopyButton(e){ e.preventDefault(); /* Mandatory for <a> and <button>. */ e.stopPropagation(); if( this.disabled ) return; /* This check is probably redundant. */ var idTarget = this.getAttribute("data-copytarget"); var elTarget = document.getElementById(idTarget); if( elTarget ){ var text = elTarget.innerText.replace(/^\s+|\s+$/g,""); var cchLength = parseInt(this.getAttribute("data-copylength")); if( !isNaN(cchLength) && cchLength>0 ){ text = text.slice(0,cchLength); /* Assume single-byte chars. */ } copyTextToClipboard(text); } } /* Create a temporary <textarea> element and copy the contents to clipboard. */ function copyTextToClipboard(text){ if( window.clipboardData && window.clipboardData.setData ){ window.clipboardData.setData("Text",text); }else{ var elTextarea = document.createElement("textarea"); |
︙ | ︙ |
Changes to src/default.css.
︙ | ︙ | |||
1134 1135 1136 1137 1138 1139 1140 | } label { white-space: nowrap; } label[for] { cursor: pointer; } | | | > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < | 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 | } label { white-space: nowrap; } label[for] { cursor: pointer; } button.copy-button, button.copy-button:hover, button.copy-button:focus, button.copy-button:active { width: 14px; height: 14px; /*Note: .24em is slightly smaller than the average width of a normal space.*/ margin: -2px .24em 0 0; padding: 0; border: 0; outline: 0; background: none; font-size: inherit; /* Required for horizontal spacing. */ vertical-align: middle; user-select: none; cursor: pointer; } button.copy-button-flipped, button.copy-button-flipped:hover, button.copy-button-flipped:focus, button.copy-button-flipped:active { margin: -2px 0 0 .24em; } button.copy-button span { display: block; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ d='M3,2h3.6l2.4,2.4v5.6h-6z'/%3E%3Cpath style='fill:rgb(80,128,208)' \ d='M4,5h4v1h-4zm0,2h4v1h-4z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ d='M5,3h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ d='M10,4.4v1.6h1.6zm-4,-0.6h3v3h-3zm0,3h6v5.4h-6z'/%3E%3Cpath style='fill:rgb(80,128,208)' \ d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: center; cursor: pointer; } button.copy-button:enabled:active span { background-size: 90%; } button.copy-button:disabled span { filter: grayscale(1); opacity: 0.4; } .nobr { white-space: nowrap; } .accordion { cursor: pointer; } .accordion_btn { |
︙ | ︙ |
Changes to src/fossil.copybutton.js.
︙ | ︙ | |||
40 41 42 43 44 45 46 | .style: optional object of properties to copy directly into e.style. .oncopy: an optional callback function which is added as an event listener for the 'text-copied' event (see below). There is functionally no difference from setting this option or adding a 'text-copied' event listener to the element, and this option is | | < < | | < < < < | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | .style: optional object of properties to copy directly into e.style. .oncopy: an optional callback function which is added as an event listener for the 'text-copied' event (see below). There is functionally no difference from setting this option or adding a 'text-copied' event listener to the element, and this option is considered to be a convenience form of that. Note that this function's own defaultOptions object holds default values for some options. Any changes made to that object affect any future calls to this function. Be aware that clipboard functionality might or might not be available in any given environment. If this button appears to have no effect, that may be because it is not enabled/available in the current platform. The copy button emits custom event 'text-copied' after it has successfully copied text to the clipboard. The event's "detail" member is an object with a "text" property holding the copied text. Other properties may be added in the future. The event is not fired if copying to the clipboard fails (e.g. is not available in the current environment). The copy button's click handler is suppressed (becomes a no-op) for as long as the element has the "disabled" attribute. Returns the copy-initialized element. Example: const button = fossil.copyButton('#my-copy-button', { copyFromId: 'some-other-element-id' }); button.addEventListener('text-copied',function(ev){ console.debug("Copied text:",ev.detail.text); }); */ F.copyButton = function f(e, opt){ if('string'===typeof e){ e = document.querySelector(e); } |
︙ | ︙ | |||
101 102 103 104 105 106 107 | ); D.copyStyle(e, opt.style); e.addEventListener( 'click', function(ev){ ev.preventDefault(); ev.stopPropagation(); | | > > > > | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | ); D.copyStyle(e, opt.style); e.addEventListener( 'click', function(ev){ ev.preventDefault(); ev.stopPropagation(); if(e.disabled) return; /* This check is probably redundant. */ const txt = extract.call(opt); if(txt && D.copyTextToClipboard(txt)){ e.dispatchEvent(new CustomEvent('text-copied',{ detail: {text: txt} })); } }, false ); if('function' === typeof opt.oncopy){ e.addEventListener('text-copied', opt.oncopy, false); } /* Make sure the <button> contains a single nested <span>. */ if(e.childElementCount!=1 || e.firstChild.tagName!='SPAN'){ D.append(D.clearElement(e), D.span()); } return e; }; F.copyButton.defaultOptions = { cssClass: 'copy-button', oncopy: undefined, style: {/*properties copied as-is into element.style*/} }; })(window.fossil); |
Changes to src/fossil.numbered-lines.js.
︙ | ︙ | |||
21 22 23 24 25 26 27 | const tdLn = tbl.querySelector('td.line-numbers'); const urlArgsRaw = (window.location.search||'?') .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */ .replace('?&','?'); const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 }; const lineTip = new F.PopupWidget({ | < < < | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | const tdLn = tbl.querySelector('td.line-numbers'); const urlArgsRaw = (window.location.search||'?') .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */ .replace('?&','?'); const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 }; const lineTip = new F.PopupWidget({ refresh: function(){ const link = this.state.link; D.clearElement(link); if(lineState.start){ const ls = [lineState.start]; if(lineState.end) ls.push(lineState.end); link.dataset.url = ( |
︙ | ︙ | |||
46 47 48 49 50 51 52 | ); }else{ D.append(link, "No lines selected."); } }, init: function(){ const e = this.e; | | | | < | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | ); }else{ D.append(link, "No lines selected."); } }, init: function(){ const e = this.e; const btnCopy = D.attr(D.button(), 'id', 'linenum-copy-button'); link = D.label('linenum-copy-button'); this.state = {link}; F.copyButton(btnCopy,{ copyFromElement: link, extractText: ()=>link.dataset.url, oncopy: (ev)=>{ setTimeout(()=>lineTip.hide(), 400); // arguably too snazzy: F.toast.message("Copied link to clipboard."); } }); D.append(this.e, btnCopy, link); } }); tbl.addEventListener('click', ()=>lineTip.hide(), true); tdLn.addEventListener('click', function f(ev){ if('SPAN'!==ev.target.tagName) return; |
︙ | ︙ |
Changes to src/fossil.page.chat.js.
︙ | ︙ | |||
899 900 901 902 903 904 905 | mouse-copying from that field collecting twice as many newlines as it should (for unknown reasons). */ const cpId = 'copy-to-clipboard-'+id; /* ^^^ copy button element ID, needed for LABEL element pairing. Recall that we destroy all child elements of `content` each time we hit this block, so we can reuse that element ID on subsequent toggles. */ | | < | 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 | mouse-copying from that field collecting twice as many newlines as it should (for unknown reasons). */ const cpId = 'copy-to-clipboard-'+id; /* ^^^ copy button element ID, needed for LABEL element pairing. Recall that we destroy all child elements of `content` each time we hit this block, so we can reuse that element ID on subsequent toggles. */ const btnCp = D.attr(D.addClass(D.button(),'copy-button'), 'id', cpId); F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw}); const lblCp = D.label(cpId, "Copy unformatted text"); D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp)); } delete e.$isToggling; D.append(content, child); return; } // We need to fetch the plain-text version... |
︙ | ︙ |
Changes to src/fossil.page.pikchrshow.js.
︙ | ︙ | |||
45 46 47 48 49 50 51 | F.onPageLoad(function() { document.body.classList.add('pikchrshow'); P.e = { /* various DOM elements we work with... */ previewTarget: E('#pikchrshow-output'), previewLegend: E('#pikchrshow-output-wrapper > legend'), previewCopyButton: D.attr( | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | F.onPageLoad(function() { document.body.classList.add('pikchrshow'); P.e = { /* various DOM elements we work with... */ previewTarget: E('#pikchrshow-output'), previewLegend: E('#pikchrshow-output-wrapper > legend'), previewCopyButton: D.attr( D.addClass(D.button(),'copy-button'), 'id','preview-copy-button' ), previewModeLabel: D.label('preview-copy-button'), btnSubmit: E('#pikchr-submit-preview'), btnStash: E('#pikchr-stash'), btnUnstash: E('#pikchr-unstash'), btnClearStash: E('#pikchr-clear-stash'), |
︙ | ︙ | |||
117 118 119 120 121 122 123 | return false; } }, false); //////////////////////////////////////////////////////////// // Setup clipboard-copy of markup/SVG... F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText}); | < | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | return false; } }, false); //////////////////////////////////////////////////////////// // Setup clipboard-copy of markup/SVG... F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText}); //////////////////////////////////////////////////////////// // Set up dark mode simulator... P.e.cbDarkMode.addEventListener('change', function(ev){ if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode'); else D.removeClass(P.e.previewTarget, 'dark-mode'); }, false); |
︙ | ︙ | |||
346 347 348 349 350 351 352 | if(this.response.isError){ D.append(D.clearElement(preTgt), D.parseHtml(P.response.raw)); D.addClass(preTgt, 'error'); this.e.previewModeLabel.innerText = "Error"; return; } D.removeClass(preTgt, 'error'); | | | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | if(this.response.isError){ D.append(D.clearElement(preTgt), D.parseHtml(P.response.raw)); D.addClass(preTgt, 'error'); this.e.previewModeLabel.innerText = "Error"; return; } D.removeClass(preTgt, 'error'); this.e.previewCopyButton.disabled = false; D.removeClass(this.e.markupAlignWrapper, 'hidden'); D.enable(this.e.previewModeToggle, this.e.markupAlignRadios); let label, svg; switch(this.previewMode){ case 0: label = "SVG"; f.showMarkupAlignment(false); |
︙ | ︙ | |||
425 426 427 428 429 430 431 | P.response.isError = isError; D.enable(fp.toDisable); P.renderPreview(); }; } D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios); D.addClass(this.e.markupAlignWrapper, 'hidden'); | | | 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | P.response.isError = isError; D.enable(fp.toDisable); P.renderPreview(); }; } D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios); D.addClass(this.e.markupAlignWrapper, 'hidden'); this.e.previewCopyButton.disabled = true; const content = this.e.taContent.value.trim(); this.response.raw = this.response.rawSvg = undefined; this.response.inputText = content; const sampleScript = fp.$_sampleScript; delete fp.$_sampleScript; if(sampleScript && sampleScript.cached){ fp.updateView(sampleScript.cached, false); |
︙ | ︙ |
Changes to src/fossil.page.pikchrshowasm.js.
︙ | ︙ | |||
310 311 312 313 314 315 316 | modes.selectedIndex = (modes.selectedIndex + 1) % modes.length; this.e.previewModeLabel.innerText = this.renderModeLabels[modes[modes.selectedIndex]]; if(this.e.pikOut.dataset.pikchr){ this.render(this.e.pikOut.dataset.pikchr); } }.bind(PS)); F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText}); | < | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | modes.selectedIndex = (modes.selectedIndex + 1) % modes.length; this.e.previewModeLabel.innerText = this.renderModeLabels[modes[modes.selectedIndex]]; if(this.e.pikOut.dataset.pikchr){ this.render(this.e.pikOut.dataset.pikchr); } }.bind(PS)); F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText}); PS.addMsgHandler('working',function f(ev){ switch(ev.data){ case 'start': /* See notes in preStartWork(). */; return; case 'end': //preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig; this.e.btnRender.removeAttribute('disabled'); |
︙ | ︙ |
Changes to src/fossil.page.wikiedit.js.
︙ | ︙ | |||
1232 1233 1234 1235 1236 1237 1238 | encodeURIComponent(wi.name), "&file=", encodeURIComponent(a.filename) ].join(''), "raw/"+a.src ].forEach(function(url){ const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url); | | | | 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 | encodeURIComponent(wi.name), "&file=", encodeURIComponent(a.filename) ].join(''), "raw/"+a.src ].forEach(function(url){ const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url); const urlCopy = D.button(); const li = D.li(ul); D.append(li, urlCopy, imgUrl); F.copyButton(urlCopy, {copyFromElement: imgUrl}); }); }); return this; }; /** Updates the in-tab title/edit status information */ |
︙ | ︙ |
Changes to src/pikchrshow.c.
︙ | ︙ | |||
460 461 462 463 464 465 466 | CX(" selected, only that part is evaluated.\n*/\n"); CX("%s</textarea></div>",zContent/*safe-for-%s*/); } CX("</fieldset><!-- .zone-wrapper.input -->"); CX("<fieldset class='zone-wrapper output'>"); { CX("<legend><div class='button-bar'>"); CX("<button id='btn-render-mode'>Render Mode</button> "); CX("<span style='white-space:nowrap'>" | | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | CX(" selected, only that part is evaluated.\n*/\n"); CX("%s</textarea></div>",zContent/*safe-for-%s*/); } CX("</fieldset><!-- .zone-wrapper.input -->"); CX("<fieldset class='zone-wrapper output'>"); { CX("<legend><div class='button-bar'>"); CX("<button id='btn-render-mode'>Render Mode</button> "); CX("<span style='white-space:nowrap'>" "<button id='preview-copy-button' " "title='Tap to copy to clipboard.'><span></span></button>" "<label for='preview-copy-button' " "title='Tap to copy to clipboard.'></label>" "</span>"); CX("</div></legend>"); CX("<div id='pikchr-output-wrapper'>"); CX("<div id='pikchr-output'></div>"); CX("<textarea class='hidden' id='pikchr-output-text'></textarea>"); |
︙ | ︙ |
Changes to src/style.c.
︙ | ︙ | |||
476 477 478 479 480 481 482 | } /* ** 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. | | | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | } /* ** 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 <button> 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). |
︙ | ︙ | |||
508 509 510 511 512 513 514 | 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\">" | | | | | | > | > | | | | | | | > | > | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 | 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\">" "<button " "class=\"copy-button\" " "id=\"copy-%h\" " "data-copytarget=\"%h\" " "data-copylength=\"%d\">" "<span>" "</span>" "</button>" "<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>" "<button " "class=\"copy-button copy-button-flipped\" " "id=\"copy-%h\" " "data-copytarget=\"%h\" " "data-copylength=\"%d\">" "<span>" "</span>" "</button>" "</span>"; if( bOutputCGI ){ cgi_printf( zBtnFmt/*works-like:"%h%s%h%h%d"*/, zTargetId,zText,zTargetId,zTargetId,cchLength); }else{ zResult = mprintf( |
︙ | ︙ |