@dodlhuat/basix 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -1
- package/css/calendar.scss +4 -3
- package/css/code-viewer.scss +1 -1
- package/css/datepicker.scss +83 -70
- package/css/editor.scss +2 -1
- package/css/flyout-menu.scss +2 -1
- package/css/form.scss +11 -32
- package/css/mixins.scss +25 -0
- package/css/range-slider.scss +63 -6
- package/css/stepper.scss +2 -1
- package/css/style.css +124 -71
- package/css/style.css.map +1 -1
- package/css/style.min.css +1 -1
- package/css/style.min.css.map +1 -1
- package/css/timepicker.scss +1 -1
- package/css/virtual-dropdown.scss +6 -16
- package/js/bottom-sheet.d.ts +3 -5
- package/js/bottom-sheet.js +39 -58
- package/js/calendar.d.ts +1 -2
- package/js/calendar.js +77 -84
- package/js/carousel.d.ts +1 -1
- package/js/carousel.js +22 -14
- package/js/chart.d.ts +1 -1
- package/js/chart.js +104 -65
- package/js/code-viewer.d.ts +1 -1
- package/js/code-viewer.js +7 -18
- package/js/color-picker.d.ts +2 -2
- package/js/color-picker.js +20 -22
- package/js/context-menu.d.ts +1 -1
- package/js/context-menu.js +10 -12
- package/js/datepicker.d.ts +14 -3
- package/js/datepicker.js +180 -99
- package/js/dropdown.d.ts +1 -1
- package/js/dropdown.js +4 -5
- package/js/editor.d.ts +1 -1
- package/js/editor.js +30 -34
- package/js/file-uploader.d.ts +2 -9
- package/js/file-uploader.js +57 -86
- package/js/flyout-menu.d.ts +3 -3
- package/js/flyout-menu.js +20 -22
- package/js/gallery.d.ts +1 -2
- package/js/gallery.js +14 -20
- package/js/group-picker.d.ts +1 -1
- package/js/group-picker.js +22 -29
- package/js/lightbox.d.ts +2 -2
- package/js/lightbox.js +28 -27
- package/js/listeners.d.ts +7 -0
- package/js/listeners.js +14 -0
- package/js/modal.d.ts +2 -1
- package/js/modal.js +13 -20
- package/js/popover.d.ts +2 -2
- package/js/popover.js +42 -37
- package/js/position.js +3 -5
- package/js/push-menu.d.ts +2 -0
- package/js/push-menu.js +21 -15
- package/js/range-slider.d.ts +15 -1
- package/js/range-slider.js +67 -7
- package/js/scroll.js +4 -7
- package/js/scrollbar.d.ts +3 -3
- package/js/scrollbar.js +39 -42
- package/js/select.d.ts +1 -1
- package/js/select.js +19 -23
- package/js/sidebar-nav.d.ts +1 -1
- package/js/sidebar-nav.js +13 -9
- package/js/stepper.d.ts +1 -1
- package/js/stepper.js +9 -11
- package/js/table.d.ts +1 -1
- package/js/table.js +24 -23
- package/js/tabs.d.ts +1 -1
- package/js/tabs.js +10 -17
- package/js/theme.d.ts +1 -0
- package/js/theme.js +6 -15
- package/js/timepicker.d.ts +8 -6
- package/js/timepicker.js +36 -36
- package/js/toast.d.ts +2 -1
- package/js/toast.js +8 -4
- package/js/tooltip.d.ts +1 -4
- package/js/tooltip.js +14 -23
- package/js/tree.d.ts +0 -1
- package/js/tree.js +6 -11
- package/js/utils.js +7 -8
- package/js/virtual-dropdown.d.ts +1 -1
- package/js/virtual-dropdown.js +26 -28
- package/package.json +1 -1
- package/css/calendar.css +0 -928
- package/css/guitar-chords.css +0 -251
- package/js/form-builder.js +0 -107
- package/js/guitar-chords.js +0 -268
- package/js/lazy-loader.js +0 -121
- package/js/request.js +0 -51
package/css/guitar-chords.css
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
.controls-container {
|
|
2
|
-
display: flex;
|
|
3
|
-
gap: 2rem;
|
|
4
|
-
align-items: center;
|
|
5
|
-
flex-wrap: wrap;
|
|
6
|
-
margin-bottom: 1rem;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.control-group {
|
|
10
|
-
display: flex;
|
|
11
|
-
flex-direction: column;
|
|
12
|
-
gap: 0.5rem;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.chord-display {
|
|
16
|
-
font-size: 1.5rem;
|
|
17
|
-
font-weight: bold;
|
|
18
|
-
color: var(--primary-color);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.fretboard-container {
|
|
22
|
-
overflow-x: auto;
|
|
23
|
-
padding: 1rem 0;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.fretboard {
|
|
27
|
-
display: flex;
|
|
28
|
-
flex-direction: column;
|
|
29
|
-
background-color: #5d4037;
|
|
30
|
-
/* Fretboard wood color */
|
|
31
|
-
padding: 20px;
|
|
32
|
-
border-radius: 4px;
|
|
33
|
-
position: relative;
|
|
34
|
-
min-width: 800px;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.string {
|
|
38
|
-
display: flex;
|
|
39
|
-
position: relative;
|
|
40
|
-
height: 40px;
|
|
41
|
-
/* Spacing between strings */
|
|
42
|
-
align-items: center;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.string::before {
|
|
46
|
-
content: '';
|
|
47
|
-
position: absolute;
|
|
48
|
-
left: 0;
|
|
49
|
-
right: 0;
|
|
50
|
-
top: 50%;
|
|
51
|
-
height: 2px;
|
|
52
|
-
/* String thickness */
|
|
53
|
-
background-color: #c0c0c0;
|
|
54
|
-
/* String color */
|
|
55
|
-
z-index: 1;
|
|
56
|
-
transform: translateY(-50%);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/* Make lower strings thicker */
|
|
60
|
-
.string:nth-child(1)::before {
|
|
61
|
-
height: 4px;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.string:nth-child(2)::before {
|
|
65
|
-
height: 3.5px;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.string:nth-child(3)::before {
|
|
69
|
-
height: 3px;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.string:nth-child(4)::before {
|
|
73
|
-
height: 2.5px;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.string:nth-child(5)::before {
|
|
77
|
-
height: 2px;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.string:nth-child(6)::before {
|
|
81
|
-
height: 1.5px;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.fret {
|
|
85
|
-
flex: 1;
|
|
86
|
-
border-right: 4px solid #aaa;
|
|
87
|
-
/* Fret wire */
|
|
88
|
-
height: 100%;
|
|
89
|
-
position: relative;
|
|
90
|
-
display: flex;
|
|
91
|
-
justify-content: center;
|
|
92
|
-
align-items: center;
|
|
93
|
-
cursor: pointer;
|
|
94
|
-
z-index: 2;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.fret.nut {
|
|
98
|
-
border-right: 8px solid #ddd;
|
|
99
|
-
/* Nut */
|
|
100
|
-
flex: 0 0 50px;
|
|
101
|
-
/* Nut width */
|
|
102
|
-
background-color: #3e2723;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.note-marker {
|
|
106
|
-
width: 24px;
|
|
107
|
-
height: 24px;
|
|
108
|
-
border-radius: 50%;
|
|
109
|
-
background-color: var(--primary-color);
|
|
110
|
-
color: white;
|
|
111
|
-
display: flex;
|
|
112
|
-
justify-content: center;
|
|
113
|
-
align-items: center;
|
|
114
|
-
font-size: 0.8rem;
|
|
115
|
-
font-weight: bold;
|
|
116
|
-
z-index: 3;
|
|
117
|
-
opacity: 0;
|
|
118
|
-
transition: opacity 0.2s;
|
|
119
|
-
pointer-events: none;
|
|
120
|
-
/* Let clicks pass through to fret */
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.fret.active .note-marker {
|
|
124
|
-
opacity: 1;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/* Fret markers (dots) */
|
|
128
|
-
.fretboard::after {
|
|
129
|
-
content: '';
|
|
130
|
-
position: absolute;
|
|
131
|
-
/* This is tricky with flexbox, might need a different approach for dots
|
|
132
|
-
or just put them on specific frets in JS or specific nth-child CSS
|
|
133
|
-
*/
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/* Simple single dots */
|
|
137
|
-
.string:nth-child(3) .fret:nth-child(4)::after,
|
|
138
|
-
/* 3rd fret */
|
|
139
|
-
.string:nth-child(3) .fret:nth-child(6)::after,
|
|
140
|
-
/* 5th fret */
|
|
141
|
-
.string:nth-child(3) .fret:nth-child(8)::after,
|
|
142
|
-
/* 7th fret */
|
|
143
|
-
.string:nth-child(3) .fret:nth-child(10)::after
|
|
144
|
-
|
|
145
|
-
/* 9th fret */
|
|
146
|
-
{
|
|
147
|
-
content: '';
|
|
148
|
-
position: absolute;
|
|
149
|
-
width: 12px;
|
|
150
|
-
height: 12px;
|
|
151
|
-
background-color: rgba(255, 255, 255, 0.5);
|
|
152
|
-
border-radius: 50%;
|
|
153
|
-
z-index: 0;
|
|
154
|
-
top: 100%;
|
|
155
|
-
/* Position below the 3rd string (between 3 and 4 visually) */
|
|
156
|
-
transform: translate(50%, -50%);
|
|
157
|
-
/* Center on the fret wire? No, center in fret */
|
|
158
|
-
left: 50%;
|
|
159
|
-
transform: translate(-50%, 20px);
|
|
160
|
-
/* Push down to between strings */
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/* Double dot at 12th fret */
|
|
164
|
-
.string:nth-child(2) .fret:nth-child(13)::after,
|
|
165
|
-
.string:nth-child(4) .fret:nth-child(13)::after {
|
|
166
|
-
content: '';
|
|
167
|
-
position: absolute;
|
|
168
|
-
width: 12px;
|
|
169
|
-
height: 12px;
|
|
170
|
-
background-color: rgba(255, 255, 255, 0.5);
|
|
171
|
-
border-radius: 50%;
|
|
172
|
-
z-index: 0;
|
|
173
|
-
left: 50%;
|
|
174
|
-
transform: translate(-50%, 20px);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
@media (max-width: 768px) {
|
|
178
|
-
.controls-container {
|
|
179
|
-
gap: 1rem;
|
|
180
|
-
flex-direction: column;
|
|
181
|
-
align-items: stretch;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.control-group {
|
|
185
|
-
width: 100%;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.button-group {
|
|
189
|
-
display: flex;
|
|
190
|
-
gap: 0.5rem;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.button-group button {
|
|
194
|
-
flex: 1;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.fretboard-container {
|
|
198
|
-
margin: 0;
|
|
199
|
-
padding: 0;
|
|
200
|
-
overflow: hidden;
|
|
201
|
-
/* Hide overflow as requested */
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
.fretboard {
|
|
206
|
-
min-width: 0;
|
|
207
|
-
/* Allow it to shrink to fit screen */
|
|
208
|
-
width: 100%;
|
|
209
|
-
padding: 10px 0;
|
|
210
|
-
/* Reduce padding */
|
|
211
|
-
box-sizing: border-box;
|
|
212
|
-
/* Ensure padding is included */
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
.fret-numbers {
|
|
216
|
-
padding-left: 0;
|
|
217
|
-
padding-right: 0;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/* Fret Numbers Styles */
|
|
222
|
-
.fret-numbers {
|
|
223
|
-
display: flex;
|
|
224
|
-
height: 30px;
|
|
225
|
-
align-items: center;
|
|
226
|
-
margin-top: 5px;
|
|
227
|
-
padding-left: 20px;
|
|
228
|
-
/* Match fretboard padding */
|
|
229
|
-
padding-right: 20px;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
.fret-number {
|
|
233
|
-
flex: 1;
|
|
234
|
-
display: flex;
|
|
235
|
-
justify-content: center;
|
|
236
|
-
align-items: center;
|
|
237
|
-
font-size: 1rem;
|
|
238
|
-
/* Increased size */
|
|
239
|
-
color: var(--primary-text);
|
|
240
|
-
/* Higher contrast */
|
|
241
|
-
font-weight: bold;
|
|
242
|
-
border-right: 4px solid transparent;
|
|
243
|
-
/* Match fret wire width to align perfectly */
|
|
244
|
-
box-sizing: border-box;
|
|
245
|
-
/* Ensure border is calculated in size if we switch that on */
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/* Ensure frets also use consistent box sizing if not already */
|
|
249
|
-
.fret {
|
|
250
|
-
box-sizing: border-box;
|
|
251
|
-
}
|
package/js/form-builder.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
let dragged_element = null;
|
|
2
|
-
let action = 'copy';
|
|
3
|
-
let row_count = 1;
|
|
4
|
-
|
|
5
|
-
let formbuilder = {
|
|
6
|
-
init() {
|
|
7
|
-
document.querySelectorAll('.draggable').forEach(function (element) {
|
|
8
|
-
element.removeEventListener("dragstart", dragAction);
|
|
9
|
-
element.addEventListener("dragstart", dragAction);
|
|
10
|
-
});
|
|
11
|
-
initDropzones();
|
|
12
|
-
},
|
|
13
|
-
addRow() {
|
|
14
|
-
document.querySelector('.form-content').innerHTML += buildRowElement();
|
|
15
|
-
},
|
|
16
|
-
addColumn() {
|
|
17
|
-
|
|
18
|
-
},
|
|
19
|
-
removeRow() {
|
|
20
|
-
|
|
21
|
-
},
|
|
22
|
-
removeColumn() {
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
load(file_name) {
|
|
26
|
-
load(file_name).then((data) => {
|
|
27
|
-
console.log(data);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const moveAction = function (event) {
|
|
33
|
-
dragged_element = event.target;
|
|
34
|
-
action = 'move';
|
|
35
|
-
}
|
|
36
|
-
const dragAction = function (event) {
|
|
37
|
-
dragged_element = event.target;
|
|
38
|
-
action = 'copy';
|
|
39
|
-
}
|
|
40
|
-
const addMoveListeners = function () {
|
|
41
|
-
document.querySelectorAll('.movable').forEach(function (element) {
|
|
42
|
-
element.removeEventListener("dragstart", moveAction);
|
|
43
|
-
element.addEventListener("dragstart", moveAction);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const load = async function (file_name) {
|
|
48
|
-
const url = new URL(file_name,
|
|
49
|
-
document.currentScript && document.currentScript.src || location.href)
|
|
50
|
-
// fetch and parse template as string
|
|
51
|
-
let template = await fetch(url)
|
|
52
|
-
template = await template.text()
|
|
53
|
-
template = new DOMParser().parseFromString(template, 'text/html')
|
|
54
|
-
.querySelector('template')
|
|
55
|
-
if (!template) throw new TypeError('No template element found')
|
|
56
|
-
return template.innerHTML.trim()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const buildRowElement = function () {
|
|
60
|
-
const row = document.createElement('div');
|
|
61
|
-
row_count++;
|
|
62
|
-
row.setAttribute('data-row-id', String(row_count));
|
|
63
|
-
row.className = 'row';
|
|
64
|
-
const col = document.createElement('div');
|
|
65
|
-
col.className = 'column';
|
|
66
|
-
const dropzone = document.createElement('div');
|
|
67
|
-
dropzone.className = 'dropzone';
|
|
68
|
-
col.innerHTML = dropzone.outerHTML;
|
|
69
|
-
row.innerHTML = col.outerHTML;
|
|
70
|
-
return row.outerHTML;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const dropEvent = function (event) {
|
|
74
|
-
event.preventDefault();
|
|
75
|
-
let node = dragged_element;
|
|
76
|
-
if (action === 'copy') {
|
|
77
|
-
node = dragged_element.cloneNode(true);
|
|
78
|
-
}
|
|
79
|
-
node.classList.remove('draggable');
|
|
80
|
-
node.classList.add('movable');
|
|
81
|
-
const label = node.querySelector('span.label');
|
|
82
|
-
if (label) {
|
|
83
|
-
label.setAttribute('contenteditable', true);
|
|
84
|
-
}
|
|
85
|
-
const pElement = node.querySelector('p');
|
|
86
|
-
if (pElement) {
|
|
87
|
-
pElement.setAttribute('contenteditable', true);
|
|
88
|
-
}
|
|
89
|
-
event.target.appendChild(node);
|
|
90
|
-
addMoveListeners();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const dragOverEvent = function (event) {
|
|
94
|
-
event.preventDefault();
|
|
95
|
-
event.dataTransfer.effectAllowed = action;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const initDropzones = function () {
|
|
99
|
-
document.querySelectorAll('.dropzone').forEach(function (dropzone) {
|
|
100
|
-
dropzone.removeEventListener('drop', dropEvent);
|
|
101
|
-
dropzone.addEventListener('drop', dropEvent);
|
|
102
|
-
dropzone.removeEventListener('dragover', dragOverEvent);
|
|
103
|
-
dropzone.addEventListener('dragover', dragOverEvent);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export {formbuilder}
|
package/js/guitar-chords.js
DELETED
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
const NOTES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
|
|
2
|
-
const STRINGS = ['E', 'B', 'G', 'D', 'A', 'E']; // High E to Low E
|
|
3
|
-
const FRETS = 15;
|
|
4
|
-
|
|
5
|
-
// Standard tuning offsets from C (C=0)
|
|
6
|
-
// E=4, B=11, G=7, D=2, A=9, E=4
|
|
7
|
-
// But we need absolute pitch to know octaves if we want to be precise,
|
|
8
|
-
// but for simple note names, relative is fine.
|
|
9
|
-
// Let's map open strings to their semitone value (0-11)
|
|
10
|
-
const STRING_TUNING = [4, 11, 7, 2, 9, 4];
|
|
11
|
-
|
|
12
|
-
// Chord Formulas (intervals in semitones)
|
|
13
|
-
const CHORD_FORMULAS = {
|
|
14
|
-
'Major': [0, 4, 7],
|
|
15
|
-
'Minor': [0, 3, 7],
|
|
16
|
-
'5': [0, 7],
|
|
17
|
-
'7': [0, 4, 7, 10],
|
|
18
|
-
'Maj7': [0, 4, 7, 11],
|
|
19
|
-
'm7': [0, 3, 7, 10],
|
|
20
|
-
'sus4': [0, 5, 7],
|
|
21
|
-
'sus2': [0, 2, 7],
|
|
22
|
-
'dim': [0, 3, 6],
|
|
23
|
-
'aug': [0, 4, 8]
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// Common open chords for the dropdown
|
|
27
|
-
const COMMON_CHORDS = {
|
|
28
|
-
'C Major': [0, 1, 0, 2, 3, -1], // -1 for muted/unused
|
|
29
|
-
'A Major': [0, 2, 2, 2, 0, -1],
|
|
30
|
-
'G Major': [3, 0, 0, 0, 2, 3],
|
|
31
|
-
'E Major': [0, 0, 1, 2, 2, 0],
|
|
32
|
-
'D Major': [2, 3, 2, 0, -1, -1],
|
|
33
|
-
'A Minor': [0, 1, 2, 2, 0, -1],
|
|
34
|
-
'E Minor': [0, 0, 0, 2, 2, 0],
|
|
35
|
-
'D Minor': [1, 3, 2, 0, -1, -1]
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// State
|
|
39
|
-
let selectedFrets = [-1, -1, -1, -1, -1, -1];
|
|
40
|
-
let startFret = 0;
|
|
41
|
-
let visibleFrets = 15;
|
|
42
|
-
|
|
43
|
-
function init() {
|
|
44
|
-
handleResize();
|
|
45
|
-
window.addEventListener('resize', handleResize);
|
|
46
|
-
|
|
47
|
-
renderFretboard();
|
|
48
|
-
populateChordSelect();
|
|
49
|
-
setupEventListeners();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function handleResize() {
|
|
53
|
-
const isMobile = window.innerWidth <= 768;
|
|
54
|
-
const newVisible = isMobile ? 5 : 15;
|
|
55
|
-
if (newVisible !== visibleFrets) {
|
|
56
|
-
visibleFrets = newVisible;
|
|
57
|
-
renderFretboard();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function renderFretboard() {
|
|
62
|
-
const container = document.getElementById('fretboard');
|
|
63
|
-
container.innerHTML = '';
|
|
64
|
-
|
|
65
|
-
STRINGS.forEach((stringName, stringIndex) => {
|
|
66
|
-
const stringDiv = document.createElement('div');
|
|
67
|
-
stringDiv.className = 'string';
|
|
68
|
-
|
|
69
|
-
// Label for string name (optional, but good for context)
|
|
70
|
-
// For now, keeping it simple as before
|
|
71
|
-
|
|
72
|
-
// Create frets (showing visibleFrets count)
|
|
73
|
-
for (let i = 0; i <= visibleFrets; i++) {
|
|
74
|
-
// Actual semantic fret number
|
|
75
|
-
const currentFretNum = startFret + i;
|
|
76
|
-
|
|
77
|
-
// Skip rendering if beyond max reasonable frets (e.g., 24), but let's just stick to logic
|
|
78
|
-
|
|
79
|
-
const fretDiv = document.createElement('div');
|
|
80
|
-
fretDiv.className = 'fret';
|
|
81
|
-
|
|
82
|
-
// Mark if this is the Nut (fret 0)
|
|
83
|
-
if (currentFretNum === 0) {
|
|
84
|
-
fretDiv.classList.add('nut');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Check if selected
|
|
88
|
-
if (selectedFrets[stringIndex] === currentFretNum) {
|
|
89
|
-
fretDiv.classList.add('active');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Note marker
|
|
93
|
-
const marker = document.createElement('div');
|
|
94
|
-
marker.className = 'note-marker';
|
|
95
|
-
|
|
96
|
-
// Calculate note
|
|
97
|
-
const noteIndex = (STRING_TUNING[stringIndex] + currentFretNum) % 12;
|
|
98
|
-
marker.textContent = NOTES[noteIndex];
|
|
99
|
-
|
|
100
|
-
fretDiv.appendChild(marker);
|
|
101
|
-
|
|
102
|
-
// Click handler
|
|
103
|
-
fretDiv.addEventListener('click', () => toggleFret(stringIndex, currentFretNum));
|
|
104
|
-
|
|
105
|
-
// Fret labels (markers)
|
|
106
|
-
// Adding dots logic dynamically might be complex.
|
|
107
|
-
// Simplified: showing note name is good enough?
|
|
108
|
-
// Let's add a small number indicator for the fret number if needed?
|
|
109
|
-
// User didn't request explicit fret numbers, but it helps.
|
|
110
|
-
// Let's leave it clean for now.
|
|
111
|
-
|
|
112
|
-
stringDiv.appendChild(fretDiv);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
container.appendChild(stringDiv);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// Add Fret Numbers Row
|
|
119
|
-
const numberRow = document.createElement('div');
|
|
120
|
-
numberRow.className = 'fret-numbers';
|
|
121
|
-
|
|
122
|
-
for (let i = 0; i <= visibleFrets; i++) {
|
|
123
|
-
const currentFretNum = startFret + i;
|
|
124
|
-
const numberDiv = document.createElement('div');
|
|
125
|
-
numberDiv.className = 'fret-number';
|
|
126
|
-
|
|
127
|
-
// Handle Nut alignment matching
|
|
128
|
-
if (currentFretNum === 0) {
|
|
129
|
-
numberDiv.classList.add('nut-number');
|
|
130
|
-
numberDiv.style.flex = '0 0 50px';
|
|
131
|
-
numberDiv.style.borderRightWidth = '8px'; // Match nut border
|
|
132
|
-
numberDiv.style.borderRightColor = 'transparent';
|
|
133
|
-
numberDiv.textContent = '0';
|
|
134
|
-
} else {
|
|
135
|
-
numberDiv.textContent = currentFretNum;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
numberRow.appendChild(numberDiv);
|
|
139
|
-
}
|
|
140
|
-
container.appendChild(numberRow);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function toggleFret(stringIndex, fretIndex) {
|
|
144
|
-
if (selectedFrets[stringIndex] === fretIndex) {
|
|
145
|
-
// Deselect
|
|
146
|
-
selectedFrets[stringIndex] = -1;
|
|
147
|
-
} else {
|
|
148
|
-
// Select
|
|
149
|
-
selectedFrets[stringIndex] = fretIndex;
|
|
150
|
-
}
|
|
151
|
-
renderFretboard();
|
|
152
|
-
detectChord();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function populateChordSelect() {
|
|
156
|
-
const select = document.getElementById('chord-select');
|
|
157
|
-
for (const [name, frets] of Object.entries(COMMON_CHORDS)) {
|
|
158
|
-
const option = document.createElement('option');
|
|
159
|
-
option.value = name;
|
|
160
|
-
option.textContent = name;
|
|
161
|
-
select.appendChild(option);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function setupEventListeners() {
|
|
166
|
-
document.getElementById('chord-select').addEventListener('change', (e) => {
|
|
167
|
-
const chordName = e.target.value;
|
|
168
|
-
if (chordName && COMMON_CHORDS[chordName]) {
|
|
169
|
-
selectedFrets = [...COMMON_CHORDS[chordName]];
|
|
170
|
-
// If selected frets are out of view, maybe jump startFret?
|
|
171
|
-
// Let's just render.
|
|
172
|
-
// Find min/max fret to adjust view?
|
|
173
|
-
// Optional polish. For now, leave startFret as user set.
|
|
174
|
-
renderFretboard();
|
|
175
|
-
detectChord();
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
document.getElementById('transpose-up').addEventListener('click', () => transpose(1));
|
|
180
|
-
document.getElementById('transpose-down').addEventListener('click', () => transpose(-1));
|
|
181
|
-
|
|
182
|
-
const startFretInput = document.getElementById('start-fret');
|
|
183
|
-
if (startFretInput) {
|
|
184
|
-
startFretInput.addEventListener('change', (e) => {
|
|
185
|
-
let val = parseInt(e.target.value);
|
|
186
|
-
if (val < 0) val = 0;
|
|
187
|
-
if (val > 15) val = 15; // Arbitrary max
|
|
188
|
-
startFret = val;
|
|
189
|
-
renderFretboard();
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function transpose(semitones) {
|
|
195
|
-
// Shift all selected frets
|
|
196
|
-
for (let i = 0; i < selectedFrets.length; i++) {
|
|
197
|
-
if (selectedFrets[i] !== -1) { // Don't shift muted strings
|
|
198
|
-
let newFret = selectedFrets[i] + semitones;
|
|
199
|
-
// logic for open strings shifting down implies they can't go below 0 unless muted.
|
|
200
|
-
if (newFret < 0) newFret = 0; // clamp
|
|
201
|
-
if (newFret > 24) newFret = 24; // arbitrary max
|
|
202
|
-
selectedFrets[i] = newFret;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
renderFretboard();
|
|
206
|
-
detectChord();
|
|
207
|
-
|
|
208
|
-
// Reset select to custom because we modified it
|
|
209
|
-
document.getElementById('chord-select').value = "";
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function detectChord() {
|
|
213
|
-
// Collect all notes being played
|
|
214
|
-
const playedNotes = [];
|
|
215
|
-
const playedIndices = [];
|
|
216
|
-
|
|
217
|
-
selectedFrets.forEach((fret, stringIndex) => {
|
|
218
|
-
if (fret !== -1) {
|
|
219
|
-
const noteIndex = (STRING_TUNING[stringIndex] + fret) % 12;
|
|
220
|
-
playedNotes.push(NOTES[noteIndex]);
|
|
221
|
-
playedIndices.push(noteIndex);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
if (playedNotes.length === 0) {
|
|
226
|
-
document.getElementById('detected-chord').textContent = "--";
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Remove duplicates and sort
|
|
231
|
-
const uniqueIndices = [...new Set(playedIndices)].sort((a, b) => a - b);
|
|
232
|
-
|
|
233
|
-
// Try to match against formulas
|
|
234
|
-
// We need to check every note as a potential root
|
|
235
|
-
let bestMatch = null;
|
|
236
|
-
|
|
237
|
-
for (const rootIndex of uniqueIndices) {
|
|
238
|
-
const rootNote = NOTES[rootIndex];
|
|
239
|
-
|
|
240
|
-
// Calculate intervals relative to this root
|
|
241
|
-
const currentIntervals = uniqueIndices.map(index => {
|
|
242
|
-
let interval = index - rootIndex;
|
|
243
|
-
if (interval < 0) interval += 12;
|
|
244
|
-
return interval;
|
|
245
|
-
}).sort((a, b) => a - b);
|
|
246
|
-
|
|
247
|
-
// Compare with formulas
|
|
248
|
-
for (const [type, formula] of Object.entries(CHORD_FORMULAS)) {
|
|
249
|
-
if (arraysEqual(currentIntervals, formula)) {
|
|
250
|
-
bestMatch = `${rootNote} ${type}`;
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (bestMatch) break;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
document.getElementById('detected-chord').textContent = bestMatch || "Unknown";
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function arraysEqual(a, b) {
|
|
261
|
-
if (a.length !== b.length) return false;
|
|
262
|
-
for (let i = 0; i < a.length; i++) {
|
|
263
|
-
if (a[i] !== b[i]) return false;
|
|
264
|
-
}
|
|
265
|
-
return true;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
init();
|