2020-07-12 11:10:56 +02:00
import { svg } from '../svg.js' ;
2020-06-26 15:04:22 +02:00
2024-03-22 13:28:38 +01:00
const addPrefix = ( str ) => ` user-content- ${ str } ` ;
const removePrefix = ( str ) => str . replace ( /^user-content-/ , '' ) ;
const hasPrefix = ( str ) => str . startsWith ( 'user-content-' ) ;
2024-03-08 09:53:01 +00:00
// scroll to anchor while respecting the `user-content` prefix that exists on the target
2024-03-22 13:28:38 +01:00
function scrollToAnchor ( encodedId ) {
if ( ! encodedId ) return ;
2024-03-21 07:02:53 +08:00
const id = decodeURIComponent ( encodedId ) ;
2024-03-22 13:28:38 +01:00
const prefixedId = addPrefix ( id ) ;
let el = document . getElementById ( prefixedId ) ;
2024-03-21 07:02:53 +08:00
// check for matching user-generated `a[name]`
if ( ! el ) {
2024-03-22 13:28:38 +01:00
const nameAnchors = document . getElementsByName ( prefixedId ) ;
2024-03-21 07:02:53 +08:00
if ( nameAnchors . length ) {
el = nameAnchors [ 0 ] ;
}
}
// compat for links with old 'user-content-' prefixed hashes
2024-03-22 13:28:38 +01:00
if ( ! el && hasPrefix ( id ) ) {
return document . getElementById ( id ) ? . scrollIntoView ( ) ;
2020-06-26 15:04:22 +02:00
}
2024-03-21 07:02:53 +08:00
2024-03-22 13:28:38 +01:00
el ? . scrollIntoView ( ) ;
2020-06-26 15:04:22 +02:00
}
2021-05-07 10:43:41 +02:00
export function initMarkupAnchors ( ) {
2024-03-21 07:02:53 +08:00
const markupEls = document . querySelectorAll ( '.markup' ) ;
if ( ! markupEls . length ) return ;
for ( const markupEl of markupEls ) {
// create link icons for markup headings, the resulting link href will remove `user-content-`
2024-03-22 13:28:38 +01:00
for ( const heading of markupEl . querySelectorAll ( 'h1, h2, h3, h4, h5, h6' ) ) {
2024-03-21 07:02:53 +08:00
const a = document . createElement ( 'a' ) ;
a . classList . add ( 'anchor' ) ;
2024-03-22 13:28:38 +01:00
a . setAttribute ( 'href' , ` # ${ encodeURIComponent ( removePrefix ( heading . id ) ) } ` ) ;
2024-03-21 07:02:53 +08:00
a . innerHTML = svg ( 'octicon-link' ) ;
heading . prepend ( a ) ;
}
// remove `user-content-` prefix from links so they don't show in url bar when clicked
for ( const a of markupEl . querySelectorAll ( 'a[href^="#"]' ) ) {
const href = a . getAttribute ( 'href' ) ;
if ( ! href . startsWith ( '#user-content-' ) ) continue ;
2024-03-22 13:28:38 +01:00
a . setAttribute ( 'href' , ` # ${ removePrefix ( href . substring ( 1 ) ) } ` ) ;
2024-03-21 07:02:53 +08:00
}
// add `user-content-` prefix to user-generated `a[name]` link targets
// TODO: this prefix should be added in backend instead
for ( const a of markupEl . querySelectorAll ( 'a[name]' ) ) {
const name = a . getAttribute ( 'name' ) ;
if ( ! name ) continue ;
2024-03-22 13:28:38 +01:00
a . setAttribute ( 'name' , addPrefix ( a . name ) ) ;
2024-03-21 07:02:53 +08:00
}
2020-06-26 15:04:22 +02:00
2024-03-21 07:02:53 +08:00
for ( const a of markupEl . querySelectorAll ( 'a[href^="#"]' ) ) {
2024-03-08 09:53:01 +00:00
a . addEventListener ( 'click' , ( e ) => {
2024-03-22 13:28:38 +01:00
scrollToAnchor ( e . currentTarget . getAttribute ( 'href' ) ? . substring ( 1 ) ) ;
2024-03-08 09:53:01 +00:00
} ) ;
}
}
2024-03-22 13:28:38 +01:00
// scroll to anchor unless the browser has already scrolled somewhere during page load
if ( ! document . querySelector ( ':target' ) ) {
scrollToAnchor ( window . location . hash ? . substring ( 1 ) ) ;
}
2020-06-26 15:04:22 +02:00
}