Fix user-defined markup links targets (#29305) (#29666)

Backport #29305 by @DanielMatiasCarvalho

This seeks to fix the bug reported on issue #29196.

Cause:
ID's with custom characters (- , _ , etc.), were not linking correctly
in the Markdown file when rendered in the browser because the ID in the
respective destinies would be different than the one in anchor, while
for IDs with only letters, the ID would be the same.

Fix:
It was suggested that to fix this bug, it should more or less like
GitHub does it. While in gitea the anchors would be put in HTML like
this:
```
<p dir="auto"><a href="#user-content-_toc152597800" rel="nofollow">Review</a></p>
<p dir="auto"><a href="#user-content-_toc152597802" rel="nofollow">Staging</a></p>
<p dir="auto"><a href="#user-content-_toc152597803" rel="nofollow">Development</a></p>
<p dir="auto"><a href="#user-content-_toc152597828" rel="nofollow">Testing</a></p>
<p dir="auto"><a href="#user-content-_toc152597829" rel="nofollow">Unit-tests</a></p>

```
In GitHub, the same anchor's href properties would be the same without
"user-content-" trailing behind.

So my code made sure to change those anchors, so it would not include
"user-content-" and then add respective Event Listeners so it would
scroll into the supposed places.

Fixes: #29196

Co-authored-by: DC <106393991+DanielMatiasCarvalho@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit bfc7c8a5985825ec28f3feefe1d050ec52525097)
This commit is contained in:
Giteabot 2024-03-09 00:53:36 +08:00 committed by Earl Warren
parent e811f3b185
commit 03caefbb02
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00

View file

@ -2,6 +2,7 @@ import {svg} from '../svg.js';
const headingSelector = '.markup h1, .markup h2, .markup h3, .markup h4, .markup h5, .markup h6'; const headingSelector = '.markup h1, .markup h2, .markup h3, .markup h4, .markup h5, .markup h6';
// scroll to anchor while respecting the `user-content` prefix that exists on the target
function scrollToAnchor(hash, initial) { function scrollToAnchor(hash, initial) {
// abort if the browser has already scrolled to another anchor during page load // abort if the browser has already scrolled to another anchor during page load
if (initial && document.querySelector(':target')) return; if (initial && document.querySelector(':target')) return;
@ -19,6 +20,7 @@ function scrollToAnchor(hash, initial) {
export function initMarkupAnchors() { export function initMarkupAnchors() {
if (!document.querySelector('.markup')) return; if (!document.querySelector('.markup')) return;
// create link icons for markup headings, the resulting link href will remove `user-content-`
for (const heading of document.querySelectorAll(headingSelector)) { for (const heading of document.querySelectorAll(headingSelector)) {
const originalId = heading.id.replace(/^user-content-/, ''); const originalId = heading.id.replace(/^user-content-/, '');
const a = document.createElement('a'); const a = document.createElement('a');
@ -31,5 +33,18 @@ export function initMarkupAnchors() {
heading.prepend(a); heading.prepend(a);
} }
// handle user-defined `name` anchors like `[Link](#link)` linking to `<a name="link"></a>Link`
for (const a of document.querySelectorAll('.markup a[href^="#"]')) {
const href = a.getAttribute('href');
if (!href.startsWith('#user-content-')) continue;
const originalId = href.replace(/^#user-content-/, '');
a.setAttribute('href', `#${encodeURIComponent(originalId)}`);
if (document.getElementsByName(originalId).length !== 1) {
a.addEventListener('click', (e) => {
scrollToAnchor(e.currentTarget.getAttribute('href'), false);
});
}
}
scrollToAnchor(window.location.hash, true); scrollToAnchor(window.location.hash, true);
} }