static/{frontend,shared}: keyboard events apply to capitalized letters

Change-Id: I7790d8c6b99115bbb61172fcc837f9857448121d
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/338174
Trust: Jamal Carvalho <jamal@golang.org>
Run-TryBot: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Julie Qiu <julie@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
diff --git a/static/frontend/frontend.js b/static/frontend/frontend.js
index 4b928cc..2bf5f0c 100644
--- a/static/frontend/frontend.js
+++ b/static/frontend/frontend.js
@@ -1,4 +1,4 @@
-(()=>{var _=Object.defineProperty;var N=e=>_(e,"__esModule",{value:!0});var P=(e,t)=>()=>(t||(t={exports:{}},e(t.exports,t)),t.exports),M=(e,t)=>{for(var o in t)_(e,o,{get:t[o],enumerable:!0})};var H=P(j=>{N(j);M(j,{default:()=>B});var d=window.CustomEvent;(!d||typeof d=="object")&&(d=function(t,o){o=o||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(t,!!o.bubbles,!!o.cancelable,o.detail||null),n},d.prototype=window.Event.prototype);function K(e){for(;e&&e!==document.body;){var t=window.getComputedStyle(e),o=function(n,i){return!(t[n]===void 0||t[n]===i)};if(t.opacity<1||o("zIndex","auto")||o("transform","none")||o("mixBlendMode","normal")||o("filter","none")||o("perspective","none")||t.isolation==="isolate"||t.position==="fixed"||t.webkitOverflowScrolling==="touch")return!0;e=e.parentElement}return!1}function f(e){for(;e;){if(e.localName==="dialog")return e;e=e.parentElement}return null}function T(e){e&&e.blur&&e!==document.body&&e.blur()}function F(e,t){for(var o=0;o<e.length;++o)if(e[o]===t)return!0;return!1}function p(e){return!e||!e.hasAttribute("method")?!1:e.getAttribute("method").toLowerCase()==="dialog"}function L(e){if(this.dialog_=e,this.replacedStyleTop_=!1,this.openAsModal_=!1,e.hasAttribute("role")||e.setAttribute("role","dialog"),e.show=this.show.bind(this),e.showModal=this.showModal.bind(this),e.close=this.close.bind(this),"returnValue"in e||(e.returnValue=""),"MutationObserver"in window){var t=new MutationObserver(this.maybeHideModal.bind(this));t.observe(e,{attributes:!0,attributeFilter:["open"]})}else{var o=!1,n=function(){o?this.downgradeModal():this.maybeHideModal(),o=!1}.bind(this),i,r=function(l){if(l.target===e){var c="DOMNodeRemoved";o|=l.type.substr(0,c.length)===c,window.clearTimeout(i),i=window.setTimeout(n,0)}};["DOMAttrModified","DOMNodeRemoved","DOMNodeRemovedFromDocument"].forEach(function(l){e.addEventListener(l,r)})}Object.defineProperty(e,"open",{set:this.setOpen.bind(this),get:e.hasAttribute.bind(e,"open")}),this.backdrop_=document.createElement("div"),this.backdrop_.className="backdrop",this.backdrop_.addEventListener("click",this.backdropClick_.bind(this))}L.prototype={get dialog(){return this.dialog_},maybeHideModal:function(){this.dialog_.hasAttribute("open")&&document.body.contains(this.dialog_)||this.downgradeModal()},downgradeModal:function(){!this.openAsModal_||(this.openAsModal_=!1,this.dialog_.style.zIndex="",this.replacedStyleTop_&&(this.dialog_.style.top="",this.replacedStyleTop_=!1),this.backdrop_.parentNode&&this.backdrop_.parentNode.removeChild(this.backdrop_),a.dm.removeDialog(this))},setOpen:function(e){e?this.dialog_.hasAttribute("open")||this.dialog_.setAttribute("open",""):(this.dialog_.removeAttribute("open"),this.maybeHideModal())},backdropClick_:function(e){if(this.dialog_.hasAttribute("tabindex"))this.dialog_.focus();else{var t=document.createElement("div");this.dialog_.insertBefore(t,this.dialog_.firstChild),t.tabIndex=-1,t.focus(),this.dialog_.removeChild(t)}var o=document.createEvent("MouseEvents");o.initMouseEvent(e.type,e.bubbles,e.cancelable,window,e.detail,e.screenX,e.screenY,e.clientX,e.clientY,e.ctrlKey,e.altKey,e.shiftKey,e.metaKey,e.button,e.relatedTarget),this.dialog_.dispatchEvent(o),e.stopPropagation()},focus_:function(){var e=this.dialog_.querySelector("[autofocus]:not([disabled])");if(!e&&this.dialog_.tabIndex>=0&&(e=this.dialog_),!e){var t=["button","input","keygen","select","textarea"],o=t.map(function(n){return n+":not([disabled])"});o.push('[tabindex]:not([disabled]):not([tabindex=""])'),e=this.dialog_.querySelector(o.join(", "))}T(document.activeElement),e&&e.focus()},updateZIndex:function(e,t){if(e<t)throw new Error("dialogZ should never be < backdropZ");this.dialog_.style.zIndex=e,this.backdrop_.style.zIndex=t},show:function(){this.dialog_.open||(this.setOpen(!0),this.focus_())},showModal:function(){if(this.dialog_.hasAttribute("open"))throw new Error("Failed to execute 'showModal' on dialog: The element is already open, and therefore cannot be opened modally.");if(!document.body.contains(this.dialog_))throw new Error("Failed to execute 'showModal' on dialog: The element is not in a Document.");if(!a.dm.pushDialog(this))throw new Error("Failed to execute 'showModal' on dialog: There are too many open modal dialogs.");K(this.dialog_.parentElement)&&console.warn("A dialog is being shown inside a stacking context. This may cause it to be unusable. For more information, see this link: https://github.com/GoogleChrome/dialog-polyfill/#stacking-context"),this.setOpen(!0),this.openAsModal_=!0,a.needsCentering(this.dialog_)?(a.reposition(this.dialog_),this.replacedStyleTop_=!0):this.replacedStyleTop_=!1,this.dialog_.parentNode.insertBefore(this.backdrop_,this.dialog_.nextSibling),this.focus_()},close:function(e){if(!this.dialog_.hasAttribute("open"))throw new Error("Failed to execute 'close' on dialog: The element does not have an 'open' attribute, and therefore cannot be closed.");this.setOpen(!1),e!==void 0&&(this.dialog_.returnValue=e);var t=new d("close",{bubbles:!1,cancelable:!1});this.dialog_.dispatchEvent(t)}};var a={};a.reposition=function(e){var t=document.body.scrollTop||document.documentElement.scrollTop,o=t+(window.innerHeight-e.offsetHeight)/2;e.style.top=Math.max(t,o)+"px"};a.isInlinePositionSetByStylesheet=function(e){for(var t=0;t<document.styleSheets.length;++t){var o=document.styleSheets[t],n=null;try{n=o.cssRules}catch(O){}if(!!n)for(var i=0;i<n.length;++i){var r=n[i],l=null;try{l=document.querySelectorAll(r.selectorText)}catch(O){}if(!(!l||!F(l,e))){var c=r.style.getPropertyValue("top"),E=r.style.getPropertyValue("bottom");if(c&&c!=="auto"||E&&E!=="auto")return!0}}}return!1};a.needsCentering=function(e){var t=window.getComputedStyle(e);return t.position!=="absolute"||e.style.top!=="auto"&&e.style.top!==""||e.style.bottom!=="auto"&&e.style.bottom!==""?!1:!a.isInlinePositionSetByStylesheet(e)};a.forceRegisterDialog=function(e){if((window.HTMLDialogElement||e.showModal)&&console.warn("This browser already supports <dialog>, the polyfill may not work correctly",e),e.localName!=="dialog")throw new Error("Failed to register dialog: The element is not a dialog.");new L(e)};a.registerDialog=function(e){e.showModal||a.forceRegisterDialog(e)};a.DialogManager=function(){this.pendingDialogStack=[];var e=this.checkDOM_.bind(this);this.overlay=document.createElement("div"),this.overlay.className="_dialog_overlay",this.overlay.addEventListener("click",function(t){this.forwardTab_=void 0,t.stopPropagation(),e([])}.bind(this)),this.handleKey_=this.handleKey_.bind(this),this.handleFocus_=this.handleFocus_.bind(this),this.zIndexLow_=1e5,this.zIndexHigh_=1e5+150,this.forwardTab_=void 0,"MutationObserver"in window&&(this.mo_=new MutationObserver(function(t){var o=[];t.forEach(function(n){for(var i=0,r;r=n.removedNodes[i];++i){if(r instanceof Element)r.localName==="dialog"&&o.push(r);else continue;o=o.concat(r.querySelectorAll("dialog"))}}),o.length&&e(o)}))};a.DialogManager.prototype.blockDocument=function(){document.documentElement.addEventListener("focus",this.handleFocus_,!0),document.addEventListener("keydown",this.handleKey_),this.mo_&&this.mo_.observe(document,{childList:!0,subtree:!0})};a.DialogManager.prototype.unblockDocument=function(){document.documentElement.removeEventListener("focus",this.handleFocus_,!0),document.removeEventListener("keydown",this.handleKey_),this.mo_&&this.mo_.disconnect()};a.DialogManager.prototype.updateStacking=function(){for(var e=this.zIndexHigh_,t=0,o;o=this.pendingDialogStack[t];++t)o.updateZIndex(--e,--e),t===0&&(this.overlay.style.zIndex=--e);var n=this.pendingDialogStack[0];if(n){var i=n.dialog.parentNode||document.body;i.appendChild(this.overlay)}else this.overlay.parentNode&&this.overlay.parentNode.removeChild(this.overlay)};a.DialogManager.prototype.containedByTopDialog_=function(e){for(;e=f(e);){for(var t=0,o;o=this.pendingDialogStack[t];++t)if(o.dialog===e)return t===0;e=e.parentElement}return!1};a.DialogManager.prototype.handleFocus_=function(e){if(!this.containedByTopDialog_(e.target)&&document.activeElement!==document.documentElement&&(e.preventDefault(),e.stopPropagation(),T(e.target),this.forwardTab_!==void 0)){var t=this.pendingDialogStack[0],o=t.dialog,n=o.compareDocumentPosition(e.target);return n&Node.DOCUMENT_POSITION_PRECEDING&&(this.forwardTab_?t.focus_():e.target!==document.documentElement&&document.documentElement.focus()),!1}};a.DialogManager.prototype.handleKey_=function(e){if(this.forwardTab_=void 0,e.keyCode===27){e.preventDefault(),e.stopPropagation();var t=new d("cancel",{bubbles:!1,cancelable:!0}),o=this.pendingDialogStack[0];o&&o.dialog.dispatchEvent(t)&&o.dialog.close()}else e.keyCode===9&&(this.forwardTab_=!e.shiftKey)};a.DialogManager.prototype.checkDOM_=function(e){var t=this.pendingDialogStack.slice();t.forEach(function(o){e.indexOf(o.dialog)!==-1?o.downgradeModal():o.maybeHideModal()})};a.DialogManager.prototype.pushDialog=function(e){var t=(this.zIndexHigh_-this.zIndexLow_)/2-1;return this.pendingDialogStack.length>=t?!1:(this.pendingDialogStack.unshift(e)===1&&this.blockDocument(),this.updateStacking(),!0)};a.DialogManager.prototype.removeDialog=function(e){var t=this.pendingDialogStack.indexOf(e);t!==-1&&(this.pendingDialogStack.splice(t,1),this.pendingDialogStack.length===0&&this.unblockDocument(),this.updateStacking())};a.dm=new a.DialogManager;a.formSubmitter=null;a.useValue=null;window.HTMLDialogElement===void 0&&(v=document.createElement("form"),v.setAttribute("method","dialog"),v.method!=="dialog"&&(s=Object.getOwnPropertyDescriptor(HTMLFormElement.prototype,"method"),s&&(S=s.get,s.get=function(){return p(this)?"dialog":S.call(this)},k=s.set,s.set=function(e){return typeof e=="string"&&e.toLowerCase()==="dialog"?this.setAttribute("method",e):k.call(this,e)},Object.defineProperty(HTMLFormElement.prototype,"method",s))),document.addEventListener("click",function(e){if(a.formSubmitter=null,a.useValue=null,!e.defaultPrevented){var t=e.target;if(!(!t||!p(t.form))){var o=t.type==="submit"&&["button","input"].indexOf(t.localName)>-1;if(!o){if(!(t.localName==="input"&&t.type==="image"))return;a.useValue=e.offsetX+","+e.offsetY}var n=f(t);!n||(a.formSubmitter=t)}}},!1),D=HTMLFormElement.prototype.submit,x=function(){if(!p(this))return D.call(this);var e=f(this);e&&e.close()},HTMLFormElement.prototype.submit=x,document.addEventListener("submit",function(e){if(!e.defaultPrevented){var t=e.target;if(!!p(t)){e.preventDefault();var o=f(t);if(!!o){var n=a.formSubmitter;n&&n.form===t?o.close(a.useValue||n.value):o.close(),a.formSubmitter=null}}}},!1));var v,s,S,k,D,x,B=a});function I(){let e=document.querySelector(".js-header"),t=document.querySelectorAll(".js-headerMenuButton");t.forEach(n=>{n.addEventListener("click",i=>{i.preventDefault(),e?.classList.toggle("is-active"),n.setAttribute("aria-expanded",String(e?.classList.contains("is-active")))})}),document.querySelector(".js-scrim")?.addEventListener("click",n=>{n.preventDefault(),e?.classList.remove("is-active"),t.forEach(i=>{i.setAttribute("aria-expanded",String(e?.classList.contains("is-active")))})})}function q(){let e=document.querySelector(".js-searchForm"),t=document.querySelector(".js-expandSearch"),o=e?.querySelector("input"),n=document.querySelector(".js-headerLogo"),i=document.querySelector(".js-headerMenuButton");t?.addEventListener("click",()=>{e?.classList.add("go-SearchForm--expanded"),n?.classList.add("go-Header-logo--hidden"),i?.classList.add("go-Header-navOpen--hidden"),o?.focus()}),document?.addEventListener("click",r=>{e?.contains(r.target)||(e?.classList.remove("go-SearchForm--expanded"),n?.classList.remove("go-Header-logo--hidden"),i?.classList.remove("go-Header-navOpen--hidden"))})}document.querySelectorAll(".js-searchModeSelect").forEach(e=>{e.addEventListener("change",t=>{let o=new URLSearchParams(window.location.search),i=Object.fromEntries(o.entries()).q;i&&(window.location.search=`q=${i}&m=${t.target.value}`)})});I();q();var m=class{constructor(t){this.el=t;this.data=t.dataset.toCopy??t.innerText,!this.data&&t.parentElement?.classList.contains("go-InputGroup")&&(this.data=(this.data||t.parentElement?.querySelector("input")?.value)??""),t.addEventListener("click",o=>this.handleCopyClick(o))}handleCopyClick(t){t.preventDefault();let o=1e3;if(!navigator.clipboard){this.showTooltipText("Unable to copy",o);return}navigator.clipboard.writeText(this.data).then(()=>{this.showTooltipText("Copied!",o)}).catch(()=>{this.showTooltipText("Unable to copy",o)})}showTooltipText(t,o){this.el.setAttribute("data-tooltip",t),setTimeout(()=>this.el.setAttribute("data-tooltip",""),o)}};var g=class{constructor(t){this.el=t;document.addEventListener("click",o=>{this.el.contains(o.target)||this.el.removeAttribute("open")})}};var y=class{constructor(t){this.el=t;this.el.addEventListener("change",o=>{let n=o.target,i=n.value;n.value.startsWith("/")||(i="/"+i),window.location.href=i})}};var b=class{constructor(t){this.el=t;!window.HTMLDialogElement&&!t.showModal&&Promise.resolve().then(()=>H()).then(({default:i})=>{i.registerDialog(t)});let o=t.id,n=document.querySelector(`[aria-controls="${o}"]`);n&&n.addEventListener("click",()=>{this.el.showModal?this.el.showModal():this.el.open=!0,t.querySelector("input")?.focus()});for(let i of this.el.querySelectorAll("[data-modal-close]"))i.addEventListener("click",()=>{this.el.close?this.el.close():this.el.open=!1})}};var u={};M(u,{func:()=>R,track:()=>w});function w(e,t,o,n){window.dataLayer??=[],typeof e=="string"?window.dataLayer.push({event:e,event_category:t,event_action:o,event_label:n}):window.dataLayer.push(e)}function R(e){window.dataLayer??=[],window.dataLayer.push(e)}var A=class{constructor(){this.handlers={},document.addEventListener("keydown",t=>this.handleKeyPress(t))}on(t,o,n,i){return this.handlers[t]??=new Set,this.handlers[t].add({description:o,callback:n,...i}),this}handleKeyPress(t){for(let o of this.handlers[t.key]??new Set){if(o.target&&o.target!==t.target)return;let n=t.target;if(!o.target&&(n?.tagName==="INPUT"||n?.tagName==="SELECT"||n?.tagName==="TEXTAREA")||n?.isContentEditable||o.withMeta&&!(t.ctrlKey||t.metaKey)||!o.withMeta&&(t.ctrlKey||t.metaKey))return;w("keypress","hotkeys",`${t.key} pressed`,o.description),o.callback(t)}}},h=new A;for(let e of document.querySelectorAll(".js-clipboard"))new m(e);for(let e of document.querySelectorAll(".js-modal"))new b(e);for(let e of document.querySelectorAll(".js-tooltip"))new g(e);for(let e of document.querySelectorAll(".js-selectNav"))new y(e);h.on("t","toggle theme",()=>{let e="dark",t=document.documentElement.getAttribute("data-theme");t==="dark"?e="light":t==="light"&&(e="auto"),document.documentElement.setAttribute("data-theme",e),document.cookie=`prefers-color-scheme=${e};path=/;max-age=31536000;`});h.on("/","focus search",e=>{let t=Array.from(document.querySelectorAll(".js-searchFocus")).pop();t&&!window.navigator.userAgent.includes("Firefox")&&(e.preventDefault(),t.focus())});h.on("y","set canonical url",()=>{let e=document.querySelector(".js-canonicalURLPath")?.dataset.canonicalUrlPath;e&&e!==""&&window.history.replaceState(null,"",e)});(function(){u.track({"gtm.start":new Date().getTime(),event:"gtm.js"})})();function C(){let e=new URLSearchParams(window.location.search),t=e.get("utm_source");if(t!=="gopls"&&t!=="godoc"&&t!=="pkggodev")return;let o=new URL(window.location.href);e.delete("utm_source"),o.search=e.toString(),window.history.replaceState(null,"",o.toString())}document.querySelector(".js-gtmID")?.dataset.gtmid&&window.dataLayer?u.func(function(){C()}):C();})();
+(()=>{var _=Object.defineProperty;var N=e=>_(e,"__esModule",{value:!0});var P=(e,t)=>()=>(t||(t={exports:{}},e(t.exports,t)),t.exports),M=(e,t)=>{for(var o in t)_(e,o,{get:t[o],enumerable:!0})};var C=P(j=>{N(j);M(j,{default:()=>B});var d=window.CustomEvent;(!d||typeof d=="object")&&(d=function(t,o){o=o||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(t,!!o.bubbles,!!o.cancelable,o.detail||null),n},d.prototype=window.Event.prototype);function K(e){for(;e&&e!==document.body;){var t=window.getComputedStyle(e),o=function(n,i){return!(t[n]===void 0||t[n]===i)};if(t.opacity<1||o("zIndex","auto")||o("transform","none")||o("mixBlendMode","normal")||o("filter","none")||o("perspective","none")||t.isolation==="isolate"||t.position==="fixed"||t.webkitOverflowScrolling==="touch")return!0;e=e.parentElement}return!1}function f(e){for(;e;){if(e.localName==="dialog")return e;e=e.parentElement}return null}function T(e){e&&e.blur&&e!==document.body&&e.blur()}function F(e,t){for(var o=0;o<e.length;++o)if(e[o]===t)return!0;return!1}function p(e){return!e||!e.hasAttribute("method")?!1:e.getAttribute("method").toLowerCase()==="dialog"}function L(e){if(this.dialog_=e,this.replacedStyleTop_=!1,this.openAsModal_=!1,e.hasAttribute("role")||e.setAttribute("role","dialog"),e.show=this.show.bind(this),e.showModal=this.showModal.bind(this),e.close=this.close.bind(this),"returnValue"in e||(e.returnValue=""),"MutationObserver"in window){var t=new MutationObserver(this.maybeHideModal.bind(this));t.observe(e,{attributes:!0,attributeFilter:["open"]})}else{var o=!1,n=function(){o?this.downgradeModal():this.maybeHideModal(),o=!1}.bind(this),i,r=function(l){if(l.target===e){var c="DOMNodeRemoved";o|=l.type.substr(0,c.length)===c,window.clearTimeout(i),i=window.setTimeout(n,0)}};["DOMAttrModified","DOMNodeRemoved","DOMNodeRemovedFromDocument"].forEach(function(l){e.addEventListener(l,r)})}Object.defineProperty(e,"open",{set:this.setOpen.bind(this),get:e.hasAttribute.bind(e,"open")}),this.backdrop_=document.createElement("div"),this.backdrop_.className="backdrop",this.backdrop_.addEventListener("click",this.backdropClick_.bind(this))}L.prototype={get dialog(){return this.dialog_},maybeHideModal:function(){this.dialog_.hasAttribute("open")&&document.body.contains(this.dialog_)||this.downgradeModal()},downgradeModal:function(){!this.openAsModal_||(this.openAsModal_=!1,this.dialog_.style.zIndex="",this.replacedStyleTop_&&(this.dialog_.style.top="",this.replacedStyleTop_=!1),this.backdrop_.parentNode&&this.backdrop_.parentNode.removeChild(this.backdrop_),a.dm.removeDialog(this))},setOpen:function(e){e?this.dialog_.hasAttribute("open")||this.dialog_.setAttribute("open",""):(this.dialog_.removeAttribute("open"),this.maybeHideModal())},backdropClick_:function(e){if(this.dialog_.hasAttribute("tabindex"))this.dialog_.focus();else{var t=document.createElement("div");this.dialog_.insertBefore(t,this.dialog_.firstChild),t.tabIndex=-1,t.focus(),this.dialog_.removeChild(t)}var o=document.createEvent("MouseEvents");o.initMouseEvent(e.type,e.bubbles,e.cancelable,window,e.detail,e.screenX,e.screenY,e.clientX,e.clientY,e.ctrlKey,e.altKey,e.shiftKey,e.metaKey,e.button,e.relatedTarget),this.dialog_.dispatchEvent(o),e.stopPropagation()},focus_:function(){var e=this.dialog_.querySelector("[autofocus]:not([disabled])");if(!e&&this.dialog_.tabIndex>=0&&(e=this.dialog_),!e){var t=["button","input","keygen","select","textarea"],o=t.map(function(n){return n+":not([disabled])"});o.push('[tabindex]:not([disabled]):not([tabindex=""])'),e=this.dialog_.querySelector(o.join(", "))}T(document.activeElement),e&&e.focus()},updateZIndex:function(e,t){if(e<t)throw new Error("dialogZ should never be < backdropZ");this.dialog_.style.zIndex=e,this.backdrop_.style.zIndex=t},show:function(){this.dialog_.open||(this.setOpen(!0),this.focus_())},showModal:function(){if(this.dialog_.hasAttribute("open"))throw new Error("Failed to execute 'showModal' on dialog: The element is already open, and therefore cannot be opened modally.");if(!document.body.contains(this.dialog_))throw new Error("Failed to execute 'showModal' on dialog: The element is not in a Document.");if(!a.dm.pushDialog(this))throw new Error("Failed to execute 'showModal' on dialog: There are too many open modal dialogs.");K(this.dialog_.parentElement)&&console.warn("A dialog is being shown inside a stacking context. This may cause it to be unusable. For more information, see this link: https://github.com/GoogleChrome/dialog-polyfill/#stacking-context"),this.setOpen(!0),this.openAsModal_=!0,a.needsCentering(this.dialog_)?(a.reposition(this.dialog_),this.replacedStyleTop_=!0):this.replacedStyleTop_=!1,this.dialog_.parentNode.insertBefore(this.backdrop_,this.dialog_.nextSibling),this.focus_()},close:function(e){if(!this.dialog_.hasAttribute("open"))throw new Error("Failed to execute 'close' on dialog: The element does not have an 'open' attribute, and therefore cannot be closed.");this.setOpen(!1),e!==void 0&&(this.dialog_.returnValue=e);var t=new d("close",{bubbles:!1,cancelable:!1});this.dialog_.dispatchEvent(t)}};var a={};a.reposition=function(e){var t=document.body.scrollTop||document.documentElement.scrollTop,o=t+(window.innerHeight-e.offsetHeight)/2;e.style.top=Math.max(t,o)+"px"};a.isInlinePositionSetByStylesheet=function(e){for(var t=0;t<document.styleSheets.length;++t){var o=document.styleSheets[t],n=null;try{n=o.cssRules}catch(O){}if(!!n)for(var i=0;i<n.length;++i){var r=n[i],l=null;try{l=document.querySelectorAll(r.selectorText)}catch(O){}if(!(!l||!F(l,e))){var c=r.style.getPropertyValue("top"),E=r.style.getPropertyValue("bottom");if(c&&c!=="auto"||E&&E!=="auto")return!0}}}return!1};a.needsCentering=function(e){var t=window.getComputedStyle(e);return t.position!=="absolute"||e.style.top!=="auto"&&e.style.top!==""||e.style.bottom!=="auto"&&e.style.bottom!==""?!1:!a.isInlinePositionSetByStylesheet(e)};a.forceRegisterDialog=function(e){if((window.HTMLDialogElement||e.showModal)&&console.warn("This browser already supports <dialog>, the polyfill may not work correctly",e),e.localName!=="dialog")throw new Error("Failed to register dialog: The element is not a dialog.");new L(e)};a.registerDialog=function(e){e.showModal||a.forceRegisterDialog(e)};a.DialogManager=function(){this.pendingDialogStack=[];var e=this.checkDOM_.bind(this);this.overlay=document.createElement("div"),this.overlay.className="_dialog_overlay",this.overlay.addEventListener("click",function(t){this.forwardTab_=void 0,t.stopPropagation(),e([])}.bind(this)),this.handleKey_=this.handleKey_.bind(this),this.handleFocus_=this.handleFocus_.bind(this),this.zIndexLow_=1e5,this.zIndexHigh_=1e5+150,this.forwardTab_=void 0,"MutationObserver"in window&&(this.mo_=new MutationObserver(function(t){var o=[];t.forEach(function(n){for(var i=0,r;r=n.removedNodes[i];++i){if(r instanceof Element)r.localName==="dialog"&&o.push(r);else continue;o=o.concat(r.querySelectorAll("dialog"))}}),o.length&&e(o)}))};a.DialogManager.prototype.blockDocument=function(){document.documentElement.addEventListener("focus",this.handleFocus_,!0),document.addEventListener("keydown",this.handleKey_),this.mo_&&this.mo_.observe(document,{childList:!0,subtree:!0})};a.DialogManager.prototype.unblockDocument=function(){document.documentElement.removeEventListener("focus",this.handleFocus_,!0),document.removeEventListener("keydown",this.handleKey_),this.mo_&&this.mo_.disconnect()};a.DialogManager.prototype.updateStacking=function(){for(var e=this.zIndexHigh_,t=0,o;o=this.pendingDialogStack[t];++t)o.updateZIndex(--e,--e),t===0&&(this.overlay.style.zIndex=--e);var n=this.pendingDialogStack[0];if(n){var i=n.dialog.parentNode||document.body;i.appendChild(this.overlay)}else this.overlay.parentNode&&this.overlay.parentNode.removeChild(this.overlay)};a.DialogManager.prototype.containedByTopDialog_=function(e){for(;e=f(e);){for(var t=0,o;o=this.pendingDialogStack[t];++t)if(o.dialog===e)return t===0;e=e.parentElement}return!1};a.DialogManager.prototype.handleFocus_=function(e){if(!this.containedByTopDialog_(e.target)&&document.activeElement!==document.documentElement&&(e.preventDefault(),e.stopPropagation(),T(e.target),this.forwardTab_!==void 0)){var t=this.pendingDialogStack[0],o=t.dialog,n=o.compareDocumentPosition(e.target);return n&Node.DOCUMENT_POSITION_PRECEDING&&(this.forwardTab_?t.focus_():e.target!==document.documentElement&&document.documentElement.focus()),!1}};a.DialogManager.prototype.handleKey_=function(e){if(this.forwardTab_=void 0,e.keyCode===27){e.preventDefault(),e.stopPropagation();var t=new d("cancel",{bubbles:!1,cancelable:!0}),o=this.pendingDialogStack[0];o&&o.dialog.dispatchEvent(t)&&o.dialog.close()}else e.keyCode===9&&(this.forwardTab_=!e.shiftKey)};a.DialogManager.prototype.checkDOM_=function(e){var t=this.pendingDialogStack.slice();t.forEach(function(o){e.indexOf(o.dialog)!==-1?o.downgradeModal():o.maybeHideModal()})};a.DialogManager.prototype.pushDialog=function(e){var t=(this.zIndexHigh_-this.zIndexLow_)/2-1;return this.pendingDialogStack.length>=t?!1:(this.pendingDialogStack.unshift(e)===1&&this.blockDocument(),this.updateStacking(),!0)};a.DialogManager.prototype.removeDialog=function(e){var t=this.pendingDialogStack.indexOf(e);t!==-1&&(this.pendingDialogStack.splice(t,1),this.pendingDialogStack.length===0&&this.unblockDocument(),this.updateStacking())};a.dm=new a.DialogManager;a.formSubmitter=null;a.useValue=null;window.HTMLDialogElement===void 0&&(v=document.createElement("form"),v.setAttribute("method","dialog"),v.method!=="dialog"&&(s=Object.getOwnPropertyDescriptor(HTMLFormElement.prototype,"method"),s&&(S=s.get,s.get=function(){return p(this)?"dialog":S.call(this)},k=s.set,s.set=function(e){return typeof e=="string"&&e.toLowerCase()==="dialog"?this.setAttribute("method",e):k.call(this,e)},Object.defineProperty(HTMLFormElement.prototype,"method",s))),document.addEventListener("click",function(e){if(a.formSubmitter=null,a.useValue=null,!e.defaultPrevented){var t=e.target;if(!(!t||!p(t.form))){var o=t.type==="submit"&&["button","input"].indexOf(t.localName)>-1;if(!o){if(!(t.localName==="input"&&t.type==="image"))return;a.useValue=e.offsetX+","+e.offsetY}var n=f(t);!n||(a.formSubmitter=t)}}},!1),D=HTMLFormElement.prototype.submit,x=function(){if(!p(this))return D.call(this);var e=f(this);e&&e.close()},HTMLFormElement.prototype.submit=x,document.addEventListener("submit",function(e){if(!e.defaultPrevented){var t=e.target;if(!!p(t)){e.preventDefault();var o=f(t);if(!!o){var n=a.formSubmitter;n&&n.form===t?o.close(a.useValue||n.value):o.close(),a.formSubmitter=null}}}},!1));var v,s,S,k,D,x,B=a});function I(){let e=document.querySelector(".js-header"),t=document.querySelectorAll(".js-headerMenuButton");t.forEach(n=>{n.addEventListener("click",i=>{i.preventDefault(),e?.classList.toggle("is-active"),n.setAttribute("aria-expanded",String(e?.classList.contains("is-active")))})}),document.querySelector(".js-scrim")?.addEventListener("click",n=>{n.preventDefault(),e?.classList.remove("is-active"),t.forEach(i=>{i.setAttribute("aria-expanded",String(e?.classList.contains("is-active")))})})}function q(){let e=document.querySelector(".js-searchForm"),t=document.querySelector(".js-expandSearch"),o=e?.querySelector("input"),n=document.querySelector(".js-headerLogo"),i=document.querySelector(".js-headerMenuButton");t?.addEventListener("click",()=>{e?.classList.add("go-SearchForm--expanded"),n?.classList.add("go-Header-logo--hidden"),i?.classList.add("go-Header-navOpen--hidden"),o?.focus()}),document?.addEventListener("click",r=>{e?.contains(r.target)||(e?.classList.remove("go-SearchForm--expanded"),n?.classList.remove("go-Header-logo--hidden"),i?.classList.remove("go-Header-navOpen--hidden"))})}document.querySelectorAll(".js-searchModeSelect").forEach(e=>{e.addEventListener("change",t=>{let o=new URLSearchParams(window.location.search),i=Object.fromEntries(o.entries()).q;i&&(window.location.search=`q=${i}&m=${t.target.value}`)})});I();q();var m=class{constructor(t){this.el=t;this.data=t.dataset.toCopy??t.innerText,!this.data&&t.parentElement?.classList.contains("go-InputGroup")&&(this.data=(this.data||t.parentElement?.querySelector("input")?.value)??""),t.addEventListener("click",o=>this.handleCopyClick(o))}handleCopyClick(t){t.preventDefault();let o=1e3;if(!navigator.clipboard){this.showTooltipText("Unable to copy",o);return}navigator.clipboard.writeText(this.data).then(()=>{this.showTooltipText("Copied!",o)}).catch(()=>{this.showTooltipText("Unable to copy",o)})}showTooltipText(t,o){this.el.setAttribute("data-tooltip",t),setTimeout(()=>this.el.setAttribute("data-tooltip",""),o)}};var g=class{constructor(t){this.el=t;document.addEventListener("click",o=>{this.el.contains(o.target)||this.el.removeAttribute("open")})}};var y=class{constructor(t){this.el=t;this.el.addEventListener("change",o=>{let n=o.target,i=n.value;n.value.startsWith("/")||(i="/"+i),window.location.href=i})}};var b=class{constructor(t){this.el=t;!window.HTMLDialogElement&&!t.showModal&&Promise.resolve().then(()=>C()).then(({default:i})=>{i.registerDialog(t)});let o=t.id,n=document.querySelector(`[aria-controls="${o}"]`);n&&n.addEventListener("click",()=>{this.el.showModal?this.el.showModal():this.el.open=!0,t.querySelector("input")?.focus()});for(let i of this.el.querySelectorAll("[data-modal-close]"))i.addEventListener("click",()=>{this.el.close?this.el.close():this.el.open=!1})}};var u={};M(u,{func:()=>R,track:()=>w});function w(e,t,o,n){window.dataLayer??=[],typeof e=="string"?window.dataLayer.push({event:e,event_category:t,event_action:o,event_label:n}):window.dataLayer.push(e)}function R(e){window.dataLayer??=[],window.dataLayer.push(e)}var H=class{constructor(){this.handlers={},document.addEventListener("keydown",t=>this.handleKeyPress(t))}on(t,o,n,i){return this.handlers[t]??=new Set,this.handlers[t].add({description:o,callback:n,...i}),this}handleKeyPress(t){for(let o of this.handlers[t.key.toLowerCase()]??new Set){if(o.target&&o.target!==t.target)return;let n=t.target;if(!o.target&&(n?.tagName==="INPUT"||n?.tagName==="SELECT"||n?.tagName==="TEXTAREA")||n?.isContentEditable||o.withMeta&&!(t.ctrlKey||t.metaKey)||!o.withMeta&&(t.ctrlKey||t.metaKey))return;w("keypress","hotkeys",`${t.key} pressed`,o.description),o.callback(t)}}},h=new H;for(let e of document.querySelectorAll(".js-clipboard"))new m(e);for(let e of document.querySelectorAll(".js-modal"))new b(e);for(let e of document.querySelectorAll(".js-tooltip"))new g(e);for(let e of document.querySelectorAll(".js-selectNav"))new y(e);h.on("t","toggle theme",()=>{let e="dark",t=document.documentElement.getAttribute("data-theme");t==="dark"?e="light":t==="light"&&(e="auto"),document.documentElement.setAttribute("data-theme",e),document.cookie=`prefers-color-scheme=${e};path=/;max-age=31536000;`});h.on("/","focus search",e=>{let t=Array.from(document.querySelectorAll(".js-searchFocus")).pop();t&&!window.navigator.userAgent.includes("Firefox")&&(e.preventDefault(),t.focus())});h.on("y","set canonical url",()=>{let e=document.querySelector(".js-canonicalURLPath")?.dataset.canonicalUrlPath;e&&e!==""&&window.history.replaceState(null,"",e)});(function(){u.track({"gtm.start":new Date().getTime(),event:"gtm.js"})})();function A(){let e=new URLSearchParams(window.location.search),t=e.get("utm_source");if(t!=="gopls"&&t!=="godoc"&&t!=="pkggodev")return;let o=new URL(window.location.href);e.delete("utm_source"),o.search=e.toString(),window.history.replaceState(null,"",o.toString())}document.querySelector(".js-gtmID")?.dataset.gtmid&&window.dataLayer?u.func(function(){A()}):A();})();
 /*!
  * @license
  * Copyright 2019-2020 The Go Authors. All rights reserved.
diff --git a/static/frontend/frontend.js.map b/static/frontend/frontend.js.map
index c0cb6f3..e872a5b 100644
--- a/static/frontend/frontend.js.map
+++ b/static/frontend/frontend.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../third_party/dialog-polyfill/dialog-polyfill.esm.js", "../shared/header/header.ts", "../shared/clipboard/clipboard.ts", "../shared/tooltip/tooltip.ts", "../shared/outline/select.ts", "../shared/modal/modal.ts", "../shared/analytics/analytics.ts", "../shared/keyboard/keyboard.ts", "../shared/shared.ts", "frontend.ts"],
-  "sourcesContent": ["// nb. This is for IE10 and lower _only_.\nvar supportCustomEvent = window.CustomEvent;\nif (!supportCustomEvent || typeof supportCustomEvent === 'object') {\n  supportCustomEvent = function CustomEvent(event, x) {\n    x = x || {};\n    var ev = document.createEvent('CustomEvent');\n    ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);\n    return ev;\n  };\n  supportCustomEvent.prototype = window.Event.prototype;\n}\n\n/**\n * @param {Element} el to check for stacking context\n * @return {boolean} whether this el or its parents creates a stacking context\n */\nfunction createsStackingContext(el) {\n  while (el && el !== document.body) {\n    var s = window.getComputedStyle(el);\n    var invalid = function(k, ok) {\n      return !(s[k] === undefined || s[k] === ok);\n    };\n\n    if (s.opacity < 1 ||\n        invalid('zIndex', 'auto') ||\n        invalid('transform', 'none') ||\n        invalid('mixBlendMode', 'normal') ||\n        invalid('filter', 'none') ||\n        invalid('perspective', 'none') ||\n        s['isolation'] === 'isolate' ||\n        s.position === 'fixed' ||\n        s.webkitOverflowScrolling === 'touch') {\n      return true;\n    }\n    el = el.parentElement;\n  }\n  return false;\n}\n\n/**\n * Finds the nearest <dialog> from the passed element.\n *\n * @param {Element} el to search from\n * @return {HTMLDialogElement} dialog found\n */\nfunction findNearestDialog(el) {\n  while (el) {\n    if (el.localName === 'dialog') {\n      return /** @type {HTMLDialogElement} */ (el);\n    }\n    el = el.parentElement;\n  }\n  return null;\n}\n\n/**\n * Blur the specified element, as long as it's not the HTML body element.\n * This works around an IE9/10 bug - blurring the body causes Windows to\n * blur the whole application.\n *\n * @param {Element} el to blur\n */\nfunction safeBlur(el) {\n  if (el && el.blur && el !== document.body) {\n    el.blur();\n  }\n}\n\n/**\n * @param {!NodeList} nodeList to search\n * @param {Node} node to find\n * @return {boolean} whether node is inside nodeList\n */\nfunction inNodeList(nodeList, node) {\n  for (var i = 0; i < nodeList.length; ++i) {\n    if (nodeList[i] === node) {\n      return true;\n    }\n  }\n  return false;\n}\n\n/**\n * @param {HTMLFormElement} el to check\n * @return {boolean} whether this form has method=\"dialog\"\n */\nfunction isFormMethodDialog(el) {\n  if (!el || !el.hasAttribute('method')) {\n    return false;\n  }\n  return el.getAttribute('method').toLowerCase() === 'dialog';\n}\n\n/**\n * @param {!HTMLDialogElement} dialog to upgrade\n * @constructor\n */\nfunction dialogPolyfillInfo(dialog) {\n  this.dialog_ = dialog;\n  this.replacedStyleTop_ = false;\n  this.openAsModal_ = false;\n\n  // Set a11y role. Browsers that support dialog implicitly know this already.\n  if (!dialog.hasAttribute('role')) {\n    dialog.setAttribute('role', 'dialog');\n  }\n\n  dialog.show = this.show.bind(this);\n  dialog.showModal = this.showModal.bind(this);\n  dialog.close = this.close.bind(this);\n\n  if (!('returnValue' in dialog)) {\n    dialog.returnValue = '';\n  }\n\n  if ('MutationObserver' in window) {\n    var mo = new MutationObserver(this.maybeHideModal.bind(this));\n    mo.observe(dialog, {attributes: true, attributeFilter: ['open']});\n  } else {\n    // IE10 and below support. Note that DOMNodeRemoved etc fire _before_ removal. They also\n    // seem to fire even if the element was removed as part of a parent removal. Use the removed\n    // events to force downgrade (useful if removed/immediately added).\n    var removed = false;\n    var cb = function() {\n      removed ? this.downgradeModal() : this.maybeHideModal();\n      removed = false;\n    }.bind(this);\n    var timeout;\n    var delayModel = function(ev) {\n      if (ev.target !== dialog) { return; }  // not for a child element\n      var cand = 'DOMNodeRemoved';\n      removed |= (ev.type.substr(0, cand.length) === cand);\n      window.clearTimeout(timeout);\n      timeout = window.setTimeout(cb, 0);\n    };\n    ['DOMAttrModified', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument'].forEach(function(name) {\n      dialog.addEventListener(name, delayModel);\n    });\n  }\n  // Note that the DOM is observed inside DialogManager while any dialog\n  // is being displayed as a modal, to catch modal removal from the DOM.\n\n  Object.defineProperty(dialog, 'open', {\n    set: this.setOpen.bind(this),\n    get: dialog.hasAttribute.bind(dialog, 'open')\n  });\n\n  this.backdrop_ = document.createElement('div');\n  this.backdrop_.className = 'backdrop';\n  this.backdrop_.addEventListener('click', this.backdropClick_.bind(this));\n}\n\ndialogPolyfillInfo.prototype = {\n\n  get dialog() {\n    return this.dialog_;\n  },\n\n  /**\n   * Maybe remove this dialog from the modal top layer. This is called when\n   * a modal dialog may no longer be tenable, e.g., when the dialog is no\n   * longer open or is no longer part of the DOM.\n   */\n  maybeHideModal: function() {\n    if (this.dialog_.hasAttribute('open') && document.body.contains(this.dialog_)) { return; }\n    this.downgradeModal();\n  },\n\n  /**\n   * Remove this dialog from the modal top layer, leaving it as a non-modal.\n   */\n  downgradeModal: function() {\n    if (!this.openAsModal_) { return; }\n    this.openAsModal_ = false;\n    this.dialog_.style.zIndex = '';\n\n    // This won't match the native <dialog> exactly because if the user set top on a centered\n    // polyfill dialog, that top gets thrown away when the dialog is closed. Not sure it's\n    // possible to polyfill this perfectly.\n    if (this.replacedStyleTop_) {\n      this.dialog_.style.top = '';\n      this.replacedStyleTop_ = false;\n    }\n\n    // Clear the backdrop and remove from the manager.\n    this.backdrop_.parentNode && this.backdrop_.parentNode.removeChild(this.backdrop_);\n    dialogPolyfill.dm.removeDialog(this);\n  },\n\n  /**\n   * @param {boolean} value whether to open or close this dialog\n   */\n  setOpen: function(value) {\n    if (value) {\n      this.dialog_.hasAttribute('open') || this.dialog_.setAttribute('open', '');\n    } else {\n      this.dialog_.removeAttribute('open');\n      this.maybeHideModal();  // nb. redundant with MutationObserver\n    }\n  },\n\n  /**\n   * Handles clicks on the fake .backdrop element, redirecting them as if\n   * they were on the dialog itself.\n   *\n   * @param {!Event} e to redirect\n   */\n  backdropClick_: function(e) {\n    if (!this.dialog_.hasAttribute('tabindex')) {\n      // Clicking on the backdrop should move the implicit cursor, even if dialog cannot be\n      // focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this\n      // would not be needed - clicks would move the implicit cursor there.\n      var fake = document.createElement('div');\n      this.dialog_.insertBefore(fake, this.dialog_.firstChild);\n      fake.tabIndex = -1;\n      fake.focus();\n      this.dialog_.removeChild(fake);\n    } else {\n      this.dialog_.focus();\n    }\n\n    var redirectedEvent = document.createEvent('MouseEvents');\n    redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window,\n        e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey,\n        e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);\n    this.dialog_.dispatchEvent(redirectedEvent);\n    e.stopPropagation();\n  },\n\n  /**\n   * Focuses on the first focusable element within the dialog. This will always blur the current\n   * focus, even if nothing within the dialog is found.\n   */\n  focus_: function() {\n    // Find element with `autofocus` attribute, or fall back to the first form/tabindex control.\n    var target = this.dialog_.querySelector('[autofocus]:not([disabled])');\n    if (!target && this.dialog_.tabIndex >= 0) {\n      target = this.dialog_;\n    }\n    if (!target) {\n      // Note that this is 'any focusable area'. This list is probably not exhaustive, but the\n      // alternative involves stepping through and trying to focus everything.\n      var opts = ['button', 'input', 'keygen', 'select', 'textarea'];\n      var query = opts.map(function(el) {\n        return el + ':not([disabled])';\n      });\n      // TODO(samthor): tabindex values that are not numeric are not focusable.\n      query.push('[tabindex]:not([disabled]):not([tabindex=\"\"])');  // tabindex != \"\", not disabled\n      target = this.dialog_.querySelector(query.join(', '));\n    }\n    safeBlur(document.activeElement);\n    target && target.focus();\n  },\n\n  /**\n   * Sets the zIndex for the backdrop and dialog.\n   *\n   * @param {number} dialogZ\n   * @param {number} backdropZ\n   */\n  updateZIndex: function(dialogZ, backdropZ) {\n    if (dialogZ < backdropZ) {\n      throw new Error('dialogZ should never be < backdropZ');\n    }\n    this.dialog_.style.zIndex = dialogZ;\n    this.backdrop_.style.zIndex = backdropZ;\n  },\n\n  /**\n   * Shows the dialog. If the dialog is already open, this does nothing.\n   */\n  show: function() {\n    if (!this.dialog_.open) {\n      this.setOpen(true);\n      this.focus_();\n    }\n  },\n\n  /**\n   * Show this dialog modally.\n   */\n  showModal: function() {\n    if (this.dialog_.hasAttribute('open')) {\n      throw new Error('Failed to execute \\'showModal\\' on dialog: The element is already open, and therefore cannot be opened modally.');\n    }\n    if (!document.body.contains(this.dialog_)) {\n      throw new Error('Failed to execute \\'showModal\\' on dialog: The element is not in a Document.');\n    }\n    if (!dialogPolyfill.dm.pushDialog(this)) {\n      throw new Error('Failed to execute \\'showModal\\' on dialog: There are too many open modal dialogs.');\n    }\n\n    if (createsStackingContext(this.dialog_.parentElement)) {\n      console.warn('A dialog is being shown inside a stacking context. ' +\n          'This may cause it to be unusable. For more information, see this link: ' +\n          'https://github.com/GoogleChrome/dialog-polyfill/#stacking-context');\n    }\n\n    this.setOpen(true);\n    this.openAsModal_ = true;\n\n    // Optionally center vertically, relative to the current viewport.\n    if (dialogPolyfill.needsCentering(this.dialog_)) {\n      dialogPolyfill.reposition(this.dialog_);\n      this.replacedStyleTop_ = true;\n    } else {\n      this.replacedStyleTop_ = false;\n    }\n\n    // Insert backdrop.\n    this.dialog_.parentNode.insertBefore(this.backdrop_, this.dialog_.nextSibling);\n\n    // Focus on whatever inside the dialog.\n    this.focus_();\n  },\n\n  /**\n   * Closes this HTMLDialogElement. This is optional vs clearing the open\n   * attribute, however this fires a 'close' event.\n   *\n   * @param {string=} opt_returnValue to use as the returnValue\n   */\n  close: function(opt_returnValue) {\n    if (!this.dialog_.hasAttribute('open')) {\n      throw new Error('Failed to execute \\'close\\' on dialog: The element does not have an \\'open\\' attribute, and therefore cannot be closed.');\n    }\n    this.setOpen(false);\n\n    // Leave returnValue untouched in case it was set directly on the element\n    if (opt_returnValue !== undefined) {\n      this.dialog_.returnValue = opt_returnValue;\n    }\n\n    // Triggering \"close\" event for any attached listeners on the <dialog>.\n    var closeEvent = new supportCustomEvent('close', {\n      bubbles: false,\n      cancelable: false\n    });\n    this.dialog_.dispatchEvent(closeEvent);\n  }\n\n};\n\nvar dialogPolyfill = {};\n\ndialogPolyfill.reposition = function(element) {\n  var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;\n  var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;\n  element.style.top = Math.max(scrollTop, topValue) + 'px';\n};\n\ndialogPolyfill.isInlinePositionSetByStylesheet = function(element) {\n  for (var i = 0; i < document.styleSheets.length; ++i) {\n    var styleSheet = document.styleSheets[i];\n    var cssRules = null;\n    // Some browsers throw on cssRules.\n    try {\n      cssRules = styleSheet.cssRules;\n    } catch (e) {}\n    if (!cssRules) { continue; }\n    for (var j = 0; j < cssRules.length; ++j) {\n      var rule = cssRules[j];\n      var selectedNodes = null;\n      // Ignore errors on invalid selector texts.\n      try {\n        selectedNodes = document.querySelectorAll(rule.selectorText);\n      } catch(e) {}\n      if (!selectedNodes || !inNodeList(selectedNodes, element)) {\n        continue;\n      }\n      var cssTop = rule.style.getPropertyValue('top');\n      var cssBottom = rule.style.getPropertyValue('bottom');\n      if ((cssTop && cssTop !== 'auto') || (cssBottom && cssBottom !== 'auto')) {\n        return true;\n      }\n    }\n  }\n  return false;\n};\n\ndialogPolyfill.needsCentering = function(dialog) {\n  var computedStyle = window.getComputedStyle(dialog);\n  if (computedStyle.position !== 'absolute') {\n    return false;\n  }\n\n  // We must determine whether the top/bottom specified value is non-auto.  In\n  // WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but\n  // Firefox returns the used value. So we do this crazy thing instead: check\n  // the inline style and then go through CSS rules.\n  if ((dialog.style.top !== 'auto' && dialog.style.top !== '') ||\n      (dialog.style.bottom !== 'auto' && dialog.style.bottom !== '')) {\n    return false;\n  }\n  return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);\n};\n\n/**\n * @param {!Element} element to force upgrade\n */\ndialogPolyfill.forceRegisterDialog = function(element) {\n  if (window.HTMLDialogElement || element.showModal) {\n    console.warn('This browser already supports <dialog>, the polyfill ' +\n        'may not work correctly', element);\n  }\n  if (element.localName !== 'dialog') {\n    throw new Error('Failed to register dialog: The element is not a dialog.');\n  }\n  new dialogPolyfillInfo(/** @type {!HTMLDialogElement} */ (element));\n};\n\n/**\n * @param {!Element} element to upgrade, if necessary\n */\ndialogPolyfill.registerDialog = function(element) {\n  if (!element.showModal) {\n    dialogPolyfill.forceRegisterDialog(element);\n  }\n};\n\n/**\n * @constructor\n */\ndialogPolyfill.DialogManager = function() {\n  /** @type {!Array<!dialogPolyfillInfo>} */\n  this.pendingDialogStack = [];\n\n  var checkDOM = this.checkDOM_.bind(this);\n\n  // The overlay is used to simulate how a modal dialog blocks the document.\n  // The blocking dialog is positioned on top of the overlay, and the rest of\n  // the dialogs on the pending dialog stack are positioned below it. In the\n  // actual implementation, the modal dialog stacking is controlled by the\n  // top layer, where z-index has no effect.\n  this.overlay = document.createElement('div');\n  this.overlay.className = '_dialog_overlay';\n  this.overlay.addEventListener('click', function(e) {\n    this.forwardTab_ = undefined;\n    e.stopPropagation();\n    checkDOM([]);  // sanity-check DOM\n  }.bind(this));\n\n  this.handleKey_ = this.handleKey_.bind(this);\n  this.handleFocus_ = this.handleFocus_.bind(this);\n\n  this.zIndexLow_ = 100000;\n  this.zIndexHigh_ = 100000 + 150;\n\n  this.forwardTab_ = undefined;\n\n  if ('MutationObserver' in window) {\n    this.mo_ = new MutationObserver(function(records) {\n      var removed = [];\n      records.forEach(function(rec) {\n        for (var i = 0, c; c = rec.removedNodes[i]; ++i) {\n          if (!(c instanceof Element)) {\n            continue;\n          } else if (c.localName === 'dialog') {\n            removed.push(c);\n          }\n          removed = removed.concat(c.querySelectorAll('dialog'));\n        }\n      });\n      removed.length && checkDOM(removed);\n    });\n  }\n};\n\n/**\n * Called on the first modal dialog being shown. Adds the overlay and related\n * handlers.\n */\ndialogPolyfill.DialogManager.prototype.blockDocument = function() {\n  document.documentElement.addEventListener('focus', this.handleFocus_, true);\n  document.addEventListener('keydown', this.handleKey_);\n  this.mo_ && this.mo_.observe(document, {childList: true, subtree: true});\n};\n\n/**\n * Called on the first modal dialog being removed, i.e., when no more modal\n * dialogs are visible.\n */\ndialogPolyfill.DialogManager.prototype.unblockDocument = function() {\n  document.documentElement.removeEventListener('focus', this.handleFocus_, true);\n  document.removeEventListener('keydown', this.handleKey_);\n  this.mo_ && this.mo_.disconnect();\n};\n\n/**\n * Updates the stacking of all known dialogs.\n */\ndialogPolyfill.DialogManager.prototype.updateStacking = function() {\n  var zIndex = this.zIndexHigh_;\n\n  for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {\n    dpi.updateZIndex(--zIndex, --zIndex);\n    if (i === 0) {\n      this.overlay.style.zIndex = --zIndex;\n    }\n  }\n\n  // Make the overlay a sibling of the dialog itself.\n  var last = this.pendingDialogStack[0];\n  if (last) {\n    var p = last.dialog.parentNode || document.body;\n    p.appendChild(this.overlay);\n  } else if (this.overlay.parentNode) {\n    this.overlay.parentNode.removeChild(this.overlay);\n  }\n};\n\n/**\n * @param {Element} candidate to check if contained or is the top-most modal dialog\n * @return {boolean} whether candidate is contained in top dialog\n */\ndialogPolyfill.DialogManager.prototype.containedByTopDialog_ = function(candidate) {\n  while (candidate = findNearestDialog(candidate)) {\n    for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {\n      if (dpi.dialog === candidate) {\n        return i === 0;  // only valid if top-most\n      }\n    }\n    candidate = candidate.parentElement;\n  }\n  return false;\n};\n\ndialogPolyfill.DialogManager.prototype.handleFocus_ = function(event) {\n  if (this.containedByTopDialog_(event.target)) { return; }\n\n  if (document.activeElement === document.documentElement) { return; }\n\n  event.preventDefault();\n  event.stopPropagation();\n  safeBlur(/** @type {Element} */ (event.target));\n\n  if (this.forwardTab_ === undefined) { return; }  // move focus only from a tab key\n\n  var dpi = this.pendingDialogStack[0];\n  var dialog = dpi.dialog;\n  var position = dialog.compareDocumentPosition(event.target);\n  if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n    if (this.forwardTab_) {\n      // forward\n      dpi.focus_();\n    } else if (event.target !== document.documentElement) {\n      // backwards if we're not already focused on <html>\n      document.documentElement.focus();\n    }\n  }\n\n  return false;\n};\n\ndialogPolyfill.DialogManager.prototype.handleKey_ = function(event) {\n  this.forwardTab_ = undefined;\n  if (event.keyCode === 27) {\n    event.preventDefault();\n    event.stopPropagation();\n    var cancelEvent = new supportCustomEvent('cancel', {\n      bubbles: false,\n      cancelable: true\n    });\n    var dpi = this.pendingDialogStack[0];\n    if (dpi && dpi.dialog.dispatchEvent(cancelEvent)) {\n      dpi.dialog.close();\n    }\n  } else if (event.keyCode === 9) {\n    this.forwardTab_ = !event.shiftKey;\n  }\n};\n\n/**\n * Finds and downgrades any known modal dialogs that are no longer displayed. Dialogs that are\n * removed and immediately readded don't stay modal, they become normal.\n *\n * @param {!Array<!HTMLDialogElement>} removed that have definitely been removed\n */\ndialogPolyfill.DialogManager.prototype.checkDOM_ = function(removed) {\n  // This operates on a clone because it may cause it to change. Each change also calls\n  // updateStacking, which only actually needs to happen once. But who removes many modal dialogs\n  // at a time?!\n  var clone = this.pendingDialogStack.slice();\n  clone.forEach(function(dpi) {\n    if (removed.indexOf(dpi.dialog) !== -1) {\n      dpi.downgradeModal();\n    } else {\n      dpi.maybeHideModal();\n    }\n  });\n};\n\n/**\n * @param {!dialogPolyfillInfo} dpi\n * @return {boolean} whether the dialog was allowed\n */\ndialogPolyfill.DialogManager.prototype.pushDialog = function(dpi) {\n  var allowed = (this.zIndexHigh_ - this.zIndexLow_) / 2 - 1;\n  if (this.pendingDialogStack.length >= allowed) {\n    return false;\n  }\n  if (this.pendingDialogStack.unshift(dpi) === 1) {\n    this.blockDocument();\n  }\n  this.updateStacking();\n  return true;\n};\n\n/**\n * @param {!dialogPolyfillInfo} dpi\n */\ndialogPolyfill.DialogManager.prototype.removeDialog = function(dpi) {\n  var index = this.pendingDialogStack.indexOf(dpi);\n  if (index === -1) { return; }\n\n  this.pendingDialogStack.splice(index, 1);\n  if (this.pendingDialogStack.length === 0) {\n    this.unblockDocument();\n  }\n  this.updateStacking();\n};\n\ndialogPolyfill.dm = new dialogPolyfill.DialogManager();\ndialogPolyfill.formSubmitter = null;\ndialogPolyfill.useValue = null;\n\n/**\n * Installs global handlers, such as click listers and native method overrides. These are needed\n * even if a no dialog is registered, as they deal with <form method=\"dialog\">.\n */\nif (window.HTMLDialogElement === undefined) {\n\n  /**\n   * If HTMLFormElement translates method=\"DIALOG\" into 'get', then replace the descriptor with\n   * one that returns the correct value.\n   */\n  var testForm = document.createElement('form');\n  testForm.setAttribute('method', 'dialog');\n  if (testForm.method !== 'dialog') {\n    var methodDescriptor = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'method');\n    if (methodDescriptor) {\n      // nb. Some older iOS and older PhantomJS fail to return the descriptor. Don't do anything\n      // and don't bother to update the element.\n      var realGet = methodDescriptor.get;\n      methodDescriptor.get = function() {\n        if (isFormMethodDialog(this)) {\n          return 'dialog';\n        }\n        return realGet.call(this);\n      };\n      var realSet = methodDescriptor.set;\n      methodDescriptor.set = function(v) {\n        if (typeof v === 'string' && v.toLowerCase() === 'dialog') {\n          return this.setAttribute('method', v);\n        }\n        return realSet.call(this, v);\n      };\n      Object.defineProperty(HTMLFormElement.prototype, 'method', methodDescriptor);\n    }\n  }\n\n  /**\n   * Global 'click' handler, to capture the <input type=\"submit\"> or <button> element which has\n   * submitted a <form method=\"dialog\">. Needed as Safari and others don't report this inside\n   * document.activeElement.\n   */\n  document.addEventListener('click', function(ev) {\n    dialogPolyfill.formSubmitter = null;\n    dialogPolyfill.useValue = null;\n    if (ev.defaultPrevented) { return; }  // e.g. a submit which prevents default submission\n\n    var target = /** @type {Element} */ (ev.target);\n    if (!target || !isFormMethodDialog(target.form)) { return; }\n\n    var valid = (target.type === 'submit' && ['button', 'input'].indexOf(target.localName) > -1);\n    if (!valid) {\n      if (!(target.localName === 'input' && target.type === 'image')) { return; }\n      // this is a <input type=\"image\">, which can submit forms\n      dialogPolyfill.useValue = ev.offsetX + ',' + ev.offsetY;\n    }\n\n    var dialog = findNearestDialog(target);\n    if (!dialog) { return; }\n\n    dialogPolyfill.formSubmitter = target;\n\n  }, false);\n\n  /**\n   * Replace the native HTMLFormElement.submit() method, as it won't fire the\n   * submit event and give us a chance to respond.\n   */\n  var nativeFormSubmit = HTMLFormElement.prototype.submit;\n  var replacementFormSubmit = function () {\n    if (!isFormMethodDialog(this)) {\n      return nativeFormSubmit.call(this);\n    }\n    var dialog = findNearestDialog(this);\n    dialog && dialog.close();\n  };\n  HTMLFormElement.prototype.submit = replacementFormSubmit;\n\n  /**\n   * Global form 'dialog' method handler. Closes a dialog correctly on submit\n   * and possibly sets its return value.\n   */\n  document.addEventListener('submit', function(ev) {\n    if (ev.defaultPrevented) { return; }  // e.g. a submit which prevents default submission\n\n    var form = /** @type {HTMLFormElement} */ (ev.target);\n    if (!isFormMethodDialog(form)) { return; }\n    ev.preventDefault();\n\n    var dialog = findNearestDialog(form);\n    if (!dialog) { return; }\n\n    // Forms can only be submitted via .submit() or a click (?), but anyway: sanity-check that\n    // the submitter is correct before using its value as .returnValue.\n    var s = dialogPolyfill.formSubmitter;\n    if (s && s.form === form) {\n      dialog.close(dialogPolyfill.useValue || s.value);\n    } else {\n      dialog.close();\n    }\n    dialogPolyfill.formSubmitter = null;\n\n  }, false);\n}\n\nexport default dialogPolyfill;\n", "function registerHeaderListeners() {\n  const header = document.querySelector('.js-header');\n  const menuButtons = document.querySelectorAll('.js-headerMenuButton');\n  menuButtons.forEach(button => {\n    button.addEventListener('click', e => {\n      e.preventDefault();\n      header?.classList.toggle('is-active');\n      button.setAttribute('aria-expanded', String(header?.classList.contains('is-active')));\n    });\n  });\n\n  const scrim = document.querySelector('.js-scrim');\n  scrim?.addEventListener('click', e => {\n    e.preventDefault();\n    header?.classList.remove('is-active');\n    menuButtons.forEach(button => {\n      button.setAttribute('aria-expanded', String(header?.classList.contains('is-active')));\n    });\n  });\n}\n\nfunction registerSearchFormListeners() {\n  const searchForm = document.querySelector('.js-searchForm');\n  const expandSearch = document.querySelector('.js-expandSearch');\n  const input = searchForm?.querySelector('input');\n  const headerLogo = document.querySelector('.js-headerLogo');\n  const menuButton = document.querySelector('.js-headerMenuButton');\n  expandSearch?.addEventListener('click', () => {\n    searchForm?.classList.add('go-SearchForm--expanded');\n    headerLogo?.classList.add('go-Header-logo--hidden');\n    menuButton?.classList.add('go-Header-navOpen--hidden');\n    input?.focus();\n  });\n  document?.addEventListener('click', e => {\n    if (!searchForm?.contains(e.target as Node)) {\n      searchForm?.classList.remove('go-SearchForm--expanded');\n      headerLogo?.classList.remove('go-Header-logo--hidden');\n      menuButton?.classList.remove('go-Header-navOpen--hidden');\n    }\n  });\n}\n\n/**\n * Listen for changes in the search dropdown.\n *\n * TODO(https://golang.org/issue/44142): Fix this flow:\n * A user will likely expect to submit the search again after selecting the\n * type of search. The change event should trigger a form submission, so that the\n * search event is still captured in analytics without a manual instrumentation.\n */\ndocument.querySelectorAll('.js-searchModeSelect').forEach(el => {\n  el.addEventListener('change', e => {\n    const urlSearchParams = new URLSearchParams(window.location.search);\n    const params = Object.fromEntries(urlSearchParams.entries());\n    const query = params['q'];\n    if (query) {\n      window.location.search = `q=${query}&m=${(e.target as HTMLSelectElement).value}`;\n    }\n  });\n});\n\nregisterHeaderListeners();\nregisterSearchFormListeners();\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * This class decorates an element to copy arbitrary data attached via a data-\n * attribute to the clipboard.\n */\nexport class ClipboardController {\n  /**\n   * The data to be copied to the clipboard.\n   */\n  private data: string;\n\n  /**\n   * @param el The element that will trigger copying text to the clipboard. The text is\n   * expected to be within its data-to-copy attribute.\n   */\n  constructor(private el: HTMLButtonElement) {\n    this.data = el.dataset['toCopy'] ?? el.innerText;\n    // if data-to-copy is empty and the button is part of an input group\n    // capture the value of the input.\n    if (!this.data && el.parentElement?.classList.contains('go-InputGroup')) {\n      this.data = (this.data || el.parentElement?.querySelector('input')?.value) ?? '';\n    }\n    el.addEventListener('click', e => this.handleCopyClick(e));\n  }\n\n  /**\n   * Handles when the primary element is clicked.\n   */\n  handleCopyClick(e: MouseEvent): void {\n    e.preventDefault();\n    const TOOLTIP_SHOW_DURATION_MS = 1000;\n\n    // This API is not available on iOS.\n    if (!navigator.clipboard) {\n      this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n      return;\n    }\n    navigator.clipboard\n      .writeText(this.data)\n      .then(() => {\n        this.showTooltipText('Copied!', TOOLTIP_SHOW_DURATION_MS);\n      })\n      .catch(() => {\n        this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n      });\n  }\n\n  /**\n   * Shows the given text in a tooltip for a specified amount of time, in milliseconds.\n   */\n  showTooltipText(text: string, durationMs: number): void {\n    this.el.setAttribute('data-tooltip', text);\n    setTimeout(() => this.el.setAttribute('data-tooltip', ''), durationMs);\n  }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ToolTipController handles closing tooltips on external clicks.\n */\nexport class ToolTipController {\n  constructor(private el: HTMLDetailsElement) {\n    document.addEventListener('click', e => {\n      const insideTooltip = this.el.contains(e.target as Element);\n      if (!insideTooltip) {\n        this.el.removeAttribute('open');\n      }\n    });\n  }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n  constructor(private el: Element) {\n    this.el.addEventListener('change', e => {\n      const target = e.target as HTMLSelectElement;\n      let href = target.value;\n      if (!target.value.startsWith('/')) {\n        href = '/' + href;\n      }\n      window.location.href = href;\n    });\n  }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n  const label = document.createElement('label');\n  label.classList.add('go-Label');\n  label.setAttribute('aria-label', 'Menu');\n  const select = document.createElement('select');\n  select.classList.add('go-Select', 'js-selectNav');\n  label.appendChild(select);\n  const outline = document.createElement('optgroup');\n  outline.label = 'Outline';\n  select.appendChild(outline);\n  const groupMap: Record<string, HTMLOptGroupElement> = {};\n  let group: HTMLOptGroupElement;\n  for (const t of tree.treeitems) {\n    if (Number(t.depth) > 4) continue;\n    if (t.groupTreeitem) {\n      group = groupMap[t.groupTreeitem.label];\n      if (!group) {\n        group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n        group.label = t.groupTreeitem.label;\n        select.appendChild(group);\n      }\n    } else {\n      group = outline;\n    }\n    const o = document.createElement('option');\n    o.label = t.label;\n    o.textContent = t.label;\n    o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n    group.appendChild(o);\n  }\n  tree.addObserver(t => {\n    const value = select.querySelector<HTMLOptionElement>(`[label=\"${t.label}\"]`)?.value;\n    if (value) {\n      select.value = value;\n    }\n  }, 50);\n  return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ModalController registers a dialog element with the polyfill if\n * necessary for the current browser, add adds event listeners to\n * close and open modals.\n */\nexport class ModalController {\n  constructor(private el: HTMLDialogElement) {\n    // Only load the dialog polyfill if necessary for the environment.\n    if (!window.HTMLDialogElement && !el.showModal) {\n      import('../../../third_party/dialog-polyfill/dialog-polyfill.esm.js').then(\n        ({ default: polyfill }) => {\n          polyfill.registerDialog(el);\n        }\n      );\n    }\n    const id = el.id;\n    const button = document.querySelector<HTMLButtonElement>(`[aria-controls=\"${id}\"]`);\n    if (button) {\n      button.addEventListener('click', () => {\n        if (this.el.showModal) {\n          this.el.showModal();\n        } else {\n          this.el.open = true;\n        }\n        el.querySelector('input')?.focus();\n      });\n    }\n    for (const close of this.el.querySelectorAll<HTMLButtonElement>('[data-modal-close]')) {\n      close.addEventListener('click', () => {\n        if (this.el.close) {\n          this.el.close();\n        } else {\n          this.el.open = false;\n        }\n      });\n    }\n  }\n}\n", "interface TagManagerEvent {\n  /**\n   * event is the name of the event, used to filter events in\n   * Google Analytics.\n   */\n  event: string;\n\n  /**\n   * event_category is a name that you supply as a way to group objects\n   * that to analyze. Typically, you will use the same category name\n   * multiple times over related UI elements (buttons, links, etc).\n   */\n  event_category?: string;\n\n  /**\n   * event_action is used to name the type of event or interaction you\n   * want to measure for a particular web object. For example, with a\n   * single \"form\" category, you can analyze a number of specific events\n   * with this parameter, such as: form entered, form submitted.\n   */\n  event_action?: string;\n\n  /**\n   * event_label provide additional information for events that you want\n   * to analyze, such as the text label of a link.\n   */\n  event_label?: string;\n\n  /**\n   * gtm.start is used to initialize Google Tag Manager.\n   */\n  'gtm.start'?: number;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ndeclare global {\n  interface Window {\n    dataLayer?: (TagManagerEvent | VoidFunction)[];\n    ga?: unknown;\n  }\n}\n\n/**\n * track sends events to Google Tag Manager.\n */\nexport function track(\n  event: string | TagManagerEvent,\n  category?: string,\n  action?: string,\n  label?: string\n): void {\n  window.dataLayer ??= [];\n  if (typeof event === 'string') {\n    window.dataLayer.push({\n      event,\n      event_category: category,\n      event_action: action,\n      event_label: label,\n    });\n  } else {\n    window.dataLayer.push(event);\n  }\n}\n\n/**\n * func adds functions to run sequentionally after\n * Google Tag Manager is ready.\n */\nexport function func(fn: () => void): void {\n  window.dataLayer ??= [];\n  window.dataLayer.push(fn);\n}\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { track } from '../analytics/analytics';\n\n/**\n * Options are keyhandler callback options.\n */\ninterface Options {\n  /**\n   * target is the element the key event should filter on. The\n   * default target is the document.\n   */\n  target?: Element;\n\n  /**\n   * withMeta specifies if the event callback should fire when\n   * the key is pressed with a meta key (ctrl, alt, etc). By\n   * default meta keypresses are ignored.\n   */\n  withMeta?: boolean;\n}\n\n/**\n * KeyHandler is the config for a keyboard event callback.\n */\ninterface KeyHandler extends Options {\n  description: string;\n  callback: (e: KeyboardEvent) => void;\n}\n\n/**\n * KeyboardController controls event callbacks for sitewide\n * keyboard events. Multiple callbacks can be registered for\n * a single key and by default the controller ignores events\n * for text input targets.\n */\nclass KeyboardController {\n  handlers: Record<string, Set<KeyHandler>>;\n\n  constructor() {\n    this.handlers = {};\n    document.addEventListener('keydown', e => this.handleKeyPress(e));\n  }\n\n  /**\n   * on registers keyboard event callbacks.\n   * @param key the key to register.\n   * @param description name of the event.\n   * @param callback event callback.\n   * @param options set target and withMeta options to override the default behaviors.\n   */\n  on(key: string, description: string, callback: (e: KeyboardEvent) => void, options?: Options) {\n    this.handlers[key] ??= new Set();\n    this.handlers[key].add({ description, callback, ...options });\n    return this;\n  }\n\n  private handleKeyPress(e: KeyboardEvent) {\n    for (const handler of this.handlers[e.key] ?? new Set()) {\n      if (handler.target && handler.target !== e.target) {\n        return;\n      }\n      const t = e.target as HTMLElement | null;\n      if (\n        !handler.target &&\n        (t?.tagName === 'INPUT' || t?.tagName === 'SELECT' || t?.tagName === 'TEXTAREA')\n      ) {\n        return;\n      }\n      if (t?.isContentEditable) {\n        return;\n      }\n      if (\n        (handler.withMeta && !(e.ctrlKey || e.metaKey)) ||\n        (!handler.withMeta && (e.ctrlKey || e.metaKey))\n      ) {\n        return;\n      }\n      track('keypress', 'hotkeys', `${e.key} pressed`, handler.description);\n      handler.callback(e);\n    }\n  }\n}\n\nexport const keyboard = new KeyboardController();\n", "/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport './header/header';\nimport { ClipboardController } from './clipboard/clipboard';\nimport { ToolTipController } from './tooltip/tooltip';\nimport { SelectNavController } from './outline/select';\nimport { ModalController } from './modal/modal';\n\nexport { keyboard } from './keyboard/keyboard';\nexport * as analytics from './analytics/analytics';\n\nfor (const el of document.querySelectorAll<HTMLButtonElement>('.js-clipboard')) {\n  new ClipboardController(el);\n}\n\nfor (const el of document.querySelectorAll<HTMLDialogElement>('.js-modal')) {\n  new ModalController(el);\n}\n\nfor (const t of document.querySelectorAll<HTMLDetailsElement>('.js-tooltip')) {\n  new ToolTipController(t);\n}\n\nfor (const el of document.querySelectorAll<HTMLSelectElement>('.js-selectNav')) {\n  new SelectNavController(el);\n}\n", "/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { analytics, keyboard } from '../shared/shared';\n\n// Temporary shortcut for testing out the dark theme.\nkeyboard.on('t', 'toggle theme', () => {\n  let nextTheme = 'dark';\n  const theme = document.documentElement.getAttribute('data-theme');\n  if (theme === 'dark') {\n    nextTheme = 'light';\n  } else if (theme === 'light') {\n    nextTheme = 'auto';\n  }\n  document.documentElement.setAttribute('data-theme', nextTheme);\n  document.cookie = `prefers-color-scheme=${nextTheme};path=/;max-age=31536000;`;\n});\n\n// Pressing '/' focuses the search box\nkeyboard.on('/', 'focus search', e => {\n  const searchInput = Array.from(\n    document.querySelectorAll<HTMLInputElement>('.js-searchFocus')\n  ).pop();\n  // Favoring the Firefox quick find feature over search input\n  // focus. See: https://github.com/golang/go/issues/41093.\n  if (searchInput && !window.navigator.userAgent.includes('Firefox')) {\n    e.preventDefault();\n    searchInput.focus();\n  }\n});\n\n// Pressing 'y' changes the browser URL to the canonical URL\n// without triggering a reload.\nkeyboard.on('y', 'set canonical url', () => {\n  const canonicalURLPath = document.querySelector<HTMLDivElement>('.js-canonicalURLPath')?.dataset[\n    'canonicalUrlPath'\n  ];\n  if (canonicalURLPath && canonicalURLPath !== '') {\n    window.history.replaceState(null, '', canonicalURLPath);\n  }\n});\n\n/**\n * setupGoogleTagManager intializes Google Tag Manager.\n */\n(function setupGoogleTagManager() {\n  analytics.track({\n    'gtm.start': new Date().getTime(),\n    event: 'gtm.js',\n  });\n})();\n\n/**\n * removeUTMSource removes the utm_source GET parameter if present.\n * This is done using JavaScript, so that the utm_source is still\n * captured by Google Analytics.\n */\nfunction removeUTMSource() {\n  const urlParams = new URLSearchParams(window.location.search);\n  const utmSource = urlParams.get('utm_source');\n  if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {\n    return;\n  }\n\n  /** Strip the utm_source query parameter and replace the URL. **/\n  const newURL = new URL(window.location.href);\n  urlParams.delete('utm_source');\n  newURL.search = urlParams.toString();\n  window.history.replaceState(null, '', newURL.toString());\n}\n\nif (document.querySelector<HTMLElement>('.js-gtmID')?.dataset.gtmid && window.dataLayer) {\n  analytics.func(function () {\n    removeUTMSource();\n  });\n} else {\n  removeUTMSource();\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["// nb. This is for IE10 and lower _only_.\nvar supportCustomEvent = window.CustomEvent;\nif (!supportCustomEvent || typeof supportCustomEvent === 'object') {\n  supportCustomEvent = function CustomEvent(event, x) {\n    x = x || {};\n    var ev = document.createEvent('CustomEvent');\n    ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);\n    return ev;\n  };\n  supportCustomEvent.prototype = window.Event.prototype;\n}\n\n/**\n * @param {Element} el to check for stacking context\n * @return {boolean} whether this el or its parents creates a stacking context\n */\nfunction createsStackingContext(el) {\n  while (el && el !== document.body) {\n    var s = window.getComputedStyle(el);\n    var invalid = function(k, ok) {\n      return !(s[k] === undefined || s[k] === ok);\n    };\n\n    if (s.opacity < 1 ||\n        invalid('zIndex', 'auto') ||\n        invalid('transform', 'none') ||\n        invalid('mixBlendMode', 'normal') ||\n        invalid('filter', 'none') ||\n        invalid('perspective', 'none') ||\n        s['isolation'] === 'isolate' ||\n        s.position === 'fixed' ||\n        s.webkitOverflowScrolling === 'touch') {\n      return true;\n    }\n    el = el.parentElement;\n  }\n  return false;\n}\n\n/**\n * Finds the nearest <dialog> from the passed element.\n *\n * @param {Element} el to search from\n * @return {HTMLDialogElement} dialog found\n */\nfunction findNearestDialog(el) {\n  while (el) {\n    if (el.localName === 'dialog') {\n      return /** @type {HTMLDialogElement} */ (el);\n    }\n    el = el.parentElement;\n  }\n  return null;\n}\n\n/**\n * Blur the specified element, as long as it's not the HTML body element.\n * This works around an IE9/10 bug - blurring the body causes Windows to\n * blur the whole application.\n *\n * @param {Element} el to blur\n */\nfunction safeBlur(el) {\n  if (el && el.blur && el !== document.body) {\n    el.blur();\n  }\n}\n\n/**\n * @param {!NodeList} nodeList to search\n * @param {Node} node to find\n * @return {boolean} whether node is inside nodeList\n */\nfunction inNodeList(nodeList, node) {\n  for (var i = 0; i < nodeList.length; ++i) {\n    if (nodeList[i] === node) {\n      return true;\n    }\n  }\n  return false;\n}\n\n/**\n * @param {HTMLFormElement} el to check\n * @return {boolean} whether this form has method=\"dialog\"\n */\nfunction isFormMethodDialog(el) {\n  if (!el || !el.hasAttribute('method')) {\n    return false;\n  }\n  return el.getAttribute('method').toLowerCase() === 'dialog';\n}\n\n/**\n * @param {!HTMLDialogElement} dialog to upgrade\n * @constructor\n */\nfunction dialogPolyfillInfo(dialog) {\n  this.dialog_ = dialog;\n  this.replacedStyleTop_ = false;\n  this.openAsModal_ = false;\n\n  // Set a11y role. Browsers that support dialog implicitly know this already.\n  if (!dialog.hasAttribute('role')) {\n    dialog.setAttribute('role', 'dialog');\n  }\n\n  dialog.show = this.show.bind(this);\n  dialog.showModal = this.showModal.bind(this);\n  dialog.close = this.close.bind(this);\n\n  if (!('returnValue' in dialog)) {\n    dialog.returnValue = '';\n  }\n\n  if ('MutationObserver' in window) {\n    var mo = new MutationObserver(this.maybeHideModal.bind(this));\n    mo.observe(dialog, {attributes: true, attributeFilter: ['open']});\n  } else {\n    // IE10 and below support. Note that DOMNodeRemoved etc fire _before_ removal. They also\n    // seem to fire even if the element was removed as part of a parent removal. Use the removed\n    // events to force downgrade (useful if removed/immediately added).\n    var removed = false;\n    var cb = function() {\n      removed ? this.downgradeModal() : this.maybeHideModal();\n      removed = false;\n    }.bind(this);\n    var timeout;\n    var delayModel = function(ev) {\n      if (ev.target !== dialog) { return; }  // not for a child element\n      var cand = 'DOMNodeRemoved';\n      removed |= (ev.type.substr(0, cand.length) === cand);\n      window.clearTimeout(timeout);\n      timeout = window.setTimeout(cb, 0);\n    };\n    ['DOMAttrModified', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument'].forEach(function(name) {\n      dialog.addEventListener(name, delayModel);\n    });\n  }\n  // Note that the DOM is observed inside DialogManager while any dialog\n  // is being displayed as a modal, to catch modal removal from the DOM.\n\n  Object.defineProperty(dialog, 'open', {\n    set: this.setOpen.bind(this),\n    get: dialog.hasAttribute.bind(dialog, 'open')\n  });\n\n  this.backdrop_ = document.createElement('div');\n  this.backdrop_.className = 'backdrop';\n  this.backdrop_.addEventListener('click', this.backdropClick_.bind(this));\n}\n\ndialogPolyfillInfo.prototype = {\n\n  get dialog() {\n    return this.dialog_;\n  },\n\n  /**\n   * Maybe remove this dialog from the modal top layer. This is called when\n   * a modal dialog may no longer be tenable, e.g., when the dialog is no\n   * longer open or is no longer part of the DOM.\n   */\n  maybeHideModal: function() {\n    if (this.dialog_.hasAttribute('open') && document.body.contains(this.dialog_)) { return; }\n    this.downgradeModal();\n  },\n\n  /**\n   * Remove this dialog from the modal top layer, leaving it as a non-modal.\n   */\n  downgradeModal: function() {\n    if (!this.openAsModal_) { return; }\n    this.openAsModal_ = false;\n    this.dialog_.style.zIndex = '';\n\n    // This won't match the native <dialog> exactly because if the user set top on a centered\n    // polyfill dialog, that top gets thrown away when the dialog is closed. Not sure it's\n    // possible to polyfill this perfectly.\n    if (this.replacedStyleTop_) {\n      this.dialog_.style.top = '';\n      this.replacedStyleTop_ = false;\n    }\n\n    // Clear the backdrop and remove from the manager.\n    this.backdrop_.parentNode && this.backdrop_.parentNode.removeChild(this.backdrop_);\n    dialogPolyfill.dm.removeDialog(this);\n  },\n\n  /**\n   * @param {boolean} value whether to open or close this dialog\n   */\n  setOpen: function(value) {\n    if (value) {\n      this.dialog_.hasAttribute('open') || this.dialog_.setAttribute('open', '');\n    } else {\n      this.dialog_.removeAttribute('open');\n      this.maybeHideModal();  // nb. redundant with MutationObserver\n    }\n  },\n\n  /**\n   * Handles clicks on the fake .backdrop element, redirecting them as if\n   * they were on the dialog itself.\n   *\n   * @param {!Event} e to redirect\n   */\n  backdropClick_: function(e) {\n    if (!this.dialog_.hasAttribute('tabindex')) {\n      // Clicking on the backdrop should move the implicit cursor, even if dialog cannot be\n      // focused. Create a fake thing to focus on. If the backdrop was _before_ the dialog, this\n      // would not be needed - clicks would move the implicit cursor there.\n      var fake = document.createElement('div');\n      this.dialog_.insertBefore(fake, this.dialog_.firstChild);\n      fake.tabIndex = -1;\n      fake.focus();\n      this.dialog_.removeChild(fake);\n    } else {\n      this.dialog_.focus();\n    }\n\n    var redirectedEvent = document.createEvent('MouseEvents');\n    redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window,\n        e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey,\n        e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);\n    this.dialog_.dispatchEvent(redirectedEvent);\n    e.stopPropagation();\n  },\n\n  /**\n   * Focuses on the first focusable element within the dialog. This will always blur the current\n   * focus, even if nothing within the dialog is found.\n   */\n  focus_: function() {\n    // Find element with `autofocus` attribute, or fall back to the first form/tabindex control.\n    var target = this.dialog_.querySelector('[autofocus]:not([disabled])');\n    if (!target && this.dialog_.tabIndex >= 0) {\n      target = this.dialog_;\n    }\n    if (!target) {\n      // Note that this is 'any focusable area'. This list is probably not exhaustive, but the\n      // alternative involves stepping through and trying to focus everything.\n      var opts = ['button', 'input', 'keygen', 'select', 'textarea'];\n      var query = opts.map(function(el) {\n        return el + ':not([disabled])';\n      });\n      // TODO(samthor): tabindex values that are not numeric are not focusable.\n      query.push('[tabindex]:not([disabled]):not([tabindex=\"\"])');  // tabindex != \"\", not disabled\n      target = this.dialog_.querySelector(query.join(', '));\n    }\n    safeBlur(document.activeElement);\n    target && target.focus();\n  },\n\n  /**\n   * Sets the zIndex for the backdrop and dialog.\n   *\n   * @param {number} dialogZ\n   * @param {number} backdropZ\n   */\n  updateZIndex: function(dialogZ, backdropZ) {\n    if (dialogZ < backdropZ) {\n      throw new Error('dialogZ should never be < backdropZ');\n    }\n    this.dialog_.style.zIndex = dialogZ;\n    this.backdrop_.style.zIndex = backdropZ;\n  },\n\n  /**\n   * Shows the dialog. If the dialog is already open, this does nothing.\n   */\n  show: function() {\n    if (!this.dialog_.open) {\n      this.setOpen(true);\n      this.focus_();\n    }\n  },\n\n  /**\n   * Show this dialog modally.\n   */\n  showModal: function() {\n    if (this.dialog_.hasAttribute('open')) {\n      throw new Error('Failed to execute \\'showModal\\' on dialog: The element is already open, and therefore cannot be opened modally.');\n    }\n    if (!document.body.contains(this.dialog_)) {\n      throw new Error('Failed to execute \\'showModal\\' on dialog: The element is not in a Document.');\n    }\n    if (!dialogPolyfill.dm.pushDialog(this)) {\n      throw new Error('Failed to execute \\'showModal\\' on dialog: There are too many open modal dialogs.');\n    }\n\n    if (createsStackingContext(this.dialog_.parentElement)) {\n      console.warn('A dialog is being shown inside a stacking context. ' +\n          'This may cause it to be unusable. For more information, see this link: ' +\n          'https://github.com/GoogleChrome/dialog-polyfill/#stacking-context');\n    }\n\n    this.setOpen(true);\n    this.openAsModal_ = true;\n\n    // Optionally center vertically, relative to the current viewport.\n    if (dialogPolyfill.needsCentering(this.dialog_)) {\n      dialogPolyfill.reposition(this.dialog_);\n      this.replacedStyleTop_ = true;\n    } else {\n      this.replacedStyleTop_ = false;\n    }\n\n    // Insert backdrop.\n    this.dialog_.parentNode.insertBefore(this.backdrop_, this.dialog_.nextSibling);\n\n    // Focus on whatever inside the dialog.\n    this.focus_();\n  },\n\n  /**\n   * Closes this HTMLDialogElement. This is optional vs clearing the open\n   * attribute, however this fires a 'close' event.\n   *\n   * @param {string=} opt_returnValue to use as the returnValue\n   */\n  close: function(opt_returnValue) {\n    if (!this.dialog_.hasAttribute('open')) {\n      throw new Error('Failed to execute \\'close\\' on dialog: The element does not have an \\'open\\' attribute, and therefore cannot be closed.');\n    }\n    this.setOpen(false);\n\n    // Leave returnValue untouched in case it was set directly on the element\n    if (opt_returnValue !== undefined) {\n      this.dialog_.returnValue = opt_returnValue;\n    }\n\n    // Triggering \"close\" event for any attached listeners on the <dialog>.\n    var closeEvent = new supportCustomEvent('close', {\n      bubbles: false,\n      cancelable: false\n    });\n    this.dialog_.dispatchEvent(closeEvent);\n  }\n\n};\n\nvar dialogPolyfill = {};\n\ndialogPolyfill.reposition = function(element) {\n  var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;\n  var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;\n  element.style.top = Math.max(scrollTop, topValue) + 'px';\n};\n\ndialogPolyfill.isInlinePositionSetByStylesheet = function(element) {\n  for (var i = 0; i < document.styleSheets.length; ++i) {\n    var styleSheet = document.styleSheets[i];\n    var cssRules = null;\n    // Some browsers throw on cssRules.\n    try {\n      cssRules = styleSheet.cssRules;\n    } catch (e) {}\n    if (!cssRules) { continue; }\n    for (var j = 0; j < cssRules.length; ++j) {\n      var rule = cssRules[j];\n      var selectedNodes = null;\n      // Ignore errors on invalid selector texts.\n      try {\n        selectedNodes = document.querySelectorAll(rule.selectorText);\n      } catch(e) {}\n      if (!selectedNodes || !inNodeList(selectedNodes, element)) {\n        continue;\n      }\n      var cssTop = rule.style.getPropertyValue('top');\n      var cssBottom = rule.style.getPropertyValue('bottom');\n      if ((cssTop && cssTop !== 'auto') || (cssBottom && cssBottom !== 'auto')) {\n        return true;\n      }\n    }\n  }\n  return false;\n};\n\ndialogPolyfill.needsCentering = function(dialog) {\n  var computedStyle = window.getComputedStyle(dialog);\n  if (computedStyle.position !== 'absolute') {\n    return false;\n  }\n\n  // We must determine whether the top/bottom specified value is non-auto.  In\n  // WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but\n  // Firefox returns the used value. So we do this crazy thing instead: check\n  // the inline style and then go through CSS rules.\n  if ((dialog.style.top !== 'auto' && dialog.style.top !== '') ||\n      (dialog.style.bottom !== 'auto' && dialog.style.bottom !== '')) {\n    return false;\n  }\n  return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);\n};\n\n/**\n * @param {!Element} element to force upgrade\n */\ndialogPolyfill.forceRegisterDialog = function(element) {\n  if (window.HTMLDialogElement || element.showModal) {\n    console.warn('This browser already supports <dialog>, the polyfill ' +\n        'may not work correctly', element);\n  }\n  if (element.localName !== 'dialog') {\n    throw new Error('Failed to register dialog: The element is not a dialog.');\n  }\n  new dialogPolyfillInfo(/** @type {!HTMLDialogElement} */ (element));\n};\n\n/**\n * @param {!Element} element to upgrade, if necessary\n */\ndialogPolyfill.registerDialog = function(element) {\n  if (!element.showModal) {\n    dialogPolyfill.forceRegisterDialog(element);\n  }\n};\n\n/**\n * @constructor\n */\ndialogPolyfill.DialogManager = function() {\n  /** @type {!Array<!dialogPolyfillInfo>} */\n  this.pendingDialogStack = [];\n\n  var checkDOM = this.checkDOM_.bind(this);\n\n  // The overlay is used to simulate how a modal dialog blocks the document.\n  // The blocking dialog is positioned on top of the overlay, and the rest of\n  // the dialogs on the pending dialog stack are positioned below it. In the\n  // actual implementation, the modal dialog stacking is controlled by the\n  // top layer, where z-index has no effect.\n  this.overlay = document.createElement('div');\n  this.overlay.className = '_dialog_overlay';\n  this.overlay.addEventListener('click', function(e) {\n    this.forwardTab_ = undefined;\n    e.stopPropagation();\n    checkDOM([]);  // sanity-check DOM\n  }.bind(this));\n\n  this.handleKey_ = this.handleKey_.bind(this);\n  this.handleFocus_ = this.handleFocus_.bind(this);\n\n  this.zIndexLow_ = 100000;\n  this.zIndexHigh_ = 100000 + 150;\n\n  this.forwardTab_ = undefined;\n\n  if ('MutationObserver' in window) {\n    this.mo_ = new MutationObserver(function(records) {\n      var removed = [];\n      records.forEach(function(rec) {\n        for (var i = 0, c; c = rec.removedNodes[i]; ++i) {\n          if (!(c instanceof Element)) {\n            continue;\n          } else if (c.localName === 'dialog') {\n            removed.push(c);\n          }\n          removed = removed.concat(c.querySelectorAll('dialog'));\n        }\n      });\n      removed.length && checkDOM(removed);\n    });\n  }\n};\n\n/**\n * Called on the first modal dialog being shown. Adds the overlay and related\n * handlers.\n */\ndialogPolyfill.DialogManager.prototype.blockDocument = function() {\n  document.documentElement.addEventListener('focus', this.handleFocus_, true);\n  document.addEventListener('keydown', this.handleKey_);\n  this.mo_ && this.mo_.observe(document, {childList: true, subtree: true});\n};\n\n/**\n * Called on the first modal dialog being removed, i.e., when no more modal\n * dialogs are visible.\n */\ndialogPolyfill.DialogManager.prototype.unblockDocument = function() {\n  document.documentElement.removeEventListener('focus', this.handleFocus_, true);\n  document.removeEventListener('keydown', this.handleKey_);\n  this.mo_ && this.mo_.disconnect();\n};\n\n/**\n * Updates the stacking of all known dialogs.\n */\ndialogPolyfill.DialogManager.prototype.updateStacking = function() {\n  var zIndex = this.zIndexHigh_;\n\n  for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {\n    dpi.updateZIndex(--zIndex, --zIndex);\n    if (i === 0) {\n      this.overlay.style.zIndex = --zIndex;\n    }\n  }\n\n  // Make the overlay a sibling of the dialog itself.\n  var last = this.pendingDialogStack[0];\n  if (last) {\n    var p = last.dialog.parentNode || document.body;\n    p.appendChild(this.overlay);\n  } else if (this.overlay.parentNode) {\n    this.overlay.parentNode.removeChild(this.overlay);\n  }\n};\n\n/**\n * @param {Element} candidate to check if contained or is the top-most modal dialog\n * @return {boolean} whether candidate is contained in top dialog\n */\ndialogPolyfill.DialogManager.prototype.containedByTopDialog_ = function(candidate) {\n  while (candidate = findNearestDialog(candidate)) {\n    for (var i = 0, dpi; dpi = this.pendingDialogStack[i]; ++i) {\n      if (dpi.dialog === candidate) {\n        return i === 0;  // only valid if top-most\n      }\n    }\n    candidate = candidate.parentElement;\n  }\n  return false;\n};\n\ndialogPolyfill.DialogManager.prototype.handleFocus_ = function(event) {\n  if (this.containedByTopDialog_(event.target)) { return; }\n\n  if (document.activeElement === document.documentElement) { return; }\n\n  event.preventDefault();\n  event.stopPropagation();\n  safeBlur(/** @type {Element} */ (event.target));\n\n  if (this.forwardTab_ === undefined) { return; }  // move focus only from a tab key\n\n  var dpi = this.pendingDialogStack[0];\n  var dialog = dpi.dialog;\n  var position = dialog.compareDocumentPosition(event.target);\n  if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n    if (this.forwardTab_) {\n      // forward\n      dpi.focus_();\n    } else if (event.target !== document.documentElement) {\n      // backwards if we're not already focused on <html>\n      document.documentElement.focus();\n    }\n  }\n\n  return false;\n};\n\ndialogPolyfill.DialogManager.prototype.handleKey_ = function(event) {\n  this.forwardTab_ = undefined;\n  if (event.keyCode === 27) {\n    event.preventDefault();\n    event.stopPropagation();\n    var cancelEvent = new supportCustomEvent('cancel', {\n      bubbles: false,\n      cancelable: true\n    });\n    var dpi = this.pendingDialogStack[0];\n    if (dpi && dpi.dialog.dispatchEvent(cancelEvent)) {\n      dpi.dialog.close();\n    }\n  } else if (event.keyCode === 9) {\n    this.forwardTab_ = !event.shiftKey;\n  }\n};\n\n/**\n * Finds and downgrades any known modal dialogs that are no longer displayed. Dialogs that are\n * removed and immediately readded don't stay modal, they become normal.\n *\n * @param {!Array<!HTMLDialogElement>} removed that have definitely been removed\n */\ndialogPolyfill.DialogManager.prototype.checkDOM_ = function(removed) {\n  // This operates on a clone because it may cause it to change. Each change also calls\n  // updateStacking, which only actually needs to happen once. But who removes many modal dialogs\n  // at a time?!\n  var clone = this.pendingDialogStack.slice();\n  clone.forEach(function(dpi) {\n    if (removed.indexOf(dpi.dialog) !== -1) {\n      dpi.downgradeModal();\n    } else {\n      dpi.maybeHideModal();\n    }\n  });\n};\n\n/**\n * @param {!dialogPolyfillInfo} dpi\n * @return {boolean} whether the dialog was allowed\n */\ndialogPolyfill.DialogManager.prototype.pushDialog = function(dpi) {\n  var allowed = (this.zIndexHigh_ - this.zIndexLow_) / 2 - 1;\n  if (this.pendingDialogStack.length >= allowed) {\n    return false;\n  }\n  if (this.pendingDialogStack.unshift(dpi) === 1) {\n    this.blockDocument();\n  }\n  this.updateStacking();\n  return true;\n};\n\n/**\n * @param {!dialogPolyfillInfo} dpi\n */\ndialogPolyfill.DialogManager.prototype.removeDialog = function(dpi) {\n  var index = this.pendingDialogStack.indexOf(dpi);\n  if (index === -1) { return; }\n\n  this.pendingDialogStack.splice(index, 1);\n  if (this.pendingDialogStack.length === 0) {\n    this.unblockDocument();\n  }\n  this.updateStacking();\n};\n\ndialogPolyfill.dm = new dialogPolyfill.DialogManager();\ndialogPolyfill.formSubmitter = null;\ndialogPolyfill.useValue = null;\n\n/**\n * Installs global handlers, such as click listers and native method overrides. These are needed\n * even if a no dialog is registered, as they deal with <form method=\"dialog\">.\n */\nif (window.HTMLDialogElement === undefined) {\n\n  /**\n   * If HTMLFormElement translates method=\"DIALOG\" into 'get', then replace the descriptor with\n   * one that returns the correct value.\n   */\n  var testForm = document.createElement('form');\n  testForm.setAttribute('method', 'dialog');\n  if (testForm.method !== 'dialog') {\n    var methodDescriptor = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'method');\n    if (methodDescriptor) {\n      // nb. Some older iOS and older PhantomJS fail to return the descriptor. Don't do anything\n      // and don't bother to update the element.\n      var realGet = methodDescriptor.get;\n      methodDescriptor.get = function() {\n        if (isFormMethodDialog(this)) {\n          return 'dialog';\n        }\n        return realGet.call(this);\n      };\n      var realSet = methodDescriptor.set;\n      methodDescriptor.set = function(v) {\n        if (typeof v === 'string' && v.toLowerCase() === 'dialog') {\n          return this.setAttribute('method', v);\n        }\n        return realSet.call(this, v);\n      };\n      Object.defineProperty(HTMLFormElement.prototype, 'method', methodDescriptor);\n    }\n  }\n\n  /**\n   * Global 'click' handler, to capture the <input type=\"submit\"> or <button> element which has\n   * submitted a <form method=\"dialog\">. Needed as Safari and others don't report this inside\n   * document.activeElement.\n   */\n  document.addEventListener('click', function(ev) {\n    dialogPolyfill.formSubmitter = null;\n    dialogPolyfill.useValue = null;\n    if (ev.defaultPrevented) { return; }  // e.g. a submit which prevents default submission\n\n    var target = /** @type {Element} */ (ev.target);\n    if (!target || !isFormMethodDialog(target.form)) { return; }\n\n    var valid = (target.type === 'submit' && ['button', 'input'].indexOf(target.localName) > -1);\n    if (!valid) {\n      if (!(target.localName === 'input' && target.type === 'image')) { return; }\n      // this is a <input type=\"image\">, which can submit forms\n      dialogPolyfill.useValue = ev.offsetX + ',' + ev.offsetY;\n    }\n\n    var dialog = findNearestDialog(target);\n    if (!dialog) { return; }\n\n    dialogPolyfill.formSubmitter = target;\n\n  }, false);\n\n  /**\n   * Replace the native HTMLFormElement.submit() method, as it won't fire the\n   * submit event and give us a chance to respond.\n   */\n  var nativeFormSubmit = HTMLFormElement.prototype.submit;\n  var replacementFormSubmit = function () {\n    if (!isFormMethodDialog(this)) {\n      return nativeFormSubmit.call(this);\n    }\n    var dialog = findNearestDialog(this);\n    dialog && dialog.close();\n  };\n  HTMLFormElement.prototype.submit = replacementFormSubmit;\n\n  /**\n   * Global form 'dialog' method handler. Closes a dialog correctly on submit\n   * and possibly sets its return value.\n   */\n  document.addEventListener('submit', function(ev) {\n    if (ev.defaultPrevented) { return; }  // e.g. a submit which prevents default submission\n\n    var form = /** @type {HTMLFormElement} */ (ev.target);\n    if (!isFormMethodDialog(form)) { return; }\n    ev.preventDefault();\n\n    var dialog = findNearestDialog(form);\n    if (!dialog) { return; }\n\n    // Forms can only be submitted via .submit() or a click (?), but anyway: sanity-check that\n    // the submitter is correct before using its value as .returnValue.\n    var s = dialogPolyfill.formSubmitter;\n    if (s && s.form === form) {\n      dialog.close(dialogPolyfill.useValue || s.value);\n    } else {\n      dialog.close();\n    }\n    dialogPolyfill.formSubmitter = null;\n\n  }, false);\n}\n\nexport default dialogPolyfill;\n", "function registerHeaderListeners() {\n  const header = document.querySelector('.js-header');\n  const menuButtons = document.querySelectorAll('.js-headerMenuButton');\n  menuButtons.forEach(button => {\n    button.addEventListener('click', e => {\n      e.preventDefault();\n      header?.classList.toggle('is-active');\n      button.setAttribute('aria-expanded', String(header?.classList.contains('is-active')));\n    });\n  });\n\n  const scrim = document.querySelector('.js-scrim');\n  scrim?.addEventListener('click', e => {\n    e.preventDefault();\n    header?.classList.remove('is-active');\n    menuButtons.forEach(button => {\n      button.setAttribute('aria-expanded', String(header?.classList.contains('is-active')));\n    });\n  });\n}\n\nfunction registerSearchFormListeners() {\n  const searchForm = document.querySelector('.js-searchForm');\n  const expandSearch = document.querySelector('.js-expandSearch');\n  const input = searchForm?.querySelector('input');\n  const headerLogo = document.querySelector('.js-headerLogo');\n  const menuButton = document.querySelector('.js-headerMenuButton');\n  expandSearch?.addEventListener('click', () => {\n    searchForm?.classList.add('go-SearchForm--expanded');\n    headerLogo?.classList.add('go-Header-logo--hidden');\n    menuButton?.classList.add('go-Header-navOpen--hidden');\n    input?.focus();\n  });\n  document?.addEventListener('click', e => {\n    if (!searchForm?.contains(e.target as Node)) {\n      searchForm?.classList.remove('go-SearchForm--expanded');\n      headerLogo?.classList.remove('go-Header-logo--hidden');\n      menuButton?.classList.remove('go-Header-navOpen--hidden');\n    }\n  });\n}\n\n/**\n * Listen for changes in the search dropdown.\n *\n * TODO(https://golang.org/issue/44142): Fix this flow:\n * A user will likely expect to submit the search again after selecting the\n * type of search. The change event should trigger a form submission, so that the\n * search event is still captured in analytics without a manual instrumentation.\n */\ndocument.querySelectorAll('.js-searchModeSelect').forEach(el => {\n  el.addEventListener('change', e => {\n    const urlSearchParams = new URLSearchParams(window.location.search);\n    const params = Object.fromEntries(urlSearchParams.entries());\n    const query = params['q'];\n    if (query) {\n      window.location.search = `q=${query}&m=${(e.target as HTMLSelectElement).value}`;\n    }\n  });\n});\n\nregisterHeaderListeners();\nregisterSearchFormListeners();\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * This class decorates an element to copy arbitrary data attached via a data-\n * attribute to the clipboard.\n */\nexport class ClipboardController {\n  /**\n   * The data to be copied to the clipboard.\n   */\n  private data: string;\n\n  /**\n   * @param el The element that will trigger copying text to the clipboard. The text is\n   * expected to be within its data-to-copy attribute.\n   */\n  constructor(private el: HTMLButtonElement) {\n    this.data = el.dataset['toCopy'] ?? el.innerText;\n    // if data-to-copy is empty and the button is part of an input group\n    // capture the value of the input.\n    if (!this.data && el.parentElement?.classList.contains('go-InputGroup')) {\n      this.data = (this.data || el.parentElement?.querySelector('input')?.value) ?? '';\n    }\n    el.addEventListener('click', e => this.handleCopyClick(e));\n  }\n\n  /**\n   * Handles when the primary element is clicked.\n   */\n  handleCopyClick(e: MouseEvent): void {\n    e.preventDefault();\n    const TOOLTIP_SHOW_DURATION_MS = 1000;\n\n    // This API is not available on iOS.\n    if (!navigator.clipboard) {\n      this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n      return;\n    }\n    navigator.clipboard\n      .writeText(this.data)\n      .then(() => {\n        this.showTooltipText('Copied!', TOOLTIP_SHOW_DURATION_MS);\n      })\n      .catch(() => {\n        this.showTooltipText('Unable to copy', TOOLTIP_SHOW_DURATION_MS);\n      });\n  }\n\n  /**\n   * Shows the given text in a tooltip for a specified amount of time, in milliseconds.\n   */\n  showTooltipText(text: string, durationMs: number): void {\n    this.el.setAttribute('data-tooltip', text);\n    setTimeout(() => this.el.setAttribute('data-tooltip', ''), durationMs);\n  }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ToolTipController handles closing tooltips on external clicks.\n */\nexport class ToolTipController {\n  constructor(private el: HTMLDetailsElement) {\n    document.addEventListener('click', e => {\n      const insideTooltip = this.el.contains(e.target as Element);\n      if (!insideTooltip) {\n        this.el.removeAttribute('open');\n      }\n    });\n  }\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { TreeNavController } from './tree.js';\n\nexport class SelectNavController {\n  constructor(private el: Element) {\n    this.el.addEventListener('change', e => {\n      const target = e.target as HTMLSelectElement;\n      let href = target.value;\n      if (!target.value.startsWith('/')) {\n        href = '/' + href;\n      }\n      window.location.href = href;\n    });\n  }\n}\n\nexport function makeSelectNav(tree: TreeNavController): HTMLLabelElement {\n  const label = document.createElement('label');\n  label.classList.add('go-Label');\n  label.setAttribute('aria-label', 'Menu');\n  const select = document.createElement('select');\n  select.classList.add('go-Select', 'js-selectNav');\n  label.appendChild(select);\n  const outline = document.createElement('optgroup');\n  outline.label = 'Outline';\n  select.appendChild(outline);\n  const groupMap: Record<string, HTMLOptGroupElement> = {};\n  let group: HTMLOptGroupElement;\n  for (const t of tree.treeitems) {\n    if (Number(t.depth) > 4) continue;\n    if (t.groupTreeitem) {\n      group = groupMap[t.groupTreeitem.label];\n      if (!group) {\n        group = groupMap[t.groupTreeitem.label] = document.createElement('optgroup');\n        group.label = t.groupTreeitem.label;\n        select.appendChild(group);\n      }\n    } else {\n      group = outline;\n    }\n    const o = document.createElement('option');\n    o.label = t.label;\n    o.textContent = t.label;\n    o.value = (t.el as HTMLAnchorElement).href.replace(window.location.origin, '').replace('/', '');\n    group.appendChild(o);\n  }\n  tree.addObserver(t => {\n    const value = select.querySelector<HTMLOptionElement>(`[label=\"${t.label}\"]`)?.value;\n    if (value) {\n      select.value = value;\n    }\n  }, 50);\n  return label;\n}\n", "/**\n * @license\n * Copyright 2021 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * ModalController registers a dialog element with the polyfill if\n * necessary for the current browser, add adds event listeners to\n * close and open modals.\n */\nexport class ModalController {\n  constructor(private el: HTMLDialogElement) {\n    // Only load the dialog polyfill if necessary for the environment.\n    if (!window.HTMLDialogElement && !el.showModal) {\n      import('../../../third_party/dialog-polyfill/dialog-polyfill.esm.js').then(\n        ({ default: polyfill }) => {\n          polyfill.registerDialog(el);\n        }\n      );\n    }\n    const id = el.id;\n    const button = document.querySelector<HTMLButtonElement>(`[aria-controls=\"${id}\"]`);\n    if (button) {\n      button.addEventListener('click', () => {\n        if (this.el.showModal) {\n          this.el.showModal();\n        } else {\n          this.el.open = true;\n        }\n        el.querySelector('input')?.focus();\n      });\n    }\n    for (const close of this.el.querySelectorAll<HTMLButtonElement>('[data-modal-close]')) {\n      close.addEventListener('click', () => {\n        if (this.el.close) {\n          this.el.close();\n        } else {\n          this.el.open = false;\n        }\n      });\n    }\n  }\n}\n", "interface TagManagerEvent {\n  /**\n   * event is the name of the event, used to filter events in\n   * Google Analytics.\n   */\n  event: string;\n\n  /**\n   * event_category is a name that you supply as a way to group objects\n   * that to analyze. Typically, you will use the same category name\n   * multiple times over related UI elements (buttons, links, etc).\n   */\n  event_category?: string;\n\n  /**\n   * event_action is used to name the type of event or interaction you\n   * want to measure for a particular web object. For example, with a\n   * single \"form\" category, you can analyze a number of specific events\n   * with this parameter, such as: form entered, form submitted.\n   */\n  event_action?: string;\n\n  /**\n   * event_label provide additional information for events that you want\n   * to analyze, such as the text label of a link.\n   */\n  event_label?: string;\n\n  /**\n   * gtm.start is used to initialize Google Tag Manager.\n   */\n  'gtm.start'?: number;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ndeclare global {\n  interface Window {\n    dataLayer?: (TagManagerEvent | VoidFunction)[];\n    ga?: unknown;\n  }\n}\n\n/**\n * track sends events to Google Tag Manager.\n */\nexport function track(\n  event: string | TagManagerEvent,\n  category?: string,\n  action?: string,\n  label?: string\n): void {\n  window.dataLayer ??= [];\n  if (typeof event === 'string') {\n    window.dataLayer.push({\n      event,\n      event_category: category,\n      event_action: action,\n      event_label: label,\n    });\n  } else {\n    window.dataLayer.push(event);\n  }\n}\n\n/**\n * func adds functions to run sequentionally after\n * Google Tag Manager is ready.\n */\nexport function func(fn: () => void): void {\n  window.dataLayer ??= [];\n  window.dataLayer.push(fn);\n}\n", "/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { track } from '../analytics/analytics';\n\n/**\n * Options are keyhandler callback options.\n */\ninterface Options {\n  /**\n   * target is the element the key event should filter on. The\n   * default target is the document.\n   */\n  target?: Element;\n\n  /**\n   * withMeta specifies if the event callback should fire when\n   * the key is pressed with a meta key (ctrl, alt, etc). By\n   * default meta keypresses are ignored.\n   */\n  withMeta?: boolean;\n}\n\n/**\n * KeyHandler is the config for a keyboard event callback.\n */\ninterface KeyHandler extends Options {\n  description: string;\n  callback: (e: KeyboardEvent) => void;\n}\n\n/**\n * KeyboardController controls event callbacks for sitewide\n * keyboard events. Multiple callbacks can be registered for\n * a single key and by default the controller ignores events\n * for text input targets.\n */\nclass KeyboardController {\n  handlers: Record<string, Set<KeyHandler>>;\n\n  constructor() {\n    this.handlers = {};\n    document.addEventListener('keydown', e => this.handleKeyPress(e));\n  }\n\n  /**\n   * on registers keyboard event callbacks.\n   * @param key the key to register.\n   * @param description name of the event.\n   * @param callback event callback.\n   * @param options set target and withMeta options to override the default behaviors.\n   */\n  on(key: string, description: string, callback: (e: KeyboardEvent) => void, options?: Options) {\n    this.handlers[key] ??= new Set();\n    this.handlers[key].add({ description, callback, ...options });\n    return this;\n  }\n\n  private handleKeyPress(e: KeyboardEvent) {\n    for (const handler of this.handlers[e.key.toLowerCase()] ?? new Set()) {\n      if (handler.target && handler.target !== e.target) {\n        return;\n      }\n      const t = e.target as HTMLElement | null;\n      if (\n        !handler.target &&\n        (t?.tagName === 'INPUT' || t?.tagName === 'SELECT' || t?.tagName === 'TEXTAREA')\n      ) {\n        return;\n      }\n      if (t?.isContentEditable) {\n        return;\n      }\n      if (\n        (handler.withMeta && !(e.ctrlKey || e.metaKey)) ||\n        (!handler.withMeta && (e.ctrlKey || e.metaKey))\n      ) {\n        return;\n      }\n      track('keypress', 'hotkeys', `${e.key} pressed`, handler.description);\n      handler.callback(e);\n    }\n  }\n}\n\nexport const keyboard = new KeyboardController();\n", "/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport './header/header';\nimport { ClipboardController } from './clipboard/clipboard';\nimport { ToolTipController } from './tooltip/tooltip';\nimport { SelectNavController } from './outline/select';\nimport { ModalController } from './modal/modal';\n\nexport { keyboard } from './keyboard/keyboard';\nexport * as analytics from './analytics/analytics';\n\nfor (const el of document.querySelectorAll<HTMLButtonElement>('.js-clipboard')) {\n  new ClipboardController(el);\n}\n\nfor (const el of document.querySelectorAll<HTMLDialogElement>('.js-modal')) {\n  new ModalController(el);\n}\n\nfor (const t of document.querySelectorAll<HTMLDetailsElement>('.js-tooltip')) {\n  new ToolTipController(t);\n}\n\nfor (const el of document.querySelectorAll<HTMLSelectElement>('.js-selectNav')) {\n  new SelectNavController(el);\n}\n", "/**\n * @license\n * Copyright 2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { analytics, keyboard } from '../shared/shared';\n\n// Temporary shortcut for testing out the dark theme.\nkeyboard.on('t', 'toggle theme', () => {\n  let nextTheme = 'dark';\n  const theme = document.documentElement.getAttribute('data-theme');\n  if (theme === 'dark') {\n    nextTheme = 'light';\n  } else if (theme === 'light') {\n    nextTheme = 'auto';\n  }\n  document.documentElement.setAttribute('data-theme', nextTheme);\n  document.cookie = `prefers-color-scheme=${nextTheme};path=/;max-age=31536000;`;\n});\n\n// Pressing '/' focuses the search box\nkeyboard.on('/', 'focus search', e => {\n  const searchInput = Array.from(\n    document.querySelectorAll<HTMLInputElement>('.js-searchFocus')\n  ).pop();\n  // Favoring the Firefox quick find feature over search input\n  // focus. See: https://github.com/golang/go/issues/41093.\n  if (searchInput && !window.navigator.userAgent.includes('Firefox')) {\n    e.preventDefault();\n    searchInput.focus();\n  }\n});\n\n// Pressing 'y' changes the browser URL to the canonical URL\n// without triggering a reload.\nkeyboard.on('y', 'set canonical url', () => {\n  const canonicalURLPath = document.querySelector<HTMLDivElement>('.js-canonicalURLPath')?.dataset[\n    'canonicalUrlPath'\n  ];\n  if (canonicalURLPath && canonicalURLPath !== '') {\n    window.history.replaceState(null, '', canonicalURLPath);\n  }\n});\n\n/**\n * setupGoogleTagManager intializes Google Tag Manager.\n */\n(function setupGoogleTagManager() {\n  analytics.track({\n    'gtm.start': new Date().getTime(),\n    event: 'gtm.js',\n  });\n})();\n\n/**\n * removeUTMSource removes the utm_source GET parameter if present.\n * This is done using JavaScript, so that the utm_source is still\n * captured by Google Analytics.\n */\nfunction removeUTMSource() {\n  const urlParams = new URLSearchParams(window.location.search);\n  const utmSource = urlParams.get('utm_source');\n  if (utmSource !== 'gopls' && utmSource !== 'godoc' && utmSource !== 'pkggodev') {\n    return;\n  }\n\n  /** Strip the utm_source query parameter and replace the URL. **/\n  const newURL = new URL(window.location.href);\n  urlParams.delete('utm_source');\n  newURL.search = urlParams.toString();\n  window.history.replaceState(null, '', newURL.toString());\n}\n\nif (document.querySelector<HTMLElement>('.js-gtmID')?.dataset.gtmid && window.dataLayer) {\n  analytics.func(function () {\n    removeUTMSource();\n  });\n} else {\n  removeUTMSource();\n}\n"],
+  "mappings": "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",
   "names": []
 }
