8277420: Provide a way to copy the hyperlink to a doc element to the clipboard
Reviewed-by: prappo
This commit is contained in:
parent
176bb23de1
commit
37ecbb461c
@ -120,7 +120,7 @@ public class SearchWriter extends HtmlDocletWriter {
|
||||
.put(HtmlAttr.ALT, copyText))
|
||||
.add(HtmlTree.SPAN(Text.of(copyText))
|
||||
.put(HtmlAttr.DATA_COPIED, copiedText))
|
||||
.addStyle(HtmlStyle.copyUrl)
|
||||
.addStyle(HtmlStyle.copy)
|
||||
.setId(HtmlId.of("page-search-copy")))
|
||||
.add(HtmlTree.P(HtmlTree.INPUT("checkbox", HtmlId.of("search-redirect")))
|
||||
.add(HtmlTree.LABEL("search-redirect",
|
||||
|
@ -459,6 +459,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
.add(new HtmlTree(TagName.IMG)
|
||||
.put(HtmlAttr.SRC, htmlWriter.pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath())
|
||||
.put(HtmlAttr.ALT, copyText))
|
||||
.addStyle(HtmlStyle.copy)
|
||||
.addStyle(HtmlStyle.snippetCopy)
|
||||
.put(HtmlAttr.ONCLICK, "copySnippet(this)"));
|
||||
return snippetContainer.add(pre.add(code));
|
||||
|
@ -655,11 +655,6 @@ public enum HtmlStyle {
|
||||
//
|
||||
// The following constants are used for items in the static and interactive search indexes.
|
||||
|
||||
/**
|
||||
* The class for a {@code button} in the search page to copy the search URL to the clipboard.
|
||||
*/
|
||||
copyUrl,
|
||||
|
||||
/**
|
||||
* The class for a {@code details} element in the search page to show additional information.
|
||||
*/
|
||||
@ -915,6 +910,11 @@ public enum HtmlStyle {
|
||||
*/
|
||||
classUses,
|
||||
|
||||
/**
|
||||
* The class for a {@code button} element to copy some page content to the clipboard.
|
||||
*/
|
||||
copy,
|
||||
|
||||
/**
|
||||
* The class of an {@code a} element for a link with an external target.
|
||||
*/
|
||||
|
@ -31,6 +31,8 @@ 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##"
|
||||
}
|
||||
const categories = {
|
||||
modules: "##REPLACE:doclet.search.modules##",
|
||||
@ -412,6 +414,16 @@ $(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();
|
||||
}
|
||||
);
|
||||
$(window).on("orientationchange", collapse).on("resize", function(e) {
|
||||
if (expanded && windowWidth !== window.innerWidth) collapse();
|
||||
});
|
||||
|
@ -105,27 +105,49 @@ function indexFilesLoaded() {
|
||||
&& memberSearchIndex
|
||||
&& tagSearchIndex;
|
||||
}
|
||||
|
||||
// Copy the contents of the local snippet to the clipboard
|
||||
function copySnippet(button) {
|
||||
copyToClipboard(button.nextElementSibling.innerText);
|
||||
switchCopyLabel(button.firstElementChild, button.parentElement);
|
||||
}
|
||||
// Copy the link to the adjacent header to the clipboard
|
||||
function copyUrl(button) {
|
||||
var id;
|
||||
var header = button.parentElement;
|
||||
if (header.hasAttribute("id")) {
|
||||
id = header.getAttribute("id");
|
||||
} else if (header.parentElement.tagName === 'SECTION' && header.parentElement.hasAttribute("id")) {
|
||||
id = header.parentElement.getAttribute("id");
|
||||
} else if (header.firstElementChild && header.firstElementChild.tagName === "A"
|
||||
&& header.firstElementChild.hasAttribute("id")) {
|
||||
id = header.firstElementChild.getAttribute("id");
|
||||
}
|
||||
var url = document.location.href;
|
||||
if (url.indexOf("#") > -1) {
|
||||
url = url.substring(0, url.indexOf("#"));
|
||||
}
|
||||
copyToClipboard(url + "#" + id);
|
||||
switchCopyLabel(button.lastElementChild, button.parentElement);
|
||||
}
|
||||
function copyToClipboard(content) {
|
||||
var textarea = document.createElement("textarea");
|
||||
textarea.style.height = 0;
|
||||
document.body.appendChild(textarea);
|
||||
textarea.value = button.nextElementSibling.innerText;
|
||||
textarea.value = content;
|
||||
textarea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textarea);
|
||||
var span = button.firstElementChild;
|
||||
}
|
||||
function switchCopyLabel(span, parent) {
|
||||
var copied = span.getAttribute("data-copied");
|
||||
if (span.innerHTML !== copied) {
|
||||
var initialLabel = span.innerHTML;
|
||||
span.innerHTML = copied;
|
||||
var parent = button.parentElement;
|
||||
parent.onmouseleave = parent.ontouchend = function() {
|
||||
span.innerHTML = initialLabel;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for scroll position not being included in browser history (8249133)
|
||||
document.addEventListener("DOMContentLoaded", function(e) {
|
||||
var contentDiv = document.querySelector("div.flex-content");
|
||||
|
@ -62,6 +62,10 @@ h5 {
|
||||
h6 {
|
||||
font-size:13px;
|
||||
}
|
||||
/* Disable font boosting */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
max-height: 2em;
|
||||
}
|
||||
ul {
|
||||
list-style-type:disc;
|
||||
}
|
||||
@ -717,52 +721,6 @@ span#page-search-link {
|
||||
background: #F8981D;
|
||||
color: #253441;
|
||||
}
|
||||
button.copy-url {
|
||||
opacity: 80%;
|
||||
transition: opacity 0.2s;
|
||||
margin-left: 0.4em;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
padding:0.3em;
|
||||
position: relative;
|
||||
top:0.13em
|
||||
}
|
||||
button.copy-url img {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
padding: 0.01em 0;
|
||||
background: none;
|
||||
position:relative;
|
||||
top: 0.15em;
|
||||
}
|
||||
div.page-search-info:hover button.copy-url {
|
||||
opacity: 90%;
|
||||
}
|
||||
div.page-search-info button.copy-url:hover {
|
||||
background-color: #dfe6f1;
|
||||
opacity: 100%;
|
||||
}
|
||||
button.copy-url span {
|
||||
color: #000000;
|
||||
content: attr(aria-label);
|
||||
font-family:'DejaVu Sans', Arial, Helvetica, sans-serif;
|
||||
font-size: 85%;
|
||||
line-height: 1.2em;
|
||||
padding: 0.2em;
|
||||
position: relative;
|
||||
top: -0.18em;
|
||||
transition: opacity 0.1s;
|
||||
opacity: 0;
|
||||
}
|
||||
div.page-search-info:hover button.copy-url span {
|
||||
opacity: 90%;
|
||||
}
|
||||
div.page-search-info button.copy-url:active {
|
||||
background-color: #cfdbee;
|
||||
opacity: 100%;
|
||||
}
|
||||
.module-graph span {
|
||||
display:none;
|
||||
position:absolute;
|
||||
@ -832,7 +790,111 @@ main a[href*="://"]:focus::after {
|
||||
132-240 240 120 120 240-240 132 132V0z" fill="%23bb7a2a"/>\
|
||||
</svg>');
|
||||
}
|
||||
|
||||
/*
|
||||
* Styles for copy-to-clipboard buttons
|
||||
*/
|
||||
button.copy {
|
||||
opacity: 80%;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
background:none;
|
||||
transition: opacity 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
button.copy:hover,
|
||||
button.copy:active {
|
||||
opacity: 100%;
|
||||
}
|
||||
button.copy img {
|
||||
position: relative;
|
||||
background: none;
|
||||
}
|
||||
button.copy span {
|
||||
color: #303030;
|
||||
position: relative;
|
||||
top: -0.1em;
|
||||
transition: all 0.1s;
|
||||
font-size: 85%;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
/* header/section copy button */
|
||||
button.copy-header {
|
||||
margin: 0 0.2em;
|
||||
padding: 0 4px;
|
||||
height: 1.35em;
|
||||
}
|
||||
button.copy-header img {
|
||||
height: 1em;
|
||||
top: 0.1em;
|
||||
}
|
||||
button.copy-header:active {
|
||||
background-color: rgba(128, 128, 160, 0.2);
|
||||
}
|
||||
/* search page copy button */
|
||||
button#page-search-copy {
|
||||
margin-left: 0.4em;
|
||||
padding:0.3em;
|
||||
top:0.13em;
|
||||
}
|
||||
button#page-search-copy img {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
padding: 0.01em 0;
|
||||
top: 0.15em;
|
||||
}
|
||||
button#page-search-copy span {
|
||||
color: #000000;
|
||||
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%;
|
||||
}
|
||||
div.page-search-info button#page-search-copy:hover {
|
||||
background-color: #dfe6f1;
|
||||
}
|
||||
div.page-search-info button#page-search-copy:active {
|
||||
background-color: #cfdbee;
|
||||
}
|
||||
/* snippet copy button */
|
||||
button.snippet-copy {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
height: 1.7em;
|
||||
opacity: 50%;
|
||||
padding: 2px;
|
||||
}
|
||||
button.snippet-copy img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
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%;
|
||||
}
|
||||
button.snippet-copy:active {
|
||||
background: #d3d3d3;
|
||||
}
|
||||
/*
|
||||
* Styles for user-provided tables.
|
||||
*
|
||||
@ -1067,49 +1129,6 @@ pre.snippet {
|
||||
div.snippet-container {
|
||||
position: relative;
|
||||
}
|
||||
button.snippet-copy {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
height: 1.7em;
|
||||
opacity: 50%;
|
||||
transition: opacity 0.2s;
|
||||
padding: 2px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
}
|
||||
button.snippet-copy img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 0.05em 0;
|
||||
background: none;
|
||||
}
|
||||
div.snippet-container:hover button.snippet-copy {
|
||||
opacity: 80%;
|
||||
}
|
||||
div.snippet-container button.snippet-copy:hover {
|
||||
opacity: 100%;
|
||||
}
|
||||
button.snippet-copy span {
|
||||
color: #3d3d3d;
|
||||
content: attr(aria-label);
|
||||
font-family:'DejaVu Sans', Arial, Helvetica, sans-serif;
|
||||
font-size: 85%;
|
||||
line-height: 1.2em;
|
||||
padding: 0.2em;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
top: -0.5em;
|
||||
display: none;
|
||||
}
|
||||
div.snippet-container:hover button.snippet-copy span {
|
||||
display: inline;
|
||||
}
|
||||
button.snippet-copy:active {
|
||||
background: #d3d3d3;
|
||||
opacity: 100%;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
pre.snippet {
|
||||
padding-top: 26px;
|
||||
|
@ -135,7 +135,7 @@ public class CheckStylesheetClasses {
|
||||
removeAll(styleSheetNames, "borderless", "plain", "striped");
|
||||
|
||||
// used in search.js and search-page.js; may be worth documenting in HtmlStyle
|
||||
removeAll(styleSheetNames, "result-highlight", "result-item",
|
||||
removeAll(styleSheetNames, "result-highlight", "result-item", "copy-header",
|
||||
"search-tag-desc-result", "search-tag-holder-result", "page-search-header",
|
||||
"ui-autocomplete", "ui-autocomplete-category", "expanded",
|
||||
"search-result-link", "two-column-search-results", "ui-static-link");
|
||||
|
@ -119,7 +119,7 @@ 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="snippet-copy" onclick="copySnippet(this)">\
|
||||
<div class="snippet-container"><button class="copy snippet-copy" onclick="copySnippet(this)">\
|
||||
<span data-copied="Copied!">Copy</span><img src="%s" alt="Copy"></button>
|
||||
<pre class="snippet"%s><code%s>%s</code></pre>
|
||||
</div>""".formatted(svgString, idString, langString, content);
|
||||
|
@ -87,7 +87,8 @@ public class TestSnippetUnnamedPackage extends SnippetTester {
|
||||
"""
|
||||
Before.
|
||||
\s
|
||||
<div class="snippet-container"><button class="snippet-copy" onclick="copySnippet(this)"><span data-copied="Copied!">Copy</span><img src="copy.svg" alt="Copy"></button>
|
||||
<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>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user