vector-score 1.0.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.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/classes/MusicStaff.d.ts +127 -0
- package/dist/classes/NoteRenderer.d.ts +27 -0
- package/dist/classes/RhythmStaff.d.ts +118 -0
- package/dist/classes/SVGRenderer.d.ts +47 -0
- package/dist/classes/ScrollingStaff.d.ts +67 -0
- package/dist/constants.d.ts +19 -0
- package/dist/glyphs.d.ts +7 -0
- package/dist/helpers/notehelpers.d.ts +9 -0
- package/dist/index.d.ts +7 -0
- package/dist/strategies/GrandStaffStrategy.d.ts +14 -0
- package/dist/strategies/SingleStaffStrategy.d.ts +12 -0
- package/dist/strategies/StrategyInterface.d.ts +20 -0
- package/dist/types.d.ts +11 -0
- package/dist/vector-score.js +1224 -0
- package/dist/vector-score.umd.cjs +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(d,T){typeof exports=="object"&&typeof module<"u"?T(exports):typeof define=="function"&&define.amd?define(["exports"],T):(d=typeof globalThis<"u"?globalThis:d||self,T(d.VectorScore={}))})(this,(function(d){"use strict";const w={treble:{staffType:"treble",paddingTop:13,paddingBottom:3,topLineNote:{name:"F",octave:5},topLineYPos:0,bottomLineYPos:40},bass:{staffType:"bass",paddingTop:0,paddingBottom:0,topLineNote:{name:"A",octave:3},topLineYPos:0,bottomLineYPos:40},alto:{staffType:"alto",paddingTop:0,paddingBottom:0,topLineNote:{name:"G",octave:4},topLineYPos:0,bottomLineYPos:40},grand:{staffType:"grand",paddingTop:13,paddingBottom:0,topLineNote:{name:"F",octave:5},topLineYPos:0,bottomLineYPos:110}},E={w:4,h:2,q:1,e:.5,s:.25},U={CLEF_TREBLE:{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",xOffset:0,yOffset:-14},CLEF_BASS:{path:"M101 0c68 0 110 46 110 114 0 120-102 190-199 237l-7 2c-3 0-5-2-5-5s2-5 6-7c92-52 146-114 146-223 0-62-18-103-60-103-40 0-64 29-64 44 0 4 1 8 7 8 5 0 9-3 19-3 20 0 38 15 38 41 0 24-17 41-42 41-32 0-48-27-48-58C2 50 33 0 101 0Zm148 32c13 0 22 10 22 22s-9 22-22 22c-12 0-21-10-21-22s9-22 21-22Zm1 99c12 0 21 9 21 21s-9 22-21 22-21-10-21-22 9-21 21-21Z",xOffset:0,yOffset:0},CLEF_ALTO:{path:"M91 9v174c0 3 2 2 3 2 11-3 27-13 36-58 1-6 3-10 7-10s6 4 8 11c5 17 15 37 43 37 25 0 32-25 32-77s-9-75-42-75c-5 0-33 2-33 10 0 2 6 5 11 6 7 3 15 11 15 26 0 17-11 27-26 27-17 0-31-11-31-32 0-25 22-50 69-50 65 0 93 45 93 87 0 54-30 92-83 92-11 0-18-2-24-4-4-1-8-1-11 1-6 3-14 16-14 24s8 21 14 24c3 2 7 2 11 1 6-2 13-4 24-4 53 0 83 38 83 92 0 42-28 87-93 87-47 0-69-25-69-50 0-21 14-32 31-32 15 0 26 10 26 27 0 15-8 23-15 26-5 1-11 4-11 6 0 8 28 10 33 10 33 0 42-23 42-75s-7-77-32-77c-28 0-38 20-43 37-2 7-4 11-8 11s-6-4-7-10c-9-45-25-55-36-58-1 0-3-1-3 2v174c0 5-3 8-8 8h-1c-5 0-8-3-8-8V9c0-5 3-8 8-8h1c5 0 8 3 8 8ZM8 1h34c6 0 9 3 9 8v382c0 5-3 8-9 8H8c-5 0-8-3-8-8V9c0-5 3-8 8-8Z",xOffset:0,yOffset:0},NOTE_HEAD_WHOLE:{path:"M77 0c34 0 74 19 74 45 0 24-19 45-77 45C21 90 0 68 0 45 0 20 30 0 77 0Zm-9 8c-17 0-30 5-30 24 0 22 23 50 47 50 16 0 28-8 28-26 0-21-20-48-45-48Z",xOffset:0,yOffset:-4.5},NOTE_HEAD_HALF:{path:"M35 90C15 90 0 79 0 60 0 42 17 0 70 0c21 0 36 12 36 30 0 12-12 60-71 60Zm-8-14c18 0 68-31 68-47l-2-6c-3-5-7-8-14-8-17 0-68 29-68 46l2 7c2 4 7 8 14 8Z",xOffset:0,yOffset:-4.5},NOTE_HEAD_QUARTER:{path:"M35 90C16 90 0 79 0 60 0 29 32 0 71 0c20 0 35 12 35 30 0 30-40 60-71 60Z",xOffset:0,yOffset:-4.5},EIGHTH_NOTE:{path:"M142 89c20 32 36 70 36 110 0 25-8 55-8 55-2 5-5 7-8 6h-1c-2-1-5-4-5-9l1-5c5-14 8-29 8-44 0-19-3-37-8-48-10-23-33-58-53-70v179c0 30-38 60-70 60-19 0-34-11-34-30 0-31 31-60 70-60 11 0 19 3 25 8V3c0-2 2-3 3-3 6 0 9 2 10 7 5 31 18 57 34 82Z",xOffset:0,yOffset:-28},EIGHTH_NOTE_FLIPPED:{path:"M9 323H0V60C0 29 32 0 71 0c20 0 35 12 35 30 0 30-40 60-71 60-19 0-26-9-26-9v158s58-56 58-89c0 0 0-25-8-43-5-12 10-17 14-6 13 34 9 48 9 48 0 39-41 97-41 97-20 27-32 77-32 77Z",xOffset:0,yOffset:-4},REST_WHOLE:{path:"M0 0h104v53H0V0Z",xOffset:0,yOffset:0},REST_HALF:{path:"M0 0h104v53H0V0Z",xOffset:0,yOffset:-5},REST_QUARTER:{path:"m28 151-18-22s-3-4-3-9c0-3 1-7 5-11 16-17 22-33 22-46 0-27-21-45-23-49l-1-6c0-5 4-8 7-8s5 1 7 3l61 71 1 7-1 5c-10 15-23 34-25 55v3c0 20 11 35 25 52 4 4 14 16 14 20 0 2-2 2-5 1-6-2-19-6-29-6-16 0-23 12-23 27 0 8 5 22 11 22 3 3 6 6 6 9s-2 5-7 5l-9-3c-27-21-43-39-43-57s13-35 32-35c3 0 10 3 14 0l-2-6-16-22Z",xOffset:0,yOffset:-16},REST_EIGHTH:{path:"M61 35c18 0 41-35 47-32 0 1 5 3 5 8l-5 17S64 189 62 190c-4 4-11 5-16 5-3 0-13 0-13-6 8-30 41-122 42-128 1-5 1-10-1-10-4 0-3 3-19 8C16 71 0 46 0 31 0 14 14 0 31 0c5 0 30 1 30 35Z",xOffset:0,yOffset:-10},ACCIDENTAL_SHARP:{path:"m67 66-8 3c-2 0-3 6-3 8v26c0 4 2 5 3 5l8-2 1-1c1 0 2 1 2 3v20c0 1-1 4-3 4l-8 4c-2 0-3 4-3 6v40c0 2-2 4-5 4-2 0-4-2-4-4v-35c0-2-1-5-4-5h-1l-17 7c-1 1-3 3-3 6v40c0 2-1 3-4 3-2 0-4-1-4-3v-35c0-1-1-5-3-5l-1 1-7 2v1c-2 0-3-1-3-3v-20c0-2 1-4 3-5l8-3c2-1 3-3 3-6V94c0-3-2-5-4-5l-7 3c-2 0-3-1-3-3V69l3-4 8-3c1-1 3-4 3-8V16c0-2 2-4 5-4 2 0 3 2 3 4v34c0 2 1 5 4 5l18-7c2-1 3-5 3-8V3c0-2 2-3 5-3 2 0 4 1 4 3v35c0 2 2 3 4 3l7-2h1c1 0 2 0 2 2v20c0 2-1 4-3 5Zm-20 46c2-11 2-23 0-34l-4-2c-7 0-20 6-21 11v35l4 1c6 0 20-5 21-11Z",xOffset:-8,yOffset:-10},ACCIDENTAL_FLAT:{path:"M4 196C1 193 0 9 0 9c0-6 5-9 10-9 3 0 6 2 6 5l-2 91c0 3 1 5 3 6h2l7-4c5-3 12-6 18-6 15 1 29 13 29 31 0 15-10 35-39 55-8 5-16 14-25 19l-2 1c-1 0-2 0-3-2Zm11-28c0 1 1 5 4 5l3-1c14-9 29-27 29-44 0-8-4-19-14-19-7 0-20 10-22 16l-1 10 1 33Z",xOffset:-8,yOffset:-14},ACCIDENTAL_NATURAL:{path:"m41 47 5-2h1l2 2v147c0 3-2 4-3 4h-4c-2 0-4-1-4-4v-43c0-2-2-3-5-3-8 0-25 7-29 8l-1 1c-2 0-3-1-3-3V4c0-3 1-4 4-4h3c2 0 4 1 4 4v48c0 2 1 2 3 2 7 0 26-7 26-7h1ZM11 88v31l3 1c8 0 24-6 24-12V78l-2-1c-7 0-25 7-25 11Z",xOffset:-8,yOffset:-10},ACCIDENTAL_DOUBLE_SHARP:{path:"M68 33h3c6 0 13 0 15-2l2-15C88 4 86 0 72 0c-6 0-12 1-14 4-2 1-2 7-2 13-1 5-8 17-12 17-5 0-9-10-11-15l-1-2c0-6-1-12-3-13-2-3-8-4-13-4C10 0 4 1 2 4L0 17l2 14c2 2 9 2 15 2h4c5 2 16 8 16 12 0 3-13 10-17 12h-3c-6 0-13 1-15 3L0 74l2 14 14 2 13-2c2-2 3-8 3-14 1-5 7-17 12-17 4 0 10 13 12 17 0 6 0 12 2 14l14 2c15 0 16-2 16-17l-2-13c-4-2-11-3-15-3h-3c-5-2-17-7-17-12 0-2 13-10 17-12Z",xOffset:-10,yOffset:-4},ACCIDENTAL_DOUBLE_FLAT:{path:"M102 93h2c15 0 29 12 29 30 0 15-10 35-39 55-8 5-16 14-26 19l-2 1-2-2-3-43c-6 7-15 16-27 25-7 5-15 14-25 19l-2 1c-1 0-2 0-3-2C2 193 0 8 0 8c0-5 6-8 10-8 3 0 6 2 6 5l-2 91c0 3 1 5 3 6h2c3 0 5-3 8-4 5-3 9-5 15-5h2c6 0 12 1 17 5L60 8c0-5 5-8 10-8 3 0 6 2 6 5l-2 91c0 3 1 5 3 6h2c3 0 5-3 7-4 6-3 10-5 16-5Zm-80 78c14-9 29-25 29-43 0-8-3-19-13-19-8 0-21 10-23 16l-1 11 1 31c0 2 1 5 4 5l3-1Zm59 0c15-9 29-25 29-43 0-6-2-12-5-16-2-2-4-3-8-3-7 0-21 10-23 16v8l1 34c0 2 1 5 3 5l3-1Z",xOffset:-14,yOffset:-14},TIME_4:{path:"M53 0h66S70 76 20 124h61V86l47-50v89h22v19h-22v27c0 9 13 9 13 9h9v10H61v-10h10s10 0 10-10v-26H0v-20S54 67 53 0Z",xOffset:0,yOffset:0},TIME_3:{path:"M2 43c0 21 15 30 30 30 5 0 28-2 28-27 0-11-16-20-16-24 0-14 56-15 56 26 0 26-19 38-31 38H45v13h24c12 0 33 15 33 41 0 38-54 40-54 25 0-3 10-11 10-21 0-20-15-27-27-27-17 0-32 8-31 30 1 30 36 43 75 43 36 0 75-19 75-50 0-34-23-47-37-47v-2c4 0 35-13 35-39 0-37-35-52-75-52C39 0 3 14 2 43Z",xOffset:0,yOffset:0}},M=/^(?<name>[A-Ga-g])(?<accidental>##|bb|[#bn]?)(?<octave>\d)(?<duration>[whqeWHQE]?)$/,Y=/^[whqeWHQE]$/,I=["C","D","E","F","G","A","B"];function m(h){const e=h.match(M);if(!e||!e.groups)throw new Error(`Invalid note string format: ${h}. Expected format: [A-Ga-g][#|b]?[0-9][w|h|q|e].`);let{name:t,accidental:s,octave:r,duration:o}=e.groups;if(t=t.toUpperCase(),o=o.toLowerCase(),!t)throw new Error(`Invalid note name: ${t}. Valid note names are: C, D, E, F, G, A, B.`);if(!r)throw new Error(`Invalid octave: ${r}. Octave must be a number between 0 and 9.`);o||(o="w");const n={name:t,octave:parseInt(r),duration:o};return s&&(n.accidental=s),n}function C(h){const e=h.match(Y);if(!e)throw new Error(`Invalid note duration '${h}'. Use w | h | q | e.`);return e.toString().toLowerCase()}function L(h){let e;switch(h){case"treble":e="CLEF_TREBLE";break;case"bass":e="CLEF_BASS";break;case"alto":e="CLEF_ALTO";break}if(!e)throw new Error(`Invalid clef type: ${h}. Valid clef types are: treble, bass, alto.`);return e}function b(h,e){const t=I.indexOf(h.name)-I.indexOf(e.name);let s=h.octave-e.octave;return s*=7,t+s}function F(h){let e=I.indexOf(h.name);return e+=h.octave*12,e}function G(h,e){return I.findIndex(s=>s===h)+1+e*7}const g=48,v=40+30/2,B=20,D=90;class H{params;rendererRef;width=0;constructor(e,t){this.rendererRef=e;const s=w[t];if(!s)throw new Error(`Staff type ${t} is not supported`);this.params=s}drawStaffLines=(e,t)=>{let s=e;for(let r=0;r<5;r++)this.rendererRef.drawLine(0,s,this.width,s,t),s+=10;return s-10};drawStaff=e=>{this.width=e;const t=this.rendererRef.getLayerByName("staff");let s=this.drawStaffLines(0,t),r=this.drawStaffLines(s+30,t);this.rendererRef.drawLine(0,0,0,r,t),this.rendererRef.drawLine(this.width,0,this.width,r,t);let o=r,n=1;const a=L("treble"),i=L("bass");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)};shouldNoteFlip(e){const t=Math.abs(e-D),s=Math.abs(e-B);return e===v?!1:t<=s?!(e>=D):e<=B}getLedgerLinesX(e,t){const s=[],r=F(e);let o=e.duration==="w"?17:12.5;if(r===g)return s.push({x1:-2,x2:o,yPos:0}),s;if(t<this.params.topLineYPos){let n=this.params.topLineYPos-10;for(;n>=t;)s.push({x1:-2,x2:o,yPos:n-t}),n-=10}else if(t>this.params.bottomLineYPos){let n=this.params.bottomLineYPos+10;for(;n<=t;)s.push({x1:-2,x2:o,yPos:n-t}),n+=10}return s}calculateNoteYPos=e=>{let s=b(this.params.topLineNote,e)*(10/2);const r=F(e);return r<g?s+=10:r===g&&(s=v),s}}const $=20;class A{params;rendererRef;constructor(e,t){this.rendererRef=e;const s=w[t];if(!s)throw new Error(`Staff type ${t} is not supported`);this.params=s}drawStaff=e=>{const t=this.rendererRef.getLayerByName("staff");let s=0;for(let a=0;a<5;a++)this.rendererRef.drawLine(0,s,e,s,t),s+=10;this.rendererRef.drawLine(0,0,0,s-10,t),this.rendererRef.drawLine(e,0,e,s-10,t);let r=s-10+1,o=1;const n=L(this.params.staffType);this.rendererRef.drawGlyph(n,t),r+=this.params.paddingTop+this.params.paddingBottom,o+=this.params.paddingTop,this.rendererRef.addTotalRootSvgHeight(r),this.rendererRef.addTotalRootSvgYOffset(o)};shouldNoteFlip(e){return e<=$}getLedgerLinesX(e,t){const s=[];let r=e.duration==="w"?17:12.5;if(t<this.params.topLineYPos){let o=this.params.topLineYPos-10;for(;o>=t;)s.push({x1:-2,x2:r,yPos:o-t}),o-=10}else if(t>this.params.bottomLineYPos){let o=this.params.bottomLineYPos+10;for(;o<=t;)s.push({x1:-2,x2:r,yPos:o-t}),o+=10}return s}calculateNoteYPos=e=>b(this.params.topLineNote,e)*(10/2)}class P{svgRendererInstance;strategyInstance;constructor(e,t){this.svgRendererInstance=e,this.strategyInstance=t}drawStem(e,t){t?this.svgRendererInstance.drawLine(0,0,0,28,e):this.svgRendererInstance.drawLine(10,0,10,-28,e)}chordOffsetConsecutiveAccidentals(e){let t=0,s=0,r=0;for(let o=0;o<e.length;o++){const n=e[o];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){const i=Array.from(n.noteGroup.getElementsByTagName("use")).find(c=>c.getAttribute("href")?.includes("ACCIDENTAL"));if(!i)continue;i.setAttribute("transform",`translate(${t+8}, 0)`)}}return-s}chordOffsetCloseNotes(e){let t=e[0],s=0;for(let r=1;r<e.length;r++){const o=e[r];if(G(o.noteObj.name,o.noteObj.octave)-G(t.noteObj.name,t.noteObj.octave)===1){s=28/2,o.noteGroup.setAttribute("transform",`translate(${s}, ${o.noteYPos})`);const i=Array.from(o.noteGroup.getElementsByTagName("use")).find(c=>c.getAttribute("href")?.includes("ACCIDENTAL"));if(i){const c=i.getAttribute("transform")?.match(/([-]?\d+)/),l=c&&c[0];let f=-s;l&&(f+=Number(l)),i.setAttribute("transform",`translate(${f}, 0)`)}r++,t=e[r];continue}t=o}return s}renderNote(e){const t=this.svgRendererInstance.createGroup("note"),s=m(e),r=this.strategyInstance.calculateNoteYPos({name:s.name,octave:s.octave});let o=this.strategyInstance.shouldNoteFlip(r);switch(s.duration){case"h":this.svgRendererInstance.drawGlyph("NOTE_HEAD_HALF",t),this.drawStem(t,o);break;case"q":this.svgRendererInstance.drawGlyph("NOTE_HEAD_QUARTER",t),this.drawStem(t,o);break;case"e":o?this.svgRendererInstance.drawGlyph("EIGHTH_NOTE_FLIPPED",t):this.svgRendererInstance.drawGlyph("EIGHTH_NOTE",t);break;default:this.svgRendererInstance.drawGlyph("NOTE_HEAD_WHOLE",t)}let n=0;switch(s.accidental){case"#":this.svgRendererInstance.drawGlyph("ACCIDENTAL_SHARP",t),n-=-8;break;case"b":this.svgRendererInstance.drawGlyph("ACCIDENTAL_FLAT",t),n-=-8;break;case"n":this.svgRendererInstance.drawGlyph("ACCIDENTAL_NATURAL",t),n-=-8;break;case"##":this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_SHARP",t),n-=-10;break;case"bb":this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_FLAT",t),n-=-14;break}return this.strategyInstance.getLedgerLinesX(s,r).forEach(i=>{this.svgRendererInstance.drawLine(i.x1,i.yPos,i.x2,i.yPos,t)}),{noteGroup:t,noteObj:s,noteYPos:r,accidentalOffset:n,cursorOffset:0}}renderChord(e){const t=this.svgRendererInstance.createGroup("chord"),s=[];for(const n of e){const a=this.renderNote(n);a.noteGroup.setAttribute("transform",`translate(0, ${a.noteYPos})`),t.appendChild(a.noteGroup),s.push({noteGroup:a.noteGroup,noteObj:a.noteObj,noteYPos:a.noteYPos,cursorOffset:0,accidentalOffset:0})}const r=this.chordOffsetConsecutiveAccidentals(s),o=this.chordOffsetCloseNotes(s);return{noteGroup:t,noteObj:s[0].noteObj,noteYPos:0,accidentalOffset:r,cursorOffset:o}}}const p="http://www.w3.org/2000/svg",k=.1,S=1200;class O{rootElementRef;svgElementRef;parentGroupContainer;musicStaffLayer;musicNotesLayer;musicUILayer;width;scale;totalYOffset=0;totalHeight=0;constructor(e,t){this.rootElementRef=e,this.width=t.width,this.scale=t.scale,this.width>S&&(this.width=S,console.warn(`Width provided for the staff ${this.width} exceeds the limit ${S}. Please use this value to fix positioning issues.`)),this.svgElementRef=document.createElementNS(p,"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),this.musicNotesLayer.setAttribute("transform","translate(38, 0)")}makeGlyphDefs(e){const t=document.createElementNS(p,"defs");Object.entries(U).filter(([r])=>e.includes(r)).forEach(([r,o])=>{const n=document.createElementNS(p,"path");n.setAttribute("id",`glyph-${r}`),n.setAttribute("d",o.path),n.setAttribute("fill","currentColor"),n.setAttribute("transform",`translate(${o.xOffset}, ${o.yOffset}) scale(${k})`),t.appendChild(n)}),this.rootSvgElement.appendChild(t)}createGroup(e){const t=document.createElementNS(p,"g");return e&&t.classList.add(`vs-${e}`),t}commitElementsToDOM(e,t=this.rootElementRef){const s=document.createDocumentFragment();Array.isArray(e)?e.forEach(r=>{s.appendChild(r)}):s.appendChild(e),t.appendChild(s)}getLayerByName(e){switch(e){case"staff":return this.musicStaffLayer;case"notes":return this.musicNotesLayer;case"ui":return this.musicUILayer;default:throw new Error(`Layer with name '${e}' does not exist.`)}}addTotalRootSvgHeight(e){this.totalHeight+=e}addTotalRootSvgYOffset(e){this.totalYOffset+=e}applySizingToRootSvg(){this.parentGroupContainer.setAttribute("transform",`translate(0, ${this.totalYOffset})`);let e=this.width*this.scale;const t=(this.totalHeight+this.totalYOffset)*this.scale;e+=2,this.svgElementRef.setAttribute("width",e.toString()),this.svgElementRef.setAttribute("height",t.toString()),this.svgElementRef.setAttribute("viewBox",`0 0 ${this.width} ${this.totalHeight+this.totalYOffset}`)}get rootSvgElement(){return this.svgElementRef}get parentGroupElement(){return this.parentGroupContainer}drawLine(e,t,s,r,o){const n=document.createElementNS(p,"line");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)}drawRect(e,t,s,r){const o=document.createElementNS(p,"rect");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}drawGlyph(e,t,s){s={xOffset:0,yOffset:0,...s};const r=document.createElementNS(p,"use");r.setAttribute("href",`#glyph-${e}`),(s.xOffset||s.yOffset)&&r.setAttribute("transform",`translate(${s.xOffset}, ${s.yOffset})`),t.appendChild(r)}destroy(){this.rootElementRef&&this.svgElementRef&&this.rootElementRef.contains(this.svgElementRef)&&this.rootElementRef.removeChild(this.svgElementRef)}}const W=["CLEF_TREBLE","CLEF_BASS","CLEF_ALTO","NOTE_HEAD_WHOLE","NOTE_HEAD_HALF","NOTE_HEAD_QUARTER","EIGHTH_NOTE","EIGHTH_NOTE_FLIPPED","ACCIDENTAL_SHARP","ACCIDENTAL_FLAT","ACCIDENTAL_NATURAL","ACCIDENTAL_DOUBLE_SHARP","ACCIDENTAL_DOUBLE_FLAT"];class Z{svgRendererInstance;strategyInstance;noteRendererInstance;options;noteEntries=[];noteCursorX=0;constructor(e,t){this.options={width:300,scale:1,staffType:"treble",spaceAbove:0,spaceBelow:0,staffColor:"black",staffBackgroundColor:"transparent",...t},this.svgRendererInstance=new O(e,{width:this.options.width,height:100,scale:this.options.scale,staffColor:this.options.staffColor,staffBackgroundColor:this.options.staffBackgroundColor,useGlyphs:W});const s=this.svgRendererInstance.rootSvgElement;switch(this.options.staffType){case"grand":this.strategyInstance=new H(this.svgRendererInstance,"grand");break;case"bass":this.strategyInstance=new A(this.svgRendererInstance,"bass");break;case"treble":this.strategyInstance=new A(this.svgRendererInstance,"treble");break;case"alto":this.strategyInstance=new A(this.svgRendererInstance,"alto");break;default:throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`)}if(this.strategyInstance.drawStaff(this.options.width),this.noteRendererInstance=new P(this.svgRendererInstance,this.strategyInstance),this.options.spaceAbove){const r=this.options.spaceAbove*10;this.svgRendererInstance.addTotalRootSvgYOffset(r)}if(this.options.spaceBelow){let r=this.options.spaceBelow*10;this.options.staffType==="grand"&&(r-=10/2),this.svgRendererInstance.addTotalRootSvgHeight(r)}this.svgRendererInstance.applySizingToRootSvg(),this.svgRendererInstance.commitElementsToDOM(s)}drawNote(e){const t=Array.isArray(e)?e:[e],s=this.svgRendererInstance.getLayerByName("notes"),r=[];for(const o of t){let n;try{n=this.noteRendererInstance.renderNote(o)}catch(a){throw r.length>0&&this.svgRendererInstance.commitElementsToDOM(r,s),a}n.noteGroup.setAttribute("transform",`translate(${this.noteCursorX+n.accidentalOffset}, ${n.noteYPos})`),this.noteEntries.push({gElement:n.noteGroup,note:n.noteObj,xPos:this.noteCursorX+n.accidentalOffset,yPos:n.noteYPos,accidentalXOffset:n.accidentalOffset}),this.noteCursorX+=28+n.accidentalOffset,r.push(n.noteGroup)}this.svgRendererInstance.commitElementsToDOM(r,s)}drawChord(e){if(e.length<2)throw new Error("Provide more than one note for a chord.");const t=this.svgRendererInstance.getLayerByName("notes"),s=this.noteRendererInstance.renderChord(e);s.noteGroup.setAttribute("transform",`translate(${this.noteCursorX+s.accidentalOffset}, 0)`),this.noteEntries.push({gElement:s.noteGroup,note:m(e[0]),xPos:this.noteCursorX+s.accidentalOffset,yPos:0,accidentalXOffset:s.accidentalOffset}),this.noteCursorX+=28+s.accidentalOffset+s.cursorOffset,this.svgRendererInstance.commitElementsToDOM(s.noteGroup,t)}justifyNotes(){const e=this.options.width-38,t=this.noteEntries.length;if(t<=0||e<=0)return;const s=Math.round(e/t);this.noteEntries.map((o,n)=>{const a=(n+.5)*s,i=o.gElement.getBBox(),c=a-i.width/2-i.x,l=Math.round(c*10)/10;return{entry:o,newX:l,isChord:o.gElement.classList.contains("chord")}}).forEach(o=>{const{entry:n,newX:a,isChord:i}=o;i?n.gElement.setAttribute("transform",`translate(${a}, 0)`):n.gElement.setAttribute("transform",`translate(${a}, ${n.yPos})`),n.xPos=a})}clearAllNotes(){this.noteCursorX=0,this.svgRendererInstance.getLayerByName("notes").replaceChildren(),this.noteEntries=[]}changeNoteByIndex(e,t){if(t>=this.noteEntries.length)throw new Error("Note index was out of bounds.");const s=this.noteEntries[t],r=this.noteRendererInstance.renderNote(e),n=s.xPos-s.accidentalXOffset+r.accidentalOffset;r.noteGroup.setAttribute("transform",`translate(${n}, ${r.noteYPos})`),this.svgRendererInstance.getLayerByName("notes").replaceChild(r.noteGroup,s.gElement),this.noteEntries[t]={gElement:r.noteGroup,note:r.noteObj,xPos:n,yPos:r.noteYPos,accidentalXOffset:r.accidentalOffset}}changeChordByIndex(e,t){if(t>=this.noteEntries.length)throw new Error("Chord index was out of bounds.");if(e.length<2)throw new Error("Notes provided need to be more than one to be considered a chord.");const s=this.noteEntries[t],r=this.noteRendererInstance.renderChord(e),n=s.xPos-s.accidentalXOffset+r.accidentalOffset;r.noteGroup.setAttribute("transform",`translate(${n}, 0)`),this.svgRendererInstance.getLayerByName("notes").replaceChild(r.noteGroup,s.gElement),this.noteEntries[t]={gElement:r.noteGroup,note:m(e[0]),xPos:n,yPos:0,accidentalXOffset:r.accidentalOffset}}addClassToNoteByIndex(e,t){if(t>=this.noteEntries.length)throw new Error("Note index was out of bounds.");this.noteEntries[t].gElement.classList.add(e)}removeClassToNoteByIndex(e,t){if(t>=this.noteEntries.length)throw new Error("Note index was out of bounds.");this.noteEntries[t].gElement.classList.remove(e)}destroy(){this.noteEntries=[],this.svgRendererInstance.destroy()}}const u=30,X=19,y=12,V=4,j=6,x=1,R=38,q=["TIME_4","TIME_3","NOTE_HEAD_WHOLE","NOTE_HEAD_HALF","NOTE_HEAD_QUARTER","EIGHTH_NOTE","REST_WHOLE","REST_HALF","REST_QUARTER","REST_EIGHTH"];class Q{rendererInstance;options;barSpacing;quarterNoteSpacing;noteCursorX=0;noteEntries=[];maxBeatCount;currentBeatCount=0;currentBeatUICount=0;currentBeatUIElement=null;currentBeatUIXPos=R;constructor(e,t){this.options={width:300,scale:1,topNumber:4,barsCount:2,spaceAbove:0,spaceBelow:0,staffColor:"black",staffBackgroundColor:"white",currentBeatUIColor:"#24ff7450",...t},this.rendererInstance=new O(e,{width:this.options.width,height:100,scale:this.options.scale,staffColor:this.options.staffColor,staffBackgroundColor:this.options.staffBackgroundColor,useGlyphs:q});const s=this.rendererInstance.rootSvgElement;let r="TIME_4";switch(this.options.topNumber){case 3:r="TIME_3";break;case 4:r="TIME_4";break;default:throw new Error(`Time signature ${this.options.topNumber} not supported. Please use either 3 or 4.`)}if(this.options.barsCount<1||this.options.barsCount>3)throw new Error(`Bars count ${this.options.barsCount} not supported. Please use 1 - 3`);if(this.options.spaceAbove){const _=this.options.spaceAbove*10;this.rendererInstance.addTotalRootSvgYOffset(_)}if(this.options.spaceBelow){let _=this.options.spaceBelow*10;this.rendererInstance.addTotalRootSvgHeight(_)}const o=this.rendererInstance.getLayerByName("staff");this.rendererInstance.addTotalRootSvgHeight(u*2);const n=this.rendererInstance.createGroup("time-signature");o.appendChild(n);const a=u-X;this.rendererInstance.drawGlyph(r,n),this.rendererInstance.drawGlyph("TIME_4",n,{yOffset:X}),n.setAttribute("transform",`translate(0, ${a})`);let i=this.options.width-38;this.options.barsCount>1&&(i-=(this.options.barsCount-1)*y),i-=x,this.rendererInstance.drawLine(0,u,this.options.width-x,u,o),this.barSpacing=i/this.options.barsCount,this.quarterNoteSpacing=Math.round(this.barSpacing/this.options.topNumber),this.maxBeatCount=this.options.barsCount*this.options.topNumber;let c=this.barSpacing+38;const l=u/2,f=u+l;for(let _=0;_<this.options.barsCount-1;_++)this.rendererInstance.drawLine(c,l,c,f,o),c+=this.barSpacing;this.rendererInstance.getLayerByName("notes").setAttribute("transform",`translate(38, ${u})`),this.rendererInstance.applySizingToRootSvg(),this.rendererInstance.commitElementsToDOM(s)}createBeatUIElement(){const e=this.rendererInstance.getLayerByName("ui");this.currentBeatUIElement=this.rendererInstance.drawRect(this.quarterNoteSpacing/2,u*2,e,{x:R,fill:this.options.currentBeatUIColor})}handleNewBar(){this.noteCursorX+=y}translateGroupByDuration(e,t){return t.setAttribute("transform",`translate(${this.noteCursorX}, 0)`),this.quarterNoteSpacing*e}drawStem(e,t){this.rendererInstance.drawLine(10+(t??0),0,10+(t??0),-28,e)}renderNote(e,t){switch(e){case"w":this.rendererInstance.drawGlyph("NOTE_HEAD_WHOLE",t);break;case"h":this.rendererInstance.drawGlyph("NOTE_HEAD_HALF",t),this.drawStem(t);break;case"q":this.rendererInstance.drawGlyph("NOTE_HEAD_QUARTER",t),this.drawStem(t);break;case"e":this.rendererInstance.drawGlyph("EIGHTH_NOTE",t),this.drawStem(t);break}}renderRest(e,t){switch(e){case"w":this.rendererInstance.drawGlyph("REST_WHOLE",t);break;case"h":this.rendererInstance.drawGlyph("REST_HALF",t);break;case"q":this.rendererInstance.drawGlyph("REST_QUARTER",t);break;case"e":this.rendererInstance.drawGlyph("REST_EIGHTH",t);break}}checkAndCreateNewBar(){const e=this.currentBeatCount>0&&this.currentBeatCount%this.options.topNumber===0,t=this.currentBeatCount<this.maxBeatCount;e&&t&&this.handleNewBar()}checkAndFillBarWithRests(e){const t=this.options.topNumber-this.currentBeatCount%this.options.topNumber;if(e>t){const s=this.createRemainingRests(t);return this.handleNewBar(),s}return null}createRemainingRests(e){const t=[];let s=e;for(;s>0;){const r=this.rendererInstance.createGroup("rest");let o=0;s-E.h>=0?(this.rendererInstance.drawGlyph("REST_HALF",r),o=E.h):s-E.q>=0?(this.rendererInstance.drawGlyph("REST_QUARTER",r),o=E.q):(this.rendererInstance.drawGlyph("REST_EIGHTH",r),o=E.e),s-=o,this.currentBeatCount+=o,r.setAttribute("transform",`translate(${this.noteCursorX}, 0)`),this.noteCursorX+=o*this.quarterNoteSpacing,t.push(r)}return t}renderBeamRect(e,t,s,r){this.rendererInstance.drawRect(e-t,V,s,{x:10,y:-28+(r??0),fill:this.options.staffColor})}drawNote(e){const t=Array.isArray(e)?e:[e],s=this.rendererInstance.getLayerByName("notes"),r=[];for(const o of t){let n="w";try{n=C(o)}catch(f){throw r.length>0&&this.rendererInstance.commitElementsToDOM(r,s),f}const a=E[n];if(this.currentBeatCount>=this.maxBeatCount)throw r.length>0&&this.rendererInstance.commitElementsToDOM(r,s),new Error("Max beat count reached. Can't add additional notes.");this.checkAndCreateNewBar();const i=this.checkAndFillBarWithRests(a);i&&i.forEach(f=>{r.push(f),this.noteEntries.push(f)});const c=this.rendererInstance.createGroup("note"),l=this.translateGroupByDuration(a,c);this.noteCursorX+=l,this.currentBeatCount+=a,this.renderNote(n,c),r.push(c),this.noteEntries.push(c)}this.rendererInstance.commitElementsToDOM(r,s)}drawRest(e){const t=Array.isArray(e)?e:[e],s=this.rendererInstance.getLayerByName("notes"),r=[];for(const o of t){let n="w";try{n=C(o)}catch(f){throw r.length>0&&this.rendererInstance.commitElementsToDOM(r,s),f}const a=this.rendererInstance.createGroup("rest"),i=E[n],c=i*this.quarterNoteSpacing;if(this.currentBeatCount>=this.maxBeatCount)throw r.length>0&&this.rendererInstance.commitElementsToDOM(r,s),new Error("Max beat count reached. Can't add additional notes.");this.checkAndCreateNewBar();const l=this.checkAndFillBarWithRests(i);l&&l.forEach(f=>{r.push(f),this.noteEntries.push(f)}),this.renderRest(n,a),a.setAttribute("transform",`translate(${this.noteCursorX}, 0)`),this.noteCursorX+=c,this.currentBeatCount+=i,r.push(a),this.noteEntries.push(a)}this.rendererInstance.commitElementsToDOM(r,s)}drawBeamedNotes(e,t){if(t<2)throw new Error("Must provide a value greater than 2 for beamed note.");if(this.currentBeatCount>=this.maxBeatCount)throw new Error("Max beat count reached. Can't add additional beamed note.");let s="s";e==="s"?s="s":s=C(e),this.checkAndCreateNewBar();const r=this.rendererInstance.getLayerByName("notes"),o=E[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");c.setAttribute("transform",`translate(${this.noteCursorX}, 0)`);let l=0;for(let f=0;f<i;f++)this.rendererInstance.drawGlyph("NOTE_HEAD_QUARTER",c,{xOffset:l}),this.drawStem(c,l),l+=n,this.currentBeatCount+=o;this.renderBeamRect(l,n,c),e==="s"&&this.renderBeamRect(l,n,c,j),this.noteCursorX+=l,this.noteEntries.push(c),this.rendererInstance.commitElementsToDOM(c,r)}incrementCurrentBeatUI(){if(this.currentBeatUIElement||this.createBeatUIElement(),this.currentBeatUICount>=this.maxBeatCount){this.currentBeatUIElement.setAttribute("display","none");return}this.currentBeatUIElement?.getAttribute("display")==="none"&&this.currentBeatUIElement.removeAttribute("display"),this.currentBeatUICount++,this.currentBeatUICount>this.options.topNumber&&this.currentBeatUICount%this.options.topNumber===1&&(this.currentBeatUIXPos+=y),this.currentBeatUICount>1&&(this.currentBeatUIXPos+=this.quarterNoteSpacing),this.currentBeatUIElement.setAttribute("x",this.currentBeatUIXPos.toString())}resetCurrentBeatUI(){this.currentBeatUICount=0,this.currentBeatUIXPos=R,this.currentBeatUIElement&&(this.currentBeatUIElement.setAttribute("display","none"),this.currentBeatUIElement.setAttribute("x",this.currentBeatUIXPos.toString()))}clearAllNotes(){this.noteCursorX=0,this.currentBeatCount=0,this.rendererInstance.getLayerByName("notes").replaceChildren(),this.noteEntries=[]}destroy(){this.noteEntries=[],this.rendererInstance.destroy()}}const z=["CLEF_TREBLE","CLEF_BASS","CLEF_ALTO","NOTE_HEAD_WHOLE","NOTE_HEAD_HALF","NOTE_HEAD_QUARTER","EIGHTH_NOTE","EIGHTH_NOTE_FLIPPED","ACCIDENTAL_SHARP","ACCIDENTAL_FLAT","ACCIDENTAL_NATURAL","ACCIDENTAL_DOUBLE_SHARP","ACCIDENTAL_DOUBLE_FLAT"],N=60,K=N;class J{svgRendererInstance;strategyInstance;noteRendererInstance;options;activeEntries=[];noteBuffer=[];notesLayer;noteCursorX=0;constructor(e,t){this.options={width:300,scale:1,staffType:"treble",spaceAbove:0,spaceBelow:0,staffColor:"black",staffBackgroundColor:"transparent",...t},this.svgRendererInstance=new O(e,{width:this.options.width,height:100,scale:this.options.scale,staffColor:this.options.staffColor,staffBackgroundColor:this.options.staffBackgroundColor,useGlyphs:z});const s=this.svgRendererInstance.rootSvgElement;switch(this.options.staffType){case"grand":this.strategyInstance=new H(this.svgRendererInstance,"grand");break;case"bass":this.strategyInstance=new A(this.svgRendererInstance,"bass");break;case"treble":this.strategyInstance=new A(this.svgRendererInstance,"treble");break;case"alto":this.strategyInstance=new A(this.svgRendererInstance,"alto");break;default:throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`)}if(this.strategyInstance.drawStaff(this.options.width),this.noteRendererInstance=new P(this.svgRendererInstance,this.strategyInstance),this.options.spaceAbove){const r=this.options.spaceAbove*10;this.svgRendererInstance.addTotalRootSvgYOffset(r)}if(this.options.spaceBelow){let r=this.options.spaceBelow*10;this.options.staffType==="grand"&&(r-=10/2),this.svgRendererInstance.addTotalRootSvgHeight(r)}this.notesLayer=this.svgRendererInstance.getLayerByName("notes"),this.notesLayer.classList.add("vs-scrolling-notes-layer"),this.svgRendererInstance.applySizingToRootSvg(),this.svgRendererInstance.commitElementsToDOM(s)}renderFirstNoteGroups(){const e=this.options.width-38+K;for(;this.noteBuffer.length>0&&this.noteCursorX<e;)this.renderNextNote(),this.noteCursorX+=N;this.activeEntries.length>1&&(this.noteCursorX-=N)}renderNextNote(){if(this.noteBuffer.length<1)return;const e=this.noteBuffer[0],t=this.svgRendererInstance.createGroup("note-wrapper");if(e.type==="chord"){const s=this.noteRendererInstance.renderChord(e.notes);s.noteGroup.setAttribute("transform",`translate(0, ${s.noteYPos})`),t.appendChild(s.noteGroup)}else{const s=this.noteRendererInstance.renderNote(e.notes[0]);s.noteGroup.setAttribute("transform",`translate(0, ${s.noteYPos})`),t.appendChild(s.noteGroup)}t.style.transform=`translate(${this.noteCursorX}px, 0px)`,this.activeEntries.push({noteWrapper:t,xPos:this.noteCursorX}),this.noteBuffer.shift(),this.notesLayer.appendChild(t)}queueNotes(e){this.clearAllNotes();for(const t of e)Array.isArray(t)?this.noteBuffer.push({type:"chord",notes:t}):this.noteBuffer.push({type:"note",notes:[t]});this.renderFirstNoteGroups()}advanceNotes(){if(this.activeEntries.length<=0){this.clearAllNotes(),this.options.onNotesOut&&this.options.onNotesOut();return}this.activeEntries.forEach(t=>{t.xPos-=N,t.noteWrapper.style.transform=`translate(${t.xPos}px, 0px)`});const e=this.activeEntries[0];e.xPos<=0&&(this.notesLayer.removeChild(e.noteWrapper),this.activeEntries.shift()),this.renderNextNote()}clearAllNotes(){this.noteCursorX=0,this.notesLayer.replaceChildren(),this.activeEntries=[],this.noteBuffer=[]}destroy(){this.svgRendererInstance.destroy(),this.activeEntries=[],this.noteBuffer=[]}}d.MusicStaff=Z,d.RhythmStaff=Q,d.ScrollingStaff=J,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vector-score",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"main": "./dist/vector-score.umd.cjs",
|
|
10
|
+
"module": "./dist/vector-score.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/vector-score.js",
|
|
16
|
+
"require": "./dist/vector-score.umd.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "tsc --noEmit && vite build",
|
|
22
|
+
"preview": "vite preview",
|
|
23
|
+
"test": "vitest",
|
|
24
|
+
"prepare": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^25.0.0",
|
|
28
|
+
"jsdom": "^27.3.0",
|
|
29
|
+
"typescript": "~5.9.3",
|
|
30
|
+
"vite": "^7.2.4",
|
|
31
|
+
"vite-plugin-dts": "^4.5.4",
|
|
32
|
+
"vitest": "^4.0.15"
|
|
33
|
+
}
|
|
34
|
+
}
|