diff --git a/static/shared/keyboard/keyboard.js b/static/shared/keyboard/keyboard.js
index b8c83dc..0079ef3 100644
--- a/static/shared/keyboard/keyboard.js
+++ b/static/shared/keyboard/keyboard.js
@@ -3,5 +3,5 @@
  * 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.
- */import{track as a}from"../analytics/analytics";class s{constructor(){this.handlers={},document.addEventListener("keydown",e=>this.handleKeyPress(e))}on(e,t,r,n){return this.handlers[e]??=new Set,this.handlers[e].add({description:t,callback:r,...n}),this}handleKeyPress(e){for(const t of this.handlers[e.key]??new Set){if(t.target&&t.target!==e.target)return;const r=e.target;if(!t.target&&(r?.tagName==="INPUT"||r?.tagName==="SELECT"||r?.tagName==="TEXTAREA")||r?.isContentEditable||t.withMeta&&!(e.ctrlKey||e.metaKey)||!t.withMeta&&(e.ctrlKey||e.metaKey))return;a("keypress","hotkeys",`${e.key} pressed`,t.description),t.callback(e)}}}export const keyboard=new s;
+ */import{track as n}from"../analytics/analytics";class s{constructor(){this.handlers={},document.addEventListener("keydown",e=>this.handleKeyPress(e))}on(e,t,r,a){return this.handlers[e]??=new Set,this.handlers[e].add({description:t,callback:r,...a}),this}handleKeyPress(e){for(const t of this.handlers[e.key.toLowerCase()]??new Set){if(t.target&&t.target!==e.target)return;const r=e.target;if(!t.target&&(r?.tagName==="INPUT"||r?.tagName==="SELECT"||r?.tagName==="TEXTAREA")||r?.isContentEditable||t.withMeta&&!(e.ctrlKey||e.metaKey)||!t.withMeta&&(e.ctrlKey||e.metaKey))return;n("keypress","hotkeys",`${e.key} pressed`,t.description),t.callback(e)}}}export const keyboard=new s;
 //# sourceMappingURL=keyboard.js.map
