|
258 | 258 | (function() { |
259 | 259 | const content = document.getElementById('content'); |
260 | 260 |
|
261 | | - function loadPage(path) { |
| 261 | + function loadPage(path, fragment) { |
262 | 262 | fetch(path) |
263 | 263 | .then(r => { if (!r.ok) throw new Error(r.status); return r.text(); }) |
264 | 264 | .then(md => { |
265 | 265 | content.innerHTML = marked.parse(md); |
266 | 266 | // Rewrite .md links to hash links |
267 | 267 | content.querySelectorAll('a[href]').forEach(a => { |
268 | 268 | const href = a.getAttribute('href'); |
269 | | - if (href && href.endsWith('.md') && !href.startsWith('http')) { |
270 | | - const base = path.substring(0, path.lastIndexOf('/') + 1); |
271 | | - const resolved = new URL(href, location.origin + '/' + base).pathname.replace(/^\//, ''); |
272 | | - a.setAttribute('href', '#' + resolved); |
| 269 | + if (href && !href.startsWith('http')) { |
| 270 | + const match = href.match(/^(.+\.md)(#.*)?$/); |
| 271 | + if (match) { |
| 272 | + const base = path.substring(0, path.lastIndexOf('/') + 1); |
| 273 | + const resolved = new URL(match[1], location.origin + '/' + base).pathname.replace(/^\//, ''); |
| 274 | + a.setAttribute('href', '#' + resolved + (match[2] || '')); |
| 275 | + } |
273 | 276 | } |
274 | 277 | }); |
275 | 278 | // Update active nav link |
276 | 279 | document.querySelectorAll('nav a').forEach(a => { |
277 | 280 | a.classList.toggle('active', a.getAttribute('href') === '#' + path); |
278 | 281 | }); |
| 282 | + if (fragment) { |
| 283 | + const el = document.getElementById(fragment); |
| 284 | + if (el) { el.scrollIntoView(); return; } |
| 285 | + } |
279 | 286 | window.scrollTo(0, 0); |
280 | 287 | }) |
281 | 288 | .catch(() => { content.innerHTML = '<p>Page not found.</p>'; }); |
282 | 289 | } |
283 | 290 |
|
284 | 291 | function onHashChange() { |
285 | | - const path = location.hash.slice(1) || 'README.md'; |
286 | | - loadPage(path); |
| 292 | + const raw = location.hash.slice(1) || 'README.md'; |
| 293 | + const parts = raw.match(/^(.+\.md)(?:#(.*))?$/); |
| 294 | + if (parts) { |
| 295 | + loadPage(parts[1], parts[2]); |
| 296 | + } else { |
| 297 | + loadPage(raw); |
| 298 | + } |
287 | 299 | } |
288 | 300 |
|
289 | 301 | window.addEventListener('hashchange', onHashChange); |
|
0 commit comments