import { chromaticScale } from "./scale.mjs"; function dots(i) { switch (i+1) { case 3: case 7: case 9: return '•' case 5: case 12: return '••' } } export default class extends HTMLElement { // TODO: probably not worth observing frets since everything just // gets re-rendered when they change. static observedAttributes = ['frets', 'tonic', 'tonic-octave']; static name = 'x-string'; static register() { console.debug('Registering Element', this.name, this); customElements.define(this.name, this); } // TODO: this is a horrible hack, but i don't know how to make // each string's input unique. maybe wrap them in their own form? myName() { return 'string'; } attributeChangedCallback(name, old, v) { console.debug('String#attributeChangedCallback', this, name, old, v); if (old != v) { this.render(); } } connectedCallback() { console.debug('String#connectedCallback'); } zot() { console.debug('String#zot', this); const tmpl = this.querySelector('template'); console.log(' -- tmpl', tmpl); this.querySelectorAll('input[slot="fret"]').forEach(elt => { console.debug(' -- del', elt, parent); // TODO: this is precisely what i was trying to avoid: the // code has to know exactly what the template looks like. elt.parentNode.parentNode.removeChild(elt.parentNode); }) } muteClicked(e) { console.debug('String#muteClicked', this, e); if (this.getAttribute('value') !== 'x') { this.setAttribute('value', 'x'); this.classList.add('muted'); } else { this.setAttribute('value', this.getAttribute('tonic')); this.classList.remove('muted'); } } fretClicked(e) { console.debug('String#fretClicked', this, e); console.debug(' -- note', e.target.note); this.setAttribute('value', e.target.getAttribute('value')); this.setAttribute('octave', e.target.getAttribute('octave')); } render() { console.debug('String#render'); this.querySelectorAll('input[name="muted"]').forEach(elt => { console.debug(' -- input', elt); elt.onchange = this.muteClicked.bind(this); }); this.zot(); const chromIndex = chromaticScale.indexOf(this.getAttribute('tonic')); let octave = this.getAttribute('tonic-octave'); const tmpl = this.querySelector('template'); this.querySelectorAll('slot[name="open"]').forEach(s => { const note = chromaticScale[chromIndex] s.setAttribute('value', note); s.setAttribute('octave', octave); s.textContent = note; s.parentNode.onclick = this.fretClicked.bind(this); }) const sel = this.getAttribute('value'); const frets = Number(this.getAttribute('frets')); for (let i = 1; i <= frets; i++) { const item = tmpl.content.cloneNode(true); item.querySelectorAll('input[slot="fret"]').forEach(s => { const value = chromaticScale[chromIndex + i]; s.setAttribute('name', this.myName()); s.setAttribute('value', value); if (value === sel) { s.checked = true; } else { s.checked = false; } if (value === 'C') { octave++; } s.setAttribute('octave', octave); s.parentNode.onclick = this.fretClicked.bind(this); }) tmpl.parentNode.insertBefore(item, tmpl); } this.querySelectorAll('slot[name="selected"]').forEach(s => { const note = this.getAttribute('value'); console.debug(' -- setting selected', note); s.textContent = note; }) } }