diff --git a/static/shared/keyboard/keyboard.js.map b/static/shared/keyboard/keyboard.js.map
index 785b216..68fb4c2 100644
--- a/static/shared/keyboard/keyboard.js.map
+++ b/static/shared/keyboard/keyboard.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["keyboard.ts"],
-  "sourcesContent": ["/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { track } from '../analytics/analytics';\n\n/**\n * Options are keyhandler callback options.\n */\ninterface Options {\n  /**\n   * target is the element the key event should filter on. The\n   * default target is the document.\n   */\n  target?: Element;\n\n  /**\n   * withMeta specifies if the event callback should fire when\n   * the key is pressed with a meta key (ctrl, alt, etc). By\n   * default meta keypresses are ignored.\n   */\n  withMeta?: boolean;\n}\n\n/**\n * KeyHandler is the config for a keyboard event callback.\n */\ninterface KeyHandler extends Options {\n  description: string;\n  callback: (e: KeyboardEvent) => void;\n}\n\n/**\n * KeyboardController controls event callbacks for sitewide\n * keyboard events. Multiple callbacks can be registered for\n * a single key and by default the controller ignores events\n * for text input targets.\n */\nclass KeyboardController {\n  handlers: Record<string, Set<KeyHandler>>;\n\n  constructor() {\n    this.handlers = {};\n    document.addEventListener('keydown', e => this.handleKeyPress(e));\n  }\n\n  /**\n   * on registers keyboard event callbacks.\n   * @param key the key to register.\n   * @param description name of the event.\n   * @param callback event callback.\n   * @param options set target and withMeta options to override the default behaviors.\n   */\n  on(key: string, description: string, callback: (e: KeyboardEvent) => void, options?: Options) {\n    this.handlers[key] ??= new Set();\n    this.handlers[key].add({ description, callback, ...options });\n    return this;\n  }\n\n  private handleKeyPress(e: KeyboardEvent) {\n    for (const handler of this.handlers[e.key] ?? new Set()) {\n      if (handler.target && handler.target !== e.target) {\n        return;\n      }\n      const t = e.target as HTMLElement | null;\n      if (\n        !handler.target &&\n        (t?.tagName === 'INPUT' || t?.tagName === 'SELECT' || t?.tagName === 'TEXTAREA')\n      ) {\n        return;\n      }\n      if (t?.isContentEditable) {\n        return;\n      }\n      if (\n        (handler.withMeta && !(e.ctrlKey || e.metaKey)) ||\n        (!handler.withMeta && (e.ctrlKey || e.metaKey))\n      ) {\n        return;\n      }\n      track('keypress', 'hotkeys', `${e.key} pressed`, handler.description);\n      handler.callback(e);\n    }\n  }\n}\n\nexport const keyboard = new KeyboardController();\n"],
-  "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOA,+CAkCA,OAAyB,CAGvB,aAAc,CACZ,KAAK,SAAW,GAChB,SAAS,iBAAiB,UAAW,GAAK,KAAK,eAAe,IAUhE,GAAG,EAAa,EAAqB,EAAsC,EAAmB,CAC5F,YAAK,SAAS,KAAS,GAAI,KAC3B,KAAK,SAAS,GAAK,IAAI,CAAE,cAAa,cAAa,IAC5C,KAGD,eAAe,EAAkB,CACvC,SAAW,KAAW,MAAK,SAAS,EAAE,MAAQ,GAAI,KAAO,CACvD,GAAI,EAAQ,QAAU,EAAQ,SAAW,EAAE,OACzC,OAEF,KAAM,GAAI,EAAE,OAUZ,GARE,CAAC,EAAQ,QACR,IAAG,UAAY,SAAW,GAAG,UAAY,UAAY,GAAG,UAAY,aAInE,GAAG,mBAIJ,EAAQ,UAAY,CAAE,GAAE,SAAW,EAAE,UACrC,CAAC,EAAQ,UAAa,GAAE,SAAW,EAAE,SAEtC,OAEF,EAAM,WAAY,UAAW,GAAG,EAAE,cAAe,EAAQ,aACzD,EAAQ,SAAS,KAKhB,YAAM,UAAW,GAAI",
+  "sourcesContent": ["/*!\n * @license\n * Copyright 2019-2020 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\nimport { track } from '../analytics/analytics';\n\n/**\n * Options are keyhandler callback options.\n */\ninterface Options {\n  /**\n   * target is the element the key event should filter on. The\n   * default target is the document.\n   */\n  target?: Element;\n\n  /**\n   * withMeta specifies if the event callback should fire when\n   * the key is pressed with a meta key (ctrl, alt, etc). By\n   * default meta keypresses are ignored.\n   */\n  withMeta?: boolean;\n}\n\n/**\n * KeyHandler is the config for a keyboard event callback.\n */\ninterface KeyHandler extends Options {\n  description: string;\n  callback: (e: KeyboardEvent) => void;\n}\n\n/**\n * KeyboardController controls event callbacks for sitewide\n * keyboard events. Multiple callbacks can be registered for\n * a single key and by default the controller ignores events\n * for text input targets.\n */\nclass KeyboardController {\n  handlers: Record<string, Set<KeyHandler>>;\n\n  constructor() {\n    this.handlers = {};\n    document.addEventListener('keydown', e => this.handleKeyPress(e));\n  }\n\n  /**\n   * on registers keyboard event callbacks.\n   * @param key the key to register.\n   * @param description name of the event.\n   * @param callback event callback.\n   * @param options set target and withMeta options to override the default behaviors.\n   */\n  on(key: string, description: string, callback: (e: KeyboardEvent) => void, options?: Options) {\n    this.handlers[key] ??= new Set();\n    this.handlers[key].add({ description, callback, ...options });\n    return this;\n  }\n\n  private handleKeyPress(e: KeyboardEvent) {\n    for (const handler of this.handlers[e.key.toLowerCase()] ?? new Set()) {\n      if (handler.target && handler.target !== e.target) {\n        return;\n      }\n      const t = e.target as HTMLElement | null;\n      if (\n        !handler.target &&\n        (t?.tagName === 'INPUT' || t?.tagName === 'SELECT' || t?.tagName === 'TEXTAREA')\n      ) {\n        return;\n      }\n      if (t?.isContentEditable) {\n        return;\n      }\n      if (\n        (handler.withMeta && !(e.ctrlKey || e.metaKey)) ||\n        (!handler.withMeta && (e.ctrlKey || e.metaKey))\n      ) {\n        return;\n      }\n      track('keypress', 'hotkeys', `${e.key} pressed`, handler.description);\n      handler.callback(e);\n    }\n  }\n}\n\nexport const keyboard = new KeyboardController();\n"],
+  "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOA,+CAkCA,OAAyB,CAGvB,aAAc,CACZ,KAAK,SAAW,GAChB,SAAS,iBAAiB,UAAW,GAAK,KAAK,eAAe,IAUhE,GAAG,EAAa,EAAqB,EAAsC,EAAmB,CAC5F,YAAK,SAAS,KAAS,GAAI,KAC3B,KAAK,SAAS,GAAK,IAAI,CAAE,cAAa,cAAa,IAC5C,KAGD,eAAe,EAAkB,CACvC,SAAW,KAAW,MAAK,SAAS,EAAE,IAAI,gBAAkB,GAAI,KAAO,CACrE,GAAI,EAAQ,QAAU,EAAQ,SAAW,EAAE,OACzC,OAEF,KAAM,GAAI,EAAE,OAUZ,GARE,CAAC,EAAQ,QACR,IAAG,UAAY,SAAW,GAAG,UAAY,UAAY,GAAG,UAAY,aAInE,GAAG,mBAIJ,EAAQ,UAAY,CAAE,GAAE,SAAW,EAAE,UACrC,CAAC,EAAQ,UAAa,GAAE,SAAW,EAAE,SAEtC,OAEF,EAAM,WAAY,UAAW,GAAG,EAAE,cAAe,EAAQ,aACzD,EAAQ,SAAS,KAKhB,YAAM,UAAW,GAAI",
   "names": []
 }
