transfeminine-science/themes/tfsci/assets/main.ts

164 lines
4.9 KiB
TypeScript

import { onRestoreAnchor, onSaveAnchor } from './functions/scroll'
import { on, ready } from './functions/browser'
import tippy from 'tippy.js'
Promise.resolve()
.then(onImageFallback)
.then(() => ready)
.then(onTableStyle)
.then(onMobileHeader)
.then(onLanguageSwitch)
.then(onRestoreAnchor)
.then(onSaveAnchor)
.then(onRestoreEmailAddress)
.then(onReferenceLinks)
.then(onAbbreviation)
.then(onLinkTitle)
function onTableStyle() {
const content = document.querySelector('article.content')
if (!content) return
const template = document.createElement('section')
template.classList.add('table-container')
for (const element of content.querySelectorAll('table')) {
element.classList.add(
'table',
'is-bordered',
'is-narrow',
'is-hoverable',
'is-fullwidth',
'is-no-wrap',
'is-align-middle'
)
const container = template.cloneNode()
container.appendChild(element.cloneNode(true))
element.replaceWith(container)
}
}
function onMobileHeader() {
on(document, 'click', '.navbar-burger', (event) => {
const target = event.target as HTMLElement
const menu = target.closest('.navbar')!.querySelector('.navbar-menu')!
menu.classList.toggle('is-active')
target.closest('.navbar-burger')!.ariaExpanded = String(menu.classList.contains('is-active'))
})
}
function onLanguageSwitch() {
on(document, 'click', '.navbar-item.language a', (event) => {
event.preventDefault()
const target = event.target as HTMLAnchorElement
const url = new URL(target.href)
url.hash = location.hash
location.replace(url.toString())
})
}
function onRestoreEmailAddress() {
const content = document.querySelector('article.content')
if (!content) return
for (const link of content.querySelectorAll<HTMLAnchorElement>('a[href^="mailto:"]')) {
const address = atob(link.pathname)
link.href = `mailto:${address}`
if (link.textContent === '') {
link.textContent = address
}
}
}
function onReferenceLinks() {
const content = document.querySelector('article.content')
if (!content) return
const citationMap = new Map<string, HTMLElement>()
for (const link of content.querySelectorAll<HTMLAnchorElement>('#references + ul a')) {
citationMap.set(link.href, link.closest('li')!)
}
if (citationMap.size === 0) return
const links = Array.from(content.querySelectorAll('a'))
.filter((link) => link.closest('#references + ul') === null)
.filter((link) => citationMap.has(link.href))
tippy(links, {
touch: 'hold',
theme: 'light-border',
interactive: true,
appendTo: document.body,
content(ref) {
const cite = citationMap.get((ref as HTMLAnchorElement).href)
const citation = document.createDocumentFragment()
for (const node of cite!.childNodes) {
citation.append(node.cloneNode(true))
}
return citation
},
})
}
function onAbbreviation() {
const content = document.querySelector('article.content')
if (!content) return
tippy(content.querySelectorAll('abbr[title][data-origin]'), {
touch: 'hold',
theme: 'light-border',
interactive: true,
appendTo: document.body,
content(ref) {
const fragment = document.createDocumentFragment()
fragment.append(
ref.getAttribute('data-origin')!,
document.createElement('br'),
document.createTextNode(`${ref.getAttribute('title')} (${ref.textContent})`)
)
return fragment
},
})
}
function onLinkTitle() {
for (const link of document.links) {
if (link.title) continue
let title = getTitle(link as HTMLAnchorElement)
if (!title) continue
title = decodeURIComponent(title)
if (link.textContent === title) continue
link.title = title
}
function getTitle(link: HTMLAnchorElement) {
const host = link.host
const path = link.pathname
let title: string | undefined
if (host === 'doi.org') return path.slice(1)
if (host === 'files.transfemscience.org' && path.startsWith('/pdfs/')) return path.slice(6)
if (host.endsWith('wikipedia.org') && path.startsWith('/wiki/')) {
title = path.slice(path.lastIndexOf('/') + 1)
if (link.hash) title = link.hash.slice(1) + ' on ' + link.title
return title.replace(/_/g, ' ')
}
}
}
function onImageFallback() {
document.addEventListener('load', onLoad, { capture: true })
document.addEventListener('error', onError, { capture: true })
const attribute = 'data-origin'
function onLoad(event: Event) {
if (!isHTMLImageElement(event.target)) return
event.target.removeAttribute(attribute)
}
function onError(event: Event) {
if (!isHTMLImageElement(event.target)) return
const origin = event.target.getAttribute(attribute)
if (origin) event.target.src = origin
event.target.removeAttribute(attribute)
}
function isHTMLImageElement(target: EventTarget | null): target is HTMLImageElement {
if (target === null) return false
return (target as HTMLElement).tagName === 'IMG'
}
}