content/static,internal/frontend,internal/middleware: add 'y' keyboard shortcut to canonicalise URL with version

The address bar URL will be updated to be the canonical url of package.
Canonical url includes the module version in address bar.

Fixes golang/go#36807

Change-Id: I4a6f9737ff7e112ebf1d093b2eebe2af311fb0c6
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/239179
Reviewed-by: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Andrew Bonventre <andybons@golang.org>
Run-TryBot: Andrew Bonventre <andybons@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Kush Patel 2020-06-20 15:19:24 -07:00 коммит произвёл Julie Qiu
Родитель 3bf54b6c75
Коммит 6440758d8a
8 изменённых файлов: 58 добавлений и 5 удалений

Просмотреть файл

@ -208,6 +208,7 @@
{{end}}
{{define "post_content"}}
<div class="js-canonicalURLPath" data-canonical-url-path="{{.CanonicalURLPath}}" hidden />
<script>
loadScript('/static/js/details.min.js');
</script>

8
content/static/js/details.min.js поставляемый
Просмотреть файл

@ -4,13 +4,13 @@
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
*/
var k="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){if(a==Array.prototype||a==Object.prototype)return a;a[b]=c.value;return a};function l(a){a=["object"==typeof globalThis&&globalThis,a,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");}var n=l(this);
function p(a,b){if(b)a:{var c=n;a=a.split(".");for(var d=0;d<a.length-1;d++){var e=a[d];if(!(e in c))break a;c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&k(c,a,{configurable:!0,writable:!0,value:b})}}
var k="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){if(a==Array.prototype||a==Object.prototype)return a;a[b]=c.value;return a};function l(a){a=["object"==typeof globalThis&&globalThis,a,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");}var m=l(this);
function p(a,b){if(b)a:{var c=m;a=a.split(".");for(var d=0;d<a.length-1;d++){var e=a[d];if(!(e in c))break a;c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&k(c,a,{configurable:!0,writable:!0,value:b})}}
p("Array.from",function(a){return a?a:function(b,c,d){c=null!=c?c:function(h){return h};var e=[],f="undefined"!=typeof Symbol&&Symbol.iterator&&b[Symbol.iterator];if("function"==typeof f){b=f.call(b);for(var g=0;!(f=b.next()).done;)e.push(c.call(d,f.value,g++))}else for(f=b.length,g=0;g<f;g++)e.push(c.call(d,b[g],g));return e}});p("Object.is",function(a){return a?a:function(b,c){return b===c?0!==b||1/b===1/c:b!==b&&c!==c}});
p("Array.prototype.includes",function(a){return a?a:function(b,c){var d=this;d instanceof String&&(d=String(d));var e=d.length;c=c||0;for(0>c&&(c=Math.max(c+e,0));c<e;c++){var f=d[c];if(f===b||Object.is(f,b))return!0}return!1}});
p("String.prototype.includes",function(a){return a?a:function(b,c){if(null==this)throw new TypeError("The 'this' value for String.prototype.includes must not be null or undefined");if(b instanceof RegExp)throw new TypeError("First argument to String.prototype.includes must not be a regular expression");return-1!==this.indexOf(b,c||0)}});function q(a){var b=this;this.a=a;this.b=a.dataset.toCopy;a.addEventListener("click",function(c){return r(b,c)})}
function r(a,b){b.preventDefault();navigator.clipboard?navigator.clipboard.writeText(a.b).then(function(){t(a,"Copied!")}).catch(function(){t(a,"Unable to copy")}):t(a,"Unable to copy")}function t(a,b){a.a.setAttribute("data-tooltip",b);setTimeout(function(){return a.a.setAttribute("data-tooltip","")},1E3)}function u(a,b){b.forEach(function(c){a.b.setAttribute("aria-hidden",c.isIntersecting)})}
function v(a){var b=this;if(!a)throw Error("Must provide an element.");this.a=a;a=this.a.querySelector("select");if(!a)throw Error("Element must contain a <select> element.");this.b=a;this.c=[];if(window.ResizeObserver){var c=0;this.c=Array.from(this.a.querySelectorAll("[role='tab']")).map(function(d,e){0===e&&(c=d.offsetLeft);return d.offsetLeft+d.offsetWidth-c});(new ResizeObserver(function(d){return w(b,d)})).observe(this.a);this.b.addEventListener("change",function(d){window.location.href=d.target.value})}else this.a.style.overflowX=
"scroll"}function w(a,b){b.forEach(function(c){var d=c.target.getBoundingClientRect(),e=[];a.a.querySelectorAll("[role='tab']").forEach(function(f,g){var h=a.c[g],m=d.width;a.a.classList.contains("is-overflowing")&&(m-=40);h=h>m;f.setAttribute("aria-hidden",h);h&&e.push(g)});a.a.classList.toggle("is-overflowing",0!==e.length);a.b.querySelectorAll("option").forEach(function(f,g){f.disabled=!e.includes(g)||"true"===f.getAttribute("data-always-disabled")})})}
"scroll"}function w(a,b){b.forEach(function(c){var d=c.target.getBoundingClientRect(),e=[];a.a.querySelectorAll("[role='tab']").forEach(function(f,g){var h=a.c[g],n=d.width;a.a.classList.contains("is-overflowing")&&(n-=40);h=h>n;f.setAttribute("aria-hidden",h);h&&e.push(g)});a.a.classList.toggle("is-overflowing",0!==e.length);a.b.querySelectorAll("option").forEach(function(f,g){f.disabled=!e.includes(g)||"true"===f.getAttribute("data-always-disabled")})})}
new function(){var a=document.querySelector(".js-fixedHeaderSentinel"),b=document.querySelector(".js-fixedHeader"),c=this;if(!a||!b)throw Error("Must provide sentinel and fixed elements to constructor.");this.a=a;this.b=b;this.c=new IntersectionObserver(function(d){return u(c,d)},{threshold:1});this.c.observe(this.a);void 0!==window.getComputedStyle(document.body)["-webkit-overflow-scrolling"]&&[document.documentElement,document.body].forEach(function(d){d.style.overflow="auto"})};document.querySelectorAll(".js-overflowingTabList").forEach(function(a){return new v(a)});
document.querySelectorAll(".js-copyToClipboard").forEach(function(a){new q(a)});
document.querySelectorAll(".js-copyToClipboard").forEach(function(a){new q(a)});var x=document.querySelector(".js-canonicalURLPath").dataset.canonicalUrlPath;x&&""!==x&&document.addEventListener("keydown",function(a){var b=a.target.tagName;if("INPUT"!==b&&"SELECT"!==b&&"TEXTAREA"!==b&&!a.target.isContentEditable&&!a.metaKey&&!a.ctrlKey)switch(a.key){case "y":window.history.replaceState(null,"",x)}});

