vibe-editor 0.0.3 → 0.0.5

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.
Files changed (87) hide show
  1. package/package.json +65 -61
  2. package/src/scripts/js/Core.js +32 -13
  3. package/src/scripts/js/entry.js +6 -5
  4. package/src/scripts/js/gui/Annotations.js +28 -13
  5. package/src/scripts/js/gui/ScoreManipulator.js +13 -11
  6. package/src/scripts/js/gui/Tabbar.js +14 -5
  7. package/src/scripts/js/gui/Toolbar.js +1 -1
  8. package/src/scripts/js/handlers/AnnotationChangeHandler.js +16 -4
  9. package/src/scripts/js/handlers/CustomAnnotationShapeDrawer.js +2 -2
  10. package/src/scripts/js/handlers/InsertModeHandler.js +7 -7
  11. package/src/scripts/js/handlers/KeyModeHandler.js +1 -1
  12. package/src/scripts/js/handlers/LabelHandler.js +1 -1
  13. package/src/scripts/js/handlers/NoteDragHandler.js +1 -1
  14. package/src/scripts/js/handlers/PhantomElementHandler.js +3 -10
  15. package/src/scripts/js/handlers/{SideBarHandler.js → SidebarHandler.js} +1 -1
  16. package/src/scripts/js/handlers/TooltipHandler.js +1 -1
  17. package/src/scripts/js/handlers/WindowHandler.js +7 -7
  18. package/src/scripts/js/scripts/Core.js +932 -0
  19. package/src/scripts/js/scripts/MusicProcessor.js +810 -0
  20. package/src/scripts/js/scripts/VIBE.js +219 -0
  21. package/src/scripts/js/{assets → scripts/assets}/mei_template.js +2 -2
  22. package/src/scripts/js/scripts/datastructures/MeasureMatrix.js +156 -0
  23. package/src/scripts/js/scripts/gui/Annotations.js +549 -0
  24. package/src/scripts/js/scripts/gui/Cursor.js +203 -0
  25. package/src/scripts/js/{gui → scripts/gui}/HarmonyLabel.js +1 -1
  26. package/src/scripts/js/scripts/gui/PhantomElement.js +132 -0
  27. package/src/scripts/js/scripts/gui/ScoreManipulator.js +188 -0
  28. package/src/scripts/js/scripts/gui/Tabbar.js +705 -0
  29. package/src/scripts/js/{gui → scripts/gui}/TempoLabel.js +1 -1
  30. package/src/scripts/js/{gui/Toolbar copy.js → scripts/gui/Toolbar.js} +15 -11
  31. package/src/scripts/js/scripts/handlers/AnnotationChangeHandler.js +650 -0
  32. package/src/scripts/js/scripts/handlers/ClickModeHandler.js +535 -0
  33. package/src/scripts/js/{gui → scripts/handlers}/CustomAnnotationShapeDrawer.js +34 -17
  34. package/src/scripts/js/{handlers/ModHandler.js → scripts/handlers/CustomToolbarHandler.js} +54 -66
  35. package/src/scripts/js/scripts/handlers/GlobalKeyboardHandler.js +372 -0
  36. package/src/scripts/js/scripts/handlers/Handler.js +2 -0
  37. package/src/scripts/js/{handlers/InsertModeHandler_deprecated.js → scripts/handlers/InsertModeHandler.js} +117 -164
  38. package/src/scripts/js/scripts/handlers/KeyModeHandler.js +405 -0
  39. package/src/scripts/js/scripts/handlers/LabelHandler.js +463 -0
  40. package/src/scripts/js/scripts/handlers/NoteDragHandler.js +97 -0
  41. package/src/scripts/js/scripts/handlers/PhantomElementHandler.js +168 -0
  42. package/src/scripts/js/scripts/handlers/ScoreManipulatorHandler.js +233 -0
  43. package/src/scripts/js/scripts/handlers/SidebarHandler.js +506 -0
  44. package/src/scripts/js/scripts/handlers/TooltipHandler.js +132 -0
  45. package/src/scripts/js/scripts/handlers/WindowHandler.js +278 -0
  46. package/src/scripts/js/scripts/utils/MEIOperations.js +2121 -0
  47. package/src/scripts/js/{utils/Mouse2MEI.js → scripts/utils/Mouse2SVG.js} +225 -57
  48. package/src/scripts/js/{utils → scripts/utils}/ReactWrapper.js +1 -1
  49. package/src/scripts/js/scripts/utils/SVGEditor.js +453 -0
  50. package/src/scripts/js/scripts/utils/Types.js +2 -0
  51. package/src/scripts/js/{utils/VerovioWrapper copy.js → scripts/utils/VerovioWrapper.js} +35 -21
  52. package/src/scripts/js/scripts/utils/coordinates.js +54 -0
  53. package/src/scripts/js/utils/MEIOperations.js +22 -22
  54. package/src/scripts/js/utils/Mouse2SVG.js +13 -6
  55. package/src/scripts/js/utils/SVGEditor.js +2 -2
  56. package/src/scripts/js/utils/VerovioWrapper.js +4 -4
  57. package/src/scripts/js/utils/coordinates.js +26 -2
  58. package/src/scripts/js/.DS_Store +0 -0
  59. package/src/scripts/js/MusicPlayer.js +0 -572
  60. package/src/scripts/js/datastructures/ScoreGraph copy.js +0 -432
  61. package/src/scripts/js/gui/CustomAnnotationDrawer.js +0 -114
  62. package/src/scripts/js/handlers/AnnotationDragHandler.js +0 -113
  63. package/src/scripts/js/handlers/AnnotationLineHandler.js +0 -113
  64. package/src/scripts/js/handlers/ArticulationHandler.js +0 -20
  65. package/src/scripts/js/handlers/HarmonyHandler.js +0 -282
  66. package/src/scripts/js/handlers/InsertModeHandler copy.js +0 -423
  67. package/src/scripts/js/handlers/KeyModeHandler copy.js +0 -407
  68. package/src/scripts/js/handlers/KeyModeHandler_deprecated.js +0 -411
  69. package/src/scripts/js/handlers/NoteDragHandler copy.js +0 -148
  70. package/src/scripts/js/handlers/NoteDragHandler_deprecated.js +0 -150
  71. package/src/scripts/js/handlers/SelectionHandler.js +0 -262
  72. package/src/scripts/js/utils/RectWrapper.js +0 -10
  73. package/src/scripts/js/utils/SVGFiller.js +0 -245
  74. package/src/scripts/js/utils/VerovioWrapperLocal.js +0 -156
  75. package/src/scripts/js/utils/firefoxBBoxes.js +0 -143
  76. package/src/styles/vibe.css +0 -785
  77. /package/src/scripts/js/{constants.js → scripts/constants.js} +0 -0
  78. /package/src/scripts/js/{datastructures → scripts/datastructures}/ScoreGraph.js +0 -0
  79. /package/src/scripts/js/{datastructures → scripts/datastructures}/ScoreGraph_deprecated.js +0 -0
  80. /package/src/scripts/js/{datastructures → scripts/datastructures}/ScoreNode.js +0 -0
  81. /package/src/scripts/js/{gui → scripts/gui}/Label.js +0 -0
  82. /package/src/scripts/js/{handlers → scripts/handlers}/DeleteHandler.js +0 -0
  83. /package/src/scripts/js/{utils → scripts/utils}/DOMCreator.js +0 -0
  84. /package/src/scripts/js/{utils → scripts/utils}/MEIConverter.js +0 -0
  85. /package/src/scripts/js/{utils → scripts/utils}/convenienceQueries.js +0 -0
  86. /package/src/scripts/js/{utils → scripts/utils}/mappings.js +0 -0
  87. /package/src/scripts/js/{utils → scripts/utils}/random.js +0 -0
