const el_song = document.getElementById('song');
const song = [];
+// 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.
+const segment = (chord, text) => html`<span class="seg"><span class="chord">${chord}</span><span class="text">${text}</span></span>`;
+
const directive_handlers = {
title: value => {
document.getElementById('title').textContent = value;
if (verseLines == null)
verseLines = [];
- // split line into parts on chords
- const parts = [];
- line.split(/(\[[^\]]+\])/).forEach(part => parts.push(part[0] == '['
- ? html`<span class="chord">${part.slice(1, -1)}</span>`
- : (part ? html`<span class="text">${part}</span>` : null)
- ));
- verseHasChords ||= parts.length > 1;
- parts.push('\n')
- verseLines.push(parts);
+ // split line into segments of one chord plus the lyrics up to the
+ // next chord; the first element is the lyrics before the first chord
+ const [lead, ...rest] = line.split(/\[([^\]]+)\]/);
+ const segments = lead ? [segment('', lead)] : [];
+ for (let i = 0; i < rest.length; i += 2) {
+ let [chord, text] = [rest[i], rest[i + 1]];
+ // spacing between chords without lyrics belongs into the
+ // chord row, so that chord-only lines stay one row tall
+ if (!text.trim()) {
+ chord += text;
+ text = '';
+ }
+ segments.push(segment(chord, text));
+ }
+ verseHasChords ||= rest.length > 0;
+ segments.push('\n');
+ verseLines.push(segments);
}
}
margin-bottom: 0.3em;
}
-.verse.chords {
- line-height: 2.3em;
+/* A segment is one chord and the lyrics up to the next chord, stacked as two
+ rows. Its width is the wider of the two, so chords can never overlap each
+ other or the next segment. Line wrapping only happens between segments;
+ spaces in the lyrics are kept as written. */
+.verse.chords .seg {
+ display: inline-block;
+ white-space: pre;
+ /* A segment without lyrics has no baseline in its lyrics row, so baseline
+ alignment would put its chord into the lyrics row of its neighbours. All
+ chord rows have the same height, so aligning the tops lines them up. */
+ vertical-align: top;
+}
+
+.verse.chords .chord,
+.verse.chords .text {
+ display: block;
}
-.chord + .chord {
- width: unset;
- margin-left: 0.3ex;
+.verse.chords .chord {
+ /* chord names have no descenders, so keep the row tight to the lyrics */
+ line-height: 1;
+ /* keep lyrics in the bottom row when there is no chord above them */
+ min-height: 1lh;
}
.chord {
- position: relative;
- top: -1em;
- display: inline-block;
- width: 0;
- overflow: visible;
- text-shadow: #CCC 1px 1px;
color: #F33;
font-weight: bold;
+ text-shadow: #CCC 1px 1px;
+ /* gap between adjacent chords */
+ padding-right: 0.3ex;
}