Skip to content

Commit b644a01

Browse files
Refactor module init (#2015)
1 parent 0c165c6 commit b644a01

19 files changed

+186
-259
lines changed

assets/js/content.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { settingsStore } from './settings-store'
55
* Updates "Run in Livebook" badges to link to a notebook
66
* corresponding to the current documentation page.
77
*/
8-
export function initialize () {
8+
9+
window.addEventListener('swup:page:view', initialize)
10+
initialize()
11+
12+
function initialize () {
913
const notebookPath = window.location.pathname.replace(/(\.html)?$/, '.livemd')
1014
const notebookUrl = encodeURIComponent(new URL(notebookPath, window.location.href).toString())
1115

assets/js/copy-button.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ let buttonTemplate
77
/**
88
* Initializes copy buttons.
99
*/
10-
export function initialize () {
10+
11+
window.addEventListener('swup:page:view', initialize)
12+
initialize()
13+
14+
function initialize () {
1115
if (!('clipboard' in navigator)) return
1216

1317
qsAll('pre:has(> code:first-child):not(:has(.copy-button))').forEach(pre => {

assets/js/entry/html.js

Lines changed: 19 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,19 @@
1-
import { onDocumentReady } from '../helpers'
2-
import { initialize as initTabsets } from '../tabsets'
3-
import { initialize as initContent } from '../content'
4-
import { initialize as initSidebarDrawer } from '../sidebar/sidebar-drawer'
5-
import { initialize as initSearch } from '../search-bar'
6-
import { initialize as initVersions } from '../sidebar/sidebar-version-select'
7-
import { initialize as initSearchPage } from '../search-page'
8-
import { initialize as initTheme } from '../theme'
9-
import { initialize as initMakeup } from '../makeup'
10-
import { initialize as initKeyboardShortcuts } from '../keyboard-shortcuts'
11-
import { initialize as initQuickSwitch } from '../quick-switch'
12-
import { initialize as initTooltips } from '../tooltips/tooltips'
13-
import { initialize as initHintsPage } from '../tooltips/hint-page'
14-
import { initialize as initCopyButton } from '../copy-button'
15-
import { initialize as initSettings } from '../settings'
16-
import { initialize as initPreview} from '../preview'
17-
18-
import Swup from 'swup'
19-
import SwupA11yPlugin from '@swup/a11y-plugin'
20-
import SwupProgressPlugin from '@swup/progress-plugin'
21-
22-
onDocumentReady(() => {
23-
const params = new URLSearchParams(window.location.search)
24-
const isEmbedded = window.self !== window.parent
25-
const isPreview = params.has('preview')
26-
const isHint = params.has('hint')
27-
28-
initTheme()
29-
30-
initTabsets()
31-
initContent()
32-
initMakeup()
33-
initTooltips()
34-
initCopyButton()
35-
36-
if (isPreview && isEmbedded) {
37-
initPreview()
38-
} if (isHint && isEmbedded) {
39-
initHintsPage()
40-
} else {
41-
if (window.location.protocol !== 'file:') {
42-
new Swup({
43-
animationSelector: false,
44-
containers: ['#main'],
45-
ignoreVisit: (url) => {
46-
const path = url.split('#')[0]
47-
return path === window.location.pathname ||
48-
path === window.location.pathname + '.html'
49-
},
50-
hooks: {
51-
'page:view': () => {
52-
initTabsets()
53-
initContent()
54-
initMakeup()
55-
initTooltips()
56-
initCopyButton()
57-
58-
initSearch()
59-
initSearchPage()
60-
initSettings()
61-
}
62-
},
63-
linkSelector: 'a[href]:not([href^="/"]):not([href^="http"])',
64-
plugins: [new SwupA11yPlugin(), new SwupProgressPlugin({delay: 500})]
65-
})
66-
}
67-
68-
initVersions()
69-
initKeyboardShortcuts()
70-
initQuickSwitch()
71-
72-
initSidebarDrawer()
73-
initSearch()
74-
initSearchPage()
75-
initSettings()
76-
}
77-
})
1+
// Load preview & hint-page first because they could remove DOM.
2+
// This prevents later modules doing unnecessary work.
3+
import '../preview'
4+
import '../tooltips/hint-page'
5+
// The remaining modules are loaded in order of visible impact.
6+
import '../theme'
7+
import '../sidebar/sidebar-drawer'
8+
import '../sidebar/sidebar-version-select'
9+
import '../tabsets'
10+
import '../content'
11+
import '../makeup'
12+
import '../search-bar'
13+
import '../tooltips/tooltips'
14+
import '../copy-button'
15+
import '../search-page'
16+
import '../settings'
17+
import '../keyboard-shortcuts'
18+
import '../quick-switch'
19+
import '../swup'

assets/js/globals.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
const params = new URLSearchParams(window.location.search)
2+
3+
export const isEmbedded = window.self !== window.parent
4+
5+
export const isPreview = params.has('preview')
6+
7+
export const isHint = params.has('hint')
8+
19
// These variables are set by other scripts (e.g. generated by the docs task).
210

311
export function getSidebarNodes () {

assets/js/helpers.js

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,17 @@ export function getCurrentPageSidebarType () {
4646
return document.getElementById('main').dataset.type
4747
}
4848

49+
const headingTagNames = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']
50+
4951
/**
5052
* Finds an element by a URL hash (e.g. a function section).
5153
*
52-
* @param {String} hash The hash part of a URL.
5354
* @param {Boolean} anything Whether or not to support any link to any header.
5455
* @returns {HTMLElement|null} The relevant element.
5556
*/
56-
export function descriptionElementFromHash (hash, anything = false) {
57+
export function descriptionElementFromHash (anything = false) {
58+
const hash = window.location.hash.replace(/^#/, '')
59+
5760
if (!hash) {
5861
if (!anything) {
5962
return null
@@ -71,40 +74,25 @@ export function descriptionElementFromHash (hash, anything = false) {
7174
}
7275

7376
// Matches a subheader in particular
74-
if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(element.tagName.toLowerCase())) {
75-
return toNextHeader(element)
76-
}
77-
78-
return null
79-
}
80-
81-
function toNextHeader (element) {
82-
const elements = [element]
83-
let nextElement = element.nextElementSibling
84-
const tagName = element.tagName.toLowerCase()
85-
86-
while (nextElement) {
87-
const nextElementTagName = nextElement.tagName.toLowerCase()
88-
if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(nextElementTagName) && nextElementTagName <= tagName) {
89-
nextElement = null
90-
} else {
91-
elements.push(nextElement)
92-
nextElement = nextElement.nextElementSibling
77+
if (headingTagNames.includes(element.tagName)) {
78+
const div = document.createElement('div')
79+
const nodes = [element]
80+
81+
// Capture all nodes under the current heading level.
82+
let node = element
83+
while ((node = node.nextSibling)) {
84+
if (headingTagNames.includes(node.tagName) && node.tagName <= element.tagName) {
85+
break
86+
} else {
87+
nodes.push(node)
88+
}
9389
}
94-
}
9590

96-
const div = document.createElement('div')
97-
div.append(...elements)
98-
return div
99-
}
91+
div.append(...nodes)
92+
return div
93+
}
10094

101-
/**
102-
* Returns current location hash without the leading hash character.
103-
*
104-
* @returns {String}
105-
*/
106-
export function getLocationHash () {
107-
return window.location.hash.replace(/^#/, '')
95+
return null
10896
}
10997

11098
/**

assets/js/keyboard-shortcuts.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { cycleTheme } from './theme'
66
import { openQuickSwitchModal } from './quick-switch'
77
import { closeModal, isModalOpen } from './modal'
88
import { openSettingsModal } from './settings'
9+
import { isEmbedded } from './globals'
910

1011
const HELP_MODAL_BODY_SELECTOR = '#settings-modal-content'
1112

@@ -60,10 +61,9 @@ const state = {
6061
}
6162

6263
/**
63-
* Registers keyboard shortcuts and sets up a help modal
64-
* listing all available options.
64+
* Registers keyboard shortcuts.
6565
*/
66-
export function initialize () {
66+
if (!isEmbedded) {
6767
document.addEventListener('keydown', handleKeyDown)
6868
document.addEventListener('keyup', handleKeyUp)
6969
}

assets/js/makeup.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ const HIGHLIGHT_CLASS = 'hll'
55
/**
66
* Sets up dynamic behaviour for code blocks processed with *makeup*.
77
*/
8+
9+
window.addEventListener('swup:page:view', initialize)
10+
initialize()
11+
812
export function initialize () {
913
// Hovering over a delimiter (bracket, parenthesis, do/end)
1014
// highlights the relevant pair of delimiters.

assets/js/preview.js

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,32 @@
1-
import { getLocationHash, descriptionElementFromHash } from './helpers'
1+
import { isEmbedded, isPreview } from './globals'
2+
import { descriptionElementFromHash } from './helpers'
23

3-
export function initialize () {
4-
const previewing = descriptionElementFromHash(getLocationHash(), true)
4+
if (isPreview && isEmbedded) {
5+
const previewing = descriptionElementFromHash(true)
56

67
if (previewing) {
7-
preview(previewing)
8-
}
9-
}
8+
document.body.classList.add('preview')
9+
document.getElementById('content').replaceChildren(...previewing.childNodes)
1010

11-
function preview (previewing) {
12-
replaceContents(previewing)
13-
makeLinksOpenInParent()
14-
scrollToTop()
15-
sendPreviewInfoToParent()
11+
// Make links open in parent.
12+
const links = document.getElementsByTagName('a:not([target=_blank]')
13+
for (const element of links) {
14+
element.setAttribute('target', '_parent')
15+
}
1616

17-
window.addEventListener('resize', event => {
18-
sendPreviewInfoToParent()
19-
})
17+
window.scrollTo(0, 0)
18+
// Stop iframe scrolling affecting parent by setting body position to fixed.
19+
document.body.style.position = 'fixed'
20+
// Defer preview message until all other scripts have run.
21+
setTimeout(sendPreviewInfoToParent)
22+
window.addEventListener('resize', sendPreviewInfoToParent)
23+
}
2024
}
2125

2226
function sendPreviewInfoToParent () {
23-
const maxHeight = document.body.scrollHeight
24-
const contentHeight = document.getElementById('content').parentElement.offsetHeight
2527
const message = {
2628
type: 'preview',
27-
maxHeight,
28-
contentHeight
29+
contentHeight: document.getElementById('content').parentElement.offsetHeight
2930
}
3031
window.parent.postMessage(message, '*')
3132
}
32-
33-
function makeLinksOpenInParent () {
34-
const links = document.getElementsByTagName('a')
35-
for (const element of links) {
36-
if (element.getAttribute('target') !== '_blank') {
37-
element.setAttribute('target', '_parent')
38-
}
39-
}
40-
}
41-
42-
function scrollToTop () {
43-
window.scrollTo(0, 0)
44-
}
45-
46-
function replaceContents (previewing) {
47-
document.body.classList.add('preview')
48-
const content = document.getElementById('content')
49-
content.innerHTML = previewing.innerHTML
50-
}

assets/js/quick-switch.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { debounce, el, qs, qsAll } from './helpers'
22
import { openModal } from './modal'
33
import quickSwitchModalBodyHtml from './handlebars/templates/quick-switch-modal-body.html'
4+
import { isEmbedded } from './globals'
45

56
const HEX_DOCS_ENDPOINT = 'https://hexdocs.pm/%%'
67
const OTP_DOCS_ENDPOINT = 'https://www.erlang.org/doc/apps/%%'
@@ -71,7 +72,13 @@ const state = {
7172
/**
7273
* Initializes the quick switch modal.
7374
*/
74-
export function initialize () {
75+
76+
if (!isEmbedded) {
77+
window.addEventListener('swup:page:view', initialize)
78+
initialize()
79+
}
80+
81+
function initialize () {
7582
qsAll(QUICK_SWITCH_LINK_SELECTOR).forEach(element => {
7683
element.addEventListener('click', openQuickSwitchModal)
7784
})

assets/js/search-bar.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
AUTOCOMPLETE_CONTAINER_SELECTOR,
1111
AUTOCOMPLETE_SUGGESTION_LIST_SELECTOR
1212
} from './autocomplete/autocomplete-list'
13+
import { isEmbedded } from './globals'
1314
import { isAppleOS, qs } from './helpers'
1415

1516
const SEARCH_INPUT_SELECTOR = 'form.search-bar input'
@@ -18,7 +19,13 @@ const SEARCH_CLOSE_BUTTON_SELECTOR = 'form.search-bar .search-close-button'
1819
/**
1920
* Initializes the sidebar search box.
2021
*/
21-
export function initialize () {
22+
23+
if (!isEmbedded) {
24+
window.addEventListener('swup:page:view', initialize)
25+
initialize()
26+
}
27+
28+
function initialize () {
2229
addEventListeners()
2330

2431
window.onTogglePreviewClick = function (event, open) {

assets/js/search-page.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ lunr.Pipeline.registerFunction(docTrimmerFunction, 'docTrimmer')
1919
*
2020
* Activates only on the `/search.html` page.
2121
*/
22-
export function initialize () {
22+
23+
window.addEventListener('swup:page:view', initialize)
24+
initialize()
25+
26+
function initialize () {
2327
const pathname = window.location.pathname
2428
if (pathname.endsWith('/search.html') || pathname.endsWith('/search')) {
2529
const query = getQueryParamByName('q')

assets/js/settings.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,13 @@ const modalTabs = [
2525
/**
2626
* Sets up the settings modal.
2727
*/
28-
export function initialize () {
29-
addEventListeners()
30-
}
3128

32-
function addEventListeners () {
29+
window.addEventListener('swup:page:view', initialize)
30+
initialize()
31+
32+
function initialize () {
3333
qsAll(SETTINGS_LINK_SELECTOR).forEach(element => {
34-
element.addEventListener('click', event => {
35-
openSettingsModal()
36-
})
34+
element.addEventListener('click', openSettingsModal)
3735
})
3836
}
3937

0 commit comments

Comments
 (0)