diff --git a/static/shared/keyboard/keyboard.test.ts b/static/shared/keyboard/keyboard.test.ts
index c1a8b9d..560df14 100644
--- a/static/shared/keyboard/keyboard.test.ts
+++ b/static/shared/keyboard/keyboard.test.ts
@@ -33,6 +33,11 @@
     expect(fn).toBeCalled();
   });
 
+  it('fires a callback when a key is capitalized', () => {
+    document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Y' }));
+    expect(fn).toBeCalled();
+  });
+
   it('skips callback when a meta key is used', () => {
     document.dispatchEvent(new KeyboardEvent('keydown', { key: 'y', metaKey: true }));
     expect(fn).not.toBeCalled();
diff --git a/static/shared/keyboard/keyboard.ts b/static/shared/keyboard/keyboard.ts
index 02d12c7..5b49d47 100644
--- a/static/shared/keyboard/keyboard.ts
+++ b/static/shared/keyboard/keyboard.ts
@@ -61,7 +61,7 @@
   }
 
   private handleKeyPress(e: KeyboardEvent) {
-    for (const handler of this.handlers[e.key] ?? new Set()) {
+    for (const handler of this.handlers[e.key.toLowerCase()] ?? new Set()) {
       if (handler.target && handler.target !== e.target) {
         return;
       }