xref: /plugin/chordsheets/script.js (revision f54a007ee3d4a53d8d906316e58939838bd52897)
1/* DOKUWIKI:include_once js/raphael.js */
2/* DOKUWIKI:include_once js/jtab.min.js */
3
4function ready() {
5    "use strict";
6    runSongHighlighter();
7}
8
9var songBlockSelector = "song-with-chords";
10var songChordSelector = "song-chord";
11var songSectionSelector = "song-section";
12var songSectionHeadingSelector = "song-section-heading";
13var chordLineSelector = "song-chordLine";
14var songTextLineSelector = "song-textLine";
15
16function parseSong(songText, transpose, showToolTips) {
17    "use strict";
18    transpose = transpose || 0;
19
20    var currentSection = "";
21
22    function parseText(text, transpose, showToolTips) {
23        var lines = text.split(/\n/g);
24
25        try {
26            transpose = Number(transpose);
27        } catch (error) {
28            transpose = 0;
29        }
30        findChords(lines, transpose, showToolTips);
31        return lines.join("\n");
32    }
33
34    function findChords(lines, transpose, showToolTips) {
35        for (var i = 0; i < lines.length; i++) {
36            var ln = parseLine(lines[i], transpose, showToolTips);
37            lines[i] = ln.text;
38        }
39        if (currentSection != "") {
40            lines.push("</div>");
41        }
42    };
43
44    function parseLine(lineText, transpose, addTooltip) {
45        addTooltip = addTooltip || true;
46        /* ignore empty lines, will be controlled by paragraphs */
47        if (isNullOrWhiteSpace(lineText)) {
48            return {
49                'isChord': false,
50                'isSection': false,
51                'text': ""
52            };
53        }
54
55        var isChord = false;
56        var isSection = false;
57
58        var chordRegEx = /(?:(\s*)([a-g]{1}[b#]?(?:no|add|sus|dim|maj|min|m|13|11|9|7|6|5|4|3|2)*(?:\s*\/\s*[a-g]{1}[b#]?)?)|\s*(\().*\))/iy;
59
60        var sectionRegex = new RegExp("^\\s*(\\[(.+)\\])", "i");
61
62        var chordRep = lineText.replace(new RegExp("[a-g]{1}[b#]?(no|add|sus|dim|maj|min|m|13|11|9|7|6|5|4|3|2)*", "gi"), "");
63        var hasTextRegExp = new RegExp("[a-z](?![^\\(]*\\))", "i");
64        var isChord = hasTextRegExp.test(chordRep) == false;
65
66        if (isChord) {
67            var newLine = "";
68            var chordMatch;
69
70            while ((chordMatch = chordRegEx.exec(lineText)) != null) {
71                if (chordMatch[3] === "(") {
72                    newLine += '<span style="color:black;">' + chordMatch[0] + '</span>';
73                    continue;
74                }
75                try {
76                    var subchords = chordMatch[2].split("/");
77                } catch (error) {
78                    var i = 0;
79                }
80
81                for (var i2 = 0; i2 < subchords.length; i2++) {
82                    var chordRootRegEx = new RegExp("([a-g]{1}[b#]?)(.*)", "i");
83                    var chordRootMatch = chordRootRegEx.exec(subchords[i2]);
84                    var transposed = substChord(chordRootMatch[1], transpose);
85                    subchords[i2] = transposed + chordRootMatch[2] || "";
86                }
87                var newChord = subchords.join("/");
88                if (addTooltip)
89                    newLine += chordMatch[0].replace(chordMatch[2], '<span class="' + songChordSelector + ' tooltip">' + newChord + '<span class="tooltiptext jtab">' + subchords[0] + '</span></span>');
90                else
91                    newLine += chordMatch[0].replace(chordMatch[2], '<span class="' + songChordSelector + '">' + newChord + '</span>');
92            }
93            lineText = '<p style="white-space: pre;color: #d73a49;font-family: \'Courier New\', Courier, monospace;margin:0;" class="' + chordLineSelector + '">' + newLine + '</p>';
94        }
95        else if (sectionRegex.test(lineText)) {
96            var match = sectionRegex.exec(lineText);
97            var sectionText = "";
98            if (currentSection != "") {
99                sectionText += "</div>\n"
100            }
101            currentSection = match[2].toLowerCase();
102            sectionText += '<div class="' + songSectionSelector + ' ' + currentSection + '">\n';
103            sectionText += '<h3 class="' + songSectionHeadingSelector + ' ' + currentSection + '">' + match[1] + '</h3>';
104            lineText = sectionText;
105            isSection = true;
106        } else {
107            lineText = '<p style="white-space: pre;font-family: \'Courier New\', Courier, monospace;margin:0;" class="' + songTextLineSelector + '">' + lineText + '</p>';
108        }
109
110        return {
111            'isChord': isChord,
112            'isSection': isSection,
113            'text': lineText
114        };
115    };
116
117    function substChord(chord, transp) {
118        chord = normalizeChord(chord);
119        var notes = ["A", "Bb", "B", "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#"];
120
121        var x = notes.indexOf(chord);
122        x = (x + transp) % 12;
123        if (x < 0) x += 12;
124        return notes[x];
125    };
126
127    function normalizeChord(chord) {
128
129        chord = chord.charAt(0).toUpperCase() + chord.slice(1);
130
131        if (chord == "A#") chord = "Bb";
132        else if (chord == "Db") chord = "C#";
133        else if (chord == "Cb") chord = "B";
134        else if (chord == "D#") chord = "Eb";
135        else if (chord == "E#") chord = "F";
136        else if (chord == "Gb") chord = "F#";
137        else if (chord == "Ab") chord = "G#";
138        return chord;
139    }
140
141    function isNullOrWhiteSpace(str) {
142        return str == null || str.replace(/\s/g, '').length < 1;
143    }
144
145    return parseText(songText, transpose, showToolTips);
146}
147
148function runSongHighlighter() {
149    var songs = document.querySelectorAll('.' + songBlockSelector);
150    for (var i = 0; i < songs.length; i++) {
151        var transpose = songs[i].dataset.transpose;
152        songs[i].rawText = songs[i].innerHTML;
153        songs[i].innerHTML = parseSong(songs[i].rawText, transpose);
154    }
155};
156
157function cSheetExportToWord(id) {
158    var song = document.getElementById(id);
159
160    function copy() {
161        try {
162            // Now that we've selected the anchor text, execute the copy command
163            var successful = document.execCommand('copy');
164            var msg = successful ? 'successfully' : 'unsuccessfully';
165            alert(msg + " copied song to clipboard. Use CTRL + V to paste it in your word document.");
166        } catch (err) {
167            alert('Oops, unable to copy');
168        }
169    }
170
171    if (song.rawText) {
172        var node = document.createElement("div");
173        node.innerHTML = parseSong(song.rawText, song.dataset.transpose, false);
174        song.appendChild(node);
175        var range = document.createRange();
176        range.selectNode(node);
177        window.getSelection().addRange(range);
178        copy();
179        song.removeChild(node);
180    } else {
181        var range = document.createRange();
182        range.selectNode(song);
183        window.getSelection().addRange(range);
184        copy();
185    }
186
187    // Remove the selections - NOTE: Should use
188    // removeRange(range) when it is supported
189    window.getSelection().removeAllRanges();
190}
191
192document.addEventListener("DOMContentLoaded", ready);