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