8299718: JavaDoc: Buttons to copy specific documentation URL are not accessible

Reviewed-by: jjg
This commit is contained in:
Hannes Wallnöfer 2023-04-03 10:37:19 +00:00
parent 4de24cdbe6
commit 094e03de6a
10 changed files with 75 additions and 79 deletions

View File

@ -89,8 +89,9 @@ public class SearchWriter extends HtmlDocletWriter {
*/
protected void addSearchFileContents(Content contentTree) {
String copyText = resources.getText("doclet.Copy_url_to_clipboard");
String copiedText = resources.getText("doclet.Copied_url_to_clipboard");
String copyText = resources.getText("doclet.Copy_to_clipboard");
String copiedText = resources.getText("doclet.Copied_to_clipboard");
String copyUrlText = resources.getText("doclet.Copy_url_to_clipboard");
Content helpSection = Text.EMPTY;
// Suppress link to help page if no help page is generated or a custom help page is used.
HtmlOptions options = configuration.getOptions();
@ -117,10 +118,11 @@ public class SearchWriter extends HtmlDocletWriter {
.add(new HtmlTree(TagName.BUTTON)
.add(new HtmlTree(TagName.IMG)
.put(HtmlAttr.SRC, pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath())
.put(HtmlAttr.ALT, copyText))
.put(HtmlAttr.ALT, copyUrlText))
.add(HtmlTree.SPAN(Text.of(copyText))
.put(HtmlAttr.DATA_COPIED, copiedText))
.addStyle(HtmlStyle.copy)
.put(HtmlAttr.ARIA_LABEL, copyUrlText)
.setId(HtmlId.of("page-search-copy")))
.add(HtmlTree.P(HtmlTree.INPUT("checkbox", HtmlId.of("search-redirect")))
.add(HtmlTree.LABEL("search-redirect",

View File

@ -711,17 +711,19 @@ public class TagletWriterImpl extends TagletWriter {
code.add(c);
}
});
String copyText = resources.getText("doclet.Copy_snippet_to_clipboard");
String copiedText = resources.getText("doclet.Copied_snippet_to_clipboard");
String copyText = resources.getText("doclet.Copy_to_clipboard");
String copiedText = resources.getText("doclet.Copied_to_clipboard");
String copySnippetText = resources.getText("doclet.Copy_snippet_to_clipboard");
var snippetContainer = HtmlTree.DIV(HtmlStyle.snippetContainer,
new HtmlTree(TagName.BUTTON)
.add(HtmlTree.SPAN(Text.of(copyText))
.put(HtmlAttr.DATA_COPIED, copiedText))
.add(new HtmlTree(TagName.IMG)
.put(HtmlAttr.SRC, htmlWriter.pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath())
.put(HtmlAttr.ALT, copyText))
.put(HtmlAttr.ALT, copySnippetText))
.addStyle(HtmlStyle.copy)
.addStyle(HtmlStyle.snippetCopy)
.put(HtmlAttr.ARIA_LABEL, copySnippetText)
.put(HtmlAttr.ONCLICK, "copySnippet(this)"));
return snippetContainer.add(pre.add(code));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -39,23 +39,8 @@ $(function() {
copy[0].onmouseenter();
}
function copyLink(e) {
var textarea = document.createElement("textarea");
textarea.style.height = 0;
document.body.appendChild(textarea);
textarea.value = this.previousSibling.innerText;
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
var span = this.lastElementChild;
var copied = span.getAttribute("data-copied");
if (span.innerHTML !== copied) {
var initialLabel = span.innerHTML;
span.innerHTML = copied;
var parent = this.parentElement.parentElement;
parent.onmouseleave = parent.ontouchend = copy[0].onmouseenter = function() {
span.innerHTML = initialLabel;
};
}
copyToClipboard(this.previousSibling.innerText);
switchCopyLabel(this, this.lastElementChild);
}
copy.click(copyLink);
copy[0].onmouseenter = function() {};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -31,8 +31,9 @@ const messages = {
loading: "##REPLACE:doclet.search.loading##",
searching: "##REPLACE:doclet.search.searching##",
redirecting: "##REPLACE:doclet.search.redirecting##",
copyUrl: "##REPLACE:doclet.Copy_url_to_clipboard##",
urlCopied: "##REPLACE:doclet.Copied_url_to_clipboard##"
copyToClipboard: "##REPLACE:doclet.Copy_to_clipboard##",
copyUrlToClipboard: "##REPLACE:doclet.Copy_url_to_clipboard##",
copiedToClipboard: "##REPLACE:doclet.Copied_to_clipboard##"
}
const categories = {
modules: "##REPLACE:doclet.search.modules##",
@ -407,16 +408,17 @@ $(function() {
$("ul.sub-nav-list-small li a").click(collapse);
$("input#search-input").focus(collapse);
$("main").click(collapse);
$("section[id] > :header, :header[id], :header:has(a[id])").hover(
function () {
$(this).append($("<button class='copy copy-header' onclick='copyUrl(this)'> " +
"<img src='" + pathtoroot + "copy.svg' alt='" + messages.copyUrl + "'> " +
"<span data-copied='" + messages.urlCopied + "'></span></button>"));
},
function () {
$(this).find("button:last").remove();
$("section[id] > :header, :header[id], :header:has(a[id])").each(function(idx, el) {
// Create copy-to-clipboard buttons for headers with an associated id attribute
var hdr = $(el);
var id = hdr.attr("id") || hdr.parent("section").attr("id") || hdr.children("a").attr("id");
if (id) {
hdr.append($("<button class='copy copy-header' onclick='copyUrl(this)' aria-label='"
+ messages.copyUrlToClipboard +"'> " + "<img src='" + pathtoroot + "copy.svg' alt='" +
messages.copyUrlToClipboard + "'> " + "<span data-copied='" + messages.copiedToClipboard +
"'>" + messages.copyToClipboard + "</span></button>"));
}
);
});
$(window).on("orientationchange", collapse).on("resize", function(e) {
if (expanded && windowWidth !== window.innerWidth) collapse();
});

View File

@ -55,10 +55,10 @@ doclet.File_not_found=File not found: {0}
doclet.snippet_file_not_found=file not found on source path or snippet path: {0}
doclet.Copy_Overwrite_warning=File {0} not copied to {1} due to existing file with same name...
doclet.Copy_Ignored_warning=File {0} not copied: invalid name
doclet.Copy_to_clipboard=Copy
doclet.Copied_to_clipboard=Copied!
doclet.Copy_url_to_clipboard=Copy URL
doclet.Copied_url_to_clipboard=Copied!
doclet.Copy_snippet_to_clipboard=Copy
doclet.Copied_snippet_to_clipboard=Copied!
doclet.Copy_snippet_to_clipboard=Copy snippet
doclet.Copying_File_0_To_Dir_1=Copying file {0} to directory {1}...
doclet.Copying_File_0_To_File_1=Copying file {0} to file {1}...
doclet.No_Public_Classes_To_Document=No public or protected classes found to document.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -202,7 +202,7 @@ function indexFilesLoaded() {
// Copy the contents of the local snippet to the clipboard
function copySnippet(button) {
copyToClipboard(button.nextElementSibling.innerText);
switchCopyLabel(button.firstElementChild, button.parentElement);
switchCopyLabel(button, button.firstElementChild);
}
// Copy the link to the adjacent header to the clipboard
function copyUrl(button) {
@ -221,7 +221,7 @@ function copyUrl(button) {
url = url.substring(0, url.indexOf("#"));
}
copyToClipboard(url + "#" + id);
switchCopyLabel(button.lastElementChild, button.parentElement);
switchCopyLabel(button, button.lastElementChild);
}
function copyToClipboard(content) {
var textarea = document.createElement("textarea");
@ -232,15 +232,19 @@ function copyToClipboard(content) {
document.execCommand("copy");
document.body.removeChild(textarea);
}
function switchCopyLabel(span, parent) {
function switchCopyLabel(button, span) {
var copied = span.getAttribute("data-copied");
if (span.innerHTML !== copied) {
var initialLabel = span.innerHTML;
span.innerHTML = copied;
parent.onmouseleave = parent.ontouchend = function() {
span.innerHTML = initialLabel;
};
}
button.classList.add("visible");
var initialLabel = span.innerHTML;
span.innerHTML = copied;
setTimeout(function() {
button.classList.remove("visible");
setTimeout(function() {
if (initialLabel !== copied) {
span.innerHTML = initialLabel;
}
}, 100);
}, 1900);
}
// Workaround for scroll position not being included in browser history (8249133)
document.addEventListener("DOMContentLoaded", function(e) {

View File

@ -915,16 +915,21 @@ main a[href*="://"]:focus::after {
* Styles for copy-to-clipboard buttons
*/
button.copy {
opacity: 80%;
opacity: 70%;
border: none;
border-radius: 3px;
position: relative;
background:none;
transition: opacity 0.2s;
transition: opacity 0.3s;
cursor: pointer;
}
:hover > button.copy {
opacity: 80%;
}
button.copy:hover,
button.copy:active {
button.copy:active,
button.copy:focus-visible,
button.copy.visible {
opacity: 100%;
}
button.copy img {
@ -942,12 +947,19 @@ button.copy span {
transition: all 0.1s;
font-size: 0.76rem;
line-height: 1.2em;
opacity: 0;
}
button.copy:hover span,
button.copy:focus-visible span,
button.copy.visible span {
opacity: 100%;
}
/* header/section copy button */
button.copy-header {
margin: 0 0.2em;
padding: 0 4px;
height: 1.16em;
opacity: 0;
}
button.copy-header img {
height: 0.88em;
@ -967,15 +979,12 @@ button#page-search-copy img {
}
button#page-search-copy span {
color: var(--body-text-color);
content: attr(aria-label);
line-height: 1.2em;
padding: 0.2em;
top: -0.18em;
opacity: 0;
}
div.page-search-info:hover button#page-search-copy,
div.page-search-info:hover button#page-search-copy span {
opacity: 90%;
opacity: 100%;
}
/* snippet copy button */
button.snippet-copy {
@ -983,7 +992,6 @@ button.snippet-copy {
top: 6px;
right: 6px;
height: 1.7em;
opacity: 50%;
padding: 2px;
}
button.snippet-copy img {
@ -992,20 +1000,12 @@ button.snippet-copy img {
padding: 0.05em 0;
}
button.snippet-copy span {
content: attr(aria-label);
line-height: 1.2em;
padding: 0.2em;
position: relative;
top: -0.5em;
display: none;
}
div.snippet-container:hover button.snippet-copy span {
display: inline;
}
div.snippet-container:hover button.snippet-copy {
opacity: 80%;
}
div.snippet-container button.snippet-copy:hover {
opacity: 100%;
}
/*

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -129,7 +129,7 @@ public class CheckStylesheetClasses {
// or if they are unused and therefore candidates to be deleted.
// false positives: file extensions and URL components
removeAll(styleSheetNames, "css", "png", "w3");
removeAll(styleSheetNames, "css", "png", "w3", "org");
// for doc-comment authors; maybe worthy of inclusion in HtmlStyle, just to be documented
removeAll(styleSheetNames, "borderless", "plain", "striped");
@ -138,7 +138,8 @@ public class CheckStylesheetClasses {
removeAll(styleSheetNames, "result-highlight", "result-item", "copy-header",
"search-tag-desc-result", "search-tag-holder-result", "page-search-header",
"ui-autocomplete", "ui-autocomplete-category", "ui-state-active", "expanded",
"search-result-link", "two-column-search-results", "ui-static-link");
"search-result-link", "two-column-search-results", "ui-static-link",
"sort-asc", "sort-desc", "visible");
// very JDK specific
styleSheetNames.remove("module-graph");
@ -203,7 +204,7 @@ public class CheckStylesheetClasses {
throw new AssertionError("Cannot find or access resource " + resource);
}
String s = new String(in.readAllBytes());
Pattern p = Pattern.compile("(?i)(\\s|([a-z][a-z0-9-]*))\\.(?<name>[a-z][a-z0-9-]+)\\b");
Pattern p = Pattern.compile("(?i)\\.(?<name>[a-z][a-z0-9-]+)\\b");
Matcher m = p.matcher(s);
while (m.find()) {
names.add(m.group("name"));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -119,8 +119,9 @@ public class SnippetTester extends JavadocTester {
var idString = id.isEmpty() ? "" : " id=\"%s\"".formatted(id.get());
var langString = lang.isEmpty() ? "" : " class=\"language-%s\"".formatted(lang.get());
return """
<div class="snippet-container"><button class="copy snippet-copy" onclick="copySnippet(this)">\
<span data-copied="Copied!">Copy</span><img src="%s" alt="Copy"></button>
<div class="snippet-container"><button class="copy snippet-copy" aria-label="Copy snippet" \
onclick="copySnippet(this)"><span data-copied="Copied!">Copy</span><img src="%s" alt="Copy \
snippet"></button>
<pre class="snippet"%s><code%s>%s</code></pre>
</div>""".formatted(svgString, idString, langString, content);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,6 +37,7 @@ import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class TestSnippetUnnamedPackage extends SnippetTester {
@ -87,11 +88,9 @@ public class TestSnippetUnnamedPackage extends SnippetTester {
"""
Before.
\s
<div class="snippet-container"><button class="copy snippet-copy" onclick="copySnippet\
(this)"><span data-copied="Copied!">Copy</span><img src="copy.svg" alt="Copy"></button>
<pre class="snippet"><code class="language-java">public class S { }</code></pre>
</div>
%s
After.""");
After.""".formatted(getSnippetHtmlRepresentation("C.html",
"public class S { }", Optional.of("java"))));
}
}