summaryrefslogtreecommitdiff
path: root/layouts/_default/baseof.html
diff options
context:
space:
mode:
Diffstat (limited to 'layouts/_default/baseof.html')
-rw-r--r--layouts/_default/baseof.html138
1 files changed, 138 insertions, 0 deletions
diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
new file mode 100644
index 0000000..d8fafb6
--- /dev/null
+++ b/layouts/_default/baseof.html
@@ -0,0 +1,138 @@
1<!DOCTYPE html>
2<html lang="{{ or site.Language.LanguageCode site.Language.Lang }}"
3 dir="{{ or site.Language.LanguageDirection `ltr` }}">
4
5 <head>
6 {{ partial "head.html" . }}
7 </head>
8
9 {{ $theme := "auto"}}
10
11 {{ with .Param "theme" }}
12 {{ $theme = .}}
13 {{ end }}
14
15 <body class="{{ $theme }}">
16
17 <div class="content">
18 <header>
19 <h1 class="center">
20 <a class="hover-underline" href="/">{{ .Site.Title }}</a>
21 </h1>
22 <hr>
23 </header>
24
25 <main class="main">
26
27 {{ block "main" . }}{{ end }}
28 </main>
29 </div>
30
31 </body>
32
33 <script>
34
35 function isAuto() {
36 return document.body.classList.contains("auto");
37 }
38
39 function setTheme() {
40 if (!isAuto()) {
41 return
42 }
43
44 document.body.classList.remove("auto");
45 let cls = "light";
46 if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
47 cls = "dark";
48 }
49
50 document.body.classList.add(cls);
51 }
52
53 function invertBody() {
54 document.body.classList.toggle("dark");
55 document.body.classList.toggle("light");
56 }
57
58 if (isAuto()) {
59 window.matchMedia('(prefers-color-scheme: dark)').addListener(invertBody);
60 }
61
62 setTheme();
63
64
65</script>
66
67
68{{ if .HasShortcode "chords" }}
69<script>
70document.addEventListener("DOMContentLoaded", () => {
71 const semitones = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
72 const flatToSharp = { 'Db': 'C#', 'Eb': 'D#', 'Gb': 'F#', 'Ab': 'G#', 'Bb': 'A#' };
73
74 function normalize(note) {
75 return flatToSharp[note] || note;
76 }
77
78 function transposeNote(note, shift) {
79 note = normalize(note);
80 const i = semitones.indexOf(note);
81 return i === -1 ? note : semitones[(i + shift + 12) % 12];
82 }
83
84 function transposeChord(fullChord, shift) {
85 const match = fullChord.match(/^([A-G][#b]?)([^\/]*)(?:\/([A-G][#b]?))?$/);
86 if (!match) return fullChord;
87 let [, root, suffix, bass] = match;
88 const transposedRoot = transposeNote(root, shift);
89 const transposedBass = bass ? transposeNote(bass, shift) : null;
90 return transposedRoot + suffix + (transposedBass ? `/${transposedBass}` : '');
91 }
92
93 function renderChords() {
94 const pattern = /\[([A-G][#b]?[^ \[\]]*)\]/g;
95 document.querySelectorAll('.chord-line').forEach(line => {
96 line.innerHTML = line.innerHTML.replace(pattern, (_, chord) => {
97 return `<span class="chord-anchor"><span class="chord" data-original="${chord}">${chord}</span></span>`;
98 });
99 });
100 }
101
102 function transposeToKey(fromKey, toKey) {
103 const fromIndex = semitones.indexOf(normalize(fromKey));
104 const toIndex = semitones.indexOf(normalize(toKey));
105 const shift = fromIndex === -1 || toIndex === -1 ? 0 : (toIndex - fromIndex + 12) % 12;
106
107 document.querySelectorAll('.chord').forEach(el => {
108 const original = el.dataset.original;
109 el.textContent = transposeChord(original, shift);
110 });
111 }
112
113 // Init
114 renderChords();
115
116 const selector = document.getElementById('key-selector');
117 const originalKey = selector?.dataset.originalKey || 'C';
118
119 // Trigger initial transpose to match the default selected key
120 transposeToKey(originalKey, selector.value);
121
122 selector?.addEventListener("change", () => {
123 transposeToKey(originalKey, selector.value);
124 });
125});
126
127document.addEventListener("DOMContentLoaded", () => {
128 const toggle = document.getElementById("toggle-chords");
129
130 toggle?.addEventListener("change", () => {
131 document.body.classList.toggle("hide-chords", !toggle.checked);
132 });
133});
134</script>
135{{ end }}
136
137
138</html> \ No newline at end of file