Skip to content

Commit 37ecbb4

Browse files
committed
8277420: Provide a way to copy the hyperlink to a doc element to the clipboard
Reviewed-by: prappo
1 parent 176bb23 commit 37ecbb4

File tree

9 files changed

+159
-104
lines changed

9 files changed

+159
-104
lines changed

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ protected void addSearchFileContents(Content contentTree) {
120120
.put(HtmlAttr.ALT, copyText))
121121
.add(HtmlTree.SPAN(Text.of(copyText))
122122
.put(HtmlAttr.DATA_COPIED, copiedText))
123-
.addStyle(HtmlStyle.copyUrl)
123+
.addStyle(HtmlStyle.copy)
124124
.setId(HtmlId.of("page-search-copy")))
125125
.add(HtmlTree.P(HtmlTree.INPUT("checkbox", HtmlId.of("search-redirect")))
126126
.add(HtmlTree.LABEL("search-redirect",

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java

+1
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ protected Content snippetTagOutput(Element element, SnippetTree tag, StyledText
459459
.add(new HtmlTree(TagName.IMG)
460460
.put(HtmlAttr.SRC, htmlWriter.pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath())
461461
.put(HtmlAttr.ALT, copyText))
462+
.addStyle(HtmlStyle.copy)
462463
.addStyle(HtmlStyle.snippetCopy)
463464
.put(HtmlAttr.ONCLICK, "copySnippet(this)"));
464465
return snippetContainer.add(pre.add(code));

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -655,11 +655,6 @@ public enum HtmlStyle {
655655
//
656656
// The following constants are used for items in the static and interactive search indexes.
657657

658-
/**
659-
* The class for a {@code button} in the search page to copy the search URL to the clipboard.
660-
*/
661-
copyUrl,
662-
663658
/**
664659
* The class for a {@code details} element in the search page to show additional information.
665660
*/
@@ -915,6 +910,11 @@ public enum HtmlStyle {
915910
*/
916911
classUses,
917912

913+
/**
914+
* The class for a {@code button} element to copy some page content to the clipboard.
915+
*/
916+
copy,
917+
918918
/**
919919
* The class of an {@code a} element for a link with an external target.
920920
*/

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js.template

+12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const messages = {
3131
loading: "##REPLACE:doclet.search.loading##",
3232
searching: "##REPLACE:doclet.search.searching##",
3333
redirecting: "##REPLACE:doclet.search.redirecting##",
34+
copyUrl: "##REPLACE:doclet.Copy_url_to_clipboard##",
35+
urlCopied: "##REPLACE:doclet.Copied_url_to_clipboard##"
3436
}
3537
const categories = {
3638
modules: "##REPLACE:doclet.search.modules##",
@@ -412,6 +414,16 @@ $(function() {
412414
$("ul.sub-nav-list-small li a").click(collapse);
413415
$("input#search-input").focus(collapse);
414416
$("main").click(collapse);
417+
$("section[id] > :header, :header[id], :header:has(a[id])").hover(
418+
function () {
419+
$(this).append($("<button class='copy copy-header' onclick='copyUrl(this)'> " +
420+
"<img src='" + pathtoroot + "copy.svg' alt='" + messages.copyUrl + "'> " +
421+
"<span data-copied='" + messages.urlCopied + "'></span></button>"));
422+
},
423+
function () {
424+
$(this).find("button:last").remove();
425+
}
426+
);
415427
$(window).on("orientationchange", collapse).on("resize", function(e) {
416428
if (expanded && windowWidth !== window.innerWidth) collapse();
417429
});

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/script.js

+27-5
Original file line numberDiff line numberDiff line change
@@ -105,27 +105,49 @@ function indexFilesLoaded() {
105105
&& memberSearchIndex
106106
&& tagSearchIndex;
107107
}
108-
108+
// Copy the contents of the local snippet to the clipboard
109109
function copySnippet(button) {
110+
copyToClipboard(button.nextElementSibling.innerText);
111+
switchCopyLabel(button.firstElementChild, button.parentElement);
112+
}
113+
// Copy the link to the adjacent header to the clipboard
114+
function copyUrl(button) {
115+
var id;
116+
var header = button.parentElement;
117+
if (header.hasAttribute("id")) {
118+
id = header.getAttribute("id");
119+
} else if (header.parentElement.tagName === 'SECTION' && header.parentElement.hasAttribute("id")) {
120+
id = header.parentElement.getAttribute("id");
121+
} else if (header.firstElementChild && header.firstElementChild.tagName === "A"
122+
&& header.firstElementChild.hasAttribute("id")) {
123+
id = header.firstElementChild.getAttribute("id");
124+
}
125+
var url = document.location.href;
126+
if (url.indexOf("#") > -1) {
127+
url = url.substring(0, url.indexOf("#"));
128+
}
129+
copyToClipboard(url + "#" + id);
130+
switchCopyLabel(button.lastElementChild, button.parentElement);
131+
}
132+
function copyToClipboard(content) {
110133
var textarea = document.createElement("textarea");
111134
textarea.style.height = 0;
112135
document.body.appendChild(textarea);
113-
textarea.value = button.nextElementSibling.innerText;
136+
textarea.value = content;
114137
textarea.select();
115138
document.execCommand("copy");
116139
document.body.removeChild(textarea);
117-
var span = button.firstElementChild;
140+
}
141+
function switchCopyLabel(span, parent) {
118142
var copied = span.getAttribute("data-copied");
119143
if (span.innerHTML !== copied) {
120144
var initialLabel = span.innerHTML;
121145
span.innerHTML = copied;
122-
var parent = button.parentElement;
123146
parent.onmouseleave = parent.ontouchend = function() {
124147
span.innerHTML = initialLabel;
125148
};
126149
}
127150
}
128-
129151
// Workaround for scroll position not being included in browser history (8249133)
130152
document.addEventListener("DOMContentLoaded", function(e) {
131153
var contentDiv = document.querySelector("div.flex-content");

src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css

+109-90
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ h5 {
6262
h6 {
6363
font-size:13px;
6464
}
65+
/* Disable font boosting */
66+
h1, h2, h3, h4, h5, h6 {
67+
max-height: 2em;
68+
}
6569
ul {
6670
list-style-type:disc;
6771
}
@@ -717,52 +721,6 @@ span#page-search-link {
717721
background: #F8981D;
718722
color: #253441;
719723
}
720-
button.copy-url {
721-
opacity: 80%;
722-
transition: opacity 0.2s;
723-
margin-left: 0.4em;
724-
border: none;
725-
border-radius: 3px;
726-
cursor: pointer;
727-
background: none;
728-
padding:0.3em;
729-
position: relative;
730-
top:0.13em
731-
}
732-
button.copy-url img {
733-
width: 1.2em;
734-
height: 1.2em;
735-
padding: 0.01em 0;
736-
background: none;
737-
position:relative;
738-
top: 0.15em;
739-
}
740-
div.page-search-info:hover button.copy-url {
741-
opacity: 90%;
742-
}
743-
div.page-search-info button.copy-url:hover {
744-
background-color: #dfe6f1;
745-
opacity: 100%;
746-
}
747-
button.copy-url span {
748-
color: #000000;
749-
content: attr(aria-label);
750-
font-family:'DejaVu Sans', Arial, Helvetica, sans-serif;
751-
font-size: 85%;
752-
line-height: 1.2em;
753-
padding: 0.2em;
754-
position: relative;
755-
top: -0.18em;
756-
transition: opacity 0.1s;
757-
opacity: 0;
758-
}
759-
div.page-search-info:hover button.copy-url span {
760-
opacity: 90%;
761-
}
762-
div.page-search-info button.copy-url:active {
763-
background-color: #cfdbee;
764-
opacity: 100%;
765-
}
766724
.module-graph span {
767725
display:none;
768726
position:absolute;
@@ -832,7 +790,111 @@ main a[href*="://"]:focus::after {
832790
132-240 240 120 120 240-240 132 132V0z" fill="%23bb7a2a"/>\
833791
</svg>');
834792
}
835-
793+
/*
794+
* Styles for copy-to-clipboard buttons
795+
*/
796+
button.copy {
797+
opacity: 80%;
798+
border: none;
799+
border-radius: 3px;
800+
position: relative;
801+
background:none;
802+
transition: opacity 0.2s;
803+
cursor: pointer;
804+
}
805+
button.copy:hover,
806+
button.copy:active {
807+
opacity: 100%;
808+
}
809+
button.copy img {
810+
position: relative;
811+
background: none;
812+
}
813+
button.copy span {
814+
color: #303030;
815+
position: relative;
816+
top: -0.1em;
817+
transition: all 0.1s;
818+
font-size: 85%;
819+
line-height: 1.2em;
820+
}
821+
/* header/section copy button */
822+
button.copy-header {
823+
margin: 0 0.2em;
824+
padding: 0 4px;
825+
height: 1.35em;
826+
}
827+
button.copy-header img {
828+
height: 1em;
829+
top: 0.1em;
830+
}
831+
button.copy-header:active {
832+
background-color: rgba(128, 128, 160, 0.2);
833+
}
834+
/* search page copy button */
835+
button#page-search-copy {
836+
margin-left: 0.4em;
837+
padding:0.3em;
838+
top:0.13em;
839+
}
840+
button#page-search-copy img {
841+
width: 1.2em;
842+
height: 1.2em;
843+
padding: 0.01em 0;
844+
top: 0.15em;
845+
}
846+
button#page-search-copy span {
847+
color: #000000;
848+
content: attr(aria-label);
849+
line-height: 1.2em;
850+
padding: 0.2em;
851+
top: -0.18em;
852+
opacity: 0;
853+
}
854+
div.page-search-info:hover button#page-search-copy,
855+
div.page-search-info:hover button#page-search-copy span {
856+
opacity: 90%;
857+
}
858+
div.page-search-info button#page-search-copy:hover {
859+
background-color: #dfe6f1;
860+
}
861+
div.page-search-info button#page-search-copy:active {
862+
background-color: #cfdbee;
863+
}
864+
/* snippet copy button */
865+
button.snippet-copy {
866+
position: absolute;
867+
top: 6px;
868+
right: 6px;
869+
height: 1.7em;
870+
opacity: 50%;
871+
padding: 2px;
872+
}
873+
button.snippet-copy img {
874+
width: 18px;
875+
height: 18px;
876+
padding: 0.05em 0;
877+
}
878+
button.snippet-copy span {
879+
content: attr(aria-label);
880+
line-height: 1.2em;
881+
padding: 0.2em;
882+
position: relative;
883+
top: -0.5em;
884+
display: none;
885+
}
886+
div.snippet-container:hover button.snippet-copy span {
887+
display: inline;
888+
}
889+
div.snippet-container:hover button.snippet-copy {
890+
opacity: 80%;
891+
}
892+
div.snippet-container button.snippet-copy:hover {
893+
opacity: 100%;
894+
}
895+
button.snippet-copy:active {
896+
background: #d3d3d3;
897+
}
836898
/*
837899
* Styles for user-provided tables.
838900
*
@@ -1067,49 +1129,6 @@ pre.snippet {
10671129
div.snippet-container {
10681130
position: relative;
10691131
}
1070-
button.snippet-copy {
1071-
position: absolute;
1072-
top: 6px;
1073-
right: 6px;
1074-
height: 1.7em;
1075-
opacity: 50%;
1076-
transition: opacity 0.2s;
1077-
padding: 2px;
1078-
border: none;
1079-
cursor: pointer;
1080-
background: none;
1081-
}
1082-
button.snippet-copy img {
1083-
width: 18px;
1084-
height: 18px;
1085-
padding: 0.05em 0;
1086-
background: none;
1087-
}
1088-
div.snippet-container:hover button.snippet-copy {
1089-
opacity: 80%;
1090-
}
1091-
div.snippet-container button.snippet-copy:hover {
1092-
opacity: 100%;
1093-
}
1094-
button.snippet-copy span {
1095-
color: #3d3d3d;
1096-
content: attr(aria-label);
1097-
font-family:'DejaVu Sans', Arial, Helvetica, sans-serif;
1098-
font-size: 85%;
1099-
line-height: 1.2em;
1100-
padding: 0.2em;
1101-
position: relative;
1102-
white-space: nowrap;
1103-
top: -0.5em;
1104-
display: none;
1105-
}
1106-
div.snippet-container:hover button.snippet-copy span {
1107-
display: inline;
1108-
}
1109-
button.snippet-copy:active {
1110-
background: #d3d3d3;
1111-
opacity: 100%;
1112-
}
11131132
@media screen and (max-width: 800px) {
11141133
pre.snippet {
11151134
padding-top: 26px;

test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ void run() throws Exception {
135135
removeAll(styleSheetNames, "borderless", "plain", "striped");
136136

137137
// used in search.js and search-page.js; may be worth documenting in HtmlStyle
138-
removeAll(styleSheetNames, "result-highlight", "result-item",
138+
removeAll(styleSheetNames, "result-highlight", "result-item", "copy-header",
139139
"search-tag-desc-result", "search-tag-holder-result", "page-search-header",
140140
"ui-autocomplete", "ui-autocomplete-category", "expanded",
141141
"search-result-link", "two-column-search-results", "ui-static-link");

test/langtools/jdk/javadoc/doclet/testSnippetTag/SnippetTester.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ protected String getSnippetHtmlRepresentation(String pathToHtmlFile,
119119
var idString = id.isEmpty() ? "" : " id=\"%s\"".formatted(id.get());
120120
var langString = lang.isEmpty() ? "" : " class=\"language-%s\"".formatted(lang.get());
121121
return """
122-
<div class="snippet-container"><button class="snippet-copy" onclick="copySnippet(this)">\
122+
<div class="snippet-container"><button class="copy snippet-copy" onclick="copySnippet(this)">\
123123
<span data-copied="Copied!">Copy</span><img src="%s" alt="Copy"></button>
124124
<pre class="snippet"%s><code%s>%s</code></pre>
125125
</div>""".formatted(svgString, idString, langString, content);

test/langtools/jdk/javadoc/doclet/testSnippetTag/TestSnippetUnnamedPackage.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ private C() { }
8787
"""
8888
Before.
8989
\s
90-
<div class="snippet-container"><button class="snippet-copy" onclick="copySnippet(this)"><span data-copied="Copied!">Copy</span><img src="copy.svg" alt="Copy"></button>
90+
<div class="snippet-container"><button class="copy snippet-copy" onclick="copySnippet\
91+
(this)"><span data-copied="Copied!">Copy</span><img src="copy.svg" alt="Copy"></button>
9192
<pre class="snippet"><code class="language-java">public class S { }</code></pre>
9293
</div>
9394

0 commit comments

Comments
 (0)