Skip to content

Commit

Permalink
Merge pull request #303 from ndw/copy-verbatim
Browse files Browse the repository at this point in the history
Support a dynamic copy button for verbatim environments
  • Loading branch information
ndw authored Apr 8, 2023
2 parents 8dbce79 + a2eaece commit 165d197
Show file tree
Hide file tree
Showing 241 changed files with 487 additions and 288 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ formattingTests.dependsOn testDocBookPy

task testSummary(
type: SaxonXsltTask,
dependsOn: ['xspecTests']
dependsOn: ['xspecTests', "makeXslt"]
) {
stylesheet file("${projectDir}/tools/test-results.xsl")
output "${buildDir}/test-results.txt"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class TestCase {

//println("Register ${testname}.expected")
def expTask = project.tasks.register("${testname}.expected", SaxonXsltTask) {
outputs.upToDateWhen { false }
input _input
stylesheet "${config.xspecDriver}"
output expected
Expand Down
4 changes: 2 additions & 2 deletions properties.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
ext {
xslTNGtitle = 'DocBook xslTNG'
xslTNGbaseName = 'docbook-xslTNG'
xslTNGversion = '2.1.0'
guideVersion = '2.1.0'
xslTNGversion = '2.1.1'
guideVersion = '2.1.1'
guidePrerelease = false

docbookVersion = '5.2CR5'
Expand Down
17 changes: 17 additions & 0 deletions src/guide/xml/ref-params.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5363,4 +5363,21 @@ introduced in the future.)</para>
</refsection>
</refentry>

<refentry>
<refmeta>
<fieldsynopsis>
<varname>copy-verbatim-js</varname>
<initializer>'js/copy-verbatim.js'</initializer>
</fieldsynopsis>
</refmeta>
<refnamediv>
<refpurpose>Script to support dynamic copy button on verbatim listings</refpurpose>
</refnamediv>
<refsection>
<title>Description</title>
<para>This script creates a dynamic “copy-to-clipboard” button on verbatim listings.
The copied text will not include any callouts or other decorations in the listing.</para>
</refsection>
</refentry>

</reference>
17 changes: 17 additions & 0 deletions src/main/scss/media-screen.scss
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ main {
grid-column-start: 2;
}

.callout-bug {
user-select: none;
}

/* ============================================================ */

nav table td.previous {
Expand Down Expand Up @@ -222,6 +226,19 @@ nav.top div {
/* ============================================================ */
/* Javascript annotations */

.copyVerbIcon {
border: 1px solid var(--hovered-color);
border-radius: 0.2rem;
}

.copyVerbIcon:hover {
cursor: pointer;
border: 1px solid var(--primary-color);
}

/* ============================================================ */
/* Javascript annotations */

.popup-annotation-wrapper {
display: none;
position: fixed;
Expand Down
133 changes: 133 additions & 0 deletions src/main/web/js/copy-verbatim.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/* @@TITLE@@ version @@VERSION@@
*
* This is copy-verbatim.js providing support for a dynamic
* copy-to-clipboard link on verbatim listings.
*
* See https://xsltng.docbook.org/
*
*/
(function() {
const COPY = "<path d='M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z'></path><path d='M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z'></path>";
const OK = "<path d='M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z'></path>";

let text = "";

const buildText = function(elem) {
const nodes = elem.childNodes;
for (let pos = 0; pos < nodes.length; pos++) {
const child = nodes[pos];
switch (child.nodeType) {
case Node.ELEMENT_NODE:
if (child.classList.contains("callout-bug")
|| child.classList.contains("co")
|| child.classList.contains("lineannotation")
|| child.classList.contains("ln")
|| child.classList.contains("nsep")) {
// Ignore these
} else {
buildText(child);
}
break;
case Node.TEXT_NODE:
text += child.textContent;
break;
default:
break;
}
}
};

const copyText = function(event) {
// The target could be anywhere inside the SVG. Go up until we find the div.
// Keep track of the "svg" element along the way.
let div = event.target;
let svg = null;
while (div && div.tagName !== "DIV") {
if (div.tagName == "svg") { // Yes, lower case!
svg = div;
}
div = div.parentNode;
}

const table = div.querySelector("table");
let pre = null;
if (table) {
div.querySelectorAll("pre").forEach(elem => {
pre = elem;
});
} else {
pre = div.querySelector("pre");
}

if (!pre) {
console.log("Error: no <pre> to copy!?");
return;
}

text = "";
buildText(pre);

// Trim away trailing blanks
text = text.replaceAll(/[ \t]+\n/g, "\n");
navigator.clipboard.writeText(text);

// Remove the event listener while we display the checkbox
svg.removeEventListener("click", copyText);

// Display the checkbox for a moment
svg.innerHTML = OK;
svg.style.fill = "#006400";
setTimeout(() => {
svg.innerHTML = COPY;
svg.style.fill = "#000000";
svg.addEventListener("click", copyText);
}, 1000);
};

const showCopyButton = function(event) {
const div = event.target;
const pre = div.querySelector("pre");
if (!pre) {
return;
}

let svg = div.querySelector(".copyVerbIcon");
if (svg) {
svg.style.display = "block";
} else {
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("aria-hidden", "true");
svg.setAttribute("width", "16");
svg.setAttribute("height", "16");
svg.setAttribute("viewBox", "0 0 16 16");
svg.setAttribute("version", "1.1");
svg.style.position = "absolute";
svg.style.top = "2px";
svg.style.right = "2px";
svg.style.padding = "4px";
svg.style.fill = "black";
svg.classList.add("copyVerbIcon");
svg.addEventListener("click", copyText);
svg.innerHTML = COPY;
div.insertBefore(svg, div.firstChild);
}
};

const hideCopyButton = function(event) {
const div = event.target;
const span = div.querySelector(".copyVerbIcon");
if (span) {
span.style.display = "none";
}
};

// Navigator.clipboard requires a localhost or https: connection
// If we can't change the clipboard, then just forget it.
if (navigator.clipboard) {
document.querySelectorAll(".pre-wrap").forEach(div => {
div.style.position="relative";
div.addEventListener("mouseenter", showCopyButton);
div.addEventListener("mouseleave", hideCopyButton);
});
}
})();
9 changes: 8 additions & 1 deletion src/main/xslt/main.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,16 @@
<script src="{$resource-base-uri}{$control-js}" defer="defer"/>
</xsl:if>
</db-script>
<db-copy-verbatim-script>
<xsl:if test="normalize-space($copy-verbatim-js) != ''">
<script src="{$resource-base-uri}{$copy-verbatim-js}" defer="defer"/>
</xsl:if>
</db-copy-verbatim-script>
<db-fallback-script>
<!-- NOT deferred! -->
<script src="{$resource-base-uri}{$fallback-js}"/>
<xsl:if test="normalize-space($fallback-js) != ''">
<script src="{$resource-base-uri}{$fallback-js}"/>
</xsl:if>
</db-fallback-script>
</xsl:if>
</html>
Expand Down
4 changes: 2 additions & 2 deletions src/main/xslt/modules/blocks.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@
</xsl:template>

<xsl:template match="math:*">
<xsl:copy>
<xsl:element name="{local-name(.)}" namespace="http://www.w3.org/1998/Math/MathML">
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:element>
</xsl:template>

<xsl:template match="db:para">
Expand Down
8 changes: 8 additions & 0 deletions src/main/xslt/modules/chunk-cleanup.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<xsl:template match="h:db-footnote|h:db-annotation|h:head
|h:db-annotation-script|h:db-xlink-script
|h:db-toc-script|h:db-pagetoc-script|h:db-fallback-script
|h:db-copy-verbatim-script
|h:db-mathml-script|h:db-script">
<!-- discard -->
</xsl:template>
Expand Down Expand Up @@ -144,6 +145,13 @@
</xsl:apply-templates>
</xsl:if>

<xsl:if test=".//h:div[contains-token(@class, 'pre-wrap')]">
<xsl:apply-templates select="/h:html/h:db-copy-verbatim-script/*">
<xsl:with-param name="rootbaseuri" select="$rbu"/>
<xsl:with-param name="chunkbaseuri" select="$cbu"/>
</xsl:apply-templates>
</xsl:if>

<!-- Unconditionally add h:db-script children. -->
<xsl:apply-templates select="/h:html/h:db-script/*">
<xsl:with-param name="rootbaseuri" select="$rbu"/>
Expand Down
88 changes: 52 additions & 36 deletions src/main/xslt/modules/programming.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -735,12 +735,14 @@

<div>
<xsl:apply-templates select="." mode="m:attributes"/>
<pre>
<xsl:apply-templates select="$package/preceding-sibling::*" mode="m:synopsis"/>
<xsl:text>package </xsl:text>
<xsl:apply-templates select="db:package"/>
<xsl:text>;&#10;</xsl:text>
</pre>
<div class="pre-wrap">
<pre>
<xsl:apply-templates select="$package/preceding-sibling::*" mode="m:synopsis"/>
<xsl:text>package </xsl:text>
<xsl:apply-templates select="db:package"/>
<xsl:text>;&#10;</xsl:text>
</pre>
</div>
<xsl:apply-templates select="$package/following-sibling::*">
<xsl:with-param name="indent" select="$indent || $classsynopsis-indent"/>
</xsl:apply-templates>
Expand All @@ -752,27 +754,34 @@
<div>
<xsl:apply-templates select="." mode="m:attributes"/>
<xsl:apply-templates select="db:classsynopsisinfo"/>
<pre>
<xsl:apply-templates select="db:ooclass/db:modifier" mode="m:synopsis"/>
<xsl:text>class </xsl:text>
<xsl:apply-templates select="db:ooclass/db:classname" mode="m:synopsis"/>
<xsl:text> {</xsl:text>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates select="* except (db:ooclass|db:classsynopsisinfo)"
mode="m:synopsis">
<xsl:with-param name="indent" select="$indent || $classsynopsis-indent"/>
</xsl:apply-templates>
<xsl:text>&#10;</xsl:text>
<xsl:text>}</xsl:text>
</pre>
<div class="pre-wrap">
<pre>
<xsl:apply-templates select="db:ooclass/db:modifier" mode="m:synopsis"/>
<xsl:text>class </xsl:text>
<xsl:apply-templates select="db:ooclass/db:classname" mode="m:synopsis"/>
<xsl:text> {</xsl:text>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates select="* except (db:ooclass|db:classsynopsisinfo)"
mode="m:synopsis">
<xsl:with-param name="indent" select="$indent || $classsynopsis-indent"/>
</xsl:apply-templates>
<xsl:text>&#10;</xsl:text>
<xsl:text>}</xsl:text>
</pre>
</div>
</div>
</xsl:template>

<xsl:template match="db:fieldsynopsis">
<xsl:param name="indent" select="''"/>
<pre class="synopsis">
<xsl:apply-templates select="." mode="m:synopsis"/>
</pre>
<div>
<xsl:apply-templates select="." mode="m:attributes"/>
<div class="pre-wrap">
<pre class="synopsis">
<xsl:apply-templates select="." mode="m:synopsis"/>
</pre>
</div>
</div>
</xsl:template>

<xsl:template match="db:fieldsynopsis" mode="m:synopsis">
Expand Down Expand Up @@ -818,9 +827,14 @@
|db:constructorsynopsis
|db:destructorsynopsis">
<xsl:param name="indent" select="''"/>
<pre class="synopsis">
<xsl:apply-templates select="." mode="m:synopsis"/>
</pre>
<div>
<xsl:apply-templates select="." mode="m:attributes"/>
<div class="pre-wrap">
<pre class="synopsis">
<xsl:apply-templates select="." mode="m:synopsis"/>
</pre>
</div>
</div>
</xsl:template>

<xsl:template match="db:methodsynopsis
Expand Down Expand Up @@ -904,17 +918,19 @@
<div>
<xsl:apply-templates select="." mode="m:attributes"/>
<xsl:apply-templates select="db:synopsisinfo"/>
<pre>
<xsl:apply-templates select="db:modifier" mode="m:synopsis"/>
<xsl:text>enum </xsl:text>
<xsl:apply-templates select="db:enumname" mode="m:synopsis"/>
<xsl:text> {</xsl:text>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates select="db:enumitem" mode="m:synopsis">
<xsl:with-param name="indent" select="$indent || $classsynopsis-indent"/>
</xsl:apply-templates>
<xsl:text>}</xsl:text>
</pre>
<div class="pre-wrap">
<pre>
<xsl:apply-templates select="db:modifier" mode="m:synopsis"/>
<xsl:text>enum </xsl:text>
<xsl:apply-templates select="db:enumname" mode="m:synopsis"/>
<xsl:text> {</xsl:text>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates select="db:enumitem" mode="m:synopsis">
<xsl:with-param name="indent" select="$indent || $classsynopsis-indent"/>
</xsl:apply-templates>
<xsl:text>}</xsl:text>
</pre>
</div>
</div>
</xsl:template>

Expand Down
2 changes: 1 addition & 1 deletion src/main/xslt/modules/tablecals.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.w3.org/1999/xhtml"
default-mode="m:docbook"
exclude-result-prefixes="array db dbe f fcals fp m map mp t tp v xs"
exclude-result-prefixes="#all"
version="3.0">

<xsl:template match="db:table[db:tgroup]">
Expand Down
Loading

0 comments on commit 165d197

Please sign in to comment.