summaryrefslogtreecommitdiffstats
path: root/fretboard.mjs
blob: dee7175e300042e2efe7af5faffe09e42257052b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { chromaticScale } from "./scale.mjs";

const strings = {
    string1: 'E',
    string2: 'A',
    string3: 'D',
    string4: 'G',
    string5: 'B',
    string6: 'E'
};

function isAccidental(note) {
    return note[1] === '#' || note[1] === 'b';
}

function alternateAccidental(note) {
    const root = chromaticScale.indexOf(note[0]);
    switch (note[1]) {
    case '#':
        return `${chromaticScale[root+2]}b`;
    case 'b':
        return `${chromaticScale[root-1]}#`;
    default:
        return note;
    }
}

function fretToNote(stringName, fretName) {
    console.debug('fretToNote', stringName, fretName);
    const string = strings[stringName];
    if (!string) {
        return null;
    }

    if (!fretName?.startsWith('fret')) {
        return string;
    }

    const root = chromaticScale.indexOf(string)
    const fret = Number(fretName.substring(4));
    console.debug('root', root, 'fret', fret);
    return chromaticScale[root+fret];
}

function formChanged(form) {
    console.log('form changed', form);
    const formData = new FormData(form);
    console.log('form', formData);
    form.querySelectorAll('p').forEach(p => {
        const val = formData.get(p.className)
        console.debug('found sel', val, p.className, formData);
        const note = fretToNote(p.className, val);
        if (isAccidental(note)) {
            p.innerText = `${note} / ${alternateAccidental(note)}`;
        } else {
            p.innerText = note;
        }
    });
}

function handleFormChanged(e) {
    console.log('handle form changed', e);
    formChanged(e.target.form);
}

let mousedownVal = undefined;

function mousedown(e) {
    // at mousedown time, the form hasn't yet been changed. to support
    // deselecting elements, we need to know what the previous
    // selection was, so store it here.
    //
    // it'd be nice to be able to do this just in the click handler.
    mousedownVal = e.target.checked;
}

function click(e) {
    const elt = e.target;
    // if this element was selected at mousedown time, deselect it
    // now.
    if (mousedownVal) {
        elt.checked = false;
    }
}

export default function Fretboard(form) {
    console.debug('Fretboard()', form);
    form.onchange = handleFormChanged;
    form.querySelectorAll('input[type="radio"]').forEach(elt => {
        elt.onmousedown = mousedown;
        elt.onclick = click;
    });
    formChanged(form);
}