From 4565d082f3686036d2de07fb57b9fda330204ee8 Mon Sep 17 00:00:00 2001 From: siroshimadenagasaki <161388775+siroshimadenagasaki@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:05:21 -0300 Subject: [PATCH] ok --- trash/.DS_Store | Bin 6148 -> 6148 bytes trash/.html | 73 +++ trash/fretboard-diagram-creator-main.zip | Bin 0 -> 7884 bytes trash/fretboard-diagram-creator-main/LICENSE | 21 + .../fretboard-diagram-creator-main/README.md | 13 + .../fretboard.css | 207 ++++++ .../fretboard.html | 42 ++ .../fretboard.js | 590 ++++++++++++++++++ 8 files changed, 946 insertions(+) create mode 100644 trash/.html create mode 100644 trash/fretboard-diagram-creator-main.zip create mode 100644 trash/fretboard-diagram-creator-main/LICENSE create mode 100644 trash/fretboard-diagram-creator-main/README.md create mode 100644 trash/fretboard-diagram-creator-main/fretboard.css create mode 100644 trash/fretboard-diagram-creator-main/fretboard.html create mode 100644 trash/fretboard-diagram-creator-main/fretboard.js diff --git a/trash/.DS_Store b/trash/.DS_Store index 8422ee56210ca69dbcf49787f69bbaf5920b5f40..5f6c119b05af490d94b56c16e8e94fdcf6749452 100644 GIT binary patch literal 6148 zcmeHK!A`?440X044ehccCzK!9VSiAjB0iu$K)XVj)Hc%uoO|O4AH{*s;yJd^royQV z0kS3MrH<`1FI!R*ky|~KOQJauO`(jVQ#79lkF)kwdv24&(7L+qHFQE9Aj{$;22i28@BDGN9+obavEPEV(gY z4E#j~_CJzu~2NHo}wrt0|NsP3otNLFz7L4FqAOlGUQAwR5t}la4}>uV&;<$<~w_(AwT6l`9|oW`Hq)$ diff --git a/trash/.html b/trash/.html new file mode 100644 index 0000000..fd094bf --- /dev/null +++ b/trash/.html @@ -0,0 +1,73 @@ + + + + + +

Guitar Fretboard Trainer

+
+
+
Start
+
Reset
+
+ Score + +
+
Play Again
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
    +
  • +
  • +
  • +
  • +
  • +
  • +
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
+ + +
?
+ +
+

Choose Note:

+
    +
  • A
  • +
  • A#
  • +
  • B
  • +
  • C
  • +
  • C#
  • +
  • D
  • +
  • D#
  • +
  • E
  • +
  • F
  • +
  • F#
  • +
  • G
  • +
  • G#
  • +
+
+
+
+
\ No newline at end of file diff --git a/trash/fretboard-diagram-creator-main.zip b/trash/fretboard-diagram-creator-main.zip new file mode 100644 index 0000000000000000000000000000000000000000..39f0c3c2bc39bb9f07bba8040829f328bf2e7e8a GIT binary patch literal 7884 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 LF&DJ96y*LdS&CNc literal 0 HcmV?d00001 diff --git a/trash/fretboard-diagram-creator-main/LICENSE b/trash/fretboard-diagram-creator-main/LICENSE new file mode 100644 index 0000000..3e03783 --- /dev/null +++ b/trash/fretboard-diagram-creator-main/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Tobias Kolditz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/trash/fretboard-diagram-creator-main/README.md b/trash/fretboard-diagram-creator-main/README.md new file mode 100644 index 0000000..85069fd --- /dev/null +++ b/trash/fretboard-diagram-creator-main/README.md @@ -0,0 +1,13 @@ +# Fretboard Diagram Creator + +A simple online tool with which you can create fretboard diagrams for the guitar. + +Demo: https://verzettelung.com/20/12/22/ + +## Features + +* Freely select notes for scale or chord diagrams +* Edit note labels (to indicate fingerings, intervals etc.) +* Color notes +* Select part of the fretboard +* Save diagram as SVG diff --git a/trash/fretboard-diagram-creator-main/fretboard.css b/trash/fretboard-diagram-creator-main/fretboard.css new file mode 100644 index 0000000..c587581 --- /dev/null +++ b/trash/fretboard-diagram-creator-main/fretboard.css @@ -0,0 +1,207 @@ +/* Diagram */ + +g.note.transparent:hover { + opacity: 1; +} + +g.note { + fill: white; + stroke-width: 1px; + stroke: black; +} + +g.note.selected>circle { + opacity: 1; + stroke-dasharray: 4, 4; + stroke-width: 3px; + stroke: black; +} + +g.note.hidden { + opacity: 0; +} + +g.note.transparent { + opacity: 0.3; +} + +g.note.visible { + opacity: 1; + fill: white; +} + +g.note.blue { + fill: steelblue; +} + +g.note.blue>text { + stroke: white; + fill: white; +} + +g.note.black { + fill: black; +} + +g.note.black>text { + stroke: white; + fill: white; +} + +g.note.red { + fill: indianred; +} + +g.note.red>text { + stroke: white; + fill: white; +} + +g.note.green { + fill: teal; +} + +g.note.green>text { + stroke: white; + fill: white; +} + +path.string { + stroke: black; + fill: none; +} + +path.frets { + stroke: black; + stroke-width: 1px; + fill: none; +} + +g.note>text { + stroke: black; + stroke-width: 0px; + fill: black; + text-anchor: middle; + dominant-baseline: middle; + font-size: 18px; +} + +#editable-div { + text-align: center; + font-size: 18px; +} + +#fretboard-diagram-creator { + padding-top: 10px; + /* border: 1px solid; */ +} + +div.menu { + text-align: center; + /* text-align: center; */ +} + +text.marker { + text-anchor: middle; + font-size: 16px; + fill: black; +} + + +/* Controls */ + +/* Actions on nodes */ + +#color-selector { + /* background-color: lightgray; */ + margin: auto; + padding-bottom: 10px; + width: 280px; + display: flex; + justify-content: space-between; +} + +button.color { + width: 44px; +} + +button { + height: 25px; +} + +button.color:hover { + transform: scale(1.1); +} + +.color.blue { + background-color: steelblue; +} + +.color.black { + background-color: black; +} + +.color.green { + background-color: teal; +} + +.color.red { + background-color: indianred; +} + +.color.white { + background-color: white; +} + +/* Global actions */ + +#global-actions { + margin: auto; + width: 280px; + display: flex; + justify-content: space-between; +} + +@supports (width: 100vw) { + figure.half-full { + max-width: 100vw; + width: 1200px; + position: relative; + left: 50%; + right: 50%; + margin-left: -600px; + margin-right: -600px; + } + @media only screen and (max-width: 1200px) { + figure.half-full { + max-width: 100vw; + width: 1000px; + position: relative; + left: 50%; + right: 50%; + margin-left: -500px; + margin-right: -500px; + overflow-y: auto; + } + } + @media only screen and (max-width: 1000px) { + figure.half-full { + max-width: 100vw; + width: 100%; + left: 0; + right: 0; + margin: 0; + overflow-y: auto; + } + } +} + +.num-input { + font-variant-numeric: tabular-nums; + font-feature-settings: "tnum"; +} + +.error { + font-size: 18px; + text-anchor: middle; +} \ No newline at end of file diff --git a/trash/fretboard-diagram-creator-main/fretboard.html b/trash/fretboard-diagram-creator-main/fretboard.html new file mode 100644 index 0000000..dd79286 --- /dev/null +++ b/trash/fretboard-diagram-creator-main/fretboard.html @@ -0,0 +1,42 @@ + + + + + + + + Fretboard Diagram Creator + + + + + +
+ + +
+ + + + + \ No newline at end of file diff --git a/trash/fretboard-diagram-creator-main/fretboard.js b/trash/fretboard-diagram-creator-main/fretboard.js new file mode 100644 index 0000000..d9f1678 --- /dev/null +++ b/trash/fretboard-diagram-creator-main/fretboard.js @@ -0,0 +1,590 @@ +function setAttributes(elem, attrs) { + for (var idx in attrs) { + if ((idx === 'styles' || idx === 'style') && typeof attrs[idx] === 'object') { + const styles = []; + for (var prop in attrs[idx]) { styles.push(`${prop}: ${attrs[idx][prop]};`); } + elem.setAttribute('style', styles.join(' ')); + } else if (idx === 'html') { + elem.innerHTML = attrs[idx]; + } else { + elem.setAttribute(idx, attrs[idx]); + } + } +} + +function generateClassValue(elem, classes) { + var classValues = elem.className.baseVal.split(" "); + if ('type' in classes) { + classValues[0] = classes.type; + } + if ('color' in classes) { + classValues[1] = classes.color; + } + if ('visibility' in classes) { + classValues[2] = classes.visibility; + } + return classValues.join(' '); +} + +function createSvgElement(tag, attributes = null) { + const elem = document.createElementNS('http://www.w3.org/2000/svg', tag); + if (typeof attributes === 'object') { + setAttributes(elem, attributes); + } + return elem; +} + + +class Fretboard { + constructor(opts) { + this.svg = opts.svg; + this.consts = { + offsetX: 40, + offsetY: 30, + stringIntervals: [24, 19, 15, 10, 5, 0], + markers: [3, 5, 7, 9, 12, 15, 17, 19, 21], + fretWidth: 70, + stringSpacing: 40, + minStringSize: 0.2, + circleRadius: 18, + notes: [['E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B', 'C', 'C#', 'D', 'D#'], + ['E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B', 'C', 'Db', 'D', 'Eb']], + sign: ['♯', '♭'], + }; + this.consts.numStrings = this.consts.stringIntervals.length; + this.consts.fretHeight = (this.consts.numStrings - 1) * this.consts.stringSpacing; + + this.state = { + selected: null, + visibility: 'transparent', + startFret: 0, + endFret: 12, + enharmonic: 0 + }; + + // Set end fret according to viewport width + this.state.endFret = Math.min(Math.floor((window.innerWidth - 2 * this.consts.offsetX ) / this.consts.fretWidth), 12); + opts.endFret.value = this.state.endFret; + + this.computeDependents(); + + this.data = {}; + + this.draw(); + } + + computeDependents() { + this.state.numFrets = this.state.endFret - this.state.startFret; + this.state.fretboardWidth = this.consts.fretWidth * this.state.numFrets; + } + + toggleEnharmonic() { + const untoggledEnharmonic = this.state.enharmonic; + this.state.enharmonic = (untoggledEnharmonic + 1) % 2; + this.erase(); + this.draw(); + return this.consts.sign[untoggledEnharmonic]; + } + + setFretWindow(fretWindow) { + const start = 'start' in fretWindow ? fretWindow.start : this.state.startFret; + const end = 'end' in fretWindow ? fretWindow.end : this.state.endFret; + this.erase(); + if (start < 0 || start > 22 || end < 1 || end > 22) { + this.drawError("Invalid fret value(s)!"); + return; + } + if (end <= start) { + this.drawError("End fret must not be smaller than start fret!"); + this.state.startFret = start; + this.state.endFret = end; + return; + } + if (end - start > 16) { + this.drawError("Maximal number of displayable frets is 16,
e.g., 1st to 16th or 4th to 19th!"); + this.state.startFret = start; + this.state.endFret = end; + return; + } + + this.state.startFret = start; + this.state.endFret = end; + + this.computeDependents(); + this.draw(); + } + + drawError(message) { + const text = createSvgElement('text', { + x: 400, + y: 140, + class: 'error', + }); + text.innerHTML = message; + this.svg.appendChild(text); + setAttributes(this.svg, { + width: 800, + }); + } + + draw() { + this.drawFrets(); + this.drawMarkers(); + this.drawStrings(); + this.drawNotes(); + this.addEditableDiv(); + + // adjust diagram width to number of selected frets + setAttributes(this.svg, { + width: this.state.fretboardWidth + 2 * this.consts.offsetX, + }) + + this.svg.addEventListener('click', () => { + if (this.state.selected) { + this.updateNote(this.state.selected, { + visibility: 'visible', + }); + this.state.selected = null; + } + }); + + document.addEventListener('keydown', (event) => { + if (!this.state.selected || !event.code) { + return; + } + const selected = this.state.selected; + switch (event.code) { + case 'Backspace': + case 'Delete': + this.deleteNote() + break; + case 'KeyB': + this.updateNote(selected, { color: "blue" }); + break; + case 'KeyD': + this.updateNote(selected, { color: "black" }); + break; + case 'KeyG': + this.updateNote(selected, { color: "green" }); + break; + case "KeyW": + this.updateNote(selected, { color: "white" }); + break; + case "KeyR": + this.updateNote(selected, { color: "red" }); + break; + } + }) + } + + deleteNote() { + // reset text + const selected = this.state.selected; + const text = selected.lastChild; + if (text) { + text.innerHTML = text.getAttribute('data-note'); + } + this.updateNote(selected, { + color: "white", visibility: this.state.visibility + }); + this.state.selected = null; + } + + updateColor(event) { + this.updateNote(this.state.selected, { + color: event.currentTarget.getAttribute("title") + }); + } + + drawFrets() { + var pathSegments = ["M " + this.consts.offsetX + " " + this.consts.offsetY]; + for (let i = this.state.startFret; i < (this.state.endFret + 1); i++) { + let factor = (i - this.state.startFret) % 2 == 0 ? 1 : -1; + pathSegments.push("v " + (factor) * this.consts.fretHeight); + pathSegments.push("m " + this.consts.fretWidth + " " + 0); + } + const path = pathSegments.join(" "); + + + const frets = createSvgElement('path', { + 'class': 'frets', + 'd': path, + }); + this.svg.appendChild(frets); + } + + drawMarkers() { + const markers = createSvgElement('g', { + class: 'markers' + }); + const filteredMarkers = this.consts.markers + .filter(i => i > this.state.startFret && i <= this.state.endFret); + for (let i of filteredMarkers) { + const marker = createSvgElement('text', { + class: 'marker', + x: this.consts.offsetX + (i - 1 - this.state.startFret) * this.consts.fretWidth + (this.consts.fretWidth / 2), + y: this.consts.offsetY + this.consts.fretHeight + this.consts.stringSpacing, + }); + marker.innerHTML = i; + markers.appendChild(marker); + } + this.svg.appendChild(markers); + } + + drawStrings() { + this.strings = createSvgElement('g', { + 'class': 'strings', + }) + this.svg.appendChild(this.strings); + for (let i = 0; i < this.consts.numStrings; i++) { + let path = "M " + this.consts.offsetX + " " + (this.consts.offsetY + i * this.consts.stringSpacing) + " h " + this.state.fretboardWidth; + const string = createSvgElement('path', { + 'class': 'string', + 'd': path, + 'styles': { + 'stroke-width': this.consts.minStringSize * (i + 1), + } + }); + this.strings.appendChild(string); + } + } + + drawNote(noteId, x, y, noteName, isOpen) { + const note = createSvgElement('g', { + 'id': noteId, + 'transform': "translate(" + x + "," + y + ")", + 'data-x': x, + 'data-y': y, + }); + this.notes.appendChild(note); + note.addEventListener("click", (event) => this.noteClickHandler(event)); + note.addEventListener("dblclick", (event) => this.noteDoubleClickHandler(event)); + + const circle = createSvgElement('circle', { + 'r': this.consts.circleRadius, + }); + if (isOpen) { + setAttributes(circle, { + // don't show circle around open notes + 'stroke': 'none', + }) + } + note.appendChild(circle); + + // compute name of note + const text = createSvgElement('text', { + 'data-note': noteName, + }); + text.innerHTML = noteName; + + note.appendChild(text); + + const update = (noteId in this.data) ? this.data[noteId] : { type: 'note', color: 'white', visibility: this.state.visibility }; + this.updateNote(note, update); + } + + computeNoteName(fret, string) { + const interval = this.consts.stringIntervals[string] + fret + 1; + return this.consts.notes[this.state.enharmonic][interval % 12]; + } + + drawNotes() { + this.notes = createSvgElement('g', { + 'class': 'notes', + }) + this.svg.appendChild(this.notes); + + // open notes (fret: -1) + for (let j = 0; j < this.consts.numStrings; j++) { + const noteId = `o-s${j}`; + const x = this.consts.offsetX / 2; + const y = this.consts.offsetY + this.consts.stringSpacing * j; + const noteName = this.computeNoteName(-1, j); + this.drawNote(noteId, x, y, noteName, true); + } + // notes on fretboard + for (let i = this.state.startFret; i < this.state.endFret; i++) { + for (let j = 0; j < this.consts.numStrings; j++) { + const noteId = `f${i}-s${j}`; + const x = this.consts.offsetX + (this.consts.fretWidth / 2) + this.consts.fretWidth * (i - this.state.startFret); + const y = this.consts.offsetY + this.consts.stringSpacing * j; + const noteName = this.computeNoteName(i, j); + this.drawNote(noteId, x, y, noteName, false); + } + } + } + + noteClickHandler(event) { + event.stopPropagation(); + const note = event.currentTarget; + note.focus(); + if (this.state.selected) { + this.updateNote(this.state.selected, { + visibility: 'visible', + }); + } + this.updateNote(note, { + visibility: 'selected', + }); + this.state.selected = note; + + if (event.ctrlKey) { + this.editSelectedLabel(); + } + } + + noteDoubleClickHandler(event) { + event.stopPropagation(); + const note = event.currentTarget; + if (this.state.selected) { + this.updateNote(this.state.selected, { + visibility: 'visible', + }); + } + this.updateNote(note, { + visibility: 'selected', + }); + this.state.selected = note; + this.editSelectedLabel(); + } + + editSelectedLabel() { + const selected = this.state.selected; + const x = selected.getAttribute('data-x'); + const y = selected.getAttribute('data-y'); + setAttributes(this.editableText, { + x: x - this.consts.circleRadius, + y: y - this.consts.circleRadius + 4, + height: 2 * this.consts.circleRadius, + width: 2 * this.consts.circleRadius, + class: 'visible', + styles: { + display: 'block', + } + }); + + const selectedText = this.state.selected.lastChild; + setAttributes(selectedText, { + styles: { + display: 'none', + } + }); + + this.editableText.children[0].innerHTML = selectedText.innerHTML; + this.editableText.children[0].focus(); + // select all text in editable div + document.execCommand('selectAll', false, null); + } + + addEditableDiv() { + this.editableText = createSvgElement('foreignObject', { + class: 'hidden', + }); + this.editableText.addEventListener('click', (event) => { + event.stopPropagation(); + }); + const div = document.createElement('div'); + div.setAttribute('contentEditable', 'true'); + div.setAttribute('id', 'editable-div') + div.addEventListener('keydown', (event) => { + event.stopPropagation(); + if (event.code === 'Enter') { + event.target.blur(); + } + }); + div.addEventListener('blur', (event) => { + if (!this.state.selected) { + return; + } + const selectedText = this.state.selected.lastChild; + + var newText = this.editableText.children[0].innerText; + // don't allow empty labels + if (newText.trim()) { + this.updateNote(this.state.selected, { + noteText: newText, + }); + } + + this.editableText.children[0].innerHTML = ''; + setAttributes(selectedText, { + styles: { + display: 'block', + } + }); + setAttributes(this.editableText, { + styles: { + display: 'none', + } + }); + }) + this.editableText.appendChild(div); + this.svg.appendChild(this.editableText); + } + + updateNote(elem, update) { + if (!(elem.id in this.data)) { + this.data[elem.id] = {}; + } + const classValue = generateClassValue(elem, update); + elem.setAttribute('class', classValue); + + if ('noteText' in update) { + elem.lastChild.innerHTML = update.noteText; + } + + const noteData = this.data[elem.id]; + for (let [key, value] of Object.entries(update)) { + noteData[key] = value; + } + } + + toggleVisibility() { + this.state.visibility = this.state.visibility === 'hidden' ? 'transparent' : 'hidden'; + for (let note of this.notes.children) { + if (note.className.baseVal.endsWith('visible') || note.className.baseVal.endsWith('selected')) { + continue; + } + this.updateNote(note, { + visibility: this.state.visibility, + }) + } + + for (let [_key, value] of Object.entries(this.data)) { + if (value['visibility'] === 'visible' || value['visibility'] === 'selected') { + continue; + } + value['visibility'] = this.state.visibility; + } + } + + clearSelection() { + if (this.state.selected) { + this.updateNote(this.state.selected, { + visibility: 'visible', + }); + this.state.selected = null; + } + } + + erase() { + this.clearSelection(); + this.svg.innerHTML = ""; + } + + reset() { + this.data = {}; + for (let note of this.notes.children) { + // reset text + const text = note.lastChild; + if (text) { + text.innerHTML = text.getAttribute('data-note'); + } + this.updateNote(note, + { type: "note", color: "white", visibility: this.state.visibility }); + this.state.selected = null; + } + } +} + +/* Main */ + +/* Initialize diagram */ + +const svg = document.getElementById('fretboard'); +const endFret = document.getElementById('end-fret'); + +const fretboard = new Fretboard({ + svg: svg, + endFret: endFret +}) + +/* Button for toggeling unselected notes */ + +const togglebutton = document.getElementById('visibility'); +togglebutton.addEventListener('click', (event) => { + fretboard.toggleVisibility(); +}); + +/* Save SVG button */ + +var svgButton = document.getElementById('save-svg'); +const svgLink = document.getElementById('svg-link'); + +svgButton.addEventListener('click', () => { + fretboard.clearSelection(); + const svgCopy = inlineCSS(svg); + var svgData = svgCopy.outerHTML; + var svgBlob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" }); + var svgUrl = URL.createObjectURL(svgBlob); + svgLink.href = svgUrl; + svgLink.click(); +}); + +const PROPERTIES = ["fill", "stroke", "stroke-width", "text-anchor", "dominant-baseline"] + +function inlineCSS(svg) { + const svgElements = document.querySelectorAll("#fretboard *"); + const clonedSVG = svg.cloneNode(deep = true); + const clonedElements = clonedSVG.querySelectorAll("*"); + for (let i = 0; i < svgElements.length; i++) { + const computedStyle = getComputedStyle(svgElements[i]); + // remove invisible elements to reduce file size + const opacity = computedStyle.getPropertyValue('opacity'); + if (opacity === '0') { + clonedElements[i].remove(); + continue; + } + const styles = { opacity: opacity } + for (let attr of PROPERTIES) { + let value = computedStyle.getPropertyValue(attr); + if (value) { + styles[attr] = value; + } + } + setAttributes(clonedElements[i], { + 'styles': styles, + }); + } + return clonedSVG; +} + +/* Reset button */ + +const resetButton = document.getElementById('reset'); +resetButton.addEventListener('click', (event) => { + const doReset = window.confirm("Do you really want to reset your diagram?"); + if (doReset) { + fretboard.reset(); + } +}); + +/* Fret window */ + +const startFret = document.getElementById('start-fret'); +startFret.addEventListener('input', (event) => { + fretboard.setFretWindow({ start: event.target.value - 1 }); +}); + +endFret.addEventListener('input', (event) => { + fretboard.setFretWindow({ end: parseInt(event.target.value) }); +}); + +/* Color selector */ + +const colorButtons = document.querySelectorAll("button.color"); +for (let button of colorButtons) { + button.addEventListener('click', (event) => { + fretboard.updateColor(event); + }); +} + +const deleteNoteButton = document.getElementById("delete-note"); +deleteNoteButton.addEventListener('click', () => fretboard.deleteNote()); + + +const enharmonicToggle = document.getElementById("enharmonic"); +enharmonicToggle.addEventListener('click', () => { + const sign = fretboard.toggleEnharmonic(); + enharmonicToggle.innerHTML = sign; +}); \ No newline at end of file