@@ -0,0 +1,2121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setArticulation = exports.getTimestamp = exports.getElementTimestampById = exports.insertTempo = exports.insertMeter = exports.replaceMeterInScoreDef = exports.insertKey = exports.replaceKeyInScoreDef = exports.insertClef = exports.replaceClefinScoreDef = exports.paste = exports.createTuplet = exports.addLayerForStaff = exports.removeStaff = exports.addStaff = exports.removeMeasure = exports.addMeasure = exports.cleanUp = exports.elementIsOverfilling = exports.changeDur = exports.changeDurationsInLayer = exports.fillLayerWithRests = exports.fillWithRests = exports.disableFeatures = exports.changeMeter = exports.transposeByStep = exports.mergeArticToParent = exports.mergeSectionScoreDefToLayer = exports.adjustAccids = exports.extrapolateMeter = exports.getAbsoluteRatio = exports.connectNotes = exports.addToMEI = exports.getMeterRatioLocal = exports.removeFromMEI = void 0;
4
+ const meiConverter = require("./MEIConverter");
5
+ const random_1 = require("./random");
6
+ const constants_1 = require("../constants");
7
+ const mappings_1 = require("./mappings");
8
+ const mei_template_1 = require("../assets/mei_template");
9
+ const MeasureMatrix_1 = require("../datastructures/MeasureMatrix");
10
+ /**
11
+ * This script is a collection of functions which help to manipulate the underlying MEI, so that it can be renderd anew.
12
+ * E.g.: Add and remove notes, process beams and slurs, cleanup empty elements, etc.
13
+ *
14
+ * Some extract features from the MEI which are not apparent in the MEI itself, like processing ratios and relationsships between notes.
15
+ *
16
+ */
17
+ const countableNoteUnitSelector = ":scope > *[dur]:not([grace])";
18
+ const overfillMeasure = false;
19
+ const siblingNames = ["note", "chord", "clef", "beam"];
20
+ ////// DELETE //////
21
+ /**
22
+ * Remove Elements from MEI.
23
+ * Some Elements (such as accid...) could are not represeented as elements in the current MEI.
24
+ * These have to be found in the parent element which have these as an attribute.
25
+ * @param scoreElements Array of Elements which are marked in the SVG Representation (notes, chords, slur, tie, accid etc..)
26
+ * @param currentMEI
27
+ * @returns
28
+ */
29
+ function removeFromMEI(scoreElements, currentMEI) {
30
+ return new Promise((resolve) => {
31
+ scoreElements.forEach(se => {
32
+ var _a;
33
+ if (currentMEI.getElementById(se === null || se === void 0 ? void 0 : se.id) !== null) { // this only applies for <note> and <rest>
34
+ //do not remove completely, replace with rest
35
+ //currentMEI.getElementById(note.id).remove()
36
+ if (["note", "chord"].some(s => se.classList.contains(s))) {
37
+ replaceWithRest(se, currentMEI);
38
+ }
39
+ else {
40
+ currentMEI.getElementById(se.id).remove(); // possibility to remove rests entirely
41
+ }
42
+ // remove all tie/ harms etc when startid is no more a note or is deleted
43
+ //currentMEI.querySelectorAll("tie").forEach(t => {
44
+ currentMEI.querySelectorAll("[startid]").forEach(t => {
45
+ var _a;
46
+ if (t.getAttribute("startid").includes(se.id)) {
47
+ if (currentMEI.getElementById(se.id) === null) {
48
+ (_a = currentMEI.querySelector(t.getAttribute("endid"))) === null || _a === void 0 ? void 0 : _a.remove();
49
+ }
50
+ t.remove();
51
+ }
52
+ });
53
+ }
54
+ else {
55
+ //may be some of the following: accid
56
+ var closestNote = currentMEI.getElementById((_a = se === null || se === void 0 ? void 0 : se.closest(".note")) === null || _a === void 0 ? void 0 : _a.id);
57
+ if (closestNote !== null) {
58
+ //console.log("removing ", se)
59
+ var attrName = se.classList.item(0).toLowerCase();
60
+ closestNote.removeAttribute(attrName);
61
+ if (attrName === "accid") {
62
+ closestNote.removeAttribute("accid.ges");
63
+ }
64
+ }
65
+ else if (se.closest(".tuplet") !== null) {
66
+ var tuplet = currentMEI.querySelector("#" + se.closest(".tuplet").id);
67
+ if (tuplet !== null)
68
+ tuplet.outerHTML = tuplet.innerHTML; //the element could be gone in current mei, but se is still present
69
+ }
70
+ }
71
+ });
72
+ cleanUp(currentMEI);
73
+ currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
74
+ resolve(currentMEI);
75
+ });
76
+ }
77
+ exports.removeFromMEI = removeFromMEI;
78
+ function checkDeleteShifts(currentMEI) {
79
+ var meterRatio = getMeterRatioGlobal(currentMEI);
80
+ var shifters = new Array;
81
+ var elements = currentMEI.getElementsByTagName("layer");
82
+ Array.from(elements).forEach(layer => {
83
+ var actualMeterFill = getAbsoluteRatio(layer);
84
+ var layerLevel = layer.getAttribute("n");
85
+ var staffLevel = layer.closest("staff").getAttribute("n");
86
+ var nextSibling = layer.closest("measure").nextElementSibling;
87
+ if (actualMeterFill < meterRatio && nextSibling !== null) {
88
+ let hasStaff = nextSibling.querySelector("staff[n$='" + staffLevel + "'") !== null ? true : false;
89
+ let hasLayer = nextSibling.querySelector("layer[n$='" + layerLevel + "'") !== null ? true : false;
90
+ if (hasStaff && hasLayer) {
91
+ nextSibling = nextSibling.querySelector("staff[n$='" + staffLevel + "'").querySelector("layer[n$='" + layerLevel + "'");
92
+ Array.from(nextSibling.querySelectorAll(countableNoteUnitSelector)).forEach(node => {
93
+ if (actualMeterFill < meterRatio) {
94
+ shifters.push(node);
95
+ }
96
+ actualMeterFill += 1 / parseInt(node.getAttribute("dur"));
97
+ });
98
+ }
99
+ }
100
+ if (shifters.length > 0) {
101
+ doShiftLeft(shifters, meterRatio);
102
+ shifters.length = 0;
103
+ checkDeleteShifts(currentMEI);
104
+ }
105
+ });
106
+ }
107
+ function getMeterRatioGlobal(currentMEI) {
108
+ var staffDef = currentMEI.getElementsByTagName("staffDef").item(0);
109
+ var meterRatio = null;
110
+ //Do I know the meter?
111
+ if (staffDef.getAttribute(constants_1.constants._METERCOUNT_) !== null && staffDef.getAttribute(constants_1.constants._METERUNIT_) !== null) {
112
+ meterRatio = parseInt(staffDef.getAttribute(constants_1.constants._METERCOUNT_)) / parseInt(staffDef.getAttribute(constants_1.constants._METERUNIT_));
113
+ }
114
+ else {
115
+ meterRatio = extrapolateMeter(currentMEI);
116
+ }
117
+ return meterRatio;
118
+ }
119
+ /**
120
+ *
121
+ * @param currentMEI
122
+ * @param refElement Must be a staff-Element at most
123
+ */
124
+ function getMeterRatioLocal(currentMEI, refElement) {
125
+ var _a, _b;
126
+ var staffElement;
127
+ if (refElement.tagName !== "staff") {
128
+ if (refElement.closest("staff") === null) {
129
+ staffElement = ((_a = currentMEI.getElementById(refElement.id)) === null || _a === void 0 ? void 0 : _a.closest("staff")) || ((_b = currentMEI.getElementById(refElement.parentElement.id)) === null || _b === void 0 ? void 0 : _b.closest("staff"));
130
+ }
131
+ else {
132
+ staffElement = refElement.closest("staff");
133
+ }
134
+ }
135
+ else {
136
+ staffElement = refElement;
137
+ }
138
+ if (staffElement === null || staffElement == undefined) {
139
+ throw new Error("RefElement must be a staff-Element at most");
140
+ }
141
+ var mm = new MeasureMatrix_1.default();
142
+ mm.populateFromMEI(currentMEI);
143
+ var measureIdx = staffElement.closest("measure").getAttribute("n");
144
+ var staffIdx = staffElement.getAttribute("n");
145
+ var mmStaff = mm.get(measureIdx, staffIdx);
146
+ return parseInt(mmStaff.meterSig.count) / parseInt(mmStaff.meterSig.unit);
147
+ }
148
+ exports.getMeterRatioLocal = getMeterRatioLocal;
149
+ //////// INSERT //////////
150
+ /**
151
+ * Insert given sound event into MEI
152
+ * @param newSound NewNote or NewChord to be inserted
153
+ * @param currentMEI MEI as Document
154
+ * @param replace Switching to replaceMode (default: False)
155
+ * @param scoreGraph
156
+ * @returns mei
157
+ */
158
+ function addToMEI(newSound, currentMEI, replace = false, scoreGraph = null) {
159
+ var _a;
160
+ //return new Promise<Document>((resolve): void => {
161
+ var currMeiClone = currentMEI.cloneNode(true);
162
+ var newElem;
163
+ var nearestNoteIsSameDurRest = false;
164
+ if (newSound.hasOwnProperty("pname")) {
165
+ if (!newSound.nearestNoteId)
166
+ return currentMEI;
167
+ var newNote = newSound;
168
+ if (newNote.rest) {
169
+ newElem = currentMEI.createElement("rest");
170
+ }
171
+ else {
172
+ newElem = currentMEI.createElement("note");
173
+ newElem.setAttribute("pname", newNote.pname);
174
+ newElem.setAttribute("oct", newNote.oct);
175
+ if (newNote.accid) {
176
+ newElem.setAttribute("accid.ges", newNote.accid);
177
+ }
178
+ if (newNote.artic) {
179
+ newElem.setAttribute("artic", newNote.artic);
180
+ }
181
+ }
182
+ newElem.setAttribute("dur", newNote.dur);
183
+ if (newNote.dots) {
184
+ newElem.setAttribute("dots", newNote.dots);
185
+ }
186
+ if (newNote.id) {
187
+ newElem.setAttribute("id", newNote.id);
188
+ }
189
+ //break up an cache beams for easier processing
190
+ //later all the beams will be reastablished
191
+ var beams = new Array();
192
+ currentMEI.querySelector("#" + newNote.nearestNoteId).closest("layer").querySelectorAll("beam").forEach(b => {
193
+ beams.push(b.cloneNode(true));
194
+ Array.from(b.children).forEach(e => {
195
+ b.parentElement.insertBefore(e, b);
196
+ });
197
+ b.remove();
198
+ });
199
+ //Do sthm with chords
200
+ if (newNote.chordElement != undefined && !newNote.rest) {
201
+ var chord;
202
+ var meiChordEl = currentMEI.getElementById(newNote.chordElement.id);
203
+ if (newNote.chordElement.classList.contains("chord") || newNote.chordElement.tagName === "chord") {
204
+ chord = meiChordEl;
205
+ chord.appendChild(newElem);
206
+ }
207
+ else {
208
+ chord = document.createElement("chord");
209
+ chord.setAttribute("id", random_1.uuidv4());
210
+ chord.setAttribute("dur", meiChordEl.getAttribute("dur"));
211
+ if (meiChordEl.getAttribute("dots") !== null) {
212
+ chord.setAttribute("dots", meiChordEl.getAttribute("dots"));
213
+ }
214
+ chord.appendChild(newElem);
215
+ meiChordEl.parentElement.insertBefore(chord, meiChordEl);
216
+ chord.appendChild(meiChordEl);
217
+ }
218
+ chord.childNodes.forEach((n) => {
219
+ n.removeAttribute("dur");
220
+ n.removeAttribute("dots");
221
+ });
222
+ // check for existing ties within the chord and make one for the new element as well
223
+ currentMEI.querySelectorAll("tie").forEach(t => {
224
+ if (Array.from(chord.querySelectorAll("note")).some(n => t.getAttribute("startid") === "#" + n.id)) {
225
+ if (!currentMEI.querySelector("tie[startid='#" + newNote.id + "']")) { // just make the tie once (since this can be called twice in recursion)
226
+ var addToChord = Object.assign({}, newNote);
227
+ addToChord.id = random_1.uuidv4();
228
+ var tieEnd = currentMEI.querySelector(t.getAttribute("endid"));
229
+ tieEnd = tieEnd.closest("chord") || tieEnd;
230
+ addToChord.chordElement = tieEnd;
231
+ addToMEI(addToChord, currentMEI, replace);
232
+ connectNotes(currentMEI.querySelector("#" + newNote.id), currentMEI.querySelector("#" + addToChord.id), "tie");
233
+ }
234
+ }
235
+ });
236
+ }
237
+ else if (newNote.nearestNoteId !== null) {
238
+ var sibling = currentMEI.getElementById(newNote.nearestNoteId);
239
+ if (sibling === null)
240
+ return;
241
+ nearestNoteIsSameDurRest = sibling.tagName === "rest" && sibling.getAttribute("dur") === newElem.getAttribute("dur") && sibling.getAttribute("dots") === newElem.getAttribute("dots");
242
+ //special rule for first element in layer
243
+ if (sibling.tagName === "layer") {
244
+ if (scoreGraph !== null) {
245
+ sibling = (_a = currentMEI.getElementById(scoreGraph.getCurrentNode().getRight().getId())) === null || _a === void 0 ? void 0 : _a.parentElement;
246
+ }
247
+ var firstChild = sibling.firstChild;
248
+ sibling.insertBefore(newElem, firstChild);
249
+ if (replace) {
250
+ changeDurationsInLayer(currentMEI, [firstChild], newElem);
251
+ }
252
+ }
253
+ else {
254
+ var parentLayer = sibling.closest("layer");
255
+ var trueParent = sibling.parentElement;
256
+ var isTrueSibling = parentLayer == trueParent;
257
+ var trueSibling = sibling;
258
+ if (!isTrueSibling) {
259
+ var currParent = trueParent;
260
+ while (!isTrueSibling) {
261
+ trueSibling = currParent;
262
+ currParent = currParent === null || currParent === void 0 ? void 0 : currParent.parentElement;
263
+ isTrueSibling = currParent.tagName === "layer";
264
+ }
265
+ }
266
+ if (replace) {
267
+ let ms = Array.from(trueSibling.parentElement.querySelectorAll("note:not(chord note), chord, rest, mRest"));
268
+ if (newNote.relPosX === "left") {
269
+ var measureSiblings = ms.filter((_, i) => i >= ms.indexOf(trueSibling));
270
+ trueSibling.parentElement.insertBefore(newElem, trueSibling);
271
+ changeDurationsInLayer(currentMEI, measureSiblings, newElem);
272
+ }
273
+ else {
274
+ if (["clef"].every(el => { var _a; return ((_a = trueSibling.nextElementSibling) === null || _a === void 0 ? void 0 : _a.tagName) !== el; }) && trueSibling.nextElementSibling !== null) {
275
+ var measureSiblings = ms.filter((_, i) => i >= ms.indexOf(trueSibling.nextElementSibling));
276
+ trueSibling.parentElement.insertBefore(newElem, trueSibling.nextElementSibling);
277
+ changeDurationsInLayer(currentMEI, measureSiblings, newElem);
278
+ }
279
+ else {
280
+ trueSibling.parentElement.insertBefore(newElem, trueSibling.nextElementSibling);
281
+ }
282
+ }
283
+ }
284
+ else {
285
+ if (newNote.relPosX === "left") {
286
+ trueSibling.parentElement.insertBefore(newElem, trueSibling);
287
+ }
288
+ else {
289
+ trueSibling.parentElement.insertBefore(newElem, trueSibling.nextElementSibling);
290
+ }
291
+ }
292
+ }
293
+ }
294
+ else {
295
+ currentMEI.getElementById(newNote.staffId).querySelector("layer").appendChild(newElem);
296
+ }
297
+ }
298
+ else { // is newChord
299
+ //TODO
300
+ var newChord = newSound;
301
+ newElem = convertToElement(newChord, currentMEI);
302
+ var nearestElem = currentMEI.getElementById(newChord.nearestNoteId);
303
+ // if (nearestElem?.tagName === "layer") {
304
+ // nearestElem.insertBefore(newElem, nearestElem.firstChild)
305
+ var sibling = currentMEI.getElementById(newChord.nearestNoteId);
306
+ if (!sibling)
307
+ return;
308
+ var parentLayer = sibling.closest("layer");
309
+ var trueParent = sibling.parentElement;
310
+ var isTrueSibling = parentLayer == trueParent;
311
+ var trueSibling = sibling;
312
+ if (!isTrueSibling) {
313
+ var currParent = trueParent;
314
+ while (!isTrueSibling) {
315
+ trueSibling = currParent;
316
+ currParent = currParent === null || currParent === void 0 ? void 0 : currParent.parentElement;
317
+ isTrueSibling = currParent.tagName === "layer";
318
+ }
319
+ }
320
+ if (replace) {
321
+ let ms = Array.from(trueSibling.parentElement.querySelectorAll("note:not(chord note), chord, rest, mRest"));
322
+ if (newChord.relPosX === "left") {
323
+ var measureSiblings = ms.filter((_, i) => i >= ms.indexOf(trueSibling));
324
+ trueSibling.parentElement.insertBefore(newElem, trueSibling);
325
+ changeDurationsInLayer(currentMEI, measureSiblings, newElem);
326
+ }
327
+ else {
328
+ if (["clef"].every(el => { var _a; return ((_a = trueSibling.nextElementSibling) === null || _a === void 0 ? void 0 : _a.tagName) !== el; }) && trueSibling.nextElementSibling !== null) {
329
+ var measureSiblings = ms.filter((_, i) => i >= ms.indexOf(trueSibling.nextElementSibling));
330
+ trueSibling.parentElement.insertBefore(newElem, trueSibling.nextElementSibling);
331
+ changeDurationsInLayer(currentMEI, measureSiblings, newElem);
332
+ }
333
+ else {
334
+ trueSibling.parentElement.insertBefore(newElem, trueSibling.nextElementSibling);
335
+ }
336
+ }
337
+ }
338
+ else if (newChord.relPosX === "left") {
339
+ nearestElem.parentElement.insertBefore(newElem, currentMEI.getElementById(newChord.nearestNoteId));
340
+ }
341
+ else {
342
+ nearestElem.parentElement.insertBefore(newElem, currentMEI.getElementById(newChord.nearestNoteId).nextSibling);
343
+ }
344
+ }
345
+ // if measure overfills tie note to next measure
346
+ var currentLayer = newElem.closest("layer");
347
+ currentMEI = tieToNextMeasure(currentMEI, newElem, currMeiClone, replace, newSound);
348
+ newElem = currentMEI.querySelector("#" + newElem.id);
349
+ fillLayerWithRests(currentLayer, currentMEI);
350
+ //reestablish beams
351
+ beams === null || beams === void 0 ? void 0 : beams.forEach(b => {
352
+ var first;
353
+ var last;
354
+ Array.from(b.children).forEach(bc => {
355
+ var existingChild = currentMEI.querySelector("#" + bc.id);
356
+ if (existingChild !== null) {
357
+ if (first == undefined) {
358
+ first = bc;
359
+ }
360
+ else {
361
+ last = bc;
362
+ }
363
+ }
364
+ else if (!first) {
365
+ first = newElem;
366
+ }
367
+ });
368
+ if (!last) {
369
+ last = newElem;
370
+ }
371
+ if (first && last) {
372
+ var newBeam = currentMEI.createElement("beam");
373
+ newBeam.id = b.id;
374
+ var beamElements = currentMEI.querySelectorAll("#" + first.id + ", #" + first.id + "~ *:not(#" + last.id + "~ *)"); // all elements in between first and last
375
+ beamElements[0].insertAdjacentElement("beforebegin", newBeam);
376
+ beamElements.forEach(be => {
377
+ newBeam.append(be);
378
+ });
379
+ }
380
+ });
381
+ cleanUp(currentMEI);
382
+ adjustAccids(currentMEI);
383
+ currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
384
+ return currentMEI;
385
+ }
386
+ exports.addToMEI = addToMEI;
387
+ /**
388
+ *
389
+ * @param currentMEI MEI to be changed
390
+ * @param newElem Element to tie to next measure
391
+ * @param currMeiClone clone of the original MEI to compute ratios before anything was changed
392
+ * @param replace replace flag, if addToMEI must be called again
393
+ * @param newSound change parameters of newSound if it has to be shifted to the right
394
+ */
395
+ function tieToNextMeasure(currentMEI, newElem, currMeiClone, replace, newSound) {
396
+ var _a;
397
+ var currentLayer = newElem.closest("layer");
398
+ if (!overfillMeasure) {
399
+ var newMeasureRatio = getAbsoluteRatio(newElem.closest("layer"));
400
+ var measureRatio = getMeterRatioLocal(currMeiClone, newElem);
401
+ if (newMeasureRatio > measureRatio) {
402
+ // Decide if next measure should be created and what should be added
403
+ var lastElement = Array.from(currentLayer.querySelectorAll(":scope > :is(note, chord)")).reverse()[0];
404
+ var lastElementRatio = getAbsoluteRatio(lastElement);
405
+ var measureOverhead = newMeasureRatio - measureRatio;
406
+ var newRatio = lastElementRatio - measureOverhead;
407
+ if (newRatio > 0) {
408
+ changeDur(lastElement, newRatio);
409
+ // create new Element and if needed, new measure
410
+ var splittedElement = lastElement.tagName === "note" ? convertToNewNote(lastElement) : convertToNewChord(lastElement);
411
+ splittedElement.id = random_1.uuidv4();
412
+ var newDur = ratioToDur(measureOverhead);
413
+ splittedElement.dur = newDur[0].toString();
414
+ splittedElement.dots = newDur[1].toString();
415
+ if (currentLayer.closest("measure") === currentLayer.closest("section").lastElementChild) {
416
+ addMeasure(currentMEI);
417
+ }
418
+ var newLayer = currentMEI.querySelector("measure[n='" + (parseInt(currentLayer.closest("measure").getAttribute("n")) + 1).toString() + "'] layer[n='" + currentLayer.getAttribute("n") + "']");
419
+ splittedElement.nearestNoteId = newLayer.id;
420
+ splittedElement.staffId = newLayer.closest("staff").id;
421
+ splittedElement.relPosX = "left";
422
+ addToMEI(splittedElement, currentMEI, replace);
423
+ connectNotes(lastElement, currentMEI.querySelector("#" + splittedElement.id), "tie");
424
+ }
425
+ else if (newRatio == 0 && measureOverhead > 0) { // if the measure is perfectly fine and there should be no tie, modify the newsound (push it one position to the right) and compute again
426
+ currentMEI = currMeiClone;
427
+ if (currentLayer.closest("measure").nextElementSibling === null) { // add measure if there is none to extend into
428
+ addMeasure(currentMEI);
429
+ }
430
+ if (newSound) {
431
+ var cl = currentMEI.getElementById(currentLayer.id);
432
+ newSound.nearestNoteId = (_a = cl.closest("measure").nextElementSibling.querySelector("staff[n='" + cl.closest("staff").getAttribute("n") + "'] layer[n='" + cl.getAttribute("n") + "'] :is(chord, note, rest, mRest)")) === null || _a === void 0 ? void 0 : _a.id;
433
+ newSound.relPosX = "left";
434
+ if (newSound.nearestNoteId !== null) {
435
+ currentMEI = addToMEI(newSound, currentMEI, replace);
436
+ }
437
+ }
438
+ }
439
+ }
440
+ }
441
+ return currentMEI;
442
+ }
443
+ /**
444
+ * Check if notes have to be shifted after insertion
445
+ * @param currentMEI
446
+ */
447
+ function checkInsertShifts(currentMEI) {
448
+ var staffDef = currentMEI.getElementsByTagName("staffDef").item(0);
449
+ var meterRatio = parseInt(staffDef.getAttribute(constants_1.constants._METERCOUNT_)) / parseInt(staffDef.getAttribute(constants_1.constants._METERUNIT_));
450
+ if (staffDef.getAttribute(constants_1.constants._METERCOUNT_) !== null && staffDef.getAttribute(constants_1.constants._METERUNIT_) !== null) {
451
+ meterRatio = parseInt(staffDef.getAttribute(constants_1.constants._METERCOUNT_)) / parseInt(staffDef.getAttribute(constants_1.constants._METERUNIT_));
452
+ }
453
+ else {
454
+ meterRatio = extrapolateMeter(currentMEI);
455
+ }
456
+ var shifters = new Array;
457
+ var elements = currentMEI.getElementsByTagName("layer");
458
+ Array.from(elements).forEach(layer => {
459
+ var i = 0;
460
+ var layerChildern = layer.querySelectorAll(countableNoteUnitSelector);
461
+ Array.from(layerChildern).forEach(node => {
462
+ i += getAbsoluteRatio(node); //1/parseInt(node.getAttribute("dur"))
463
+ if (i > meterRatio) {
464
+ shifters.push(node);
465
+ }
466
+ });
467
+ if (shifters.length > 0) {
468
+ doShiftRight(shifters, meterRatio, layer);
469
+ shifters.length = 0;
470
+ checkInsertShifts(currentMEI);
471
+ }
472
+ });
473
+ }
474
+ /**
475
+ * Shift all Elements to the right (according to measure borders)
476
+ * @param arr Array of Elements to be shifted
477
+ * @param meterRatio
478
+ * @param currentLayer
479
+ */
480
+ function doShiftRight(arr, meterRatio, currentLayer) {
481
+ arr.forEach((element, elementIdx) => {
482
+ var parentMeasure = element.closest("measure");
483
+ var parentMeasureSibling = null;
484
+ parentMeasureSibling = parentMeasure.nextElementSibling;
485
+ if (parentMeasureSibling === null) {
486
+ parentMeasureSibling = parentMeasure.parentElement.appendChild(createEmptyCopy(parentMeasure));
487
+ }
488
+ var layerLevel = element.closest("layer").getAttribute("n");
489
+ var staffLevel = element.closest("staff").getAttribute("n");
490
+ var targetStaff = parentMeasureSibling.querySelector("staff[n$='" + staffLevel + "'");
491
+ var targetLayer;
492
+ if (targetStaff.querySelector("layer[n$='" + layerLevel + "'") !== null) {
493
+ targetLayer = targetStaff.querySelector("layer[n$='" + layerLevel + "'");
494
+ }
495
+ else {
496
+ targetLayer = document.createElement("layer");
497
+ targetLayer.setAttribute("id", "layer-" + random_1.uuidv4());
498
+ targetLayer.setAttribute("n", layerLevel);
499
+ targetStaff.appendChild(targetLayer);
500
+ }
501
+ var absLayerRatio = getAbsoluteRatio(currentLayer);
502
+ var elementRatio = getAbsoluteRatio(element);
503
+ var chunkDurRight = absLayerRatio - meterRatio;
504
+ var chunkDurLeft = elementRatio - chunkDurRight;
505
+ if (chunkDurRight > elementRatio) {
506
+ chunkDurRight = elementRatio;
507
+ chunkDurLeft = 0;
508
+ }
509
+ //check if note must be split
510
+ if ((absLayerRatio + elementRatio) > meterRatio && chunkDurRight * chunkDurLeft !== 0) {
511
+ //check for dots
512
+ if (Number.isInteger(1 / chunkDurLeft) && Number.isInteger(1 / chunkDurRight)) {
513
+ element.removeAttribute("dots");
514
+ var splitRightElement = element.cloneNode(true);
515
+ splitRightElement.setAttribute("id", random_1.uuidv4());
516
+ splitRightElement.setAttribute("dur", (Math.abs(1 / chunkDurRight)).toString());
517
+ var beforeElement = elementIdx === 0 ? targetLayer.firstChild : targetLayer.children.item(elementIdx);
518
+ targetLayer.insertBefore(splitRightElement, beforeElement);
519
+ //change already existing element
520
+ element.setAttribute("dur", (Math.abs(1 / chunkDurLeft)).toString());
521
+ }
522
+ else {
523
+ var dottedElements = splitDottedNote(element, chunkDurLeft, chunkDurRight);
524
+ dottedElements.left.forEach(lel => currentLayer.appendChild(lel));
525
+ var beforeElement = elementIdx === 0 ? targetLayer.firstChild : targetLayer.children.item(elementIdx);
526
+ dottedElements.right.forEach(rel => {
527
+ rel.setAttribute("id", random_1.uuidv4());
528
+ if (rel.tagName === "chord") {
529
+ rel.querySelectorAll("note").forEach(rl => {
530
+ rl.setAttribute("id", random_1.uuidv4());
531
+ });
532
+ }
533
+ targetLayer.insertBefore(rel, beforeElement);
534
+ });
535
+ element.remove();
536
+ }
537
+ }
538
+ else {
539
+ var beforeElement = elementIdx === 0 ? targetLayer.firstChild : targetLayer.children.item(elementIdx);
540
+ targetLayer.insertBefore(element, beforeElement);
541
+ }
542
+ });
543
+ }
544
+ function createEmptyCopy(element) {
545
+ let copy = element.cloneNode(true);
546
+ let childrenToDelete = Array.from(copy.querySelectorAll("layer > *, measure > slur"));
547
+ childrenToDelete.forEach(child => {
548
+ child.parentNode.removeChild(child);
549
+ });
550
+ //set new ids for everything
551
+ copy.setAttribute("id", random_1.uuidv4());
552
+ copy.setAttribute("n", (parseInt(element.getAttribute("n")) + 1).toString());
553
+ let allElements = copy.querySelectorAll("*");
554
+ allElements.forEach(e => e.setAttribute("id", random_1.uuidv4()));
555
+ return copy;
556
+ }
557
+ function connectNotes(left, right, connectionShape) {
558
+ var leftpname = left.getAttribute("pname");
559
+ var leftoct = left.getAttribute("oct");
560
+ var leftAccid = left.getAttribute("accid") || left.getAttribute("accid.ges");
561
+ var rightpname = right.getAttribute("pname");
562
+ var rightoct = right.getAttribute("oct");
563
+ var rightAccid = right.getAttribute("accid") || right.getAttribute("accid.ges");
564
+ if (!(leftpname === rightpname && leftoct === rightoct && leftAccid === rightAccid)) {
565
+ connectionShape = "slur";
566
+ }
567
+ var currentMEI = left.getRootNode();
568
+ var connections = currentMEI.querySelectorAll("tie, slur");
569
+ var deleted = false;
570
+ connections.forEach(c => {
571
+ var sid = c.getAttribute("startid").replace("#", "");
572
+ var eid = c.getAttribute("endid").replace("#", "");
573
+ if (sid === left.id && eid === right.id) {
574
+ c.remove();
575
+ deleted = true;
576
+ }
577
+ });
578
+ if (!deleted) {
579
+ var tieElement = currentMEI.createElementNS(constants_1.constants._MEINS_, connectionShape);
580
+ tieElement.setAttribute("startid", "#" + left.id);
581
+ tieElement.setAttribute("endid", "#" + right.id);
582
+ tieElement.setAttribute("id", random_1.uuidv4());
583
+ currentMEI.getElementById(left.id).closest("measure").append(tieElement);
584
+ }
585
+ }
586
+ exports.connectNotes = connectNotes;
587
+ ///// GENERAL OPERATIONS /////
588
+ /**
589
+ * Calculates the length of an element, e.g., a chord, a note, ...
590
+ * @param el The element
591
+ * @returns The length of the element, NaN for mRests
592
+ */
593
+ function getAbsoluteRatio(el) {
594
+ var i = 0;
595
+ var arr;
596
+ if (el === null) {
597
+ return 0;
598
+ }
599
+ if (el.tagName !== "layer") { //if single Element is given, eg. chord, note
600
+ arr = [el];
601
+ }
602
+ else {
603
+ arr = Array.from(el.querySelectorAll(countableNoteUnitSelector));
604
+ }
605
+ arr.forEach(node => {
606
+ i += 1 / parseInt(node.getAttribute("dur"));
607
+ let baseDur = parseInt(node.getAttribute("dur"));
608
+ if (node.getAttribute("dots") !== null) {
609
+ let dots = parseInt(node.getAttribute("dots"));
610
+ i += dots == 0 ? 0 : (dots * 2 - 1) / (baseDur * 2 * dots);
611
+ }
612
+ });
613
+ return i;
614
+ }
615
+ exports.getAbsoluteRatio = getAbsoluteRatio;
616
+ function ratioToDur(ratio) {
617
+ var dur;
618
+ var dots = 0;
619
+ //1. next smallest ratio of basedur
620
+ var basedur = 1;
621
+ while (basedur > ratio) {
622
+ basedur = basedur / 2;
623
+ }
624
+ dur = 1 / basedur;
625
+ ratio -= basedur;
626
+ if (ratio > 0) {
627
+ if (ratio > dur / 2) {
628
+ dots = 2;
629
+ }
630
+ else {
631
+ dots = 1;
632
+ }
633
+ }
634
+ return [dur, dots];
635
+ }
636
+ /**
637
+ * Shift Elements to left (according to measure borders)
638
+ * @param arr Array of Elements to shift
639
+ * @param meterRatio meterRatio of the piece
640
+ */
641
+ function doShiftLeft(arr, meterRatio) {
642
+ arr.forEach(element => {
643
+ var parentMeasure = element.closest("measure");
644
+ var parentMeasureSibling = parentMeasure.previousElementSibling;
645
+ var layerLevel = element.closest("layer").getAttribute("n");
646
+ var targetLayer = parentMeasureSibling.querySelector("layer[n$='" + layerLevel + "'"); // should be <layer>
647
+ var absLayerRatio = getAbsoluteRatio(targetLayer);
648
+ var elementRatio = getAbsoluteRatio(element);
649
+ //check if note must be split
650
+ if ((absLayerRatio + elementRatio) > meterRatio) {
651
+ var chunkDurLeft = meterRatio - absLayerRatio;
652
+ var chunkDurRight = elementRatio - chunkDurLeft;
653
+ //check for dots
654
+ if (Number.isInteger(1 / chunkDurLeft) && Number.isInteger(1 / chunkDurRight)) {
655
+ element.removeAttribute("dots");
656
+ var splitLeftElement = element.cloneNode(true);
657
+ splitLeftElement.setAttribute("id", random_1.uuidv4());
658
+ splitLeftElement.setAttribute("dur", (Math.abs(1 / chunkDurLeft)).toString());
659
+ targetLayer.appendChild(splitLeftElement);
660
+ //change already existing element
661
+ element.setAttribute("dur", (Math.abs(1 / chunkDurRight)).toString());
662
+ }
663
+ else {
664
+ var elements = splitDottedNote(element, chunkDurLeft, chunkDurRight);
665
+ elements.left.forEach(lel => {
666
+ lel.setAttribute("id", random_1.uuidv4());
667
+ if (lel.tagName === "chord") {
668
+ lel.querySelectorAll("note").forEach(ll => {
669
+ ll.setAttribute("id", random_1.uuidv4());
670
+ });
671
+ }
672
+ targetLayer.appendChild(lel);
673
+ });
674
+ elements.right.forEach(rel => element.parentElement.insertBefore(rel, element));
675
+ element.remove();
676
+ }
677
+ }
678
+ else {
679
+ targetLayer.appendChild(element);
680
+ //is current Layer empty and should be deleted? if split occured this should not be the case
681
+ var parentLayer = parentMeasure.querySelector("layer[n$='" + layerLevel + "'"); // should always be <layer>
682
+ // if(parentLayer.childNodes.length === 0){
683
+ // parentMeasure.remove();
684
+ // }
685
+ }
686
+ });
687
+ }
688
+ /**
689
+ * Operations to split dotted notes
690
+ * @param note reference note elements
691
+ * @param chunkLeftDur calculated ratio left
692
+ * @param chunkRightDur calculated ratio right
693
+ * @returns collection of right ans left elements
694
+ */
695
+ function splitDottedNote(note, chunkLeftDur, chunkRightDur) {
696
+ let gcdLeft = gcd(chunkLeftDur);
697
+ let gcdRight = gcd(chunkRightDur);
698
+ let countLeftSubNotes = findDotsRecursive(chunkLeftDur, gcdLeft); //return z.B.: [8, 16]
699
+ let countRightSubNotes = findDotsRecursive(chunkRightDur, gcdRight); //return z.B. [2, 8, 16]
700
+ let newLeftElement = createElementsFromSubNotes(note, countLeftSubNotes);
701
+ let newRightElement = createElementsFromSubNotes(note, countRightSubNotes);
702
+ return { left: newLeftElement, right: newRightElement };
703
+ }
704
+ /**
705
+ * Create actual XML Elements from sequence of dotted notes
706
+ * @param note
707
+ * @param subNoteDurs
708
+ * @returns
709
+ */
710
+ function createElementsFromSubNotes(note, subNoteDurs) {
711
+ let newElements = new Array();
712
+ //find sliceBoundaries in array
713
+ let arraySliceIdx = new Array();
714
+ for (var i = 0; i < subNoteDurs.length; i++) {
715
+ if (i > 0) {
716
+ if (subNoteDurs[i] !== subNoteDurs[i - 1] * 2) {
717
+ arraySliceIdx.push(i);
718
+ }
719
+ }
720
+ }
721
+ //find actual slices
722
+ let durSlices = new Array();
723
+ for (var i = 0; i < arraySliceIdx.length + 1; i++) {
724
+ if (i === 0) {
725
+ durSlices.push(subNoteDurs.slice(0, arraySliceIdx[i]));
726
+ }
727
+ else if (i === arraySliceIdx.length) {
728
+ durSlices.push(subNoteDurs.slice(arraySliceIdx[i - 1]));
729
+ }
730
+ else {
731
+ durSlices.push(subNoteDurs.slice(arraySliceIdx[i - 1], arraySliceIdx[i]));
732
+ }
733
+ }
734
+ //create notes
735
+ let createArr = durSlices.length > 0 ? durSlices : [subNoteDurs];
736
+ createArr.forEach(durs => {
737
+ let newElement = note.cloneNode(true);
738
+ newElement.removeAttribute("dots"); //eventual dots could be in original note value
739
+ newElement.setAttribute("dur", Math.abs(durs[0]).toString());
740
+ let dots = 0;
741
+ durs.forEach((dur, i) => {
742
+ if (i > 0) {
743
+ dots += 1;
744
+ }
745
+ });
746
+ if (dots > 0) {
747
+ newElement.setAttribute("dots", dots.toString());
748
+ }
749
+ newElements.push(newElement);
750
+ });
751
+ return newElements;
752
+ }
753
+ /**
754
+ * Compute greatest integer divisor
755
+ * @param chunkDur Duration of given Chunk
756
+ * @returns
757
+ */
758
+ function gcd(chunkDur) {
759
+ var largestModulo = null;
760
+ var baseValue = 1;
761
+ var mod = 0;
762
+ while (largestModulo === null) {
763
+ mod = chunkDur % baseValue;
764
+ if (mod === 0) {
765
+ largestModulo = baseValue;
766
+ }
767
+ baseValue = baseValue / 2;
768
+ }
769
+ return largestModulo;
770
+ }
771
+ /**
772
+ * Splits duration of given chunk into possible dotted sequences
773
+ * @param chunk
774
+ * @param smallestUnit = greatest integer divisor
775
+ * @returns
776
+ */
777
+ function findDotsRecursive(chunk, smallestUnit) {
778
+ var arr = new Array();
779
+ var sliceChunk = chunk / smallestUnit;
780
+ if (Math.floor(sliceChunk) > 1) {
781
+ arr = arr.concat(findDotsRecursive(chunk, smallestUnit * 2));
782
+ }
783
+ else if (Math.floor(sliceChunk) < 1) {
784
+ arr = arr.concat(findDotsRecursive(chunk, smallestUnit / 2));
785
+ }
786
+ else if (!Number.isInteger(sliceChunk)) {
787
+ arr.push(1 / 1 / smallestUnit);
788
+ arr = arr.concat(findDotsRecursive(chunk - smallestUnit, smallestUnit));
789
+ }
790
+ else {
791
+ arr.push(1 / 1 / smallestUnit);
792
+ }
793
+ return arr; //.sort((a,b) => a-b)
794
+ }
795
+ /**
796
+ * Extrapolates meter, if is not given in scoreDef. Iterates through each staff to get the mostly found ratio
797
+ * @param currentMEI
798
+ * @returns meter ratio
799
+ */
800
+ function extrapolateMeter(currentMEI) {
801
+ var ratioMap = new Map();
802
+ var xmlCopy = currentMEI.cloneNode(true);
803
+ var layers = Array.from(xmlCopy.querySelectorAll("layer"));
804
+ var mostlyUsedRatio = 0;
805
+ layers.forEach(layer => {
806
+ if (layer.childElementCount === 0) {
807
+ return;
808
+ }
809
+ //strip all unnecessary elements: garce notes, beams
810
+ //which do not contribute to count of measure duration
811
+ var beams = Array.from(layer.querySelectorAll("beam"));
812
+ beams.forEach(beam => {
813
+ Array.from(beam.children).forEach(c => {
814
+ beam.parentElement.append(c);
815
+ });
816
+ xmlCopy.getElementById(beam.id).remove();
817
+ });
818
+ var graceNotes = Array.from(layer.querySelectorAll("[grace]"));
819
+ graceNotes.forEach(g => {
820
+ xmlCopy.getElementById(g.id).remove();
821
+ });
822
+ var childElements = Array.from(layer.children);
823
+ var ratio = 0;
824
+ childElements.forEach(element => {
825
+ ratio += getAbsoluteRatio(element);
826
+ });
827
+ if (!ratioMap.has(ratio)) {
828
+ ratioMap.set(ratio, 1);
829
+ }
830
+ else {
831
+ ratioMap.set(ratio, ratioMap.get(ratio) + 1);
832
+ }
833
+ var prevItCount = 0;
834
+ for (const [key, value] of ratioMap.entries()) {
835
+ if (value > prevItCount) {
836
+ prevItCount = value;
837
+ mostlyUsedRatio = key;
838
+ }
839
+ }
840
+ });
841
+ return mostlyUsedRatio;
842
+ }
843
+ exports.extrapolateMeter = extrapolateMeter;
844
+ /**
845
+ * Adjust all accids according to key signature
846
+ * e.g. after changing global Key
847
+ * @param currentMEI
848
+ * @returns
849
+ */
850
+ function adjustAccids(currentMEI) {
851
+ var measureMatrix = new MeasureMatrix_1.default();
852
+ measureMatrix.populateFromMEI(currentMEI);
853
+ var prevAccidMap = new Map(); // key: pname+oct
854
+ var currentLayer = "1";
855
+ var currentStaff = "1";
856
+ var currentMeasure = "1";
857
+ currentMEI.querySelectorAll("note").forEach(note => {
858
+ var layerN = note.closest("layer").getAttribute("n");
859
+ var staffN = note.closest("staff").getAttribute("n");
860
+ var measureN = note.closest("measure").getAttribute("n");
861
+ if (layerN !== currentLayer || staffN !== currentStaff || measureN !== currentMeasure) {
862
+ prevAccidMap = new Map(); // key: pname+oct
863
+ currentLayer = layerN;
864
+ currentStaff = staffN;
865
+ currentMeasure = measureN;
866
+ }
867
+ var sig = measureMatrix.get(measureN, staffN).keysig;
868
+ var sigSymbol = sig.charAt(1);
869
+ var signedNotes = mappings_1.keysigToNotes.get(sig);
870
+ var accid = note.getAttribute("accid") || note.getAttribute("accid.ges");
871
+ accid = accid === "n" || accid === "" ? null : accid;
872
+ // remove all accids so I don't have to look it up several times
873
+ note.removeAttribute("accid");
874
+ note.removeAttribute("accid.ges");
875
+ var pname = note.getAttribute("pname");
876
+ var oct = note.getAttribute("oct");
877
+ var mapKey = pname + oct;
878
+ var noteInKey = signedNotes.some(sn => sn === pname);
879
+ if (accid === null) { // "I have no accid"
880
+ accid = "n";
881
+ if (prevAccidMap.has(mapKey)) { // "does someone before me has any accid?"
882
+ if (prevAccidMap.get(mapKey) === "n") {
883
+ prevAccidMap.delete(mapKey);
884
+ }
885
+ else {
886
+ note.setAttribute("accid", accid);
887
+ prevAccidMap.set(mapKey, accid);
888
+ }
889
+ }
890
+ else if (noteInKey) {
891
+ note.setAttribute("accid", accid);
892
+ prevAccidMap.set(mapKey, accid);
893
+ }
894
+ }
895
+ else { // "I have accid"
896
+ if (prevAccidMap.has(mapKey)) {
897
+ if (prevAccidMap.get(mapKey) === accid) {
898
+ note.setAttribute("accid.ges", accid);
899
+ prevAccidMap.set(mapKey, accid);
900
+ }
901
+ else {
902
+ note.setAttribute("accid", accid);
903
+ prevAccidMap.set(mapKey, accid);
904
+ }
905
+ }
906
+ else {
907
+ if (noteInKey) {
908
+ if (sigSymbol === accid) {
909
+ if (prevAccidMap.has(mapKey)) {
910
+ if (prevAccidMap.get(mapKey) === accid) {
911
+ note.setAttribute("accid.ges", accid);
912
+ prevAccidMap.set(mapKey, accid);
913
+ }
914
+ else {
915
+ note.setAttribute("accid", accid);
916
+ prevAccidMap.set(mapKey, accid);
917
+ }
918
+ }
919
+ else {
920
+ if (sigSymbol === accid) {
921
+ note.setAttribute("accid.ges", accid);
922
+ prevAccidMap.set(mapKey, accid);
923
+ }
924
+ else {
925
+ note.setAttribute("accid", accid);
926
+ prevAccidMap.set(mapKey, accid);
927
+ }
928
+ }
929
+ }
930
+ else {
931
+ note.setAttribute("accid", accid);
932
+ prevAccidMap.set(mapKey, accid);
933
+ }
934
+ }
935
+ else {
936
+ note.setAttribute("accid", accid);
937
+ prevAccidMap.set(mapKey, accid);
938
+ }
939
+ }
940
+ }
941
+ });
942
+ return currentMEI;
943
+ }
944
+ exports.adjustAccids = adjustAccids;
945
+ /**
946
+ * Merge all scoreDefs in sections in the respective layers, e.g. when importing a file.
947
+ * This is important since MeasureMartrix only handles signature elements (keySig, meterSig, clef) in layers
948
+ * @param currentMEI
949
+ */
950
+ function mergeSectionScoreDefToLayer(currentMEI) {
951
+ var mei;
952
+ if (typeof currentMEI === "string") {
953
+ mei = new DOMParser().parseFromString(currentMEI, "text/xml");
954
+ }
955
+ else {
956
+ mei = currentMEI;
957
+ }
958
+ mei.querySelectorAll("section").forEach(sec => {
959
+ var secChildren = sec.querySelectorAll(":scope > *");
960
+ secChildren.forEach((e, i) => {
961
+ if (e.tagName === "scoreDef") {
962
+ secChildren[i + 1].querySelectorAll("layer").forEach(layer => {
963
+ e.querySelectorAll(":scope > *").forEach(sig => {
964
+ var newElem = sig.cloneNode(true);
965
+ layer.prepend(newElem);
966
+ });
967
+ });
968
+ }
969
+ });
970
+ sec.querySelectorAll(":scope > scoreDef").forEach(sd => sd.remove());
971
+ });
972
+ return mei;
973
+ }
974
+ exports.mergeSectionScoreDefToLayer = mergeSectionScoreDefToLayer;
975
+ function mergeArticToParent(currentMEI) {
976
+ currentMEI.querySelectorAll("artic").forEach(a => {
977
+ a.parentElement.setAttribute("artic", a.getAttribute("artic"));
978
+ a.remove();
979
+ });
980
+ return currentMEI;
981
+ }
982
+ exports.mergeArticToParent = mergeArticToParent;
983
+ /**
984
+ * Transpose marked notes according to direcion (up or down)
985
+ * @param currentMEI
986
+ * @param direction
987
+ * @returns
988
+ */
989
+ function transposeByStep(currentMEI, direction) {
990
+ //document.querySelectorAll(".activeContainer #vrvSVG :is(.note.marked, .note.lastAdded)").forEach(nm => {
991
+ document.querySelectorAll(".activeContainer :is(.note.marked, .note.lastAdded)").forEach(nm => {
992
+ if (nm.id === null || nm.id == undefined || nm.id === "")
993
+ return; // make shure that only the id is taken from the verovio svg so that the element will only be effected once
994
+ var id = nm.id;
995
+ var noteMEI = currentMEI.getElementById(id);
996
+ var pname = noteMEI.getAttribute("pname");
997
+ var oct = parseInt(noteMEI.getAttribute("oct"));
998
+ var accid = noteMEI.getAttribute("accid") || noteMEI.getAttribute("accid.ges");
999
+ if (accid === null || typeof accid == "undefined" || accid === "n") {
1000
+ accid = "";
1001
+ }
1002
+ var nextNote;
1003
+ if (direction === "up") {
1004
+ nextNote = mappings_1.nextStepUp.get(pname + accid);
1005
+ }
1006
+ else if (direction === "down") {
1007
+ nextNote = mappings_1.nextStepDown.get(pname + accid);
1008
+ }
1009
+ noteMEI.setAttribute("pname", nextNote.charAt(0));
1010
+ if (nextNote.charAt(1) !== "") {
1011
+ noteMEI.setAttribute("accid", nextNote.charAt(1));
1012
+ }
1013
+ else {
1014
+ noteMEI.removeAttribute("accid");
1015
+ noteMEI.removeAttribute("accid.ges");
1016
+ }
1017
+ //Change Octave
1018
+ if (["c", "cf"].includes(pname + accid) && direction === "down") {
1019
+ noteMEI.setAttribute("oct", (oct - 1).toString());
1020
+ }
1021
+ if (["b", "bs"].includes(pname + accid) && direction === "up") {
1022
+ noteMEI.setAttribute("oct", (oct + 1).toString());
1023
+ }
1024
+ });
1025
+ return adjustAccids(currentMEI);
1026
+ }
1027
+ exports.transposeByStep = transposeByStep;
1028
+ /**
1029
+ * Change Meter according to #timeUnit and #timeCount in side bar option.
1030
+ * @param currentMEI
1031
+ * @returns changed mei; null, if input has no valid values
1032
+ */
1033
+ function changeMeter(currentMEI) {
1034
+ var timeCount = document.querySelector(".activeElement #timeCount");
1035
+ var timeUnit = document.querySelector(".activeElement #timeUnit");
1036
+ //@ts-ignore
1037
+ var timeCountValue = timeCount.value; //getAttribute("value")
1038
+ //@ts-ignore
1039
+ var timeUnitValue = timeUnit.value; //getAttribute("value")
1040
+ if (timeCountValue !== null && timeUnitValue !== null) {
1041
+ timeCountValue = timeCountValue.trim();
1042
+ timeUnitValue = timeUnitValue.trim();
1043
+ if (!isNaN(parseInt(timeCountValue)) && !isNaN(parseInt(timeUnitValue))) {
1044
+ var oldMeterRatio = getMeterRatioGlobal(currentMEI);
1045
+ currentMEI.querySelectorAll("staffDef").forEach(sd => {
1046
+ sd.setAttribute("meter.count", timeCountValue);
1047
+ sd.setAttribute("meter.unit", timeUnitValue);
1048
+ });
1049
+ // adjust noteposition
1050
+ var newMeterRatio = getMeterRatioGlobal(currentMEI);
1051
+ if (oldMeterRatio > newMeterRatio) {
1052
+ checkInsertShifts(currentMEI);
1053
+ }
1054
+ else if (oldMeterRatio < newMeterRatio) {
1055
+ checkDeleteShifts(currentMEI);
1056
+ }
1057
+ if (oldMeterRatio !== newMeterRatio) {
1058
+ return currentMEI;
1059
+ }
1060
+ }
1061
+ }
1062
+ return currentMEI; //null
1063
+ }
1064
+ exports.changeMeter = changeMeter;
1065
+ /**
1066
+ * disable features if necesseray (only supposed to be used for debugging)
1067
+ * @param features Array of TagNames and AttributeNames which have to be disabled (deleted)
1068
+ * @param currentMEI mei
1069
+ * @returns
1070
+ */
1071
+ function disableFeatures(features, currentMEI) {
1072
+ //console.log("Features disabled:", features)
1073
+ features.forEach(f => {
1074
+ var elements = Array.from(currentMEI.getElementsByTagName(f));
1075
+ elements.forEach(e => {
1076
+ let parent = e.parentElement;
1077
+ e.remove();
1078
+ if (parent.childElementCount === 0) {
1079
+ parent.remove();
1080
+ }
1081
+ });
1082
+ elements = Array.from(currentMEI.querySelectorAll("*[" + f + "]"));
1083
+ elements.forEach(e => {
1084
+ let parent = e.parentElement;
1085
+ e.remove();
1086
+ if (parent.childElementCount === 0) {
1087
+ parent.remove();
1088
+ }
1089
+ });
1090
+ });
1091
+ return currentMEI;
1092
+ }
1093
+ exports.disableFeatures = disableFeatures;
1094
+ /**
1095
+ * When a note is shortened, fill old remaining duration with rests
1096
+ * @param newElement
1097
+ * @param oldElement
1098
+ * @param currentMEI
1099
+ */
1100
+ function fillWithRests(newElement, oldElement, currentMEI) {
1101
+ var newRatio = getAbsoluteRatio(newElement);
1102
+ var oldRatio = getAbsoluteRatio(oldElement);
1103
+ if (newRatio < oldRatio) {
1104
+ var remainRatio = oldRatio - newRatio;
1105
+ var smallestUnit = gcd(remainRatio);
1106
+ var restDur = ratioToDur(smallestUnit)[0];
1107
+ var restCount = remainRatio / smallestUnit;
1108
+ newElement.classList.add("changed");
1109
+ for (var i = 0; i < restCount; i++) {
1110
+ var rest = createNewRestElement(restDur);
1111
+ currentMEI.getElementById(newElement.id).parentElement.insertBefore(rest, newElement.nextElementSibling);
1112
+ }
1113
+ }
1114
+ return currentMEI;
1115
+ }
1116
+ exports.fillWithRests = fillWithRests;
1117
+ /**
1118
+ *
1119
+ * @param layer Fills up the layer with an adequate amount of rests
1120
+ * @param currentMEI The MEI document
1121
+ */
1122
+ function fillLayerWithRests(layer, currentMEI) {
1123
+ var targetDur = getMeterRatioLocal(currentMEI, layer);
1124
+ var currentDur = getAbsoluteRatio(layer);
1125
+ let durCandidates = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512];
1126
+ while (currentDur < targetDur) {
1127
+ var toGo = targetDur - currentDur;
1128
+ for (const dur of durCandidates) {
1129
+ var rest = 1. / dur;
1130
+ if (rest <= toGo && currentDur % rest == 0.) {
1131
+ var restEl = createNewRestElement(dur);
1132
+ Array.from(layer.querySelectorAll("note, chord, rest")).reverse()[0].insertAdjacentElement("afterend", restEl);
1133
+ currentDur += rest;
1134
+ break;
1135
+ }
1136
+ }
1137
+ }
1138
+ }
1139
+ exports.fillLayerWithRests = fillLayerWithRests;
1140
+ /**
1141
+ * Fill Empty Space with rest
1142
+ * @deprecated
1143
+ * @param currentMEI
1144
+ */
1145
+ function _fillWithRests(currentMEI) {
1146
+ var staffDef = currentMEI.getElementsByTagName("staffDef").item(0);
1147
+ var meterCount;
1148
+ var meterUnit;
1149
+ var meterRatio;
1150
+ if (staffDef.getAttribute(constants_1.constants._METERCOUNT_) !== null && staffDef.getAttribute(constants_1.constants._METERUNIT_) !== null) {
1151
+ meterCount = staffDef.getAttribute(constants_1.constants._METERCOUNT_);
1152
+ meterUnit = staffDef.getAttribute(constants_1.constants._METERUNIT_);
1153
+ meterRatio = parseInt(meterCount) / parseInt(meterUnit);
1154
+ }
1155
+ else {
1156
+ var meterRatio = getMeterRatioGlobal(currentMEI);
1157
+ meterCount = (meterRatio * 4).toString();
1158
+ meterUnit = "4";
1159
+ }
1160
+ currentMEI.querySelectorAll("measure").forEach(m => {
1161
+ m.querySelectorAll("staff").forEach(s => {
1162
+ s.querySelectorAll("layer").forEach((l, idx) => {
1163
+ //mRest for empty Layer
1164
+ if (l.childElementCount === 0) {
1165
+ if (idx === 0) {
1166
+ var restEl = document.createElementNS(constants_1.constants._MEINS_, "mRest");
1167
+ l.appendChild(restEl);
1168
+ }
1169
+ else { // remove 1+ empty layer
1170
+ l.remove();
1171
+ }
1172
+ }
1173
+ else {
1174
+ var actualMeterFill = getAbsoluteRatio(l);
1175
+ var ratioDiff = Math.abs(actualMeterFill - meterRatio);
1176
+ var smallestValue = gcd(ratioDiff);
1177
+ //var restDurs = findDotsRecursive(ratioDiff, gcd(ratioDiff))
1178
+ if (Number.isInteger(ratioDiff / smallestValue) && ratioDiff > 0) {
1179
+ var leftRatio = ratioDiff;
1180
+ var durArr = new Array();
1181
+ while (!Number.isInteger(1 / leftRatio)) {
1182
+ var leftRatio = ratioDiff - smallestValue;
1183
+ durArr.push(1 / smallestValue);
1184
+ }
1185
+ durArr.push(1 / leftRatio);
1186
+ durArr = durArr.reverse();
1187
+ durArr.forEach(dur => {
1188
+ var newRest = currentMEI.createElementNS(constants_1.constants._MEINS_, "rest");
1189
+ newRest.setAttribute("dur", dur.toString());
1190
+ l.appendChild(newRest);
1191
+ });
1192
+ }
1193
+ //console.log(document.getElementById(l.id), ratioDiff, gcd(ratioDiff), durArr)
1194
+ }
1195
+ });
1196
+ });
1197
+ });
1198
+ }
1199
+ /**
1200
+ * Replace given id with rest
1201
+ * @param element element from svg
1202
+ * @param currentMEI
1203
+ */
1204
+ function replaceWithRest(element, currentMEI) {
1205
+ var elmei = currentMEI.getElementById(element.id);
1206
+ //var closestChord: Element = currentMEI .getElementById(element.id).closest("chord")
1207
+ //if(closestChord !== null){elmei = closestChord}
1208
+ var dur = elmei.getAttribute("dur");
1209
+ var dots = elmei.getAttribute("dots");
1210
+ var newRest = currentMEI.createElementNS(constants_1.constants._MEINS_, "rest");
1211
+ newRest.setAttribute("dur", dur);
1212
+ if (dots !== null) {
1213
+ newRest.setAttribute("dots", dots);
1214
+ }
1215
+ elmei.parentElement.insertBefore(newRest, elmei);
1216
+ elmei.remove();
1217
+ }
1218
+ function addRatios(elements) {
1219
+ var r = 0;
1220
+ elements.forEach((v, i) => {
1221
+ //if(i > 0){
1222
+ r += getAbsoluteRatio(v);
1223
+ //}
1224
+ });
1225
+ return r;
1226
+ }
1227
+ /**
1228
+ *
1229
+ * @param currentMEI current used MEI to be changed
1230
+ * @param additionalElements elements which duration has to be changed which occur after the refElement in the same layer
1231
+ * @param refElement actual current Element which will be changed in duration
1232
+ * @param remainRatio remaining ratio of the current layer
1233
+ * @param meiToReset MEI which can be used to reset the whole process
1234
+ * @returns
1235
+ */
1236
+ function changeDurationsInLayer(currentMEI, additionalElements = new Array(), refElement = null, remainRatio = null, meiToReset = null) {
1237
+ var meiCopy = meiToReset || currentMEI.cloneNode(true);
1238
+ let ms = Array.from(refElement.parentElement.querySelectorAll("note:not(chord note), chord, rest")); // querySelectorAll("note:not(chord note), chord, beam, rest")
1239
+ var measureSiblings = ms.filter((_, i) => i <= ms.indexOf(refElement));
1240
+ var ratioUpTpRef = addRatios(measureSiblings);
1241
+ var refElementRatio = getAbsoluteRatio(refElement);
1242
+ var remainBarRatio = getMeterRatioLocal(currentMEI, refElement) - ratioUpTpRef;
1243
+ if (getAbsoluteRatio(refElement.parentElement) === getMeterRatioLocal(currentMEI, refElement)) { // bar has right size,
1244
+ return currentMEI;
1245
+ }
1246
+ if (additionalElements.length > 0) {
1247
+ var nextNote = additionalElements.shift();
1248
+ var nnRatio = getAbsoluteRatio(nextNote);
1249
+ remainRatio = remainRatio || refElementRatio;
1250
+ var currEl;
1251
+ var harm;
1252
+ if (remainRatio < nnRatio) {
1253
+ var diffRatio = nnRatio - remainRatio;
1254
+ var nextNoteMEI = currentMEI.getElementById(nextNote.id);
1255
+ changeDur(nextNoteMEI, diffRatio);
1256
+ harm = currentMEI.querySelector('harm[startid="' + nextNote.id + '"]');
1257
+ if (harm !== null) {
1258
+ harm.setAttribute("startid", refElement.id);
1259
+ }
1260
+ }
1261
+ else if (remainRatio === nnRatio) {
1262
+ currEl = currentMEI.getElementById(nextNote.id);
1263
+ harm = currentMEI.querySelector('harm[startid="' + nextNote.id + '"]');
1264
+ //if(currEl.tagName === "rest" && harm !== null){
1265
+ if (harm !== null) {
1266
+ harm.setAttribute("startid", refElement.id);
1267
+ }
1268
+ currEl.remove();
1269
+ }
1270
+ else {
1271
+ currEl = currentMEI.getElementById(nextNote.id);
1272
+ harm = currentMEI.querySelector('harm[startid="' + nextNote.id + '"]');
1273
+ //if(currEl.tagName === "rest" && harm !== null){
1274
+ if (harm !== null) {
1275
+ harm.setAttribute("startid", refElement.id);
1276
+ }
1277
+ remainRatio = remainRatio - nnRatio;
1278
+ currEl.remove();
1279
+ changeDurationsInLayer(currentMEI, additionalElements, refElement, remainRatio, meiCopy);
1280
+ }
1281
+ }
1282
+ else {
1283
+ currentMEI = tieToNextMeasure(currentMEI, refElement, currentMEI.cloneNode(true), true, null);
1284
+ }
1285
+ cleanUp(currentMEI);
1286
+ return currentMEI;
1287
+ }
1288
+ exports.changeDurationsInLayer = changeDurationsInLayer;
1289
+ /**
1290
+ * Change duration attributes for given Element for the given ratio.
1291
+ * Will also change dot attribute if necessary
1292
+ * @param element
1293
+ * @param ratio
1294
+ */
1295
+ function changeDur(element, ratio) {
1296
+ var dur = ratioToDur(ratio);
1297
+ element.setAttribute("dur", dur.shift().toString());
1298
+ if (dur.length > 0) {
1299
+ element.setAttribute("dots", dur.shift().toString());
1300
+ }
1301
+ }
1302
+ exports.changeDur = changeDur;
1303
+ /**
1304
+ * Check if elment is overfilling the current layer element. Must provide previous MEI for reference.
1305
+ * Violate rule: true; follow rules: false
1306
+ * @param element
1307
+ * @param currMeiClone
1308
+ * @returns
1309
+ */
1310
+ function elementIsOverfilling(element, currMeiClone) {
1311
+ if (!overfillMeasure) {
1312
+ var newMeasureRatio = getAbsoluteRatio(element.closest("layer"));
1313
+ var localRatio = getMeterRatioLocal(currMeiClone, element);
1314
+ if (newMeasureRatio > localRatio) { //getMeterRatioGlobal(currMeiClone)){
1315
+ return true;
1316
+ }
1317
+ }
1318
+ return false;
1319
+ }
1320
+ exports.elementIsOverfilling = elementIsOverfilling;
1321
+ /**
1322
+ * Clean up mei after changing values
1323
+ * @param currentMEI
1324
+ */
1325
+ function cleanUp(currentMEI) {
1326
+ deleteDefSequences(currentMEI);
1327
+ reorganizeBeams(currentMEI);
1328
+ removeEmptyElements(currentMEI);
1329
+ adjustRests(currentMEI);
1330
+ redistributeHarms(currentMEI);
1331
+ reformatBreve(currentMEI);
1332
+ }
1333
+ exports.cleanUp = cleanUp;
1334
+ /**
1335
+ * Delete all redundant definition sequences in staffDefs and layers
1336
+ * @param currentMEI
1337
+ */
1338
+ function deleteDefSequences(currentMEI) {
1339
+ var staffCount = currentMEI.querySelectorAll("staffDef").length;
1340
+ for (var i = 0; i < staffCount; i++) {
1341
+ var n = (i + 1).toString();
1342
+ var prevElement = null;
1343
+ var prevShape = null;
1344
+ var prevLine = null;
1345
+ currentMEI.querySelectorAll("staffDef[n=\"" + n + "\"] clef, staff[n=\"" + n + "\"] clef").forEach(clefElement => {
1346
+ var shape = clefElement.getAttribute("shape");
1347
+ var line = clefElement.getAttribute("line");
1348
+ if (prevElement != null) {
1349
+ prevShape = prevElement.getAttribute("shape");
1350
+ prevLine = prevElement.getAttribute("line");
1351
+ if (prevShape === shape && prevLine === line) {
1352
+ clefElement.remove();
1353
+ }
1354
+ else {
1355
+ prevElement = clefElement;
1356
+ }
1357
+ }
1358
+ else {
1359
+ prevElement = clefElement;
1360
+ }
1361
+ });
1362
+ prevElement = null;
1363
+ var prevSig = null;
1364
+ currentMEI.querySelectorAll("staffDef[n=\"" + n + "\"] keySig, staff[n=\"" + n + "\"] keySig").forEach(sigElement => {
1365
+ var sig = sigElement.getAttribute("sig");
1366
+ if (prevElement != null) {
1367
+ prevSig = prevElement.getAttribute("sig");
1368
+ if (prevSig === sig) {
1369
+ sigElement.remove();
1370
+ }
1371
+ else {
1372
+ prevElement = sigElement;
1373
+ }
1374
+ }
1375
+ else {
1376
+ prevElement = sigElement;
1377
+ }
1378
+ });
1379
+ prevElement = null;
1380
+ currentMEI.querySelectorAll("staffDef[n=\"" + n + "\"] meterSig, staff[n=\"" + n + "\"] meterSig").forEach(meterElement => {
1381
+ var count = meterElement.getAttribute("count");
1382
+ var unit = meterElement.getAttribute("unit");
1383
+ if (prevElement != null) {
1384
+ var lastCount = prevElement.getAttribute("count");
1385
+ var lastUnit = prevElement.getAttribute("unit");
1386
+ if (lastCount === count && lastUnit === unit) {
1387
+ meterElement.remove();
1388
+ }
1389
+ else {
1390
+ prevElement = meterElement;
1391
+ }
1392
+ }
1393
+ else {
1394
+ prevElement = meterElement;
1395
+ }
1396
+ });
1397
+ }
1398
+ }
1399
+ function reorganizeBeams(currentMEI) {
1400
+ // if beams have elements, which shouldn be there
1401
+ currentMEI.querySelectorAll("beam").forEach(b => {
1402
+ var beamNotes = Array.from(b.children);
1403
+ if (!beamNotes.every(c => parseInt(c.getAttribute("dur")) >= 8) && beamNotes.length > 0) {
1404
+ beamNotes.forEach(n => {
1405
+ if (parseInt(n.getAttribute("dur")) >= 8) {
1406
+ if (n.previousElementSibling !== null) {
1407
+ if (n.previousElementSibling.tagName === "beam") { // check for previous beams to merge with
1408
+ n.previousElementSibling.appendChild(n);
1409
+ }
1410
+ }
1411
+ else { // else make new beam
1412
+ var newBeam = currentMEI.createElementNS(constants_1.constants._MEINS_, "beam");
1413
+ newBeam.setAttribute("id", random_1.uuidv4());
1414
+ n.parentElement.insertBefore(newBeam, n);
1415
+ newBeam.append(n);
1416
+ }
1417
+ }
1418
+ });
1419
+ //set all inner elements outseide of old beam
1420
+ b.outerHTML = b.innerHTML;
1421
+ }
1422
+ });
1423
+ }
1424
+ /**
1425
+ * After manipulating elements in the score, some elements could be empty
1426
+ * @param currentMEI
1427
+ */
1428
+ function removeEmptyElements(currentMEI) {
1429
+ Array.from(currentMEI.querySelectorAll("beam")).forEach(b => {
1430
+ var _a;
1431
+ if (b.childElementCount === 0) {
1432
+ (_a = currentMEI.getElementById(b.id)) === null || _a === void 0 ? void 0 : _a.remove();
1433
+ }
1434
+ if (b.childElementCount === 1) {
1435
+ //b.parentElement.insertBefore(b, b.firstChild)
1436
+ //b.remove()
1437
+ b.outerHTML = b.innerHTML;
1438
+ }
1439
+ var bArr = Array.from(b.children);
1440
+ if (bArr.every(c => c.tagName === "rest") && bArr.length > 0) {
1441
+ // Array.from(b.children).forEach(c => {
1442
+ // b.parentElement.insertBefore(c, b)
1443
+ // })
1444
+ // b.remove()
1445
+ b.outerHTML = b.innerHTML;
1446
+ }
1447
+ // Avoids that unvalid rests will be displayed as double full notes
1448
+ Array.from(currentMEI.querySelectorAll("rest")).forEach(r => {
1449
+ if (r.getAttribute("dur") === "0" || r.getAttribute("dur") === null) {
1450
+ r.removeAttribute("dur");
1451
+ r.removeAttribute("dots");
1452
+ r.outerHTML = r.outerHTML.replace("rest>", "mRest>");
1453
+ }
1454
+ });
1455
+ Array.from(currentMEI.querySelectorAll("*[xmlns]")).forEach(x => {
1456
+ var attr = x.getAttribute("xmlns");
1457
+ if (attr === "" || attr === null || attr == undefined) {
1458
+ x.removeAttribute("xmlns");
1459
+ }
1460
+ });
1461
+ });
1462
+ // allow no empty and rest-note element chord elements
1463
+ Array.from(currentMEI.querySelectorAll("chord")).forEach(c => {
1464
+ if (c.childElementCount === 0) {
1465
+ currentMEI.getElementById(c.id).remove();
1466
+ }
1467
+ else if (c.childElementCount === 1) {
1468
+ c.outerHTML = c.innerHTML;
1469
+ }
1470
+ else if (c.childElementCount === 2 && c.querySelector("rest") !== null && c.querySelector("note") !== null) {
1471
+ c.querySelector("note").setAttribute("dur", c.getAttribute("dur"));
1472
+ c.querySelector("rest").remove();
1473
+ c.outerHTML = c.innerHTML;
1474
+ }
1475
+ });
1476
+ // Empty harms could be somewhere
1477
+ Array.from(currentMEI.querySelectorAll("harm")).forEach(h => {
1478
+ if (h.childElementCount > 0) {
1479
+ if (h.firstElementChild.childElementCount === 0)
1480
+ h.remove();
1481
+ }
1482
+ else if (h.textContent === "") {
1483
+ h.remove();
1484
+ }
1485
+ });
1486
+ // remove all xmlns since they are someties empty and also are not parsable sometimes
1487
+ Array.from(currentMEI.querySelectorAll("*")).forEach(el => {
1488
+ if (el.tagName.toLowerCase() === "xml")
1489
+ return;
1490
+ el.removeAttribute("xmlns");
1491
+ });
1492
+ // Array.from(currentMEI .querySelectorAll("measure")).forEach(m => {
1493
+ // if(m.querySelectorAll("note, chord").length === 0){
1494
+ // currentMEI .getElementById(m.id).remove()
1495
+ // }
1496
+ // })
1497
+ }
1498
+ /**
1499
+ * Apply some additional rules for rests, Elements where added
1500
+ * @param currentMEI
1501
+ */
1502
+ function adjustRests(currentMEI) {
1503
+ //mRest and any Element with dur attribute are not allowed in the same layer
1504
+ currentMEI.querySelectorAll("layer").forEach(l => {
1505
+ var hasAnyDurAttributes = l.querySelectorAll("*[dur]").length > 0;
1506
+ var hasTags = l.querySelectorAll("clef, keySig").length > 0;
1507
+ var hasMrest = l.querySelectorAll("mRest").length > 0;
1508
+ if (l.children.length === 0 || (hasTags && !hasAnyDurAttributes && !hasMrest)) {
1509
+ //no layer should be empty, has at least an mRest (therefore: mRests are virtually not deletable)
1510
+ var newMrest = new mei_template_1.default().createMRest();
1511
+ l.append(newMrest);
1512
+ }
1513
+ else {
1514
+ Array.from(l.children).forEach(cn => {
1515
+ if (cn.tagName === "mRest" && hasAnyDurAttributes) {
1516
+ cn.remove();
1517
+ }
1518
+ });
1519
+ }
1520
+ });
1521
+ }
1522
+ /**
1523
+ * Give harm new start id if related note was included in chord during process
1524
+ * @param currentMEI
1525
+ */
1526
+ function redistributeHarms(currentMEI) {
1527
+ currentMEI.querySelectorAll("harm").forEach(h => {
1528
+ var startid = h.getAttribute("startid");
1529
+ if (startid !== null) {
1530
+ var note = currentMEI.getElementById(startid);
1531
+ if ((note === null || note === void 0 ? void 0 : note.parentElement.tagName) === "chord")
1532
+ h.setAttribute("startid", note.parentElement.id);
1533
+ }
1534
+ });
1535
+ }
1536
+ /**
1537
+ * Remove tie from all layers if length of layer exceeds global Ratio
1538
+ * @param currentMEI
1539
+ */
1540
+ function removeTiesFromDoc(currentMEI) {
1541
+ var globalRatio = getMeterRatioGlobal(currentMEI);
1542
+ currentMEI.querySelectorAll("layer").forEach(l => {
1543
+ var layerRatio = getAbsoluteRatio(l);
1544
+ if (layerRatio > globalRatio) {
1545
+ var m = l.closest("measure");
1546
+ m.querySelectorAll("tie").forEach(t => {
1547
+ var _a;
1548
+ (_a = l.querySelector(t.getAttribute("endid"))) === null || _a === void 0 ? void 0 : _a.remove();
1549
+ t.remove();
1550
+ });
1551
+ }
1552
+ });
1553
+ }
1554
+ function reformatBreve(currentMEI) {
1555
+ currentMEI.querySelectorAll("[dur='0.5']").forEach(d => d.setAttribute("dur", "breve"));
1556
+ }
1557
+ function addMeasure(currentMEI) {
1558
+ var lastMeasure = Array.from(currentMEI.querySelectorAll("measure")).reverse()[0];
1559
+ var staffCounts = Array.from(lastMeasure.querySelectorAll("staff")).map(s => { return parseInt(s.getAttribute("n")); });
1560
+ var staffCount = Math.max.apply(Math, staffCounts);
1561
+ var layerCounts = Array.from(lastMeasure.querySelectorAll("layer")).map(s => { return parseInt(s.getAttribute("n")); });
1562
+ var layerCount = Math.max.apply(Math, layerCounts);
1563
+ var newMeasure = new mei_template_1.default().createMeasure(1, staffCount, layerCount);
1564
+ lastMeasure.parentElement.append(newMeasure);
1565
+ var i = 1;
1566
+ currentMEI.querySelectorAll("measure").forEach(m => {
1567
+ m.setAttribute("n", i.toString());
1568
+ i++;
1569
+ });
1570
+ newMeasure.setAttribute("id", random_1.uuidv4());
1571
+ newMeasure.querySelectorAll("*").forEach(el => {
1572
+ if (el.id === null || el.id === "") {
1573
+ el.setAttribute("id", random_1.uuidv4());
1574
+ }
1575
+ });
1576
+ cleanUp(currentMEI);
1577
+ }
1578
+ exports.addMeasure = addMeasure;
1579
+ function removeMeasure(currentMEI) {
1580
+ var measures = Array.from(currentMEI.querySelectorAll("measure")).reverse();
1581
+ if (measures.length > 1) {
1582
+ measures[0].remove();
1583
+ }
1584
+ else {
1585
+ measures[0].querySelectorAll("layer").forEach(l => {
1586
+ l.innerHTML = "";
1587
+ l.appendChild(currentMEI.createElement("mRest"));
1588
+ });
1589
+ }
1590
+ cleanUp(currentMEI);
1591
+ }
1592
+ exports.removeMeasure = removeMeasure;
1593
+ function addStaff(currentMEI, referenceStaff, relPos) {
1594
+ var staffNum = referenceStaff.getAttribute("n");
1595
+ var refn;
1596
+ var refElement;
1597
+ currentMEI.querySelectorAll("staff[n=\"" + staffNum + "\"]").forEach(s => {
1598
+ var _a;
1599
+ var layers = s.querySelectorAll("layer");
1600
+ var newStaff = new mei_template_1.default().createStaff(1, 1);
1601
+ switch (relPos) {
1602
+ case "above":
1603
+ refElement = s;
1604
+ break;
1605
+ case "below":
1606
+ refElement = s.nextElementSibling || s;
1607
+ break;
1608
+ default:
1609
+ console.error(relPos, " was never an option");
1610
+ }
1611
+ if (relPos === "below" && refElement === s) { // => new staff at the end
1612
+ s.parentElement.append(newStaff);
1613
+ }
1614
+ else {
1615
+ s.parentElement.insertBefore(newStaff, refElement);
1616
+ }
1617
+ //copy elements from the current Staff that have to appear in new staff
1618
+ var newLayer = newStaff.querySelector("layer");
1619
+ var copyMeter = (_a = s.querySelector("meterSig")) === null || _a === void 0 ? void 0 : _a.cloneNode(true);
1620
+ if (copyMeter) {
1621
+ newLayer.insertBefore(copyMeter, newLayer.firstChild);
1622
+ }
1623
+ refn = (refElement === null || refElement === void 0 ? void 0 : refElement.getAttribute("n")) || staffNum; // s.getAttribute("n")
1624
+ });
1625
+ //new StaffDef
1626
+ var refStaffDef = currentMEI.querySelector("staffDef[n=\"" + refn + "\"]");
1627
+ var refCopy = refStaffDef.cloneNode(true);
1628
+ refCopy.querySelectorAll("*[id]").forEach(i => {
1629
+ i.removeAttribute("id");
1630
+ });
1631
+ refStaffDef.parentElement.insertBefore(refCopy, refStaffDef);
1632
+ currentMEI.querySelectorAll("measure").forEach(m => {
1633
+ var i = 1;
1634
+ m.querySelectorAll("staff").forEach(s => {
1635
+ s.setAttribute("n", i.toString());
1636
+ i++;
1637
+ });
1638
+ });
1639
+ var i = 1;
1640
+ currentMEI.querySelectorAll("staffDef").forEach(sd => {
1641
+ sd.setAttribute("n", i.toString());
1642
+ i++;
1643
+ });
1644
+ cleanUp(currentMEI);
1645
+ }
1646
+ exports.addStaff = addStaff;
1647
+ function removeStaff(currentMEI, referenceStaff, relPos) {
1648
+ var staff = currentMEI.getElementById(referenceStaff.id);
1649
+ var staffNum = staff.getAttribute("n");
1650
+ var refn;
1651
+ currentMEI.querySelectorAll("staff[n=\"" + staffNum + "\"]").forEach(s => {
1652
+ switch (relPos) {
1653
+ case "above":
1654
+ refn = s.previousElementSibling.getAttribute("n");
1655
+ s.previousElementSibling.remove();
1656
+ break;
1657
+ case "below":
1658
+ refn = s.nextElementSibling.getAttribute("n");
1659
+ s.nextElementSibling.remove();
1660
+ break;
1661
+ default:
1662
+ console.error(relPos, " was never an option");
1663
+ }
1664
+ });
1665
+ currentMEI.querySelector("staffDef[n=\"" + refn + "\"]").remove();
1666
+ currentMEI.querySelectorAll("measure").forEach(m => {
1667
+ var i = 1;
1668
+ m.querySelectorAll("staff").forEach(s => {
1669
+ s.setAttribute("n", i.toString());
1670
+ i++;
1671
+ });
1672
+ });
1673
+ var i = 1;
1674
+ currentMEI.querySelectorAll("staffDef").forEach(sd => {
1675
+ sd.setAttribute("n", i.toString());
1676
+ i++;
1677
+ });
1678
+ cleanUp(currentMEI);
1679
+ }
1680
+ exports.removeStaff = removeStaff;
1681
+ function addLayerForStaff(currentMEI, staffN, layerN) {
1682
+ currentMEI.querySelectorAll(`staff[n='${staffN}']`).forEach(staff => {
1683
+ staff.append(new mei_template_1.default().createLayer(layerN));
1684
+ });
1685
+ }
1686
+ exports.addLayerForStaff = addLayerForStaff;
1687
+ /**
1688
+ * Create a tuplet out of given Elements.
1689
+ * Tuplet duration and kind will be derived from number and attributes of the given notes.
1690
+ * E.g.: Selecting 3 Notes will make a triplet, 4 Notes will make a quadruplet, etc. The after the tuplet the missing durations will be added as rests
1691
+ * @param meiElements
1692
+ */
1693
+ function createTuplet(meiElements, currentMEI) {
1694
+ meiElements = meiElements.filter(me => {
1695
+ if (me != undefined) {
1696
+ return me.getAttribute("dur") !== null;
1697
+ }
1698
+ return;
1699
+ });
1700
+ console.log("create tuplets:", meiElements);
1701
+ var arrCopy = [...meiElements];
1702
+ var minDurElement = arrCopy.sort((a, b) => parseFloat(b.getAttribute("dur")) - parseFloat(a.getAttribute("dur")))[0];
1703
+ var ratioTotal = 0;
1704
+ meiElements.forEach(me => ratioTotal += getAbsoluteRatio(me));
1705
+ var ratioMinElement = getAbsoluteRatio(minDurElement);
1706
+ var numMinNotes = (ratioTotal / ratioMinElement);
1707
+ var numTarget = numMinNotes - 1;
1708
+ var ratio = numTarget * getAbsoluteRatio(minDurElement);
1709
+ var [dur, dots] = ratioToDur(ratio);
1710
+ var tuplet = currentMEI.createElement("tuplet");
1711
+ tuplet.setAttribute("dur", dur.toString());
1712
+ if (dots > 0) {
1713
+ tuplet.setAttribute("dots.ges", dots.toString());
1714
+ }
1715
+ tuplet.setAttribute("num", numMinNotes.toString());
1716
+ tuplet.setAttribute("numbase", numTarget.toString());
1717
+ currentMEI.querySelector("#" + meiElements[0].id).insertAdjacentElement("beforebegin", tuplet);
1718
+ meiElements.forEach(me => tuplet.append(me));
1719
+ // fill missing values with rest
1720
+ var [durRest, dotsRest] = ratioToDur((numMinNotes * getAbsoluteRatio(minDurElement)) - ratio);
1721
+ if (ratio < 1) {
1722
+ if (durRest > 0) {
1723
+ var rest = currentMEI.createElement("rest");
1724
+ rest.setAttribute("id", random_1.uuidv4());
1725
+ rest.setAttribute("dur", durRest.toString());
1726
+ if (dotsRest > 0) {
1727
+ rest.setAttribute("dots", dotsRest.toString());
1728
+ }
1729
+ tuplet.insertAdjacentElement("afterend", rest);
1730
+ }
1731
+ }
1732
+ }
1733
+ exports.createTuplet = createTuplet;
1734
+ /**
1735
+ * Paste copied ids. First position to which the Elements are copied is the Element according to the refId (= RefElement).
1736
+ * If multiple staffs are copied, overhanging staffs will be pasted to the staffs below the staff of the RefElement, if definedstaffs exist.
1737
+ * Else these copiedId will be not pasted.
1738
+ * @param ids Array of Element IDs to be pasted
1739
+ * @param pastePosition
1740
+ */
1741
+ function paste(ids, pastePosition, currentMEI) {
1742
+ var _a;
1743
+ //ordered by staff
1744
+ var meiElements = new Array();
1745
+ ids.forEach(id => {
1746
+ var el = currentMEI.getElementById(id);
1747
+ //order copiable elements by staff
1748
+ if (["CHORD", "NOTE", "REST"].includes(el === null || el === void 0 ? void 0 : el.tagName.toUpperCase())) {
1749
+ if (!(el.tagName.toUpperCase() === "NOTE" && el.closest("chord") !== null)) {
1750
+ var staff = el.closest("staff");
1751
+ var num = parseInt(staff.getAttribute("n")) - 1;
1752
+ if (meiElements[num] == undefined) {
1753
+ meiElements[num] = new Array();
1754
+ }
1755
+ var cel = el.cloneNode(true);
1756
+ cel.setAttribute("id", random_1.uuidv4());
1757
+ meiElements[num].push(cel);
1758
+ }
1759
+ }
1760
+ });
1761
+ var refElement = currentMEI.getElementById(pastePosition);
1762
+ refElement = (refElement === null || refElement === void 0 ? void 0 : refElement.closest("chord")) || refElement;
1763
+ var refStaff = refElement === null || refElement === void 0 ? void 0 : refElement.closest("staff");
1764
+ var refLayer = refElement === null || refElement === void 0 ? void 0 : refElement.closest("layer");
1765
+ var refMeasure = refElement === null || refElement === void 0 ? void 0 : refElement.closest("measure");
1766
+ var currentMeasure;
1767
+ var anyNew;
1768
+ //console.log(...meiElements)
1769
+ meiElements.forEach((staff, staffIdx) => {
1770
+ var _a, _b, _c, _d, _e, _f, _g;
1771
+ if (!refElement)
1772
+ return;
1773
+ currentMeasure = refElement.closest("measure");
1774
+ staff.forEach((element, elementIdx) => {
1775
+ var _a, _b;
1776
+ if (["NOTE", "REST"].includes(element.tagName.toUpperCase())) {
1777
+ const newNote = convertToNewNote(element);
1778
+ newNote.nearestNoteId = refElement.id;
1779
+ newNote.relPosX = elementIdx === 0 ? "left" : "right"; // make sure to replace the element on the pasteposition
1780
+ anyNew = newNote;
1781
+ }
1782
+ else if (element.tagName.toUpperCase() === "CHORD") {
1783
+ const newChord = convertToNewChord(element);
1784
+ newChord.nearestNoteId = refElement.id;
1785
+ newChord.relPosX = elementIdx === 0 ? "left" : "right"; // make sure to replace the element on the pasteposition
1786
+ anyNew = newChord;
1787
+ var elementArr = Array.from(element.querySelectorAll("note"));
1788
+ }
1789
+ const replace = ((_b = (_a = document.querySelector(".activeContainer")) === null || _a === void 0 ? void 0 : _a.querySelector("#insertToggle")) === null || _b === void 0 ? void 0 : _b.checked) || true;
1790
+ currentMEI = addToMEI(anyNew, meiConverter.cleanIdAttr(currentMEI), replace);
1791
+ refElement = convertToElement(anyNew, currentMEI); //element
1792
+ });
1793
+ //when changing next staff, refElement musst be staff + 1
1794
+ var targetStaffN = (_c = (parseInt((_b = (_a = currentMEI.getElementById(refElement.id)) === null || _a === void 0 ? void 0 : _a.closest("staff")) === null || _b === void 0 ? void 0 : _b.getAttribute("n")) + 1)) === null || _c === void 0 ? void 0 : _c.toString();
1795
+ var refLayerN = (_e = (_d = currentMEI.getElementById(refElement.id)) === null || _d === void 0 ? void 0 : _d.closest("layer")) === null || _e === void 0 ? void 0 : _e.getAttribute("n");
1796
+ var refMeasureN = (_g = (_f = currentMEI.getElementById(refElement.id)) === null || _f === void 0 ? void 0 : _f.closest("measure")) === null || _g === void 0 ? void 0 : _g.getAttribute("n");
1797
+ refElement = currentMEI.querySelector("measure[n=\"" + refMeasureN + "\"] > staff[n=\"" + targetStaffN + "\"] > layer[n=\"" + refLayerN + "\"]");
1798
+ });
1799
+ //Element gets replaced in all other modes except keymode/textmode
1800
+ if (!document.querySelector(".activeContainer").classList.contains("textmode") && ((_a = currentMEI.getElementById(pastePosition)) === null || _a === void 0 ? void 0 : _a.tagName) !== "LAYER") {
1801
+ removeFromMEI([currentMEI.getElementById(pastePosition)], currentMEI);
1802
+ }
1803
+ return [currentMEI, anyNew === null || anyNew === void 0 ? void 0 : anyNew.id];
1804
+ }
1805
+ exports.paste = paste;
1806
+ /**
1807
+ * Replace clef in main/ first score definition
1808
+ * @param targetid
1809
+ * @param newClef
1810
+ * @param currentMEI
1811
+ * @returns
1812
+ */
1813
+ function replaceClefinScoreDef(target, newClef, currentMEI) {
1814
+ const staffN = document.querySelector(".activeContainer #vrvSVG #" + target.id).closest(".staff").getAttribute("n");
1815
+ const staffDefClef = currentMEI.querySelector("staffDef[n=\"" + staffN + "\"] > clef");
1816
+ const shape = newClef.charAt(0);
1817
+ var line = mappings_1.clefToLine.get(newClef.charAt(0));
1818
+ if (shape === "C") { // get line depending on id of the cclef
1819
+ line = mappings_1.clefToLine.get(newClef.match(/CClef(.*)/)[1]);
1820
+ }
1821
+ const disPlace = newClef.includes("OctDown") ? "below" : newClef.includes("OctUp") ? "above" : null;
1822
+ if (staffDefClef) {
1823
+ staffDefClef.setAttribute("shape", shape);
1824
+ staffDefClef.setAttribute("line", line);
1825
+ if (disPlace) {
1826
+ staffDefClef.setAttribute("dis", "8");
1827
+ staffDefClef.setAttribute("dis.place", disPlace);
1828
+ }
1829
+ else {
1830
+ staffDefClef.removeAttribute("dis");
1831
+ staffDefClef.removeAttribute("dis.place");
1832
+ }
1833
+ }
1834
+ else {
1835
+ const staffDef = currentMEI.querySelector("staffDef[n=\"" + staffN + "\"]");
1836
+ staffDef === null || staffDef === void 0 ? void 0 : staffDef.setAttribute("clef.shape", shape);
1837
+ staffDef === null || staffDef === void 0 ? void 0 : staffDef.setAttribute("clef.line", line);
1838
+ if (disPlace) {
1839
+ staffDef === null || staffDef === void 0 ? void 0 : staffDef.setAttribute("clef.dis", "8");
1840
+ staffDef === null || staffDef === void 0 ? void 0 : staffDef.setAttribute("clef.dis.place", disPlace);
1841
+ }
1842
+ else {
1843
+ staffDef === null || staffDef === void 0 ? void 0 : staffDef.removeAttribute("clef.dis");
1844
+ staffDef === null || staffDef === void 0 ? void 0 : staffDef.removeAttribute("clef.dis.place");
1845
+ }
1846
+ }
1847
+ cleanUp(currentMEI);
1848
+ currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
1849
+ return currentMEI;
1850
+ }
1851
+ exports.replaceClefinScoreDef = replaceClefinScoreDef;
1852
+ /**
1853
+ * Layer to which a new clef object has to be inserted
1854
+ * @param targetid Usually a barline before which new clef should stand
1855
+ * @param newClef Name of new Clef to be inserted
1856
+ */
1857
+ function insertClef(target, newClef, currentMEI) {
1858
+ var _a, _b;
1859
+ var targetStaffId = ((_a = target.closest(".measure").querySelector(".staff[n=\"" + target.getAttribute("n") + "\"]")) === null || _a === void 0 ? void 0 : _a.id) || ((_b = target.closest(".staff")) === null || _b === void 0 ? void 0 : _b.id);
1860
+ var targetLayerId = currentMEI.getElementById(targetStaffId).querySelector("layer").id;
1861
+ currentMEI.getElementById(targetLayerId).querySelectorAll("clef").forEach(c => c.remove());
1862
+ const shape = newClef.charAt(0);
1863
+ var line = mappings_1.clefToLine.get(newClef.charAt(0));
1864
+ if (shape === "C") { // get line depending on id of the cclef
1865
+ line = mappings_1.clefToLine.get(newClef.match(/CClef(.*)/)[1]);
1866
+ }
1867
+ var disPlace = newClef.includes("OctDown") ? "below" : newClef.includes("OctUp") ? "above" : null;
1868
+ var clefElement = currentMEI.createElement("clef");
1869
+ clefElement.setAttribute("id", random_1.uuidv4());
1870
+ clefElement.setAttribute("shape", shape);
1871
+ clefElement.setAttribute("line", line);
1872
+ if (disPlace) {
1873
+ clefElement.setAttribute("dis", "8");
1874
+ clefElement.setAttribute("dis.place", disPlace);
1875
+ }
1876
+ currentMEI.getElementById(targetLayerId).append(clefElement);
1877
+ cleanUp(currentMEI);
1878
+ currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
1879
+ return currentMEI;
1880
+ }
1881
+ exports.insertClef = insertClef;
1882
+ function findAttributeRecursive(element, attributeName, currentValue = null) {
1883
+ var value = currentValue || element.getAttribute(attributeName);
1884
+ if (value === null) {
1885
+ Array.from(element.children).forEach(c => {
1886
+ if (value !== null)
1887
+ return;
1888
+ value = findAttributeRecursive(c, attributeName, value);
1889
+ });
1890
+ }
1891
+ console.log(element, value);
1892
+ return value;
1893
+ }
1894
+ /**
1895
+ * If Key is already defined in scoreDef, replace values
1896
+ * @param target
1897
+ * @param newSig
1898
+ * @param currentMEI
1899
+ * @returns
1900
+ */
1901
+ function replaceKeyInScoreDef(target, newSig, currentMEI) {
1902
+ var _a;
1903
+ var staffN = document.querySelector(".activeContainer #vrvSVG #" + target.id).closest(".staff").getAttribute("n");
1904
+ var staffDefSig = currentMEI.querySelector("staffDef[n=\"" + staffN + "\"] > keySig");
1905
+ if (staffDefSig !== null) {
1906
+ staffDefSig.setAttribute("sig", mappings_1.keyIdToSig.get(newSig));
1907
+ }
1908
+ else {
1909
+ var newSigElement = new mei_template_1.default().createKeySig("major", mappings_1.keyIdToSig.get(newSig));
1910
+ (_a = currentMEI.querySelector("staffDef[n=\"" + staffN + "\"]")) === null || _a === void 0 ? void 0 : _a.append(newSigElement);
1911
+ }
1912
+ adjustAccids(currentMEI);
1913
+ cleanUp(currentMEI);
1914
+ currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
1915
+ return currentMEI;
1916
+ }
1917
+ exports.replaceKeyInScoreDef = replaceKeyInScoreDef;
1918
+ /**
1919
+ * Create a whole new Sig Element and Insert to MEI at given target
1920
+ * @param target
1921
+ * @param newSig
1922
+ * @param currentMEI
1923
+ * @returns
1924
+ */
1925
+ function insertKey(target, newSig, currentMEI) {
1926
+ var _a, _b;
1927
+ console.log("insertKey", target, newSig);
1928
+ var targetStaff = target.closest(".measure").querySelector(".staff[n=\"" + target.getAttribute("n") + "\"]") || target.closest(".staff");
1929
+ var staffN = targetStaff.getAttribute("n");
1930
+ var parentMeasure = currentMEI.getElementById(targetStaff.id).closest("measure");
1931
+ var pmn = parseInt(parentMeasure.getAttribute("n")) + 1;
1932
+ var targetLayerId = (_a = parentMeasure.parentElement.querySelector("measure[n=\"" + pmn.toString() + "\"] > staff[n=\"" + staffN + "\"] > layer")) === null || _a === void 0 ? void 0 : _a.id;
1933
+ (_b = currentMEI.getElementById(targetLayerId).querySelectorAll("keySig")) === null || _b === void 0 ? void 0 : _b.forEach(c => c.remove());
1934
+ var newSigElement = new mei_template_1.default().createKeySig("major", mappings_1.keyIdToSig.get(newSig));
1935
+ currentMEI.getElementById(targetLayerId).insertBefore(newSigElement, currentMEI.getElementById(targetLayerId).firstElementChild);
1936
+ adjustAccids(currentMEI);
1937
+ cleanUp(currentMEI);
1938
+ currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
1939
+ return currentMEI;
1940
+ }
1941
+ exports.insertKey = insertKey;
1942
+ function replaceMeterInScoreDef(target, currentMEI) {
1943
+ var staffN = document.querySelector(".activeContainer #vrvSVG #" + target.id).closest(".staff").getAttribute("n");
1944
+ var staffDefMeter = currentMEI.querySelector("staffDef[n=\"" + staffN + "\"]");
1945
+ var count = document.querySelector(".activeContainer #timeCount").value;
1946
+ var unit = document.querySelector(".activeContainer #timeUnit").value;
1947
+ staffDefMeter.setAttribute("meter.count", count);
1948
+ staffDefMeter.setAttribute("meter.unit", unit);
1949
+ cleanUp(currentMEI);
1950
+ currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
1951
+ return currentMEI;
1952
+ }
1953
+ exports.replaceMeterInScoreDef = replaceMeterInScoreDef;
1954
+ function insertMeter(target, currentMEI) {
1955
+ var targetStaff = target.closest(".measure").querySelector(".staff[n=\"" + target.getAttribute("n") + "\"]") || target.closest(".staff");
1956
+ var parentMeasure = currentMEI.getElementById(targetStaff.id).closest("measure");
1957
+ var pmn = parseInt(parentMeasure.getAttribute("n")) + 1;
1958
+ var targetLayers = parentMeasure.parentElement.querySelectorAll("measure[n=\"" + pmn.toString() + "\"] layer");
1959
+ targetLayers.forEach(tl => {
1960
+ var _a;
1961
+ (_a = currentMEI.getElementById(tl.id).querySelectorAll("meterSig")) === null || _a === void 0 ? void 0 : _a.forEach(c => c.remove());
1962
+ });
1963
+ var count = document.querySelector(".activeContainer #selectTime #timeCount").value;
1964
+ var unit = document.querySelector(".activeContainer #selectTime #timeUnit").value;
1965
+ // change for all layers in given measure
1966
+ targetLayers.forEach(tl => {
1967
+ let newMeterElement = new mei_template_1.default().createMeterSig(count, unit); // must be in loop, otherwise same reference gets reassigned every time
1968
+ currentMEI.getElementById(tl.id).insertBefore(newMeterElement, currentMEI.getElementById(tl.id).firstElementChild);
1969
+ });
1970
+ cleanUp(currentMEI);
1971
+ currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
1972
+ return currentMEI;
1973
+ }
1974
+ exports.insertMeter = insertMeter;
1975
+ function insertTempo(target, currentMEI) {
1976
+ var measure = currentMEI.getElementById(target.id).closest("measure");
1977
+ var existingTempo = measure.querySelectorAll("tempo");
1978
+ var sameTempos = Array.from(existingTempo).filter(et => {
1979
+ var hasSameTimeStamp = parseFloat(et.getAttribute("timeStamp")) === getElementTimestampById(target.id, currentMEI);
1980
+ var hasSameStartId = et.getAttribute("startId") === target.id;
1981
+ return hasSameTimeStamp || hasSameStartId;
1982
+ });
1983
+ var mmUnit = document.querySelector(".activeContainer #selectTempo #timeCount").value;
1984
+ var mm = document.querySelector(".activeContainer #selectTempo #timeUnit").value;
1985
+ measure.appendChild(new mei_template_1.default().createTempo(mm, mmUnit, getElementTimestampById(target.id, currentMEI).toString(), target.id));
1986
+ sameTempos.forEach(st => st.remove());
1987
+ cleanUp(currentMEI);
1988
+ currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
1989
+ return currentMEI;
1990
+ }
1991
+ exports.insertTempo = insertTempo;
1992
+ /**
1993
+ * Gets timestamp of element. Computes it, if no such attribute is present for the element
1994
+ * @param id
1995
+ * @param currentMEI
1996
+ * @returns
1997
+ */
1998
+ function getElementTimestampById(id, currentMEI) {
1999
+ var element = currentMEI.getElementById(id);
2000
+ var timestamp = element.getAttribute("tstamp");
2001
+ if (timestamp === null) {
2002
+ var parentLayer = element.closest("layer");
2003
+ var count = 0;
2004
+ var units = parentLayer.querySelectorAll(countableNoteUnitSelector);
2005
+ for (var i = 0; i < units.length; i++) {
2006
+ if (units[i].getAttribute("dur") !== null) {
2007
+ if (units[i].id === id) {
2008
+ var fraction = 4;
2009
+ if (currentMEI.querySelector("meterSig") !== null) {
2010
+ fraction = parseInt(currentMEI.querySelector("meterSig").getAttribute("unit"));
2011
+ }
2012
+ timestamp = (count * fraction + 1).toString(); // add 1 to accomodate for shift ratio sum
2013
+ break;
2014
+ }
2015
+ count += getAbsoluteRatio(units[i]);
2016
+ }
2017
+ }
2018
+ }
2019
+ return parseFloat(timestamp);
2020
+ }
2021
+ exports.getElementTimestampById = getElementTimestampById;
2022
+ /**
2023
+ * Get Timestamp in specific layer
2024
+ * @param note
2025
+ * @returns
2026
+ */
2027
+ function getTimestamp(note) {
2028
+ var layer = note.closest("layer");
2029
+ var elements = Array.from(layer.querySelectorAll("*[dur]"));
2030
+ elements = elements.filter((v, i) => i <= elements.indexOf(note));
2031
+ var tstamp = 0;
2032
+ elements.forEach(e => {
2033
+ var dur = parseInt(e.getAttribute("dur"));
2034
+ tstamp += 4 / dur;
2035
+ var dots = e.getAttribute("dots");
2036
+ var add = dur;
2037
+ if (dots !== null) {
2038
+ for (var i = 0; i < parseInt(dots); i++) {
2039
+ add = add / 2;
2040
+ tstamp += add;
2041
+ }
2042
+ }
2043
+ });
2044
+ return tstamp;
2045
+ }
2046
+ exports.getTimestamp = getTimestamp;
2047
+ function setArticulation(currentMEI, artic) {
2048
+ document.querySelectorAll(".activeContainer #vrvSVG :is(.chord.marked, .note.marked, .note.lastAdded").forEach(note => {
2049
+ var meiElement = currentMEI.querySelector("#" + note.id);
2050
+ if (meiElement === null)
2051
+ return;
2052
+ if (meiElement.tagName !== "note")
2053
+ return;
2054
+ if (meiElement.tagName === "note" && meiElement.parentElement.tagName === "chord") {
2055
+ meiElement = meiElement.parentElement;
2056
+ }
2057
+ if (meiElement.getAttribute("artic") === null || meiElement.getAttribute("artic") !== artic) {
2058
+ meiElement.setAttribute("artic", artic);
2059
+ }
2060
+ else {
2061
+ meiElement.removeAttribute("artic");
2062
+ }
2063
+ });
2064
+ return currentMEI;
2065
+ }
2066
+ exports.setArticulation = setArticulation;
2067
+ //PRIVATE
2068
+ function convertToNewNote(element) {
2069
+ var newNote = {
2070
+ id: random_1.uuidv4(),
2071
+ pname: element.getAttribute("pname"),
2072
+ dur: element.getAttribute("dur"),
2073
+ dots: element.getAttribute("dots"),
2074
+ oct: element.getAttribute("oct"),
2075
+ accid: element.getAttribute("accid") || element.getAttribute("accid.ges"),
2076
+ rest: element.tagName.toUpperCase() === "REST" ? true : false
2077
+ };
2078
+ return newNote;
2079
+ }
2080
+ function convertToElement(n, currentMEI) {
2081
+ var nn;
2082
+ var newElement;
2083
+ if (n.hasOwnProperty("pname")) {
2084
+ nn = n;
2085
+ newElement = currentMEI.createElement("note");
2086
+ newElement.setAttribute("pname", nn.pname);
2087
+ newElement.setAttribute("oct", nn.oct);
2088
+ newElement.setAttribute("accid", nn.accid);
2089
+ }
2090
+ else {
2091
+ nn = n;
2092
+ newElement = currentMEI.createElement("chord");
2093
+ nn.noteElements.forEach(ne => {
2094
+ newElement.append(convertToElement(ne, currentMEI));
2095
+ });
2096
+ }
2097
+ newElement.setAttribute("id", nn.id);
2098
+ newElement.setAttribute("dur", nn.dur);
2099
+ newElement.setAttribute("dots", nn.dots);
2100
+ return newElement;
2101
+ }
2102
+ function convertToNewChord(element) {
2103
+ var newNotes = Array.from(element.querySelectorAll("note")).map(n => {
2104
+ return convertToNewNote(n);
2105
+ });
2106
+ var newChord = {
2107
+ id: random_1.uuidv4(),
2108
+ dur: element.getAttribute("dur"),
2109
+ dots: element.getAttribute("dots"),
2110
+ noteElements: newNotes
2111
+ };
2112
+ return newChord;
2113
+ }
2114
+ function createNewRestElement(dur, dots = undefined) {
2115
+ var newElem = document.createElementNS(constants_1.constants._MEINS_, "rest");
2116
+ newElem.setAttribute("dur", dur.toString());
2117
+ if (dots != undefined)
2118
+ newElem.setAttribute("dots", dots.toString());
2119
+ newElem.setAttribute("id", random_1.uuidv4());
2120
+ return newElem;
2121
+ }