Просмотреть файл

@ -0,0 +1,32 @@
/**
* @license
* Copyright 2019-2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
// This file implements the behavior of the keyboard shortcut which allows
// for users to press 'y' to to change browser URL to the canonical URL
// without triggering a reload.
const canonicalURLPath = document.querySelector('.js-canonicalURLPath').dataset['canonicalUrlPath'];
if (canonicalURLPath && canonicalURLPath !== '') {
document.addEventListener('keydown', e => {
// TODO(golang.org/issue/40246): consolidate keyboard shortcut behavior across the site.
const t = e.target.tagName;
if (t === 'INPUT' || t === 'SELECT' || t === 'TEXTAREA') {
return;
}
if (e.target.isContentEditable) {
return;
}
if (e.metaKey || e.ctrlKey) {
return;
}
switch (e.key) {
case 'y':
window.history.replaceState(null, '', canonicalURLPath);
break;
}
});
}

Просмотреть файл

@ -45,7 +45,7 @@ main() {
cmd=check
fi
$cmd $JSDIR/base.min.js $JSDIR/{site,analytics}.js
$cmd $JSDIR/details.min.js -advanced $JSDIR/{clipboard,fixed_header,overflowing_tab_list,details}.js
$cmd $JSDIR/details.min.js -advanced $JSDIR/{clipboard,fixed_header,overflowing_tab_list,details,keyboard}.js
$cmd $JSDIR/fetch.min.js $JSDIR/fetch.js
$cmd $JSDIR/playground.min.js $JSDIR/playground.js
$cmd $JSDIR/badge.min.js $JSDIR/badge.js

Просмотреть файл

@ -55,6 +55,12 @@ type DetailsPage struct {
// Tabs contains data to render the varioius tabs on each details page.
Tabs []TabSettings
// CanonicalURLPath is the representation of the URL path for the details
// page, after the requested version and module path have been resolved.
// For example, if the latest version of /my.module/pkg is version v1.5.2,
// the canonical url for that path would be /my.module@v1.5.2/pkg
CanonicalURLPath string
}
const (

Просмотреть файл

@ -105,6 +105,11 @@ func (s *Server) legacyServeDirectoryPage(ctx context.Context, w http.ResponseWr
CanShowDetails: true,
Tabs: directoryTabSettings,
PageType: pageTypeDirectory,
CanonicalURLPath: constructPackageURL(
dbDir.Path,
dbDir.ModulePath,
linkVersion(dbDir.Version, dbDir.ModulePath),
),
}
s.servePage(ctx, w, settings.TemplateName, page)
return nil

Просмотреть файл

@ -87,6 +87,10 @@ func (s *Server) serveModulePage(ctx context.Context, w http.ResponseWriter, r *
CanShowDetails: canShowDetails,
Tabs: moduleTabSettings,
PageType: pageType,
CanonicalURLPath: constructModuleURL(
mi.ModulePath,
linkVersion(mi.Version, mi.ModulePath),
),
}
s.servePage(ctx, w, settings.TemplateName, page)
return nil

Просмотреть файл

@ -138,6 +138,11 @@ func (s *Server) legacyServePackagePageWithPackage(w http.ResponseWriter, r *htt
CanShowDetails: canShowDetails,
Tabs: packageTabSettings,
PageType: pageType,
CanonicalURLPath: constructPackageURL(
pkg.Path,
pkg.ModulePath,
linkVersion(pkg.Version, pkg.ModulePath),
),
}
s.servePage(r.Context(), w, settings.TemplateName, page)
return nil