]> piware.de Git - chordpro-web.git/commitdiff
Optionally repeat the chorus where it is recalled main
authorMartin Pitt <martin@piware.de>
Wed, 23 Sep 2026 21:29:24 +0000 (23:29 +0200)
committerMartin Pitt <martin@piware.de>
Wed, 23 Sep 2026 21:29:29 +0000 (23:29 +0200)
{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.

chordpro-web.html
chordpro-web.js

index 63f981d7488ceff8e7359075c7f4b39fc96b2582..cf178833df17a17c01a2a9627cee78f72abfe829 100644 (file)
@@ -17,6 +17,7 @@
   <hr />
 
   <input id="open" type="file" />
   <hr />
 
   <input id="open" type="file" />
+  <label><input id="repeat-chorus" type="checkbox" /> repeat chorus</label>
 
   <script type="module" src="chordpro-web.js"></script>
 </body>
 
   <script type="module" src="chordpro-web.js"></script>
 </body>
index a7914c193898d6b88b76f843671923a32cd91986..b3c752c3f1dd7d4ff1833ae9c1d1e835466ef7bd 100644 (file)
@@ -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 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.
 
 // 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) {
 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
     const song = [];
     const chords = [];
     let chorus = null; // content of the chorus being parsed, if any
+    let lastChorus = null;
     let verseLines = [];
     let verseHasChords = false;
 
     let verseLines = [];
     let verseHasChords = false;
 
@@ -101,7 +108,8 @@ function renderChordpro(text) {
     const endChorus = () => {
         if (chorus === null)
             return;
     const endChorus = () => {
         if (chorus === null)
             return;
-        song.push(html`<div class="chorus">${chorus}</div>`);
+        lastChorus = html`<div class="chorus">${chorus}</div>`;
+        song.push(lastChorus);
         chorus = null;
     };
 
         chorus = null;
     };
 
@@ -120,6 +128,9 @@ function renderChordpro(text) {
                 chorus = [];
             } else if (['end_of_chorus', 'eoc'].includes(directive)) {
                 endChorus();
                 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)
             } 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()));
 
 
 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 {
 const loadURL = new URL(location).searchParams.get('l');
 if (loadURL) {
     try {