blob: 0d5bb060a793dfed3ef86eceb537141df01900b9 [file] [log] [blame]
{
"version": 3,
"sources": ["jump.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\n// This file implements the behavior of the \"jump to identifer\" dialog for Go\n// package documentation, as well as the simple dialog that displays keyboard\n// shortcuts.\n\n// The DOM for the dialogs is at the bottom of content/static/html/pages/pkg_doc.tmpl.\n// The CSS is in content/static/css/stylesheet.css.\n\n// The dialog is activated by pressing the 'f' key. It presents a list\n// (#JumpDialog-list) of all Go identifiers displayed in the documentation.\n// Entering text in the dialog's text box (#JumpDialog-filter) restricts the\n// list to identifiers containing the text. Clicking on an identifier jumps to\n// its documentation.\n\n// This code is based on\n// https://go.googlesource.com/gddo/+/refs/heads/master/gddo-server/assets/site.js.\n// It was modified to remove the dependence on jquery and bootstrap.\n\nconst jumpDialog = document.querySelector<HTMLDialogElement>('.JumpDialog');\nconst jumpBody = jumpDialog?.querySelector<HTMLDivElement>('.JumpDialog-body');\nconst jumpList = jumpDialog?.querySelector<HTMLDivElement>('.JumpDialog-list');\nconst jumpFilter = jumpDialog?.querySelector<HTMLInputElement>('.JumpDialog-input');\nconst searchInput = document.querySelector<HTMLInputElement>('.js-searchFocus');\nconst doc = document.querySelector<HTMLDivElement>('.js-documentation');\n\nfunction loadPolyfill() {\n return import(`${window.location.origin}/third_party/dialog-polyfill/dialog-polyfill.esm.js`);\n}\n\nif (jumpDialog && !jumpDialog.showModal) {\n loadPolyfill().then(({ default: dialogPolyfill }) => {\n dialogPolyfill.registerDialog(jumpDialog);\n });\n}\n\ninterface JumpListItem {\n link: HTMLAnchorElement;\n name: string;\n kind: string;\n lower: string;\n}\n\nlet jumpListItems: JumpListItem[] | undefined; // All the identifiers in the doc; computed only once.\n\n// collectJumpListItems returns a list of items, one for each identifier in the\n// documentation on the current page.\n//\n// It uses the data-kind attribute generated in the documentation HTML to find\n// the identifiers and their id attributes.\n//\n// If there are no data-kind attributes, then we have older doc; fall back to\n// a less precise method.\nfunction collectJumpListItems() {\n const items = [];\n if (!doc) return;\n for (const el of doc.querySelectorAll('[data-kind]')) {\n items.push(newJumpListItem(el));\n }\n\n // Clicking on any of the links closes the dialog.\n for (const item of items) {\n item.link.addEventListener('click', function () {\n jumpDialog?.close();\n });\n }\n // Sort case-insensitively by identifier name.\n items.sort(function (a, b) {\n return a.lower.localeCompare(b.lower);\n });\n return items;\n}\n\n// newJumpListItem creates a new item for the DOM element el.\n// An item is an object with:\n// - name: the element's id (which is the identifer name)\n// - kind: the element's kind (function, variable, etc.),\n// - link: a link ('a' tag) to the element\n// - lower: the name in lower case, just for sorting\nfunction newJumpListItem(el: Element): JumpListItem {\n const a = document.createElement('a');\n const name = el.getAttribute('id');\n a.setAttribute('href', '#' + name);\n a.setAttribute('tabindex', '-1');\n const kind = el.getAttribute('data-kind');\n return {\n link: a,\n name: name ?? '',\n kind: kind ?? '',\n lower: name?.toLowerCase() ?? '', // for sorting\n };\n}\n\nlet lastFilterValue: string; // The last contents of the filter text box.\nlet activeJumpItem = -1; // The index of the currently active item in the list.\n\n// updateJumpList sets the elements of the dialog list to\n// everything whose name contains filter.\nfunction updateJumpList(filter: string) {\n lastFilterValue = filter;\n if (!jumpListItems) {\n jumpListItems = collectJumpListItems();\n }\n setActiveJumpItem(-1);\n\n // Remove all children from list.\n while (jumpList?.firstChild) {\n jumpList.firstChild.remove();\n }\n\n if (filter) {\n // A filter is set. We treat the filter as a substring that can appear in\n // an item name (case insensitive), and find the following matches - in\n // order of priority:\n //\n // 1. Exact matches (the filter matches the item's name exactly)\n // 2. Prefix matches (the item's name starts with filter)\n // 3. Infix matches (the filter is a substring of the item's name)\n const filterLowerCase = filter.toLowerCase();\n\n const exactMatches = [];\n const prefixMatches = [];\n const infixMatches = [];\n\n // makeLinkHtml creates the link name HTML for a list item. item is the DOM\n // item. item.name.substr(boldStart, boldEnd) will be bolded.\n const makeLinkHtml = (item: JumpListItem, boldStart: number, boldEnd: number) => {\n return (\n item.name.substring(0, boldStart) +\n '<b>' +\n item.name.substring(boldStart, boldEnd) +\n '</b>' +\n item.name.substring(boldEnd)\n );\n };\n\n for (const item of jumpListItems ?? []) {\n const nameLowerCase = item.name.toLowerCase();\n\n if (nameLowerCase === filterLowerCase) {\n item.link.innerHTML = makeLinkHtml(item, 0, item.name.length);\n exactMatches.push(item);\n } else if (nameLowerCase.startsWith(filterLowerCase)) {\n item.link.innerHTML = makeLinkHtml(item, 0, filter.length);\n prefixMatches.push(item);\n } else {\n const index = nameLowerCase.indexOf(filterLowerCase);\n if (index > -1) {\n item.link.innerHTML = makeLinkHtml(item, index, index + filter.length);\n infixMatches.push(item);\n }\n }\n }\n\n for (const item of exactMatches.concat(prefixMatches).concat(infixMatches)) {\n jumpList?.appendChild(item.link);\n }\n } else {\n if (!jumpListItems || jumpListItems.length === 0) {\n const msg = document.createElement('i');\n msg.innerHTML = 'There are no identifiers on this page.';\n jumpList?.appendChild(msg);\n }\n // No filter set; display all items in their existing order.\n for (const item of jumpListItems ?? []) {\n item.link.innerHTML = item.name + ' <i>' + item.kind + '</i>';\n jumpList?.appendChild(item.link);\n }\n }\n\n if (jumpBody) {\n jumpBody.scrollTop = 0;\n }\n if (jumpListItems?.length && jumpList && jumpList.children.length > 0) {\n setActiveJumpItem(0);\n }\n}\n\n// Set the active jump item to n.\nfunction setActiveJumpItem(n: number) {\n const cs = jumpList?.children as HTMLCollectionOf<HTMLElement> | null | undefined;\n if (!cs || !jumpBody) {\n return;\n }\n if (activeJumpItem >= 0) {\n cs[activeJumpItem].classList.remove('JumpDialog-active');\n }\n if (n >= cs.length) {\n n = cs.length - 1;\n }\n if (n >= 0) {\n cs[n].classList.add('JumpDialog-active');\n\n // Scroll so the active item is visible.\n // For some reason cs[n].scrollIntoView() doesn't behave as I'd expect:\n // it moves the entire dialog box in the viewport.\n\n // Get the top and bottom of the active item relative to jumpBody.\n const activeTop = cs[n].offsetTop - cs[0].offsetTop;\n const activeBottom = activeTop + cs[n].clientHeight;\n if (activeTop < jumpBody.scrollTop) {\n // Off the top; scroll up.\n jumpBody.scrollTop = activeTop;\n } else if (activeBottom > jumpBody.scrollTop + jumpBody.clientHeight) {\n // Off the bottom; scroll down.\n jumpBody.scrollTop = activeBottom - jumpBody.clientHeight;\n }\n }\n activeJumpItem = n;\n}\n\n// Increment the activeJumpItem by delta.\nfunction incActiveJumpItem(delta: number) {\n if (activeJumpItem < 0) {\n return;\n }\n let n = activeJumpItem + delta;\n if (n < 0) {\n n = 0;\n }\n setActiveJumpItem(n);\n}\n\n// Pressing a key in the filter updates the list (if the filter actually changed).\njumpFilter?.addEventListener('keyup', function () {\n if (jumpFilter.value.toUpperCase() != lastFilterValue.toUpperCase()) {\n updateJumpList(jumpFilter.value);\n }\n});\n\n// Pressing enter in the filter selects the first element in the list.\njumpFilter?.addEventListener('keydown', function (event) {\n const upArrow = 38;\n const downArrow = 40;\n const enterKey = 13;\n switch (event.which) {\n case upArrow:\n incActiveJumpItem(-1);\n event.preventDefault();\n break;\n case downArrow:\n incActiveJumpItem(1);\n event.preventDefault();\n break;\n case enterKey:\n if (activeJumpItem >= 0) {\n if (jumpList) {\n (jumpList.children[activeJumpItem] as HTMLElement).click();\n }\n }\n break;\n }\n});\n\nconst shortcutsDialog = document.querySelector<HTMLDialogElement>('.ShortcutsDialog');\nif (shortcutsDialog && !shortcutsDialog.showModal) {\n loadPolyfill().then(({ default: dialogPolyfill }) => {\n dialogPolyfill.registerDialog(shortcutsDialog);\n });\n}\n\n// Keyboard shortcuts:\n// - Pressing '/' focuses the search box\n// - Pressing 'f' or 'F' opens the jump-to-identifier dialog.\n// - Pressing '?' opens up the shortcut dialog.\n// Ignore a keypress if a dialog is already open, or if it is pressed on a\n// component that wants to consume it.\ndocument.addEventListener('keypress', function (e) {\n if (jumpDialog?.open || shortcutsDialog?.open) {\n return;\n }\n const target = e.target as HTMLElement | null;\n const t = target?.tagName;\n if (t == 'INPUT' || t == 'SELECT' || t == 'TEXTAREA') {\n return;\n }\n if (target?.contentEditable == 'true') {\n return;\n }\n if (e.metaKey || e.ctrlKey) {\n return;\n }\n const ch = String.fromCharCode(e.which);\n switch (ch) {\n case 'f':\n case 'F':\n e.preventDefault();\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n jumpDialog?.showModal();\n updateJumpList('');\n break;\n case '?':\n shortcutsDialog?.showModal();\n break;\n case '/':\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 break;\n }\n});\n\nconst jumpOutlineInput = document.querySelector('.js-jumpToInput');\nif (jumpOutlineInput) {\n jumpOutlineInput.addEventListener('click', () => {\n if (jumpFilter) {\n jumpFilter.value = '';\n }\n jumpDialog?.showModal();\n updateJumpList('');\n });\n}\n"],
"mappings": "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",
"names": []
}