var Transposer = (function ($) { const wrapRegex = /(?:(?:DO|RE|MI|FA|SOL|LA|SI)(?:b|\#)?(?:\d+[\+\-]?)?(?:m?(?:aug|dim|add|b|#)?(?:\d+[\+\-]?)?(?:\/?\d+[\+\-]?)?)(?:\/(?:DO|RE|MI|FA|SOL|LA|SI)(?:b|\#)*)?)/g; const lineRegex = /^\s*(?:(?:DO|RE|MI|FA|SOL|LA|SI)(?:b|\#)?(?:\d+[\+\-]?)?(?:m?(?:aug|dim|add|b|#)?(?:\d+[\+\-]?)?(?:\/?\d+[\+\-]?)?)(?:\/(?:DO|RE|MI|FA|SOL|LA|SI)(?:b|\#)*)?\s*)+$/; const noteRegex = /\b(?:((?:DO|RE|MI|FA|SOL|LA|SI)(?:b|\#)?)(?=(?:aug|m|dim)?(?:\s|\/))*)/; const noteGRe = new RegExp(noteRegex, "g"); const simpleRegex = /(?:((?:DO|RE|MI|FA|SOL|LA|SI)(?:b|\#)?)((?:\d+[\+\-]?)?(m|aug|dim|add|b|#)?(?:\d+[\+\-]?)?(?:\/?\d+[\+\-]?)?(?:\/((?:DO|RE|MI|FA|SOL|LA|SI)(?:b|\#)*))?))/; const legalNotes = ["DO", "REb", "RE", "MIb", "MI", "FA", "FA#", "SOL", "LAb", "LA", "SIb", "SI"]; const N_KEYS = 12; const scales = { '#': ['DO', 'DO#', 'RE', 'RE#', 'MI', 'FA', 'FA#', 'SOL', 'SOL#', 'LA', 'LA#', 'SI'], 'b': ['DO', 'REb', 'RE', 'MIb', 'MI', 'FA', 'SOLb', 'SOL', 'LAb', 'LA', 'SIb', 'SI'] }; const Notes = { "DO": { index: 0, type: "N" }, "DO#": { index: 1, type: "#" }, "REb": { index: 1, type: "b" }, "RE": { index: 2, type: "#" }, "RE#": { index: 3, type: "#" }, "MIb": { index: 3, type: "b" }, "MI": { index: 4, type: "#" }, "FA": { index: 5, type: "b" }, "FA#": { index: 6, type: "#" }, "SOLb": { index: 6, type: "b" }, "SOL": { index: 7, type: "#" }, "SOL#": { index: 8, type: "#" }, "LAb": { index: 8, type: "b" }, "LA": { index: 9, type: "#" }, "LA#": { index: 10, type: "#" }, "SIb": { index: 10, type: "b" }, "SI": { index: 11, type: "#" } }; let diffToSimple = {}; var IS_SIMPLE = 0; String.prototype.insertAt = function (pos, str) { return [this.slice(0, pos), str, this.slice(pos)].join(''); }; function init() { var $pre = $('pre'); var $chiave = $("div.chiave", $pre); if ($chiave) { var lines = $pre.html().split('\n'); wrapChords(lines); $pre.html(lines.join('\n')); /* ---- CONTROLS ---- */ $('body').on('keydown', function (e) { switch (e.which) { case 107: // + pitchUp(); break; case 109: // - pitchDown(); break; default: break; } }) .on('click', '#pitchUp', pitchUp) .on('click', '#pitchDown', pitchDown) .on('click', '#pitchUpresp', pitchUpresp) .on('click', '#pitchDownresp', pitchDownresp) .on("click", "#simplify", onSClick) .on("click", "#unsimplify", onUSClick); $('span.chord').on("mouseover", onChordOver); $("#text-magnify, #text-magnify-responsive").click(function () { $("#text-reduce, #text-reduce-responsive").removeClass("disabled"); $('#output-text').html(function (i, val) { if (val < 3) { var prePosition = $('.bottom-ad').offset(); var fontSize = parseFloat($('pre').css("font-size")); var fontSize = fontSize + 3 + "px"; var lineHeight = parseFloat($('pre').css("line-height")); var lineHeight = lineHeight + 2.8 + "px"; $('pre').css({'font-size':fontSize}); $('pre').css({'line-height':lineHeight}); var ret = val * 1 + 1; return ret > 0 ? "+" + ret : ret; } else { $("#text-magnify, #text-magnify-responsive").addClass("disabled"); } }); }); $("#text-reduce, #text-reduce-responsive").click(function () { $("#text-magnify, #text-magnify-responsive").removeClass("disabled"); $('#output-text').html(function (i, val) { if (val > -1) { var prePosition = $('.bottom-ad').offset(); var fontSize = parseFloat($('pre').css("font-size")); var fontSize = fontSize - 3 + "px"; var lineHeight = parseFloat($('pre').css("line-height")); var lineHeight = lineHeight - 2.8 + "px"; $('pre').css({'font-size':fontSize}); $('pre').css({'line-height':lineHeight}); var ret = val * 1 - 1; return ret > 0 ? "+" + ret : ret; } else { $("#text-reduce, #text-reduce-responsive").addClass("disabled"); } }); }); restoreFavouriteK($chiave.attr('name')); } } function pitchUp(e) { e.preventDefault(); e.stopPropagation(); $('#output').html(function (i, val) { if (val < 11) { transpose(1); var ret = val * 1 + 1; return ret > 0 ? "+" + ret : ret; } }); } function pitchUpresp(e) { e.preventDefault(); e.stopPropagation(); $('#output-responsive').html(function (i, val) { if (val < 11) { transpose(1); var ret = val * 1 + 1; return ret > 0 ? "+" + ret : ret; } }); } function pitchDown(e) { e.preventDefault(); e.stopPropagation(); $('#output').html(function (i, val) { if (val > -11) { transpose(-1); var ret = val * 1 - 1; return ret > 0 ? "+" + ret : ret; } }); } function pitchDownresp(e) { e.preventDefault(); e.stopPropagation(); $('#output-responsive').html(function (i, val) { if (val > -11) { transpose(-1); var ret = val * 1 - 1; return ret > 0 ? "+" + ret : ret; } }); } function onSClick(e) { e.preventDefault(); e.stopPropagation(); if (!IS_SIMPLE) { simplify(); IS_SIMPLE = 1; $(this).addClass('checked'); $(this).attr("id", "unsimplify"); $('.chord').addClass('simplified'); } } function onUSClick(e) { e.preventDefault(); e.stopPropagation(); if (IS_SIMPLE) { unsimplify(); IS_SIMPLE = 0; $(this).removeClass('checked'); $(this).attr("id", "simplify"); $('.chord').removeClass('simplified'); } } function onChordOver(e) { e.preventDefault(); e.stopPropagation(); WidgetFactory.newWidget(this); } function restoreFavouriteK(oldK) { var $btn = $('button#asfp-remove-fav-btn'); if ($btn.length) { var newK = $btn.data("key"); var dlt = delta(Notes[oldK].index, Notes[newK].index); for (var i = 1; i < N_KEYS; i++) { var posDlt = delta(Notes[oldK].index, i); var negDlt = delta(Notes[oldK].index, - i); if (newK == shiftKey(oldK, posDlt)) { dlt = posDlt; break; } else if (newK == shiftKey(oldK, negDlt)) { dlt = negDlt; break; } } transpose(dlt); if (dlt == 0 || dlt == 12 || dlt == -12) { $('#output').html("0"); $('#output-responsive').html("0"); } else if (dlt < -6) { var dlt2 = 12 + dlt; $('#output').html(dlt2); $('#output-responsive').html(dlt2); } else if (dlt > 6) { var dlt2 = - 12 + dlt; $('#output').html(dlt2); $('#output-responsive').html(dlt2); } else if (dlt > 0) { var dlt2 = 12 - dlt; $('#output').html("+" + dlt); $('#output-responsive').html("+" + dlt); } else { $('#output').html(dlt); $('#output-responsive').html(dlt); } } } function transpose(amount) { var keys = $('pre > div.chiave'); keys.each(function () { var $key = $(this); var oldK = $key.attr("name"); switch (oldK) { case "DO#": oldK = "REb"; break; case "RE#": oldK = "MIb"; break; case "SOLb": oldK = "FA#"; break; case "SOL#": oldK = "LAb"; break; case "LA#": oldK = "SIb"; break; } var newK = shiftKey(oldK, amount); changeNotes(oldK, newK, $key); $key.attr("name", newK); }); } function changeNotes(oldK, newK, context) { $("span.note", context).each(function () { var $note = $(this); var newNote = changePitch($note.html(), oldK, newK); $note.html(newNote); }); $("span.chord[data-old-bass]", context).each(function () { var $chord = $(this); var $newB = changePitch($chord.attr("data-old-bass"), oldK, newK); $chord.attr("data-old-bass", $newB); }) } function changePitch(note, oldK, newK) { var keyDelta = delta(Notes[oldK].index, Notes[newK].index); var newKType = Notes[newK].type; var newNoteIndex = circularIndex(Notes[note].index, keyDelta, N_KEYS); return (newKType === "N") ? legalNotes[newNoteIndex] : scales[newKType][newNoteIndex]; } function simplify() { var chords = $('span.chord'); chords.each(function () { var $chord = $(this); var $oldText = $chord.text(); var matches = simpleRegex.exec($oldText); if (matches[2] !== "") { $chord.replaceWith(simplifyChord(matches)); } }); } function simplifyChord(matches) { var erer = /(\d+[+-]?\d*)/.exec(matches[2]); var $rootNote = $("", {"class": "note"}) .text(matches[1]); var alter = matches[2]; var $simpleChord = $("", {"class": "chord"}) .on("mouseover", onChordOver) .append($rootNote); var whiteSp = 0; var $simpleText = ""; if (!/dim/.test(alter)) { if (/m/.test(alter)) { if (!/d/.test(alter)) { whiteSp--; } $simpleText = "m"; alter = alter.replace(/m/, ""); } } whiteSp = whiteSp + alter.length; for (var i = 0; i < whiteSp; i++) { $simpleText = $simpleText + " "; } $simpleChord.html($simpleChord.html() + $simpleText); if (matches[4]) { var alters = alter.split("/"); if (alters[0]) { $simpleChord.attr("data-old", alters[0]); } $simpleChord.attr("data-old-bass", alters[1]); } else { $simpleChord.attr("data-old", alter); } return $simpleChord; } function unsimplify() { $("span.chord[data-old]").each(function () { var $chord = $(this); var oldAlt = $chord.attr("data-old"); $chord.html($chord.html().trim()); $chord.html($chord.html() + oldAlt); $chord.removeAttr("data-old"); }); $("span.chord[data-old-bass]").each(function () { var $chord = $(this); var oldB = $chord.attr("data-old-bass"); $chord.html($chord.html().trim()); $chord.html($chord.html() + "/") .append( $("", { "class": "note" }) .text(oldB) ); $chord.removeAttr("data-old-bass"); }); } function wrapChords(lines) { lines.forEach(function (line, i) { if (lineRegex.test(line)) { line = insertChordTag(line); line = insertNoteTag(line); lines[i] = line; } }); } function insertChordTag(line) { var chord; var chords = []; while ((chord = wrapRegex.exec(line)) !== null) { chords.push(chord); } chords.reverse().forEach(function (chord) { line = line.insertAt(chord.index + chord[0].length, ''); line = line.insertAt(chord.index, ''); }); return line; } function insertNoteTag(line) { var note; var notes = []; while ((note = noteGRe.exec(line)) !== null) { notes.push(note); } notes.reverse().forEach(function (note) { line = line.insertAt(note.index + note[0].length, ''); line = line.insertAt(note.index, ''); }); return line; } function shiftKey(note, amount) { return legalNotes[circularIndex(legalNotes.indexOf(note), amount, N_KEYS)]; } return { init: init, transpose: transpose } })(jQuery);