From: Martin Pitt Date: Wed, 23 Sep 2026 21:29:24 +0000 (+0200) Subject: Optionally repeat the chorus where it is recalled X-Git-Url: https://piware.de/gitweb/?a=commitdiff_plain;p=chordpro-web.git Optionally repeat the chorus where it is recalled {chorus} recalls the last chorus, and files in the wild write {c: Chorus} for the same purpose. Both render as a "Chorus" comment by default. With `chorus=repeat` in the URL, or the equivalent checkbox at the bottom, the chorus is shown again in full instead. --- diff --git a/chordpro-web.html b/chordpro-web.html index 63f981d..cf17883 100644 --- a/chordpro-web.html +++ b/chordpro-web.html @@ -17,6 +17,7 @@
+ diff --git a/chordpro-web.js b/chordpro-web.js index a7914c1..b3c752c 100644 --- a/chordpro-web.js +++ b/chordpro-web.js @@ -4,6 +4,11 @@ import { html, render } from 'https://cdn.jsdelivr.net/gh/lit/dist@3.3.3/core/li const re_directive = /^\{(\w+)(?::\s*(.*?))?\s*\}$/; const el_song = document.getElementById('song'); +const el_repeat_chorus = document.getElementById('repeat-chorus'); +let songText = ''; // for re-rendering with changed options + +// The URL is the single source of truth for options, so that views can be shared +const repeatChorus = () => new URL(location).searchParams.get('chorus') === 'repeat'; // The chord span is emitted even when empty, so that lyrics before the first // chord of a line still end up in the lyrics row; see chordpro.css. @@ -84,9 +89,11 @@ directive_handlers.st = directive_handlers.subtitle; directive_handlers.c = directive_handlers.comment; function renderChordpro(text) { + songText = text; const song = []; const chords = []; let chorus = null; // content of the chorus being parsed, if any + let lastChorus = null; let verseLines = []; let verseHasChords = false; @@ -101,7 +108,8 @@ function renderChordpro(text) { const endChorus = () => { if (chorus === null) return; - song.push(html`
${chorus}
`); + lastChorus = html`
${chorus}
`; + song.push(lastChorus); chorus = null; }; @@ -120,6 +128,9 @@ function renderChordpro(text) { chorus = []; } else if (['end_of_chorus', 'eoc'].includes(directive)) { endChorus(); + } else if (directive === 'chorus' || (['comment', 'c'].includes(directive) && /^chorus:?$/i.test(value))) { + // {chorus} recalls the last chorus; files in the wild use a comment for that + target.push(repeatChorus() && lastChorus ? lastChorus : directive_handlers.comment(value || 'Chorus')); } else { const handler = directive_handlers[directive]; if (handler) @@ -159,6 +170,17 @@ function renderChordpro(text) { document.getElementById('open').addEventListener('change', async ev => renderChordpro(await ev.target.files[0].text())); +el_repeat_chorus.checked = repeatChorus(); +el_repeat_chorus.addEventListener('change', () => { + const url = new URL(location); + if (el_repeat_chorus.checked) + url.searchParams.set('chorus', 'repeat'); + else + url.searchParams.delete('chorus'); + history.replaceState(null, '', url); + renderChordpro(songText); +}); + const loadURL = new URL(location).searchParams.get('l'); if (loadURL) { try {