UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

901 lines (900 loc) 26.4 kB
/* eslint-disable @typescript-eslint/no-unused-vars */ import { NamedElement } from './namedelement'; /** * A wrapper around NamedElement that performs mutative actions. * * This allows callers to exclusively work with NamedElements, as opposed to casting NamedElements to their native form. * It also prevents the need for NamedElement to have an mutative actions. */ class NamedElementEditor { element; constructor(element) { this.element = element; } /** Appends the elements to the wrapped element as direct children. */ append(...elements) { this.element.native().append(...elements.map((element) => element.native())); } /** Sets the text content of the wrapped element. */ setTextContent(textContent) { this.element.native().textContent = textContent; } /** Sets the attribute of the wrapped element. */ setAttribute(name, value) { this.element.native().setAttribute(name, value); } } /** Creates a document node. */ export const createDocument = () => document.implementation.createDocument(null, null); /** Creates an XML element with the specified tag name. */ export const createElement = (tagName) => { return createDocument().createElement(tagName); }; /** Creates a NamedElement with the specified tag name. */ export const createNamedElement = (tagName) => { return NamedElement.of(createElement(tagName)); }; /** * Creates an element factory. * * Factories are invoked by invoking the returned function with the arguments. * @example * const fooFactory = createNamedElementFactory<'foo', { bar: string }>('foo', (e, { bar }) => { * if (typeof bar === 'string') { * e.setTextContent(bar); * } * }); * const fooNamedElement = fooFactory('bar'); */ const createNamedElementFactory = (tagName, builder) => { return (args) => { const element = createNamedElement(tagName); builder(new NamedElementEditor(element), args ?? {}); return element; }; }; export const musicXML = (scorePartwise) => { const root = createDocument(); root.appendChild(scorePartwise.native()); return root; }; export const scorePartwise = createNamedElementFactory('score-partwise', (e, { parts, partList, defaults }) => { if (parts) { e.append(...parts); } if (partList) { e.append(partList); } if (defaults) { e.append(defaults); } }); export const partList = createNamedElementFactory('part-list', (e, { scoreParts }) => { if (scoreParts) { e.append(...scoreParts); } }); export const scorePart = createNamedElementFactory('score-part', (e, { id, partName }) => { if (id) { e.setAttribute('id', id); } if (partName) { e.append(partName); } }); export const partName = createNamedElementFactory('part-name', (e, { textContent }) => { if (textContent) { e.setTextContent(textContent); } }); export const part = createNamedElementFactory('part', (e, { id, measures }) => { if (id) { e.setAttribute('id', id); } if (measures) { e.append(...measures); } }); export const measure = createNamedElementFactory('measure', (e, { width, entries, implicit, number, notes, attributes, barlines, prints }) => { if (entries) { e.append(...entries); } if (notes) { e.append(...notes); } if (attributes) { e.append(...attributes); } if (barlines) { e.append(...barlines); } if (prints) { e.append(...prints); } if (implicit) { e.setAttribute('implicit', implicit); } if (typeof width === 'number') { e.setAttribute('width', width.toString()); } if (typeof number === 'string') { e.setAttribute('number', number); } }); export const note = createNamedElementFactory('note', (e, { printObject, type, grace, stem, dots, rest, pitch, accidental, timeModification, notehead, duration, notations, voice, staff, beams, chord, lyrics, }) => { if (typeof printObject === 'string') { e.setAttribute('print-object', printObject); } if (grace) { e.append(grace); } if (chord) { e.append(chord); } if (pitch) { e.append(pitch); } if (rest) { e.append(rest); } if (duration) { e.append(duration); } if (voice) { e.append(voice); } if (type) { e.append(type); } if (dots) { e.append(...dots); } if (accidental) { e.append(accidental); } if (timeModification) { e.append(timeModification); } if (stem) { e.append(stem); } if (notehead) { e.append(notehead); } if (staff) { e.append(staff); } if (beams) { e.append(...beams); } if (notations) { e.append(...notations); } if (lyrics) { e.append(...lyrics); } }); export const backup = createNamedElementFactory('backup', (e, { duration }) => { if (duration) { e.append(duration); } }); export const forward = createNamedElementFactory('forward', (e, { duration }) => { if (duration) { e.append(duration); } }); export const type = createNamedElementFactory('type', (e, { textContent }) => { if (textContent) { e.setTextContent(textContent); } }); export const attributes = createNamedElementFactory('attributes', (e, { divisions, staves, clefs, times, keys, staffDetails, measureStyles }) => { if (divisions) { e.append(divisions); } if (keys) { e.append(...keys); } if (times) { e.append(...times); } if (clefs) { e.append(...clefs); } if (staves) { e.append(staves); } if (staffDetails) { e.append(...staffDetails); } if (measureStyles) { e.append(...measureStyles); } }); export const divisions = createNamedElementFactory('divisions', (e, { positiveDivisions }) => { if (typeof positiveDivisions === 'number') { e.setTextContent(positiveDivisions.toString()); } }); export const print = createNamedElementFactory('print', (e, { newSystem, newPage, staffLayouts, systemLayout }) => { if (typeof newSystem === 'boolean') { e.setAttribute('new-system', newSystem ? 'yes' : 'no'); } if (typeof newPage === 'boolean') { e.setAttribute('new-page', newPage ? 'yes' : 'no'); } if (staffLayouts) { e.append(...staffLayouts); } if (systemLayout) { e.append(systemLayout); } }); export const defaults = createNamedElementFactory('defaults', (e, { staffLayouts, systemLayout }) => { if (staffLayouts) { e.append(...staffLayouts); } if (systemLayout) { e.append(systemLayout); } }); export const staffLayout = createNamedElementFactory('staff-layout', (e, { number, staffDistance }) => { if (typeof number === 'number') { e.setAttribute('number', number.toString()); } if (staffDistance) { e.append(staffDistance); } }); export const staffDistance = createNamedElementFactory('staff-distance', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const systemLayout = createNamedElementFactory('system-layout', (e, { systemMargins, systemDistance, topSystemDistance, systemDividers }) => { if (systemMargins) { e.append(systemMargins); } if (systemDistance) { e.append(systemDistance); } if (topSystemDistance) { e.append(topSystemDistance); } if (systemDividers) { e.append(systemDividers); } }); export const systemMargins = createNamedElementFactory('system-margins', (e, { leftMargin, rightMargin }) => { if (leftMargin) { e.append(leftMargin); } if (rightMargin) { e.append(rightMargin); } }); export const leftMargin = createNamedElementFactory('left-margin', (e, { tenths }) => { if (typeof tenths === 'number') { e.setTextContent(tenths.toString()); } }); export const rightMargin = createNamedElementFactory('right-margin', (e, { tenths }) => { if (typeof tenths === 'number') { e.setTextContent(tenths.toString()); } }); export const systemDistance = createNamedElementFactory('system-distance', (e, { tenths }) => { if (typeof tenths === 'number') { e.setTextContent(tenths.toString()); } }); export const topSystemDistance = createNamedElementFactory('top-system-distance', (e, { tenths }) => { if (typeof tenths === 'number') { e.setTextContent(tenths.toString()); } }); export const direction = createNamedElementFactory('direction', (e, { placement, types }) => { if (placement) { e.setAttribute('placement', placement); } if (types) { e.append(...types); } }); export const directionType = createNamedElementFactory('direction-type', (e, { segnos, codas, wedge, pedal, metronome, octaveShift, tokens }) => { if (segnos) { e.append(...segnos); } if (codas) { e.append(...codas); } if (wedge) { e.append(wedge); } if (pedal) { e.append(pedal); } if (metronome) { e.append(metronome); } if (octaveShift) { e.append(octaveShift); } if (tokens) { e.append(...tokens); } }); export const words = createNamedElementFactory('words', (e, { content }) => { if (content) { e.setTextContent(content); } }); export const symbolic = createNamedElementFactory('symbol', (e, { smuflGlyphName }) => { if (smuflGlyphName) { e.setTextContent(smuflGlyphName); } }); export const metronome = createNamedElementFactory('metronome', (e, { parentheses, content }) => { if (typeof parentheses === 'string') { e.setAttribute('parentheses', parentheses); } if (content) { e.append(...content); } }); export const beatUnit = createNamedElementFactory('beat-unit', (e, { noteTypeValue }) => { if (noteTypeValue) { e.setTextContent(noteTypeValue); } }); export const beatUnitDot = createNamedElementFactory('beat-unit-dot', () => { }); export const perMinute = createNamedElementFactory('per-minute', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const barline = createNamedElementFactory('barline', (e, { location, barStyle, repeat, ending }) => { if (location) { e.setAttribute('location', location); } if (barStyle) { e.append(barStyle); } if (repeat) { e.append(repeat); } if (ending) { e.append(ending); } }); export const staves = createNamedElementFactory('staves', (e, { staveCount }) => { if (typeof staveCount === 'number') { e.setTextContent(staveCount.toString()); } }); export const barStyle = createNamedElementFactory('bar-style', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const repeat = createNamedElementFactory('repeat', (e, { direction }) => { if (direction) { e.setAttribute('direction', direction); } }); export const ending = createNamedElementFactory('ending', (e, { number, type, textContent }) => { if (number) { e.setAttribute('number', number); } if (type) { e.setAttribute('type', type); } if (textContent) { e.setTextContent(textContent); } }); export const stem = createNamedElementFactory('stem', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const dot = createNamedElementFactory('dot', (_, __) => { // noop }); export const rest = createNamedElementFactory('rest', (e, { displayStep, displayOctave }) => { if (displayStep) { e.append(displayStep); } if (displayOctave) { e.append(displayOctave); } }); export const displayStep = createNamedElementFactory('display-step', (e, { step }) => { if (step) { e.setTextContent(step); } }); export const displayOctave = createNamedElementFactory('display-octave', (e, { octave }) => { if (octave) { e.setTextContent(octave); } }); export const pitch = createNamedElementFactory('pitch', (e, { step, alter, octave }) => { if (step) { e.append(step); } if (alter) { e.append(alter); } if (octave) { e.append(octave); } }); export const step = createNamedElementFactory('step', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const alter = createNamedElementFactory('alter', (e, { value }) => { if (typeof value === 'number') { e.setTextContent(value.toString()); } }); export const octave = createNamedElementFactory('octave', (e, { value }) => { if (value) { e.setTextContent(value.toString()); } }); export const accidental = createNamedElementFactory('accidental', (e, { value, cautionary }) => { if (value) { e.setTextContent(value); } if (cautionary) { e.setAttribute('cautionary', cautionary); } }); export const grace = createNamedElementFactory('grace', (e, { slash }) => { if (slash) { e.setAttribute('slash', slash); } }); export const duration = createNamedElementFactory('duration', (e, { positiveDivisions }) => { if (typeof positiveDivisions === 'number') { e.setTextContent(positiveDivisions.toString()); } }); export const key = createNamedElementFactory('key', (e, { fifths, mode }) => { if (fifths) { e.append(fifths); } if (mode) { e.append(mode); } }); export const fifths = createNamedElementFactory('fifths', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const mode = createNamedElementFactory('mode', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const time = createNamedElementFactory('time', (e, { staveNumber, symbol, times, senzaMisura }) => { if (typeof staveNumber === 'number') { e.setAttribute('number', staveNumber.toString()); } if (symbol) { e.setAttribute('symbol', symbol); } if (times) { for (const { beats, beatType } of times) { if (beats) { e.append(beats); } if (beatType) { e.append(beatType); } } } if (senzaMisura) { e.append(senzaMisura); } }); export const senzaMisura = createNamedElementFactory('senza-misura', (e, { content }) => { if (content) { e.setTextContent(content); } }); export const clef = createNamedElementFactory('clef', (e, { number, sign, line, clefOctaveChange }) => { if (typeof number === 'number') { e.setAttribute('number', number.toString()); } if (sign) { e.append(sign); } if (line) { e.append(line); } if (clefOctaveChange) { e.append(clefOctaveChange); } }); export const coda = createNamedElementFactory('coda', (_, __) => { // noop }); export const segno = createNamedElementFactory('segno', (_, __) => { // noop }); export const sign = createNamedElementFactory('sign', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const line = createNamedElementFactory('line', (e, { value }) => { if (typeof value === 'number') { e.setTextContent(value.toString()); } }); export const clefOctaveChange = createNamedElementFactory('clef-octave-change', (e, { value }) => { if (typeof value === 'number') { e.setTextContent(value.toString()); } }); export const beats = createNamedElementFactory('beats', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const beatType = createNamedElementFactory('beat-type', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const lyric = createNamedElementFactory('lyric', (e, { number, components }) => { if (typeof number === 'number') { e.setAttribute('number', number.toString()); } if (components) { e.append(...components); } }); export const syllabic = createNamedElementFactory('syllabic', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const text = createNamedElementFactory('text', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const elision = createNamedElementFactory('elision', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const notations = createNamedElementFactory('notations', (e, { tieds, slurs, tuplets, arpeggiate, ornaments, fermatas, articulations, accidentalMarks }) => { if (tieds) { e.append(...tieds); } if (slurs) { e.append(...slurs); } if (tuplets) { e.append(...tuplets); } if (arpeggiate) { e.append(arpeggiate); } if (ornaments) { e.append(...ornaments); } if (articulations) { e.append(...articulations); } if (fermatas) { e.append(...fermatas); } if (accidentalMarks) { e.append(...accidentalMarks); } }); export const arpeggiate = createNamedElementFactory('arpeggiate', (e, { direction }) => { if (direction) { e.setAttribute('direction', direction); } }); export const voice = createNamedElementFactory('voice', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const staff = createNamedElementFactory('staff', (e, { number }) => { if (typeof number === 'number') { e.setTextContent(number.toString()); } }); export const notehead = createNamedElementFactory('notehead', (e, { value }) => { if (value) { e.setTextContent(value); } }); export const chord = createNamedElementFactory('chord', (_, __) => { // noop }); export const beam = createNamedElementFactory('beam', (e, { number, beamValue }) => { if (typeof number === 'number') { e.setAttribute('number', number.toString()); } if (typeof beamValue === 'string') { e.setTextContent(beamValue); } }); export const staffDetails = createNamedElementFactory('staff-details', (e, { number, staffType, staffLines }) => { if (typeof number === 'number') { e.setAttribute('number', number.toString()); } if (staffType) { e.append(staffType); } if (staffLines) { e.append(staffLines); } }); export const measureStyle = createNamedElementFactory('measure-style', (e, { staffNumber, multipleRest }) => { if (typeof staffNumber === 'number') { e.setAttribute('number', staffNumber.toString()); } if (multipleRest) { e.append(multipleRest); } }); export const multipleRest = createNamedElementFactory('multiple-rest', (e, { multipleRestCount }) => { if (typeof multipleRestCount === 'number') { e.setTextContent(multipleRestCount.toString()); } }); export const staffLines = createNamedElementFactory('staff-lines', (e, { value }) => { if (typeof value === 'number') { e.setTextContent(value.toString()); } }); export const staffType = createNamedElementFactory('staff-type', (e, { value }) => { if (typeof value === 'string') { e.setTextContent(value); } }); export const tuplet = createNamedElementFactory('tuplet', (e, { type, placement, showNumber }) => { if (type) { e.setAttribute('type', type); } if (placement) { e.setAttribute('placement', placement); } if (showNumber) { e.setAttribute('show-number', showNumber); } }); export const timeModification = createNamedElementFactory('time-modification', (e, { actualNotes, normalNotes }) => { if (actualNotes) { e.append(actualNotes); } if (normalNotes) { e.append(normalNotes); } }); export const actualNotes = createNamedElementFactory('actual-notes', (e, { value }) => { if (typeof value === 'number') { e.setTextContent(value.toString()); } }); export const normalNotes = createNamedElementFactory('normal-notes', (e, { value }) => { if (typeof value === 'number') { e.setTextContent(value.toString()); } }); export const slur = createNamedElementFactory('slur', (e, { type, placement, number, lineType }) => { if (type) { e.setAttribute('type', type); } if (placement) { e.setAttribute('placement', placement); } if (typeof number === 'number') { e.setAttribute('number', number.toString()); } if (lineType) { e.setAttribute('line-type', lineType); } }); export const tied = createNamedElementFactory('tied', (e, { type, placement, number, lineType }) => { if (type) { e.setAttribute('type', type); } if (placement) { e.setAttribute('placement', placement); } if (typeof number === 'number') { e.setAttribute('number', number.toString()); } if (lineType) { e.setAttribute('line-type', lineType); } }); export const wedge = createNamedElementFactory('wedge', (e, { type, spread }) => { if (type) { e.setAttribute('type', type); } if (typeof spread === 'number') { e.setAttribute('spread', spread.toString()); } }); export const ornaments = createNamedElementFactory('ornaments', (e, { contents }) => { if (contents) { e.append(...contents); } }); export const trillMark = createNamedElementFactory('trill-mark', (e) => { }); export const wavyLine = createNamedElementFactory('wavy-line', (e, { number, type }) => { if (typeof number === 'number') { e.setAttribute('number', number.toString()); } if (type) { e.setAttribute('type', type); } }); export const octaveShift = createNamedElementFactory('octave-shift', (e, { type, size }) => { if (type) { e.setAttribute('type', type); } if (typeof size === 'number') { e.setAttribute('size', size.toString()); } }); export const pedal = createNamedElementFactory('pedal', (e, { type, line, sign }) => { if (type) { e.setAttribute('type', type); } if (line) { e.setAttribute('line', line); } if (sign) { e.setAttribute('sign', sign); } }); export const container = createNamedElementFactory('container', (e, { rootfiles }) => { if (rootfiles) { e.append(rootfiles); } }); export const rootfiles = createNamedElementFactory('rootfiles', (e, { rootfiles }) => { if (rootfiles) { e.append(...rootfiles); } }); export const rootfile = createNamedElementFactory('rootfile', (e, { fullPath, mediaType }) => { if (fullPath) { e.setAttribute('full-path', fullPath); } if (mediaType) { e.setAttribute('media-type', mediaType); } }); export const fermata = createNamedElementFactory('fermata', (e, { shape, type }) => { if (shape) { e.setTextContent(shape); } if (type) { e.setAttribute('type', type); } }); export const articulations = createNamedElementFactory('articulations', (e, { accents, strongAccents, staccatos, tenutos, detachedLegatos, staccatissimos, scoops, plops, doits, falloffs, breathMarks, }) => { if (accents) { e.append(...accents); } if (strongAccents) { e.append(...strongAccents); } if (staccatos) { e.append(...staccatos); } if (tenutos) { e.append(...tenutos); } if (detachedLegatos) { e.append(...detachedLegatos); } if (staccatissimos) { e.append(...staccatissimos); } if (scoops) { e.append(...scoops); } if (plops) { e.append(...plops); } if (doits) { e.append(...doits); } if (falloffs) { e.append(...falloffs); } if (breathMarks) { e.append(...breathMarks); } }); export const accent = createNamedElementFactory('accent', (e, { placement }) => { if (placement) { e.setAttribute('placement', placement); } }); export const strongAccent = createNamedElementFactory('strong-accent', (e, { placement }) => { if (placement) { e.setAttribute('placement', placement); } }); export const staccato = createNamedElementFactory('staccato', (e, { placement }) => { if (placement) { e.setAttribute('placement', placement); } }); export const tenuto = createNamedElementFactory('tenuto', (e, { placement }) => { if (placement) { e.setAttribute('placement', placement); } }); export const detachedLegato = createNamedElementFactory('detached-legato', (e, { placement }) => { if (placement) { e.setAttribute('placement', placement); } }); export const staccatissimo = createNamedElementFactory('staccatissimo', (e, { placement }) => { if (placement) { e.setAttribute('placement', placement); } }); export const scoop = createNamedElementFactory('scoop', (e, { placement, lineType }) => { if (placement) { e.setAttribute('placement', placement); } if (lineType) { e.setAttribute('line-type', lineType); } }); export const plop = createNamedElementFactory('plop', (e, { placement, lineType }) => { if (placement) { e.setAttribute('placement', placement); } if (lineType) { e.setAttribute('line-type', lineType); } }); export const doit = createNamedElementFactory('doit', (e, { placement, lineType }) => { if (placement) { e.setAttribute('placement', placement); } if (lineType) { e.setAttribute('line-type', lineType); } }); export const falloff = createNamedElementFactory('falloff', (e, { placement, lineType }) => { if (placement) { e.setAttribute('placement', placement); } if (lineType) { e.setAttribute('line-type', lineType); } }); export const breathMark = createNamedElementFactory('breath-mark', (e, { placement }) => { if (placement) { e.setAttribute('placement', placement); } }); export const accidentalMark = createNamedElementFactory('accidental-mark', (e, { type }) => { if (type) { e.setTextContent(type); } });