vector-score 1.0.3 → 1.2.0

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.
@@ -1,4 +1,4 @@
1
- const B = {
1
+ const S = {
2
2
  treble: {
3
3
  staffType: "treble",
4
4
  paddingTop: 13,
@@ -31,13 +31,66 @@ const B = {
31
31
  topLineYPos: 0,
32
32
  bottomLineYPos: 110
33
33
  }
34
- }, d = {
34
+ }, p = {
35
35
  w: 4,
36
36
  h: 2,
37
37
  q: 1,
38
38
  e: 0.5,
39
39
  s: 0.25
40
- }, X = {
40
+ }, st = 6, rt = 5, I = 22, T = 30, E = 10, f = 6, nt = 4, ot = E * 2, it = 40, at = 16, X = 1, F = 4, U = 1, Y = f * 2 + F + 4, D = 16, C = 14, ct = 14, M = D, ht = E + 2, $ = 8, lt = 40, J = {
41
+ sharp: ["F", "C", "G", "D", "A", "E", "B"],
42
+ flat: ["B", "E", "A", "D", "G", "C", "F"]
43
+ }, k = {
44
+ C: { type: "sharp", count: 0 },
45
+ G: { type: "sharp", count: 1 },
46
+ D: { type: "sharp", count: 2 },
47
+ A: { type: "sharp", count: 3 },
48
+ E: { type: "sharp", count: 4 },
49
+ B: { type: "sharp", count: 5 },
50
+ "F#": { type: "sharp", count: 6 },
51
+ F: { type: "flat", count: 1 },
52
+ Bb: { type: "flat", count: 2 },
53
+ Eb: { type: "flat", count: 3 },
54
+ Ab: { type: "flat", count: 4 },
55
+ Db: { type: "flat", count: 5 },
56
+ Gb: { type: "flat", count: 6 }
57
+ }, B = {
58
+ treble: {
59
+ sharp: [5, 5, 5, 5, 4, 5, 4],
60
+ // F5 C5 G5 D5 A4 E5 B4
61
+ flat: [4, 5, 4, 5, 4, 5, 4]
62
+ // B4 E5 A4 D5 G4 C5 F4
63
+ },
64
+ bass: {
65
+ sharp: [3, 3, 3, 3, 2, 3, 2],
66
+ // F3 C3 G3 D3 A2 E3 B2
67
+ flat: [2, 3, 2, 3, 2, 3, 3]
68
+ // B2 E3 A2 D3 G2 C3 F3
69
+ },
70
+ alto: {
71
+ sharp: [4, 4, 4, 4, 3, 4, 4],
72
+ // F5 C5 G5 D5 A4 E5 B4
73
+ flat: [4, 5, 4, 5, 4, 5, 4]
74
+ // B4 E5 A4 D5 G4 C5 F4
75
+ },
76
+ grand: { sharp: [], flat: [] }
77
+ };
78
+ function dt(h) {
79
+ const e = k[h];
80
+ if (!e) throw new Error(`Unknown key signature "${h}". Valid keys: ${Object.keys(k).join(", ")}`);
81
+ return e;
82
+ }
83
+ function ft(h, e, t, s) {
84
+ const r = dt(t);
85
+ if (r.count === 0) return 0;
86
+ const o = r.type === "sharp" ? "ACCIDENTAL_SHARP" : "ACCIDENTAL_FLAT", n = h.getLayerByName("staff");
87
+ return e.getKeySignatureYPositions(r.type, r.count).forEach((a) => {
88
+ a.forEach((c, l) => {
89
+ h.drawGlyph(o, n, { xOffset: s + l * $, yOffset: c });
90
+ });
91
+ }), r.count * $;
92
+ }
93
+ const Et = {
41
94
  CLEF_TREBLE: {
42
95
  path: "m150 273 10 58c2 7 2 7 12 7 59 0 96 46 96 97 0 45-26 79-67 95-5 2-6 2-5 7 4 25 12 63 12 85 0 68-52 80-79 80-61 0-76-39-76-65 0-25 16-46 43-46 24 0 38 19 38 41 0 23-14 34-27 38-9 2-13 4-13 6 0 6 11 12 32 12 24 0 64-7 64-66 0-19-6-54-11-81-1-5-1-4-6-3l-27 2C48 540 0 474 0 404c0-80 61-138 119-185 5-4 4-5 3-10-2-16-5-42-5-65 0-42 9-92 39-125 8-9 20-19 26-19 4 0 15 11 21 20 16 24 26 58 26 93 0 61-33 112-76 153-3 2-3 2-3 7Zm38-211c-24 0-53 38-53 101l2 37c1 5 3 5 5 3 32-28 70-64 70-108 0-22-11-33-24-33Zm-44 272-8-51c-1-4-2-5-6-1-18 15-37 30-61 56-33 38-37 70-37 93 0 56 45 95 115 95l23-2c6-2 6-2 5-6l-20-119c-1-5-1-5-8-3-24 6-40 24-40 46 0 19 12 36 29 43 3 1 6 3 6 5 0 3-2 5-5 5l-11-3c-27-9-46-34-46-70 0-34 23-66 58-78 8-2 8-2 6-10Zm28 64 20 114c0 5 1 5 6 2 22-11 38-31 38-56 0-36-27-63-60-66-4 0-5 1-4 6Z",
43
96
  xOffset: 0,
@@ -133,17 +186,13 @@ const B = {
133
186
  xOffset: 0,
134
187
  yOffset: 0
135
188
  }
136
- }, x = /^(?<name>[A-Ga-g])(?<accidental>##|bb|[#bn]?)(?<octave>\d)(?<duration>[whqeWHQE]?)$/, U = /^[whqeWHQE]$/, I = ["C", "D", "E", "F", "G", "A", "B"];
137
- function g(h) {
138
- const e = h.match(x);
189
+ }, ut = /^(?<name>[A-Ga-g])(?<accidental>##|bb|[#bn]?)(?<octave>\d)(?<duration>[whqeWHQE]?)$/, gt = /^[whqeWHQE]$/, H = ["C", "D", "E", "F", "G", "A", "B"];
190
+ function P(h) {
191
+ const e = h.match(ut);
139
192
  if (!e || !e.groups)
140
193
  throw new Error(`Invalid note string format: ${h}. Expected format: [A-Ga-g][#|b]?[0-9][w|h|q|e].`);
141
194
  let { name: t, accidental: s, octave: r, duration: o } = e.groups;
142
- if (t = t.toUpperCase(), o = o.toLowerCase(), !t)
143
- throw new Error(`Invalid note name: ${t}. Valid note names are: C, D, E, F, G, A, B.`);
144
- if (!r)
145
- throw new Error(`Invalid octave: ${r}. Octave must be a number between 0 and 9.`);
146
- o || (o = "w");
195
+ t = t.toUpperCase(), o = o.toLowerCase(), o || (o = "w");
147
196
  const n = {
148
197
  name: t,
149
198
  octave: parseInt(r),
@@ -151,12 +200,12 @@ function g(h) {
151
200
  };
152
201
  return s && (n.accidental = s), n;
153
202
  }
154
- function T(h) {
155
- const e = h.match(U);
203
+ function R(h) {
204
+ const e = h.match(gt);
156
205
  if (!e) throw new Error(`Invalid note duration '${h}'. Use w | h | q | e.`);
157
- return e.toString().toLowerCase();
206
+ return e[0].toString().toLowerCase();
158
207
  }
159
- function S(h) {
208
+ function x(h) {
160
209
  let e;
161
210
  switch (h) {
162
211
  case "treble":
@@ -172,26 +221,22 @@ function S(h) {
172
221
  if (!e) throw new Error(`Invalid clef type: ${h}. Valid clef types are: treble, bass, alto.`);
173
222
  return e;
174
223
  }
175
- function D(h, e) {
176
- const t = I.indexOf(h.name) - I.indexOf(e.name);
177
- let s = h.octave - e.octave;
178
- return s *= 7, t + s;
179
- }
180
- function R(h) {
181
- let e = I.indexOf(h.name);
224
+ function W(h) {
225
+ let e = H.indexOf(h.name);
182
226
  return e += h.octave * 12, e;
183
227
  }
184
- function y(h, e) {
185
- return I.findIndex((s) => s === h) + 1 + e * 7;
228
+ function m(h, e) {
229
+ return H.indexOf(h.name) - H.indexOf(e.name) + (h.octave - e.octave) * 7;
186
230
  }
187
- const N = 48, w = 40 + 30 / 2, b = 20, F = 90;
188
- class H {
231
+ const y = 48, Z = 40 + 30 / 2, V = 20, q = 90;
232
+ class tt {
189
233
  params;
190
234
  rendererRef;
191
235
  width = 0;
236
+ trebleStaffHeight = 0;
192
237
  constructor(e, t) {
193
238
  this.rendererRef = e;
194
- const s = B[t];
239
+ const s = S[t];
195
240
  if (!s) throw new Error(`Staff type ${t} is not supported`);
196
241
  this.params = s;
197
242
  }
@@ -205,19 +250,19 @@ class H {
205
250
  this.width = e;
206
251
  const t = this.rendererRef.getLayerByName("staff");
207
252
  let s = this.drawStaffLines(0, t), r = this.drawStaffLines(s + 30, t);
208
- this.rendererRef.drawLine(0, 0, 0, r, t), this.rendererRef.drawLine(this.width, 0, this.width, r, t);
253
+ this.rendererRef.drawLine(0, 0, 0, r, t), this.rendererRef.drawLine(this.width, 0, this.width, r, t), this.trebleStaffHeight = s;
209
254
  let o = r, n = 1;
210
- const a = S("treble"), i = S("bass");
211
- this.rendererRef.drawGlyph(a, t), this.rendererRef.drawGlyph(i, t, { yOffset: s + 30 }), o += this.params.paddingBottom + 1, n += this.params.paddingTop, this.rendererRef.addTotalRootSvgHeight(o), this.rendererRef.addTotalRootSvgYOffset(n);
255
+ const i = x("treble"), a = x("bass");
256
+ this.rendererRef.drawGlyph(i, t), this.rendererRef.drawGlyph(a, t, { yOffset: s + 30 }), o += this.params.paddingBottom + 1, n += this.params.paddingTop, this.rendererRef.addTotalRootSvgHeight(o), this.rendererRef.addTotalRootSvgYOffset(n);
212
257
  };
213
258
  shouldNoteFlip(e) {
214
- const t = Math.abs(e - F), s = Math.abs(e - b);
215
- return e === w ? !1 : t <= s ? !(e >= F) : e <= b;
259
+ const t = Math.abs(e - q), s = Math.abs(e - V);
260
+ return e === Z ? !1 : t <= s ? !(e >= q) : e <= V;
216
261
  }
217
262
  getLedgerLinesX(e, t) {
218
- const s = [], r = R(e);
263
+ const s = [], r = W(e);
219
264
  let o = e.duration === "w" ? 17 : 12.5;
220
- if (r === N)
265
+ if (r === y)
221
266
  return s.push({ x1: -2, x2: o, yPos: 0 }), s;
222
267
  if (t < this.params.topLineYPos) {
223
268
  let n = this.params.topLineYPos - 10;
@@ -239,33 +284,41 @@ class H {
239
284
  return s;
240
285
  }
241
286
  calculateNoteYPos = (e) => {
242
- let s = D(this.params.topLineNote, e) * (10 / 2);
243
- const r = R(e);
244
- return r < N ? s += 10 : r === N && (s = w), s;
287
+ let s = m(this.params.topLineNote, e) * (10 / 2);
288
+ const r = W(e);
289
+ return r < y ? s += 10 : r === y && (s = Z), s;
245
290
  };
291
+ getKeySignatureYPositions(e, t) {
292
+ const s = J[e].slice(0, t), r = B.treble[e], o = B.bass[e], n = s.map(
293
+ (a, c) => m(S.treble.topLineNote, { name: a, octave: r[c] }) * (10 / 2)
294
+ ), i = s.map(
295
+ (a, c) => m(S.bass.topLineNote, { name: a, octave: o[c] }) * (10 / 2) + this.trebleStaffHeight + 30
296
+ );
297
+ return [n, i];
298
+ }
246
299
  }
247
- const M = 20;
248
- class A {
300
+ const pt = 20;
301
+ class _ {
249
302
  params;
250
303
  rendererRef;
251
304
  constructor(e, t) {
252
305
  this.rendererRef = e;
253
- const s = B[t];
306
+ const s = S[t];
254
307
  if (!s) throw new Error(`Staff type ${t} is not supported`);
255
308
  this.params = s;
256
309
  }
257
310
  drawStaff = (e) => {
258
311
  const t = this.rendererRef.getLayerByName("staff");
259
312
  let s = 0;
260
- for (let a = 0; a < 5; a++)
313
+ for (let i = 0; i < 5; i++)
261
314
  this.rendererRef.drawLine(0, s, e, s, t), s += 10;
262
315
  this.rendererRef.drawLine(0, 0, 0, s - 10, t), this.rendererRef.drawLine(e, 0, e, s - 10, t);
263
316
  let r = s - 10 + 1, o = 1;
264
- const n = S(this.params.staffType);
317
+ const n = x(this.params.staffType);
265
318
  this.rendererRef.drawGlyph(n, t), r += this.params.paddingTop + this.params.paddingBottom, o += this.params.paddingTop, this.rendererRef.addTotalRootSvgHeight(r), this.rendererRef.addTotalRootSvgYOffset(o);
266
319
  };
267
320
  shouldNoteFlip(e) {
268
- return e <= M;
321
+ return e <= pt;
269
322
  }
270
323
  getLedgerLinesX(e, t) {
271
324
  const s = [];
@@ -289,9 +342,15 @@ class A {
289
342
  }
290
343
  return s;
291
344
  }
292
- calculateNoteYPos = (e) => D(this.params.topLineNote, e) * (10 / 2);
345
+ calculateNoteYPos = (e) => m(this.params.topLineNote, e) * (10 / 2);
346
+ getKeySignatureYPositions(e, t) {
347
+ const s = J[e].slice(0, t), r = B[this.params.staffType][e];
348
+ return [s.map(
349
+ (n, i) => this.calculateNoteYPos({ name: n, octave: r[i] })
350
+ )];
351
+ }
293
352
  }
294
- class P {
353
+ class et {
295
354
  svgRendererInstance;
296
355
  strategyInstance;
297
356
  constructor(e, t) {
@@ -305,9 +364,9 @@ class P {
305
364
  for (let o = 0; o < e.length; o++) {
306
365
  const n = e[o];
307
366
  if (n.noteObj.accidental && r < 3 ? (t += -8, s = Math.min(s, t), r++) : n.noteObj.accidental && r <= 3 ? (t = -8, r = 1) : (t = 0, r = 0), t !== 0) {
308
- const i = Array.from(n.noteGroup.getElementsByTagName("use")).find((c) => c.getAttribute("href")?.includes("ACCIDENTAL"));
309
- if (!i) continue;
310
- i.setAttribute("transform", `translate(${t + 8}, 0)`);
367
+ const a = Array.from(n.noteGroup.getElementsByTagName("use")).find((c) => c.getAttribute("href")?.includes("ACCIDENTAL"));
368
+ if (!a) continue;
369
+ a.setAttribute("transform", `translate(${t + 8}, 0)`);
311
370
  }
312
371
  }
313
372
  return -s;
@@ -316,13 +375,13 @@ class P {
316
375
  let t = e[0], s = 0;
317
376
  for (let r = 1; r < e.length; r++) {
318
377
  const o = e[r];
319
- if (y(o.noteObj.name, o.noteObj.octave) - y(t.noteObj.name, t.noteObj.octave) === 1) {
378
+ if (-m(t.noteObj, o.noteObj) === 1) {
320
379
  s = 28 / 2, o.noteGroup.setAttribute("transform", `translate(${s}, ${o.noteYPos})`);
321
- const i = Array.from(o.noteGroup.getElementsByTagName("use")).find((c) => c.getAttribute("href")?.includes("ACCIDENTAL"));
322
- if (i) {
323
- const c = i.getAttribute("transform")?.match(/([-]?\d+)/), l = c && c[0];
324
- let f = -s;
325
- l && (f += Number(l)), i.setAttribute("transform", `translate(${f}, 0)`);
380
+ const a = Array.from(o.noteGroup.getElementsByTagName("use")).find((c) => c.getAttribute("href")?.includes("ACCIDENTAL"));
381
+ if (a) {
382
+ const c = a.getAttribute("transform")?.match(/([-]?\d+)/), l = c && c[0];
383
+ let d = -s;
384
+ l && (d += Number(l)), a.setAttribute("transform", `translate(${d}, 0)`);
326
385
  }
327
386
  r++, t = e[r];
328
387
  continue;
@@ -333,7 +392,7 @@ class P {
333
392
  }
334
393
  // Handles drawing the glyphs to internal group, applies the xPositioning to note Cursor X
335
394
  renderNote(e) {
336
- const t = this.svgRendererInstance.createGroup("note"), s = g(e), r = this.strategyInstance.calculateNoteYPos({
395
+ const t = this.svgRendererInstance.createGroup("note"), s = P(e), r = this.strategyInstance.calculateNoteYPos({
337
396
  name: s.name,
338
397
  octave: s.octave
339
398
  });
@@ -369,8 +428,8 @@ class P {
369
428
  this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_FLAT", t), n -= -14;
370
429
  break;
371
430
  }
372
- return this.strategyInstance.getLedgerLinesX(s, r).forEach((i) => {
373
- this.svgRendererInstance.drawLine(i.x1, i.yPos, i.x2, i.yPos, t);
431
+ return this.strategyInstance.getLedgerLinesX(s, r).forEach((a) => {
432
+ this.svgRendererInstance.drawLine(a.x1, a.yPos, a.x2, a.yPos, t);
374
433
  }), {
375
434
  noteGroup: t,
376
435
  noteObj: s,
@@ -382,11 +441,11 @@ class P {
382
441
  renderChord(e) {
383
442
  const t = this.svgRendererInstance.createGroup("chord"), s = [];
384
443
  for (const n of e) {
385
- const a = this.renderNote(n);
386
- a.noteGroup.setAttribute("transform", `translate(0, ${a.noteYPos})`), t.appendChild(a.noteGroup), s.push({
387
- noteGroup: a.noteGroup,
388
- noteObj: a.noteObj,
389
- noteYPos: a.noteYPos,
444
+ const i = this.renderNote(n);
445
+ i.noteGroup.setAttribute("transform", `translate(0, ${i.noteYPos})`), t.appendChild(i.noteGroup), s.push({
446
+ noteGroup: i.noteGroup,
447
+ noteObj: i.noteObj,
448
+ noteYPos: i.noteYPos,
390
449
  cursorOffset: 0,
391
450
  accidentalOffset: 0
392
451
  });
@@ -401,8 +460,8 @@ class P {
401
460
  };
402
461
  }
403
462
  }
404
- const u = "http://www.w3.org/2000/svg", Y = 0.1, m = 1200;
405
- class O {
463
+ const g = "http://www.w3.org/2000/svg", At = 0.1, w = 1200;
464
+ class L {
406
465
  rootElementRef;
407
466
  svgElementRef;
408
467
  // Layers
@@ -420,18 +479,18 @@ class O {
420
479
  // from get rootSvgElement().
421
480
  // HEIGHT and YOfsset should be set externally, values are calculated internally.
422
481
  constructor(e, t) {
423
- this.rootElementRef = e, this.width = t.width, this.scale = t.scale, this.width > m && (this.width = m, console.warn(`Width provided for the staff ${this.width} exceeds the limit ${m}. Please use this value to fix positioning issues.`)), this.svgElementRef = document.createElementNS(u, "svg"), this.svgElementRef.classList.add("vs-svg-renderer-root"), this.svgElementRef.style.maxWidth = "100%", this.svgElementRef.style.height = "auto", this.svgElementRef.style.display = "block", this.svgElementRef.setAttribute("color", t.staffColor), this.svgElementRef.style.backgroundColor = t.staffBackgroundColor, this.makeGlyphDefs(t.useGlyphs), this.parentGroupContainer = this.createGroup("svg-renderer-parent"), this.svgElementRef.appendChild(this.parentGroupContainer), this.musicStaffLayer = this.createGroup("music-staff-layer"), this.musicNotesLayer = this.createGroup("music-notes-layer"), this.musicUILayer = this.createGroup("music-ui-layer"), this.parentGroupContainer.appendChild(this.musicStaffLayer), this.parentGroupContainer.appendChild(this.musicNotesLayer), this.parentGroupContainer.appendChild(this.musicUILayer);
482
+ this.rootElementRef = e, this.width = t.width, this.scale = t.scale, this.width > w && (this.width = w, console.warn(`Width provided for the staff ${this.width} exceeds the limit ${w}. Please use this value to fix positioning issues.`)), this.svgElementRef = document.createElementNS(g, "svg"), this.svgElementRef.classList.add("vs-svg-renderer-root"), this.svgElementRef.style.maxWidth = "100%", this.svgElementRef.style.height = "auto", this.svgElementRef.style.display = "block", this.svgElementRef.setAttribute("color", t.staffColor), this.svgElementRef.style.backgroundColor = t.staffBackgroundColor, this.makeGlyphDefs(t.useGlyphs), this.parentGroupContainer = this.createGroup("svg-renderer-parent"), this.svgElementRef.appendChild(this.parentGroupContainer), this.musicStaffLayer = this.createGroup("music-staff-layer"), this.musicNotesLayer = this.createGroup("music-notes-layer"), this.musicUILayer = this.createGroup("music-ui-layer"), this.parentGroupContainer.appendChild(this.musicStaffLayer), this.parentGroupContainer.appendChild(this.musicNotesLayer), this.parentGroupContainer.appendChild(this.musicUILayer);
424
483
  }
425
484
  // Creates SVG defs for all glyphs in GLYPH_ENTRIES, applies global scale and offsets, appends to root SVG
426
485
  makeGlyphDefs(e) {
427
- const t = document.createElementNS(u, "defs");
428
- Object.entries(X).filter(([r]) => e.includes(r)).forEach(([r, o]) => {
429
- const n = document.createElementNS(u, "path");
430
- n.setAttribute("id", `glyph-${r}`), n.setAttribute("d", o.path), n.setAttribute("fill", "currentColor"), n.setAttribute("transform", `translate(${o.xOffset}, ${o.yOffset}) scale(${Y})`), t.appendChild(n);
486
+ const t = document.createElementNS(g, "defs");
487
+ Object.entries(Et).filter(([r]) => e.includes(r)).forEach(([r, o]) => {
488
+ const n = document.createElementNS(g, "path");
489
+ n.setAttribute("id", `glyph-${r}`), n.setAttribute("d", o.path), n.setAttribute("fill", "currentColor"), n.setAttribute("transform", `translate(${o.xOffset}, ${o.yOffset}) scale(${At})`), t.appendChild(n);
431
490
  }), this.rootSvgElement.appendChild(t);
432
491
  }
433
492
  createGroup(e) {
434
- const t = document.createElementNS(u, "g");
493
+ const t = document.createElementNS(g, "g");
435
494
  return e && t.classList.add(`vs-${e}`), t;
436
495
  }
437
496
  commitElementsToDOM(e, t = this.rootElementRef) {
@@ -455,6 +514,9 @@ class O {
455
514
  addTotalRootSvgHeight(e) {
456
515
  this.totalHeight += e;
457
516
  }
517
+ setTotalRootSvgHeight(e) {
518
+ this.totalHeight = e;
519
+ }
458
520
  addTotalRootSvgYOffset(e) {
459
521
  this.totalYOffset += e;
460
522
  }
@@ -472,12 +534,12 @@ class O {
472
534
  }
473
535
  // Drawing Methods
474
536
  drawLine(e, t, s, r, o) {
475
- const n = document.createElementNS(u, "line");
537
+ const n = document.createElementNS(g, "line");
476
538
  n.setAttribute("x1", e.toString()), n.setAttribute("y1", t.toString()), n.setAttribute("x2", s.toString()), n.setAttribute("y2", r.toString()), n.setAttribute("stroke", "currentColor"), n.setAttribute("stroke-width", "1"), o.appendChild(n);
477
539
  }
478
540
  drawRect(e, t, s, r) {
479
- const o = document.createElementNS(u, "rect");
480
- return o.setAttribute("width", e.toString()), o.setAttribute("height", t.toString()), o.setAttribute("x", (r?.x ?? 0).toString()), o.setAttribute("y", (r?.y ?? 0).toString()), r?.fill ? o.setAttribute("fill", r.fill) : o.setAttribute("fill", "currentColor"), s.appendChild(o), o;
541
+ const o = document.createElementNS(g, "rect");
542
+ return o.setAttribute("width", e.toString()), o.setAttribute("height", t.toString()), o.setAttribute("x", (r?.x ?? 0).toString()), o.setAttribute("y", (r?.y ?? 0).toString()), r?.fill ? o.setAttribute("fill", r.fill) : o.setAttribute("fill", "currentColor"), r?.rx && o.setAttribute("rx", r.rx.toString()), s.appendChild(o), o;
481
543
  }
482
544
  drawGlyph(e, t, s) {
483
545
  s = {
@@ -485,14 +547,22 @@ class O {
485
547
  yOffset: 0,
486
548
  ...s
487
549
  };
488
- const r = document.createElementNS(u, "use");
550
+ const r = document.createElementNS(g, "use");
489
551
  r.setAttribute("href", `#glyph-${e}`), (s.xOffset || s.yOffset) && r.setAttribute("transform", `translate(${s.xOffset}, ${s.yOffset})`), t.appendChild(r);
490
552
  }
553
+ drawCircle(e, t, s, r, o) {
554
+ const n = document.createElementNS(g, "circle");
555
+ return n.setAttribute("cx", e.toString()), n.setAttribute("cy", t.toString()), n.setAttribute("r", s.toString()), o?.filled === !1 ? (n.setAttribute("fill", "none"), n.setAttribute("stroke", "currentColor"), n.setAttribute("stroke-width", (o?.strokeWidth ?? 1.5).toString())) : n.setAttribute("fill", o?.fill ?? "currentColor"), r.appendChild(n), n;
556
+ }
557
+ drawText(e, t, s, r, o) {
558
+ const n = document.createElementNS(g, "text");
559
+ return n.setAttribute("x", t.toString()), n.setAttribute("y", s.toString()), n.setAttribute("text-anchor", o?.anchor ?? "middle"), n.setAttribute("font-size", (o?.fontSize ?? 10).toString()), n.setAttribute("fill", o?.fill ?? "currentColor"), o?.fontWeight && n.setAttribute("font-weight", o.fontWeight), n.textContent = e, r.appendChild(n), n;
560
+ }
491
561
  destroy() {
492
562
  this.rootElementRef && this.svgElementRef && this.rootElementRef.contains(this.svgElementRef) && this.rootElementRef.removeChild(this.svgElementRef);
493
563
  }
494
564
  }
495
- const $ = [
565
+ const It = [
496
566
  "CLEF_TREBLE",
497
567
  "CLEF_BASS",
498
568
  "CLEF_ALTO",
@@ -507,7 +577,7 @@ const $ = [
507
577
  "ACCIDENTAL_DOUBLE_SHARP",
508
578
  "ACCIDENTAL_DOUBLE_FLAT"
509
579
  ];
510
- class j {
580
+ class Rt {
511
581
  svgRendererInstance;
512
582
  strategyInstance;
513
583
  noteRendererInstance;
@@ -532,40 +602,44 @@ class j {
532
602
  staffColor: "black",
533
603
  staffBackgroundColor: "transparent",
534
604
  ...t
535
- }, this.svgRendererInstance = new O(e, {
605
+ }, this.svgRendererInstance = new L(e, {
536
606
  width: this.options.width,
537
607
  height: 100,
538
608
  scale: this.options.scale,
539
609
  staffColor: this.options.staffColor,
540
610
  staffBackgroundColor: this.options.staffBackgroundColor,
541
- useGlyphs: $
611
+ useGlyphs: It
542
612
  });
543
613
  const s = this.svgRendererInstance.rootSvgElement;
544
614
  switch (this.options.staffType) {
545
615
  case "grand":
546
- this.strategyInstance = new H(this.svgRendererInstance, "grand");
616
+ this.strategyInstance = new tt(this.svgRendererInstance, "grand");
547
617
  break;
548
618
  case "bass":
549
- this.strategyInstance = new A(this.svgRendererInstance, "bass");
619
+ this.strategyInstance = new _(this.svgRendererInstance, "bass");
550
620
  break;
551
621
  case "treble":
552
- this.strategyInstance = new A(this.svgRendererInstance, "treble");
622
+ this.strategyInstance = new _(this.svgRendererInstance, "treble");
553
623
  break;
554
624
  case "alto":
555
- this.strategyInstance = new A(this.svgRendererInstance, "alto");
625
+ this.strategyInstance = new _(this.svgRendererInstance, "alto");
556
626
  break;
557
627
  default:
558
628
  throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`);
559
629
  }
560
- if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new P(this.svgRendererInstance, this.strategyInstance), this.options.spaceAbove) {
561
- const r = this.options.spaceAbove * 10;
562
- this.svgRendererInstance.addTotalRootSvgYOffset(r);
630
+ if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new et(this.svgRendererInstance, this.strategyInstance), this.options.spaceAbove) {
631
+ const o = this.options.spaceAbove * 10;
632
+ this.svgRendererInstance.addTotalRootSvgYOffset(o);
563
633
  }
564
634
  if (this.options.spaceBelow) {
565
- let r = this.options.spaceBelow * 10;
566
- this.options.staffType === "grand" && (r -= 10 / 2), this.svgRendererInstance.addTotalRootSvgHeight(r);
635
+ let o = this.options.spaceBelow * 10;
636
+ this.options.staffType === "grand" && (o -= 10 / 2), this.svgRendererInstance.addTotalRootSvgHeight(o);
567
637
  }
568
- this.noteStartX = 38 + this.options.noteStartX, this.svgRendererInstance.getLayerByName("notes").setAttribute("transform", `translate(${this.noteStartX}, 0)`), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
638
+ let r = 0;
639
+ this.options.keySignature && (r = this.createKeySignature(this.options.keySignature)), this.noteStartX = 38 + this.options.noteStartX + r, this.svgRendererInstance.getLayerByName("notes").setAttribute("transform", `translate(${this.noteStartX}, 0)`), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
640
+ }
641
+ createKeySignature(e) {
642
+ return ft(this.svgRendererInstance, this.strategyInstance, e, lt);
569
643
  }
570
644
  /**
571
645
  * Draws a note on the staff.
@@ -593,8 +667,8 @@ class j {
593
667
  let n;
594
668
  try {
595
669
  n = this.noteRendererInstance.renderNote(o);
596
- } catch (a) {
597
- throw r.length > 0 && this.svgRendererInstance.commitElementsToDOM(r, s), a;
670
+ } catch (i) {
671
+ throw r.length > 0 && this.svgRendererInstance.commitElementsToDOM(r, s), i;
598
672
  }
599
673
  n.noteGroup.setAttribute("transform", `translate(${this.noteCursorX + n.accidentalOffset}, ${n.noteYPos})`), this.noteEntries.push({
600
674
  gElement: n.noteGroup,
@@ -626,7 +700,7 @@ class j {
626
700
  const t = this.svgRendererInstance.getLayerByName("notes"), s = this.noteRendererInstance.renderChord(e);
627
701
  s.noteGroup.setAttribute("transform", `translate(${this.noteCursorX + s.accidentalOffset}, 0)`), this.noteEntries.push({
628
702
  gElement: s.noteGroup,
629
- note: g(e[0]),
703
+ note: P(e[0]),
630
704
  xPos: this.noteCursorX + s.accidentalOffset,
631
705
  yPos: 0,
632
706
  accidentalXOffset: s.accidentalOffset
@@ -637,19 +711,19 @@ class j {
637
711
  * @returns Returns early if no notes are on the staff
638
712
  */
639
713
  justifyNotes() {
640
- const e = this.options.width - 38, t = this.noteEntries.length;
714
+ const e = this.options.width - this.noteStartX, t = this.noteEntries.length;
641
715
  if (t <= 0 || e <= 0) return;
642
716
  const s = Math.round(e / t);
643
717
  this.noteEntries.map((o, n) => {
644
- const a = (n + 0.5) * s, i = o.gElement.getBBox(), c = a - i.width / 2 - i.x, l = Math.round(c * 10) / 10;
718
+ const i = (n + 0.5) * s, a = o.gElement.getBBox(), c = i - a.width / 2 - a.x, l = Math.round(c * 10) / 10;
645
719
  return {
646
720
  entry: o,
647
721
  newX: l,
648
722
  isChord: o.gElement.classList.contains("chord")
649
723
  };
650
724
  }).forEach((o) => {
651
- const { entry: n, newX: a, isChord: i } = o;
652
- i ? n.gElement.setAttribute("transform", `translate(${a}, 0)`) : n.gElement.setAttribute("transform", `translate(${a}, ${n.yPos})`), n.xPos = a;
725
+ const { entry: n, newX: i, isChord: a } = o;
726
+ a ? n.gElement.setAttribute("transform", `translate(${i}, 0)`) : n.gElement.setAttribute("transform", `translate(${i}, ${n.yPos})`), n.xPos = i;
653
727
  });
654
728
  }
655
729
  /**
@@ -708,7 +782,7 @@ class j {
708
782
  const s = this.noteEntries[t], r = this.noteRendererInstance.renderChord(e), n = s.xPos - s.accidentalXOffset + r.accidentalOffset;
709
783
  r.noteGroup.setAttribute("transform", `translate(${n}, 0)`), this.svgRendererInstance.getLayerByName("notes").replaceChild(r.noteGroup, s.gElement), this.noteEntries[t] = {
710
784
  gElement: r.noteGroup,
711
- note: g(e[0]),
785
+ note: P(e[0]),
712
786
  xPos: n,
713
787
  yPos: 0,
714
788
  accidentalXOffset: r.accidentalOffset
@@ -744,7 +818,7 @@ class j {
744
818
  this.noteEntries = [], this.svgRendererInstance.destroy();
745
819
  }
746
820
  }
747
- const E = 30, v = 19, C = 12, k = 4, W = 6, G = 1, L = 38, Z = [
821
+ const A = 30, z = 19, O = 12, _t = 4, Tt = 6, j = 1, b = 38, mt = [
748
822
  "TIME_4",
749
823
  "TIME_3",
750
824
  "NOTE_HEAD_WHOLE",
@@ -756,7 +830,7 @@ const E = 30, v = 19, C = 12, k = 4, W = 6, G = 1, L = 38, Z = [
756
830
  "REST_QUARTER",
757
831
  "REST_EIGHTH"
758
832
  ];
759
- class Q {
833
+ class yt {
760
834
  rendererInstance;
761
835
  options;
762
836
  barSpacing;
@@ -767,7 +841,7 @@ class Q {
767
841
  currentBeatCount = 0;
768
842
  currentBeatUICount = 0;
769
843
  currentBeatUIElement = null;
770
- currentBeatUIXPos = L;
844
+ currentBeatUIXPos = b;
771
845
  /**
772
846
  * Creates an instance of a RhythmStaff, A single staff that will automatically apply positioning of elements based on the duration of a note.
773
847
  *
@@ -787,13 +861,13 @@ class Q {
787
861
  staffBackgroundColor: "white",
788
862
  currentBeatUIColor: "#24ff7450",
789
863
  ...t
790
- }, this.rendererInstance = new O(e, {
864
+ }, this.rendererInstance = new L(e, {
791
865
  width: this.options.width,
792
866
  height: 100,
793
867
  scale: this.options.scale,
794
868
  staffColor: this.options.staffColor,
795
869
  staffBackgroundColor: this.options.staffBackgroundColor,
796
- useGlyphs: Z
870
+ useGlyphs: mt
797
871
  });
798
872
  const s = this.rendererInstance.rootSvgElement;
799
873
  let r = "TIME_4";
@@ -809,41 +883,41 @@ class Q {
809
883
  }
810
884
  if (this.options.barsCount < 1 || this.options.barsCount > 3) throw new Error(`Bars count ${this.options.barsCount} not supported. Please use 1 - 3`);
811
885
  if (this.options.spaceAbove) {
812
- const p = this.options.spaceAbove * 10;
813
- this.rendererInstance.addTotalRootSvgYOffset(p);
886
+ const u = this.options.spaceAbove * 10;
887
+ this.rendererInstance.addTotalRootSvgYOffset(u);
814
888
  }
815
889
  if (this.options.spaceBelow) {
816
- let p = this.options.spaceBelow * 10;
817
- this.rendererInstance.addTotalRootSvgHeight(p);
890
+ let u = this.options.spaceBelow * 10;
891
+ this.rendererInstance.addTotalRootSvgHeight(u);
818
892
  }
819
893
  const o = this.rendererInstance.getLayerByName("staff");
820
- this.rendererInstance.addTotalRootSvgHeight(E * 2);
894
+ this.rendererInstance.addTotalRootSvgHeight(A * 2);
821
895
  const n = this.rendererInstance.createGroup("time-signature");
822
896
  o.appendChild(n);
823
- const a = E - v;
824
- this.rendererInstance.drawGlyph(r, n), this.rendererInstance.drawGlyph("TIME_4", n, { yOffset: v }), n.setAttribute("transform", `translate(0, ${a})`);
825
- let i = this.options.width - 38;
826
- this.options.barsCount > 1 && (i -= (this.options.barsCount - 1) * C), i -= G, this.rendererInstance.drawLine(0, E, this.options.width - G, E, o), this.barSpacing = i / this.options.barsCount, this.quarterNoteSpacing = Math.round(this.barSpacing / this.options.topNumber), this.maxBeatCount = this.options.barsCount * this.options.topNumber;
897
+ const i = A - z;
898
+ this.rendererInstance.drawGlyph(r, n), this.rendererInstance.drawGlyph("TIME_4", n, { yOffset: z }), n.setAttribute("transform", `translate(0, ${i})`);
899
+ let a = this.options.width - 38;
900
+ this.options.barsCount > 1 && (a -= (this.options.barsCount - 1) * O), a -= j, this.rendererInstance.drawLine(0, A, this.options.width - j, A, o), this.barSpacing = a / this.options.barsCount, this.quarterNoteSpacing = Math.round(this.barSpacing / this.options.topNumber), this.maxBeatCount = this.options.barsCount * this.options.topNumber;
827
901
  let c = this.barSpacing + 38;
828
- const l = E / 2, f = E + l;
829
- for (let p = 0; p < this.options.barsCount - 1; p++)
830
- this.rendererInstance.drawLine(c, l, c, f, o), c += this.barSpacing;
831
- this.rendererInstance.getLayerByName("notes").setAttribute("transform", `translate(38, ${E})`), this.rendererInstance.applySizingToRootSvg(), this.rendererInstance.commitElementsToDOM(s);
902
+ const l = A / 2, d = A + l;
903
+ for (let u = 0; u < this.options.barsCount - 1; u++)
904
+ this.rendererInstance.drawLine(c, l, c, d, o), c += this.barSpacing;
905
+ this.rendererInstance.getLayerByName("notes").setAttribute("transform", `translate(38, ${A})`), this.rendererInstance.applySizingToRootSvg(), this.rendererInstance.commitElementsToDOM(s);
832
906
  }
833
907
  createBeatUIElement() {
834
908
  const e = this.rendererInstance.getLayerByName("ui");
835
909
  this.currentBeatUIElement = this.rendererInstance.drawRect(
836
910
  this.quarterNoteSpacing / 2,
837
- E * 2,
911
+ A * 2,
838
912
  e,
839
913
  {
840
- x: L,
914
+ x: b,
841
915
  fill: this.options.currentBeatUIColor
842
916
  }
843
917
  );
844
918
  }
845
919
  handleNewBar() {
846
- this.noteCursorX += C;
920
+ this.noteCursorX += O;
847
921
  }
848
922
  // Translates group, returns cursor increment amount
849
923
  translateGroupByDuration(e, t) {
@@ -903,14 +977,14 @@ class Q {
903
977
  for (; s > 0; ) {
904
978
  const r = this.rendererInstance.createGroup("rest");
905
979
  let o = 0;
906
- s - d.h >= 0 ? (this.rendererInstance.drawGlyph("REST_HALF", r), o = d.h) : s - d.q >= 0 ? (this.rendererInstance.drawGlyph("REST_QUARTER", r), o = d.q) : (this.rendererInstance.drawGlyph("REST_EIGHTH", r), o = d.e), s -= o, this.currentBeatCount += o, r.setAttribute("transform", `translate(${this.noteCursorX}, 0)`), this.noteCursorX += o * this.quarterNoteSpacing, t.push(r);
980
+ s - p.h >= 0 ? (this.rendererInstance.drawGlyph("REST_HALF", r), o = p.h) : s - p.q >= 0 ? (this.rendererInstance.drawGlyph("REST_QUARTER", r), o = p.q) : (this.rendererInstance.drawGlyph("REST_EIGHTH", r), o = p.e), s -= o, this.currentBeatCount += o, r.setAttribute("transform", `translate(${this.noteCursorX}, 0)`), this.noteCursorX += o * this.quarterNoteSpacing, t.push(r);
907
981
  }
908
982
  return t;
909
983
  }
910
984
  renderBeamRect(e, t, s, r) {
911
985
  this.rendererInstance.drawRect(
912
986
  e - t,
913
- k,
987
+ _t,
914
988
  s,
915
989
  {
916
990
  x: 10,
@@ -942,20 +1016,20 @@ class Q {
942
1016
  for (const o of t) {
943
1017
  let n = "w";
944
1018
  try {
945
- n = T(o);
946
- } catch (f) {
947
- throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), f;
1019
+ n = R(o);
1020
+ } catch (d) {
1021
+ throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), d;
948
1022
  }
949
- const a = d[n];
1023
+ const i = p[n];
950
1024
  if (this.currentBeatCount >= this.maxBeatCount)
951
1025
  throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), new Error("Max beat count reached. Can't add additional notes.");
952
1026
  this.checkAndCreateNewBar();
953
- const i = this.checkAndFillBarWithRests(a);
954
- i && i.forEach((f) => {
955
- r.push(f), this.noteEntries.push(f);
1027
+ const a = this.checkAndFillBarWithRests(i);
1028
+ a && a.forEach((d) => {
1029
+ r.push(d), this.noteEntries.push(d);
956
1030
  });
957
- const c = this.rendererInstance.createGroup("note"), l = this.translateGroupByDuration(a, c);
958
- this.noteCursorX += l, this.currentBeatCount += a, this.renderNote(n, c), r.push(c), this.noteEntries.push(c);
1031
+ const c = this.rendererInstance.createGroup("note"), l = this.translateGroupByDuration(i, c);
1032
+ this.noteCursorX += l, this.currentBeatCount += i, this.renderNote(n, c), r.push(c), this.noteEntries.push(c);
959
1033
  }
960
1034
  this.rendererInstance.commitElementsToDOM(r, s);
961
1035
  }
@@ -982,18 +1056,18 @@ class Q {
982
1056
  for (const o of t) {
983
1057
  let n = "w";
984
1058
  try {
985
- n = T(o);
986
- } catch (f) {
987
- throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), f;
1059
+ n = R(o);
1060
+ } catch (d) {
1061
+ throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), d;
988
1062
  }
989
- const a = this.rendererInstance.createGroup("rest"), i = d[n], c = i * this.quarterNoteSpacing;
1063
+ const i = this.rendererInstance.createGroup("rest"), a = p[n], c = a * this.quarterNoteSpacing;
990
1064
  if (this.currentBeatCount >= this.maxBeatCount)
991
1065
  throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), new Error("Max beat count reached. Can't add additional notes.");
992
1066
  this.checkAndCreateNewBar();
993
- const l = this.checkAndFillBarWithRests(i);
994
- l && l.forEach((f) => {
995
- r.push(f), this.noteEntries.push(f);
996
- }), this.renderRest(n, a), a.setAttribute("transform", `translate(${this.noteCursorX}, 0)`), this.noteCursorX += c, this.currentBeatCount += i, r.push(a), this.noteEntries.push(a);
1067
+ const l = this.checkAndFillBarWithRests(a);
1068
+ l && l.forEach((d) => {
1069
+ r.push(d), this.noteEntries.push(d);
1070
+ }), this.renderRest(n, i), i.setAttribute("transform", `translate(${this.noteCursorX}, 0)`), this.noteCursorX += c, this.currentBeatCount += a, r.push(i), this.noteEntries.push(i);
997
1071
  }
998
1072
  this.rendererInstance.commitElementsToDOM(r, s);
999
1073
  }
@@ -1020,13 +1094,13 @@ class Q {
1020
1094
  if (this.currentBeatCount >= this.maxBeatCount)
1021
1095
  throw new Error("Max beat count reached. Can't add additional beamed note.");
1022
1096
  let s = "s";
1023
- e === "s" ? s = "s" : s = T(e), this.checkAndCreateNewBar();
1024
- const r = this.rendererInstance.getLayerByName("notes"), o = d[s], n = o * this.quarterNoteSpacing, a = this.options.topNumber - this.currentBeatCount % this.options.topNumber, i = Math.min(t, a / o), c = this.rendererInstance.createGroup("beamed-note");
1097
+ e === "s" ? s = "s" : s = R(e), this.checkAndCreateNewBar();
1098
+ const r = this.rendererInstance.getLayerByName("notes"), o = p[s], n = o * this.quarterNoteSpacing, i = this.options.topNumber - this.currentBeatCount % this.options.topNumber, a = Math.min(t, i / o), c = this.rendererInstance.createGroup("beamed-note");
1025
1099
  c.setAttribute("transform", `translate(${this.noteCursorX}, 0)`);
1026
1100
  let l = 0;
1027
- for (let f = 0; f < i; f++)
1101
+ for (let d = 0; d < a; d++)
1028
1102
  this.rendererInstance.drawGlyph("NOTE_HEAD_QUARTER", c, { xOffset: l }), this.drawStem(c, l), l += n, this.currentBeatCount += o;
1029
- this.renderBeamRect(l, n, c), e === "s" && this.renderBeamRect(l, n, c, W), this.noteCursorX += l, this.noteEntries.push(c), this.rendererInstance.commitElementsToDOM(c, r);
1103
+ this.renderBeamRect(l, n, c), e === "s" && this.renderBeamRect(l, n, c, Tt), this.noteCursorX += l, this.noteEntries.push(c), this.rendererInstance.commitElementsToDOM(c, r);
1030
1104
  }
1031
1105
  /**
1032
1106
  * Will increment the UI showing the current beat in quarters. Once exceeded, must be reset with `resetCurrentBeatUI()`
@@ -1037,14 +1111,14 @@ class Q {
1037
1111
  this.currentBeatUIElement.setAttribute("display", "none");
1038
1112
  return;
1039
1113
  }
1040
- this.currentBeatUIElement?.getAttribute("display") === "none" && this.currentBeatUIElement.removeAttribute("display"), this.currentBeatUICount++, this.currentBeatUICount > this.options.topNumber && this.currentBeatUICount % this.options.topNumber === 1 && (this.currentBeatUIXPos += C), this.currentBeatUICount > 1 && (this.currentBeatUIXPos += this.quarterNoteSpacing), this.currentBeatUIElement.setAttribute("x", this.currentBeatUIXPos.toString());
1114
+ this.currentBeatUIElement?.getAttribute("display") === "none" && this.currentBeatUIElement.removeAttribute("display"), this.currentBeatUICount++, this.currentBeatUICount > this.options.topNumber && this.currentBeatUICount % this.options.topNumber === 1 && (this.currentBeatUIXPos += O), this.currentBeatUICount > 1 && (this.currentBeatUIXPos += this.quarterNoteSpacing), this.currentBeatUIElement.setAttribute("x", this.currentBeatUIXPos.toString());
1041
1115
  }
1042
1116
  /**
1043
1117
  * Resets the ui showing the current beat value.
1044
1118
  * @returns void
1045
1119
  */
1046
1120
  resetCurrentBeatUI() {
1047
- this.currentBeatUICount = 0, this.currentBeatUIXPos = L, this.currentBeatUIElement && (this.currentBeatUIElement.setAttribute("display", "none"), this.currentBeatUIElement.setAttribute("x", this.currentBeatUIXPos.toString()));
1121
+ this.currentBeatUICount = 0, this.currentBeatUIXPos = b, this.currentBeatUIElement && (this.currentBeatUIElement.setAttribute("display", "none"), this.currentBeatUIElement.setAttribute("x", this.currentBeatUIXPos.toString()));
1048
1122
  }
1049
1123
  /**
1050
1124
  * Clears staff of notes and resets internal positioning.
@@ -1061,7 +1135,7 @@ class Q {
1061
1135
  this.noteEntries = [], this.rendererInstance.destroy();
1062
1136
  }
1063
1137
  }
1064
- const V = [
1138
+ const Ct = [
1065
1139
  "CLEF_TREBLE",
1066
1140
  "CLEF_BASS",
1067
1141
  "CLEF_ALTO",
@@ -1075,8 +1149,8 @@ const V = [
1075
1149
  "ACCIDENTAL_NATURAL",
1076
1150
  "ACCIDENTAL_DOUBLE_SHARP",
1077
1151
  "ACCIDENTAL_DOUBLE_FLAT"
1078
- ], _ = 60, q = _;
1079
- class z {
1152
+ ], N = 60, St = N;
1153
+ class wt {
1080
1154
  svgRendererInstance;
1081
1155
  strategyInstance;
1082
1156
  noteRendererInstance;
@@ -1103,32 +1177,32 @@ class z {
1103
1177
  staffColor: "black",
1104
1178
  staffBackgroundColor: "transparent",
1105
1179
  ...t
1106
- }, this.svgRendererInstance = new O(e, {
1180
+ }, this.svgRendererInstance = new L(e, {
1107
1181
  width: this.options.width,
1108
1182
  height: 100,
1109
1183
  scale: this.options.scale,
1110
1184
  staffColor: this.options.staffColor,
1111
1185
  staffBackgroundColor: this.options.staffBackgroundColor,
1112
- useGlyphs: V
1186
+ useGlyphs: Ct
1113
1187
  });
1114
1188
  const s = this.svgRendererInstance.rootSvgElement;
1115
1189
  switch (this.options.staffType) {
1116
1190
  case "grand":
1117
- this.strategyInstance = new H(this.svgRendererInstance, "grand");
1191
+ this.strategyInstance = new tt(this.svgRendererInstance, "grand");
1118
1192
  break;
1119
1193
  case "bass":
1120
- this.strategyInstance = new A(this.svgRendererInstance, "bass");
1194
+ this.strategyInstance = new _(this.svgRendererInstance, "bass");
1121
1195
  break;
1122
1196
  case "treble":
1123
- this.strategyInstance = new A(this.svgRendererInstance, "treble");
1197
+ this.strategyInstance = new _(this.svgRendererInstance, "treble");
1124
1198
  break;
1125
1199
  case "alto":
1126
- this.strategyInstance = new A(this.svgRendererInstance, "alto");
1200
+ this.strategyInstance = new _(this.svgRendererInstance, "alto");
1127
1201
  break;
1128
1202
  default:
1129
1203
  throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`);
1130
1204
  }
1131
- if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new P(this.svgRendererInstance, this.strategyInstance), this.options.spaceAbove) {
1205
+ if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new et(this.svgRendererInstance, this.strategyInstance), this.options.spaceAbove) {
1132
1206
  const r = this.options.spaceAbove * 10;
1133
1207
  this.svgRendererInstance.addTotalRootSvgYOffset(r);
1134
1208
  }
@@ -1139,10 +1213,10 @@ class z {
1139
1213
  this.notesLayer = this.svgRendererInstance.getLayerByName("notes"), this.notesLayer.classList.add("vs-scrolling-notes-layer"), this.noteStartX = 38 + this.options.noteStartX, this.svgRendererInstance.getLayerByName("notes").setAttribute("transform", `translate(${this.noteStartX}, 0)`), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
1140
1214
  }
1141
1215
  renderFirstNoteGroups() {
1142
- const e = this.options.width - this.options.noteStartX + q;
1216
+ const e = this.options.width - this.options.noteStartX + St;
1143
1217
  for (; this.noteBuffer.length > 0 && this.noteCursorX < e; )
1144
- this.renderNextNote(), this.noteCursorX += _;
1145
- this.activeEntries.length > 1 && (this.noteCursorX -= _);
1218
+ this.renderNextNote(), this.noteCursorX += N;
1219
+ this.activeEntries.length > 1 && (this.noteCursorX -= N);
1146
1220
  }
1147
1221
  renderNextNote() {
1148
1222
  if (this.noteBuffer.length < 1) return;
@@ -1201,7 +1275,7 @@ class z {
1201
1275
  return;
1202
1276
  }
1203
1277
  this.activeEntries.forEach((t) => {
1204
- t.xPos -= _, t.noteWrapper.style.transform = `translate(${t.xPos}px, 0px)`;
1278
+ t.xPos -= N, t.noteWrapper.style.transform = `translate(${t.xPos}px, 0px)`;
1205
1279
  });
1206
1280
  const e = this.activeEntries[0];
1207
1281
  e.xPos <= 0 && (this.notesLayer.removeChild(e.noteWrapper), this.activeEntries.shift()), this.renderNextNote();
@@ -1221,8 +1295,297 @@ class z {
1221
1295
  this.svgRendererInstance.destroy(), this.activeEntries = [], this.noteBuffer = [];
1222
1296
  }
1223
1297
  }
1298
+ const Nt = /^[xX\da-zA-Z]+$/, Lt = /^[\d]+$/;
1299
+ function K(h, e) {
1300
+ const t = [];
1301
+ for (let s = 0; s < h.length; s++)
1302
+ t.push({
1303
+ fret: h[s],
1304
+ finger: e[s]
1305
+ });
1306
+ return t;
1307
+ }
1308
+ function Q(h, e) {
1309
+ const t = h.filter((o) => o.toLowerCase() !== "x" && o !== "0").map((o) => parseInt(o, 36));
1310
+ if (t.length === 0) return 1;
1311
+ const s = Math.min(...t);
1312
+ return Math.max(...t) <= e ? 1 : s;
1313
+ }
1314
+ function v(h) {
1315
+ if (!Nt.test(h)) throw new Error("Invalid frets string. Only 'x', 'X', and digits are allowed.");
1316
+ return h.split("");
1317
+ }
1318
+ function G(h) {
1319
+ if (!Lt.test(h)) throw new Error("Invalid fingers string. Only digits are allowed.");
1320
+ return h.split("");
1321
+ }
1322
+ class Ot {
1323
+ svgRendererInstance;
1324
+ options;
1325
+ chordEntries = [];
1326
+ cursorX = 0;
1327
+ cursorY = 0;
1328
+ // Fixed per-instance sizing, derived once from options
1329
+ diagramWidth;
1330
+ diagramHeight;
1331
+ gridTopY;
1332
+ rowHeight;
1333
+ /**
1334
+ * Creates an instance of a GuitarChord diagram renderer.
1335
+ *
1336
+ * @param rootElementCtx - The element (div) reference that the chord diagram(s) will be appended to.
1337
+ * @param options - Optional configuration. Can adjust total width, scale, string/fret count, and coloring.
1338
+ * All config options are in the type GuitarChordOptions.
1339
+ */
1340
+ constructor(e, t) {
1341
+ if (this.options = {
1342
+ width: 300,
1343
+ scale: 1,
1344
+ stringCount: st,
1345
+ fretCount: rt,
1346
+ stringLabels: [],
1347
+ color: "black",
1348
+ backgroundColor: "transparent",
1349
+ ...t
1350
+ }, this.diagramWidth = (this.options.stringCount - 1) * I, this.diagramHeight = this.options.fretCount * T, this.gridTopY = M + Y, this.rowHeight = this.gridTopY + this.diagramHeight + X, this.options.stringLabels.length >= 1 && (this.rowHeight += ot + nt), this.diagramWidth > this.options.width)
1351
+ throw new Error(`The configured width (${this.options.width}) is too small to fit a single ${this.options.stringCount}-string diagram (needs at least ${this.diagramWidth}). Increase 'width'.`);
1352
+ this.svgRendererInstance = new L(e, {
1353
+ width: this.options.width,
1354
+ height: 100,
1355
+ scale: this.options.scale,
1356
+ staffColor: this.options.color,
1357
+ staffBackgroundColor: this.options.backgroundColor,
1358
+ useGlyphs: []
1359
+ });
1360
+ const s = this.svgRendererInstance.rootSvgElement;
1361
+ this.svgRendererInstance.setTotalRootSvgHeight(this.rowHeight), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
1362
+ }
1363
+ drawStringDots(e, t, s, r) {
1364
+ const o = this.gridTopY - Y / 2;
1365
+ for (let n = 0; n < this.options.stringCount; n++) {
1366
+ const i = n * I, a = e[n].toLowerCase(), c = t[n];
1367
+ if (a === "x")
1368
+ this.svgRendererInstance.drawLine(i - f, o - f, i + f, o + f, r), this.svgRendererInstance.drawLine(i - f, o + f, i + f, o - f, r);
1369
+ else if (a === "0")
1370
+ this.svgRendererInstance.drawCircle(i, o, f, r, { filled: !1 });
1371
+ else {
1372
+ const l = parseInt(a, 36) - s + 1;
1373
+ if (l < 1 || l > this.options.fretCount)
1374
+ throw new Error(`Fret ${a} on string ${n + 1} is outside the visible range (${s}-${s + this.options.fretCount - 1}). Adjust 'startFret' or 'fretCount'.`);
1375
+ const d = this.gridTopY + (l - 0.5) * T;
1376
+ if (this.svgRendererInstance.drawCircle(i, d, E, r), c !== "0") {
1377
+ const u = this.options.backgroundColor === "transparent" ? "white" : this.options.backgroundColor;
1378
+ this.svgRendererInstance.drawText(c, i, d + C / 3, r, {
1379
+ fontSize: C,
1380
+ fill: u
1381
+ });
1382
+ }
1383
+ }
1384
+ }
1385
+ }
1386
+ drawStringLabels(e, t) {
1387
+ if (!t || t.length === 0) return;
1388
+ const s = E - 1, r = this.rowHeight - s - X;
1389
+ for (let o = 0; o < this.options.stringCount; o++) {
1390
+ const n = t[o];
1391
+ if (!n) continue;
1392
+ const i = o * I, a = r + s / 2;
1393
+ this.svgRendererInstance.drawCircle(i, r, s, e, { filled: !1 }), this.svgRendererInstance.drawText(n, i, a, e, { fontSize: D });
1394
+ }
1395
+ }
1396
+ // Passed in 'from' and 'to' index values are zero-based
1397
+ drawBarre(e, t) {
1398
+ const s = t.fret;
1399
+ if (t.fromIndex < 0 || t.toIndex >= this.options.stringCount || t.fromIndex >= t.toIndex)
1400
+ throw new Error(`Barre fret error: Invalid string range ${t.fromIndex + 1}:${t.toIndex + 1}.`);
1401
+ if (s < t.startingFret || s > t.startingFret + (this.options.fretCount - 1))
1402
+ throw new Error(`Barre fret error: ${t.fret} is outside the visible range.`);
1403
+ const r = s - t.startingFret, o = t.fromIndex * I, n = t.toIndex * I, i = E * 2, a = n - o + i, c = this.gridTopY + E / 2 + T * r;
1404
+ if (this.svgRendererInstance.drawRect(a, i, e, {
1405
+ x: o - E,
1406
+ y: c,
1407
+ rx: E
1408
+ }), t.finger) {
1409
+ const l = this.options.backgroundColor === "transparent" ? "white" : this.options.backgroundColor;
1410
+ this.svgRendererInstance.drawText(t.finger.toString(), (o + n) / 2, c + C, e, {
1411
+ fontSize: C,
1412
+ fill: l
1413
+ });
1414
+ }
1415
+ }
1416
+ // Builds a single chord diagram's group element: grid, nut/fret-position label, and per-string markers.
1417
+ renderChordDiagram(e, t, s) {
1418
+ const r = this.svgRendererInstance.createGroup("chord"), o = s?.startFret ?? 1;
1419
+ s?.label && this.svgRendererInstance.drawText(s.label, this.diagramWidth / 2, M - 5, r, {
1420
+ fontSize: D,
1421
+ fontWeight: "bold"
1422
+ });
1423
+ for (let n = 0; n <= this.options.fretCount; n++) {
1424
+ const i = this.gridTopY + n * T;
1425
+ n === 0 && o === 1 ? this.svgRendererInstance.drawRect(this.diagramWidth + U * 2, F, r, {
1426
+ x: -U,
1427
+ y: i - F / 2
1428
+ }) : this.svgRendererInstance.drawLine(0, i, this.diagramWidth, i, r);
1429
+ }
1430
+ o > 1 && this.svgRendererInstance.drawText(`${o}fr`, this.diagramWidth + ht, this.gridTopY + T / 2 + 4, r, {
1431
+ anchor: "start",
1432
+ fontSize: ct,
1433
+ fontWeight: "bold"
1434
+ });
1435
+ for (let n = 0; n < this.options.stringCount; n++) {
1436
+ const i = n * I;
1437
+ this.svgRendererInstance.drawLine(i, this.gridTopY, i, this.gridTopY + this.diagramHeight, r);
1438
+ }
1439
+ return this.drawStringDots(e, t, o, r), this.options.stringLabels.length >= 1 && this.drawStringLabels(r, this.options.stringLabels), s?.barres?.forEach((n) => {
1440
+ this.drawBarre(r, {
1441
+ fromIndex: n.fromString - 1,
1442
+ toIndex: n.toString - 1,
1443
+ fret: n.fret,
1444
+ finger: n.finger,
1445
+ startingFret: o
1446
+ });
1447
+ }), r;
1448
+ }
1449
+ // Repositions every existing chord diagram from scratch, in order, wrapping rows as needed.
1450
+ // Called after any add/remove/change so the layout never has to track incremental deltas.
1451
+ relayoutChords() {
1452
+ if (this.cursorX = E, this.cursorY = 0, this.chordEntries.length === 1) {
1453
+ const t = this.chordEntries[0];
1454
+ this.cursorX = this.options.width / 2 - this.diagramWidth / 2, t.xPos = this.cursorX, t.yPos = this.cursorY, t.gElement.setAttribute("transform", `translate(${this.cursorX}, ${this.cursorY})`);
1455
+ } else
1456
+ this.chordEntries.forEach((t, s) => {
1457
+ s > 0 && this.cursorX + this.diagramWidth > this.options.width && (this.cursorX = E, this.cursorY += this.rowHeight + at), t.xPos = this.cursorX, t.yPos = this.cursorY, t.gElement.setAttribute("transform", `translate(${this.cursorX}, ${this.cursorY})`), this.cursorX += this.diagramWidth + it;
1458
+ });
1459
+ const e = this.cursorY + this.rowHeight;
1460
+ this.svgRendererInstance.setTotalRootSvgHeight(e), this.svgRendererInstance.applySizingToRootSvg();
1461
+ }
1462
+ /**
1463
+ * Adds a new chord diagram. Diagrams are placed left-to-right in the order added, wrapping to a
1464
+ * new row automatically once the configured width is exceeded.
1465
+ *
1466
+ * @param frets - A string, with each value being ordered low-to-high (e.g. first position in string 0 is the low E string)
1467
+ * * `x` - muted string
1468
+ * * `0` - open string
1469
+ * * `1...n` - fretted at the given absolute fret number (e.g. `3`)
1470
+ * @param fingers - A string, with each value being ordered low-to-high (e.g. first position in string 0 is the low E string)
1471
+ * * `0` - No finger labeled
1472
+ * * `1...n` - Finger labeled as number provided
1473
+ *
1474
+ * @param options - Optional per-chord settings: `startFret` (default `1`, for diagrams higher up the neck)
1475
+ * and `label` (a chord name drawn above the diagram).
1476
+ *
1477
+ * @returns The index of the newly added chord, for later use with the CRUD-by-index methods.
1478
+ *
1479
+ * @throws {Error} If fret or finger's string length doesn't match the configured string count.
1480
+ *
1481
+ * @example
1482
+ * // Draw an open C major chord
1483
+ * guitarChord.addChord("x32010", "032010", { label: "C" });
1484
+ */
1485
+ addChord(e, t, s) {
1486
+ const r = v(e), o = G(t);
1487
+ if (r.length !== this.options.stringCount) throw new Error("Invalid number of frets provided, entries should be equal to number of strings in diagram.");
1488
+ if (o.length !== this.options.stringCount) throw new Error("Invalid number of fingers provided, entries should be equal to number of strings in diagram.");
1489
+ const n = s?.startFret ?? Q(r, this.options.fretCount), i = s?.label, a = s?.barres, c = { startFret: n, label: i, barres: a }, l = this.renderChordDiagram(r, o, c), d = this.svgRendererInstance.getLayerByName("notes");
1490
+ return this.svgRendererInstance.commitElementsToDOM(l, d), this.chordEntries.push({
1491
+ gElement: l,
1492
+ strings: K(r, o),
1493
+ ...c,
1494
+ xPos: 0,
1495
+ yPos: 0
1496
+ }), this.relayoutChords(), this.chordEntries.length - 1;
1497
+ }
1498
+ /**
1499
+ * Replaces the chord diagram at the specified index with a new one, in place.
1500
+ *
1501
+ * @param frets - A string, with each value being ordered low-to-high (e.g. first position in string 0 is the low E string)
1502
+ * * `x` - muted string
1503
+ * * `0` - open string
1504
+ * * `1...n` - fretted at the given absolute fret number (e.g. `3`)
1505
+ * @param fingers - A string, with each value being ordered low-to-high (e.g. first position in string 0 is the low E string)
1506
+ * * `0` - No finger labeled
1507
+ * * `1...n` - Finger labeled as number provided
1508
+ *
1509
+ * @param chordIndex - The index of the chord to replace (as returned by addChord).
1510
+ *
1511
+ * @param options - Optional per-chord settings, see addChord.
1512
+ *
1513
+ * @returns void
1514
+ *
1515
+ * @throws {Error} If the index is out of bounds, or if chordDef is invalid (see addChord).
1516
+ *
1517
+ * @example
1518
+ * // Change chord at index 0 to a D major chord
1519
+ * guitarChord.modifyChordByIndex("xx0232", "000132", 0, { label: "D" });
1520
+ *
1521
+ */
1522
+ modifyChordByIndex(e, t, s, r) {
1523
+ if (s >= this.chordEntries.length || s < 0) throw new Error("Chord index was out of bounds.");
1524
+ const o = v(e), n = G(t);
1525
+ if (o.length !== this.options.stringCount) throw new Error("Invalid number of frets provided, entries should be equal to number of strings in diagram.");
1526
+ if (n.length !== this.options.stringCount) throw new Error("Invalid number of fingers provided, entries should be equal to number of strings in diagram.");
1527
+ const i = this.chordEntries[s], a = r && "label" in r ? r.label === "" ? void 0 : r.label : i.label, c = r?.startFret ?? Q(o, this.options.fretCount), l = { ...r, label: a, startFret: c }, d = this.renderChordDiagram(o, n, l);
1528
+ this.svgRendererInstance.getLayerByName("notes").replaceChild(d, i.gElement), this.chordEntries[s] = {
1529
+ gElement: d,
1530
+ strings: K(o, n),
1531
+ ...l,
1532
+ xPos: i.xPos,
1533
+ yPos: i.yPos
1534
+ }, this.relayoutChords();
1535
+ }
1536
+ /**
1537
+ * Removes the chord diagram at the specified index. Remaining chords are re-flowed to close the gap.
1538
+ * @param chordIndex - The index of the chord to remove.
1539
+ * @returns void
1540
+ * @throws {Error} If the index is out of bounds.
1541
+ */
1542
+ removeChordByIndex(e) {
1543
+ if (e >= this.chordEntries.length || e < 0) throw new Error("Chord index was out of bounds.");
1544
+ this.chordEntries[e].gElement.remove(), this.chordEntries.splice(e, 1), this.relayoutChords();
1545
+ }
1546
+ /**
1547
+ * Removes all chord diagrams and resets internal positioning.
1548
+ * @returns void
1549
+ */
1550
+ clearAllChords() {
1551
+ this.svgRendererInstance.getLayerByName("notes").replaceChildren(), this.chordEntries = [], this.cursorX = 0, this.cursorY = 0, this.svgRendererInstance.setTotalRootSvgHeight(this.rowHeight), this.svgRendererInstance.applySizingToRootSvg();
1552
+ }
1553
+ /**
1554
+ * * Used to automatically determine options for creating barre lines.
1555
+ * * The returned value then can be used in addChord and modifyChord methods.
1556
+ * @returns GuitarBarreDef[]
1557
+ */
1558
+ determineBarreOptions(e, t, s) {
1559
+ const r = v(e), o = G(t), n = [];
1560
+ return s.forEach((i) => {
1561
+ const a = {};
1562
+ for (let c = 0; c < r.length; c++) {
1563
+ if (r[c].toLowerCase() === "x") continue;
1564
+ if (parseInt(r[c], 36) === i && o[c] !== "0") {
1565
+ const d = o[c];
1566
+ a[d] || (a[d] = []), a[d].push(c);
1567
+ }
1568
+ }
1569
+ for (const [c, l] of Object.entries(a))
1570
+ l.length > 1 && n.push({
1571
+ fret: i,
1572
+ fromString: Math.min(...l) + 1,
1573
+ toString: Math.max(...l) + 1,
1574
+ finger: Number(c)
1575
+ });
1576
+ }), n;
1577
+ }
1578
+ /**
1579
+ * Removes the root svg element and cleans up arrays.
1580
+ * @returns void
1581
+ */
1582
+ destroy() {
1583
+ this.chordEntries = [], this.svgRendererInstance.destroy();
1584
+ }
1585
+ }
1224
1586
  export {
1225
- j as MusicStaff,
1226
- Q as RhythmStaff,
1227
- z as ScrollingStaff
1587
+ Ot as GuitarChord,
1588
+ Rt as MusicStaff,
1589
+ yt as RhythmStaff,
1590
+ wt as ScrollingStaff
1228
1591
  };