{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.
<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>
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.
directive_handlers.c = directive_handlers.comment;
function renderChordpro(text) {
directive_handlers.c = directive_handlers.comment;
function renderChordpro(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 verseLines = [];
let verseHasChords = false;
let verseLines = [];
let verseHasChords = false;
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 = [];
} 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)
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 {