vibe-editor 0.0.0 → 0.0.1

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 (38) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +12 -9
  3. package/package.json +11 -5
  4. package/src/scripts/js/Core.js +212 -186
  5. package/src/scripts/js/MusicProcessor.js +286 -128
  6. package/src/scripts/js/{VerovioScoreEditor.js → VIBE.js} +62 -28
  7. package/src/scripts/js/assets/mei_template.js +5 -1
  8. package/src/scripts/js/datastructures/MeasureMatrix.js +6 -85
  9. package/src/scripts/js/datastructures/ScoreGraph.js +1 -1
  10. package/src/scripts/js/entry.js +3 -2
  11. package/src/scripts/js/gui/Annotations.js +188 -111
  12. package/src/scripts/js/gui/HarmonyLabel.js +26 -2
  13. package/src/scripts/js/gui/ScoreManipulator.js +61 -31
  14. package/src/scripts/js/gui/Tabbar.js +41 -21
  15. package/src/scripts/js/gui/Toolbar.js +4 -4
  16. package/src/scripts/js/handlers/AnnotationChangeHandler.js +131 -60
  17. package/src/scripts/js/handlers/ClickModeHandler.js +406 -143
  18. package/src/scripts/js/handlers/CustomToolbarHandler.js +26 -24
  19. package/src/scripts/js/handlers/GlobalKeyboardHandler.js +12 -7
  20. package/src/scripts/js/handlers/InsertModeHandler.js +26 -32
  21. package/src/scripts/js/handlers/KeyModeHandler.js +12 -86
  22. package/src/scripts/js/handlers/LabelHandler.js +3 -2
  23. package/src/scripts/js/handlers/PhantomElementHandler.js +1 -1
  24. package/src/scripts/js/handlers/ScoreManipulatorHandler.js +101 -14
  25. package/src/scripts/js/handlers/SelectionHandler.js +80 -36
  26. package/src/scripts/js/handlers/SideBarHandler.js +10 -3
  27. package/src/scripts/js/handlers/WindowHandler.js +25 -4
  28. package/src/scripts/js/utils/DOMCreator.js +1 -1
  29. package/src/scripts/js/utils/MEIConverter.js +13 -1
  30. package/src/scripts/js/utils/MEIOperations.js +180 -187
  31. package/src/scripts/js/utils/Mouse2SVG.js +200 -43
  32. package/src/scripts/js/utils/ReactWrapper.js +46 -0
  33. package/src/scripts/js/utils/RectWrapper.js +10 -0
  34. package/src/scripts/js/utils/SVGEditor.js +94 -3
  35. package/src/scripts/js/utils/VerovioWrapper.js +6 -1
  36. package/src/scripts/js/utils/convenienceQueries.js +2 -0
  37. package/src/scripts/js/utils/mappings.js +322 -56
  38. package/src/styles/VerovioScoreEditor.css +0 -694
@@ -2,166 +2,88 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const coordinates = require("../utils/coordinates");
4
4
  const cq = require("../utils/convenienceQueries");
5
+ const mappings_1 = require("../utils/mappings");
6
+ const marked = "marked";
5
7
  class ClickModeHandler {
6
8
  constructor() {
9
+ this.isSelecting = false;
10
+ this.selectDist = 0;
11
+ this.dragOnce = false;
12
+ this.shiftPressed = false;
13
+ this.selectEndEvent = new Event("selectEnd");
7
14
  /**
8
15
  * Event handler for inserting Notes
9
16
  */
10
- this.clickHandler = (function clickHandler(e) {
11
- var _a;
12
- var t = e.target;
13
- if (this.interactionOverlay.querySelector(".moving") !== null)
14
- return;
15
- if (["clef", "meterSig", "keySig", "rest", "notehead"].some(c => t.parentElement.classList.contains(c))) {
16
- this.hideCursor();
17
- return;
18
- }
19
- // when over other interactable elements discard event
20
- if (!this.phantomElementHandler.getIsTrackingMouse()) {
21
- return;
22
- }
23
- if (this.musicPlayer.getIsPlaying() === true) {
24
- return;
25
- } // getIsPlaying could also be undefined
26
- // var pt = new DOMPoint(e.clientX, e.clientY)
27
- // var vrvSVG = this.vrvSVG as unknown as SVGGraphicsElement
28
- // var pospt = pt.matrixTransform(vrvSVG.getScreenCTM().inverse())
29
- var pospt = coordinates.transformToDOMMatrixCoordinates(e.clientX, e.clientY, this.vrvSVG);
30
- var posx = pospt.x;
31
- var posy = pospt.y;
32
- var target = e.target;
33
- var options = {};
34
- if (target.classList.contains("staffLine")) {
35
- options["staffLineId"] = target.id;
36
- }
37
- if ((_a = this.interactionOverlay.querySelector("#phantomNote")) === null || _a === void 0 ? void 0 : _a.classList.contains("onChord")) {
38
- options["targetChord"] = this.findScoreTarget(posx, posy);
39
- }
40
- //this.m2s.defineNote(e.pageX, e.pageY, options);
41
- this.m2s.defineNote(posx, posy, options);
42
- var newNote = this.m2s.getNewNote();
43
- if (newNote == undefined)
44
- return; //Eingabemaske in Chrome: zusätzliche Notenlinien in Noteneditor #10
45
- var meiDoc = this.m2s.getCurrentMei();
46
- var pitchExists = false;
47
- // do not insert same note more than once in chord
48
- if (newNote.chordElement != undefined) {
49
- var chordEl = meiDoc.getElementById(newNote.chordElement.id);
50
- if (chordEl.getAttribute("pname") === newNote.pname && chordEl.getAttribute("oct") === newNote.oct) {
51
- pitchExists = true;
52
- }
53
- else {
54
- for (let c of chordEl.children) {
55
- if (c.getAttribute("pname") === newNote.pname && c.getAttribute("oct") === newNote.oct) {
56
- pitchExists = true;
57
- break;
58
- }
59
- }
60
- }
61
- }
62
- if (!pitchExists) {
63
- var replace = true; //(this.container.querySelector("#insertToggle") as HTMLInputElement).checked && newNote.chordElement == undefined
64
- this.insertCallback(this.m2s.getNewNote(), replace).then(() => {
65
- this.musicPlayer.generateTone(this.m2s.getNewNote());
66
- }).catch(() => {
67
- //alert("Your bar is to small")
68
- });
69
- }
17
+ this.insertNoteHandler = (function insertNoteHandler(e) {
18
+ this.insertNote(e);
70
19
  }).bind(this);
20
+ /**
21
+ * Hide the phantom element for the cursor.
22
+ */
71
23
  this.hideCursor = function () {
72
- if (this.interactionOverlay.querySelector(".moving") !== null)
24
+ if (this.interactionOverlay.querySelector(".moving"))
73
25
  return;
74
26
  this.container.querySelectorAll("#phantomCanvas > *").forEach(ph => {
75
27
  ph.setAttribute("visibility", "hidden");
76
28
  }); // make phantoms invisible
77
29
  }.bind(this);
30
+ /**
31
+ * Show phantom element for cursor.
32
+ */
78
33
  this.showCursor = function () {
79
34
  this.container.querySelectorAll("#phantomCanvas > *").forEach(ph => ph.setAttribute("visibility", "visible")); // make phantoms invisible
80
35
  }.bind(this);
36
+ /**
37
+ * Handler for {@link mouseOverChord}
38
+ */
81
39
  this.mouseOverChordHandler = (function mouseOverHandler(e) {
82
- var _a;
83
- if (!this.phantomElementHandler.getIsTrackingMouse()) {
84
- return;
85
- }
86
- var coords = coordinates.transformToDOMMatrixCoordinates(e.clientX, e.clientY, this.interactionOverlay);
87
- var posx = coords.x; //e.offsetX
88
- var posy = coords.y; //e.offsetY
89
- var elementToHighlight = this.findScoreTarget(posx, posy);
90
- if (elementToHighlight == undefined || elementToHighlight.closest(".mRest") !== null || elementToHighlight.closest(".rest") !== null) {
91
- return;
92
- }
93
- if (this.prevElementToHighlight == undefined || this.currentElementToHighlight !== elementToHighlight) {
94
- //update focussed layer if element and layer do not match
95
- if (elementToHighlight.closest(".layer").id !== ((_a = this.m2s.getMouseEnterElementByName("layer")) === null || _a === void 0 ? void 0 : _a.id) && this.m2s.getMouseEnterElementByName("layer") !== null) {
96
- this.m2s.setMouseEnterElements(elementToHighlight);
97
- }
98
- //snap note to closest Chord
99
- var phantom = this.interactionOverlay.querySelector("#phantomNote");
100
- var cx = parseFloat(phantom.getAttribute("cx"));
101
- var bboxElement = this.interactionOverlay.querySelector("[refId=" + elementToHighlight.id + "]");
102
- var ptLeft = new DOMPoint(bboxElement.getBoundingClientRect().left, 0);
103
- var ptRight = new DOMPoint(bboxElement.getBoundingClientRect().right, 0);
104
- var vrvSVG = this.interactionOverlay; //cq.getVrvSVG(this.containerId) as unknown as SVGGraphicsElement
105
- var left = ptLeft.matrixTransform(vrvSVG.getScreenCTM().inverse()).x;
106
- var right = ptRight.matrixTransform(vrvSVG.getScreenCTM().inverse()).x;
107
- //snap only when within boundaries of target Chord
108
- if (cx > left && cx < right) {
109
- var snapTarget;
110
- var snapTargetBBox;
111
- var phantomSnapX;
112
- var targetwidth;
113
- var snapCoord;
114
- // if(navigator.userAgent.toLowerCase().includes("firefox")){ // special rules for buggy firefox
115
- // if(elementToHighlight.querySelector(".notehead") === null) return
116
- // targetwidth = right - left//elementToHighlight.querySelector(".notehead")?.getBoundingClientRect().width
117
- // snapTarget = elementToHighlight.classList.contains("chord") ? elementToHighlight : elementToHighlight.querySelector(".note") || elementToHighlight
118
- // console.log("snapTarget", snapTarget)
119
- // snapTargetBBox = snapTarget.getBoundingClientRect()
120
- // // phantomSnapX = snapTargetBBox.x - window.scrollX - rootBBox.x - root.scrollLeft
121
- // snapCoord = snapTargetBBox.x
122
- // }else{
123
- snapTarget = bboxElement; //elementToHighlight.querySelector(".notehead")|| elementToHighlight
124
- snapTargetBBox = snapTarget.getBoundingClientRect();
125
- // phantomSnapX = snapTargetBBox.x + snapTargetBBox.width/2 - window.scrollX - rootBBox.x - root.scrollLeft
126
- snapCoord = snapTargetBBox.x + snapTargetBBox.width / 2;
127
- //}
128
- let snappt = new DOMPoint(snapCoord, 0);
129
- phantomSnapX = snappt.matrixTransform(vrvSVG.getScreenCTM().inverse()).x;
130
- if (elementToHighlight.querySelector(".chord") !== null) {
131
- console.log(phantomSnapX);
132
- }
133
- phantom.setAttribute("cx", phantomSnapX.toString());
134
- if (!phantom.classList.contains("onChord")) {
135
- phantom.classList.add("onChord");
136
- phantom.classList.add("l" + elementToHighlight.closest(".layer").getAttribute("n"));
137
- if (!elementToHighlight.classList.contains("chord")) {
138
- elementToHighlight.classList.add("highlighted");
139
- }
140
- else {
141
- elementToHighlight.querySelectorAll(".note").forEach((c) => {
142
- c.classList.add("highlighted");
143
- });
144
- }
145
- }
40
+ this.mouseOverChord(e);
41
+ }).bind(this);
42
+ // SELECTION STUFF
43
+ this.clickSelectHandler = (function clickSelectHandler(e) {
44
+ this.clickSelect(e);
45
+ }).bind(this);
46
+ /**
47
+ * Initialize start coordinates of selecting rectangle.
48
+ * {@link getSelectCoordsHandler} will compute distances on dragging the mouse to decide on initialisation of selection.
49
+ */
50
+ this.selStartHandler = (function selStartHandler(e) {
51
+ this.selectStartX = this.selectStartX || e.clientX;
52
+ this.selectStartY = this.selectStartY || e.clientY;
53
+ }).bind(this);
54
+ this.selectingHandler = (function selectingHandler(e) {
55
+ this.selecting(e);
56
+ }).bind(this);
57
+ /**
58
+ * Handler for {@link selEnd}.
59
+ */
60
+ this.selEndHandler = (function selEndHandler(e) {
61
+ this.selEnd(e);
62
+ }).bind(this);
63
+ /**
64
+ * Handler for {@link getSelectCoords}
65
+ */
66
+ this.getSelectCoordsHandler = (function getSelectCoordsHandler(e) {
67
+ this.getSelectCoords(e);
68
+ }).bind(this);
69
+ /**
70
+ * Handle shift presses and set shiftPressed flag.
71
+ */
72
+ this.shiftKeyHandler = (function shiftKeyHandler(e) {
73
+ //e.preventDefault()
74
+ if (e.key === "Shift") {
75
+ if (e.type === "keydown") {
76
+ this.shiftPressed = true;
146
77
  }
147
- else {
148
- for (const [key, value] of phantom.classList.entries()) {
149
- if (value.indexOf("l") === 0) {
150
- phantom.classList.remove(value);
151
- }
152
- }
153
- phantom.classList.remove("onChord");
154
- phantom.setAttribute("fill", "black");
155
- this.container.querySelectorAll(".highlighted").forEach(h => {
156
- h.classList.remove("highlighted");
157
- });
78
+ if (e.type === "keyup") {
79
+ this.shiftPressed = false;
158
80
  }
159
- this.currentElementToHighlight = elementToHighlight;
160
81
  }
161
82
  }).bind(this);
162
83
  }
163
84
  setListeners() {
164
- this.interactionOverlay.addEventListener('click', this.clickHandler);
85
+ // Insert Notation Listeners
86
+ this.interactionOverlay.addEventListener("mouseup", this.insertNoteHandler);
165
87
  this.interactionOverlay.addEventListener("mousemove", this.mouseOverChordHandler);
166
88
  this.interactionOverlay.querySelectorAll("#scoreRects > *").forEach(sr => {
167
89
  if (["clef", "meterSig", "keySig", "rest", "notehead", "harm",].some(c => sr.classList.contains(c))) {
@@ -171,6 +93,16 @@ class ClickModeHandler {
171
93
  sr.addEventListener("mouseover", this.showCursor);
172
94
  }
173
95
  });
96
+ //Selection Listeners
97
+ this.interactionOverlay.addEventListener("mousedown", this.selStartHandler);
98
+ this.interactionOverlay.addEventListener("mousemove", this.selectingHandler);
99
+ this.interactionOverlay.addEventListener("mousemove", this.getSelectCoordsHandler);
100
+ this.interactionOverlay.addEventListener("mouseup", this.selEndHandler);
101
+ this.interactionOverlay.querySelectorAll(".notehead, .rest").forEach(el => el.addEventListener("click", this.clickSelectHandler));
102
+ //shiftkey for special function
103
+ document.addEventListener("keydown", this.shiftKeyHandler);
104
+ document.addEventListener("keyup", this.shiftKeyHandler);
105
+ //hide or show cursor based on position
174
106
  this.interactionOverlay.querySelectorAll("#manipulatorCanvas *, #annotationCanvas *").forEach(sr => {
175
107
  sr.addEventListener("mouseover", this.hideCursor);
176
108
  sr.addEventListener("mouseleave", this.showCursor);
@@ -178,11 +110,16 @@ class ClickModeHandler {
178
110
  // Listener just for staves
179
111
  var staves = this.interactionOverlay.querySelectorAll(".staffLine");
180
112
  Array.from(staves).forEach(element => {
181
- element.addEventListener('click', this.clickHandler);
113
+ element.addEventListener('mouseup', this.insertNoteHandler);
182
114
  });
183
115
  }
184
116
  removeListeners() {
185
- this.interactionOverlay.removeEventListener('click', this.clickHandler);
117
+ this.interactionOverlay.removeEventListener("mousedown", this.selStartHandler);
118
+ this.interactionOverlay.removeEventListener("mousemove", this.selectingHandler);
119
+ this.interactionOverlay.removeEventListener("mousemove", this.getSelectCoordsHandler);
120
+ this.interactionOverlay.removeEventListener("mouseup", this.selEndHandler);
121
+ this.interactionOverlay.querySelectorAll(".notehead, .rest").forEach(el => el.removeEventListener("click", this.clickSelectHandler));
122
+ this.interactionOverlay.removeEventListener("mouseup", this.insertNoteHandler);
186
123
  this.interactionOverlay.removeEventListener("mousemove", this.mouseOverChordHandler);
187
124
  if (this.annotations != undefined) {
188
125
  var highLightElements = this.annotations.getAnnotationCanvas().querySelectorAll(".highlightChord");
@@ -190,6 +127,8 @@ class ClickModeHandler {
190
127
  el.remove();
191
128
  });
192
129
  }
130
+ document.removeEventListener("keydown", this.shiftKeyHandler);
131
+ document.removeEventListener("keyup", this.shiftKeyHandler);
193
132
  this.container.querySelectorAll(".highlighted").forEach((c) => {
194
133
  c.classList.remove("highlighted");
195
134
  });
@@ -204,13 +143,172 @@ class ClickModeHandler {
204
143
  // Listener just for staves
205
144
  var staves = this.interactionOverlay.querySelectorAll(".staffLine");
206
145
  Array.from(staves).forEach(element => {
207
- element.removeEventListener('click', this.clickHandler);
146
+ element.removeEventListener('mouseup', this.insertNoteHandler);
208
147
  });
209
148
  }
210
149
  resetListeners() {
211
150
  this.removeListeners();
212
151
  this.setListeners();
213
152
  }
153
+ /**
154
+ * Insert a note when mouseup is fires based on mouse position.
155
+ * @param e
156
+ * @returns
157
+ */
158
+ insertNote(e) {
159
+ var _a;
160
+ // when mouseup is fired, selEndHandler and insertNotehandler are alled.
161
+ // The isSelecting flag is set to true as soon as a selection startet, only after that an insertion of a note should be possible
162
+ if (this.isSelecting) {
163
+ this.isSelecting = false;
164
+ return;
165
+ }
166
+ var t = e.target;
167
+ if (cq.getContainer(this.containerId).querySelectorAll(`.${marked}`).length > 1 && !this.shiftPressed) {
168
+ cq.getContainer(this.containerId).querySelectorAll(`.${marked}`).forEach(m => m.classList.remove(marked));
169
+ return; // when more than one element is marked is likely to remove marked status of all notes
170
+ }
171
+ if (cq.getContainer(this.containerId).classList.contains("annotMode"))
172
+ return; // prevent selecting when resizing annotation objects
173
+ if (cq.getContainer(this.containerId).querySelector("#phantomNote").getAttribute("visibility") === "hidden")
174
+ return; //the cursor is possibly somewhere where no note input is possible
175
+ if (cq.getContainer(this.containerId).querySelector("[contenteditable=true]"))
176
+ return;
177
+ e.preventDefault();
178
+ // when mouse is over other interactable elements discard this event
179
+ if (["clef", "meterSig", "keySig", "rest", "notehead", "manipulator"].some(c => {
180
+ let parentCondition = t.parentElement.classList.contains(c);
181
+ let layerCondition = false;
182
+ if (!t.closest(".activeLayer")) {
183
+ layerCondition = true;
184
+ }
185
+ return parentCondition && layerCondition;
186
+ })) {
187
+ this.hideCursor();
188
+ return;
189
+ }
190
+ if (!this.phantomElementHandler.getIsTrackingMouse()) {
191
+ return;
192
+ }
193
+ if (this.musicPlayer.getIsPlaying() === true) {
194
+ return;
195
+ } // getIsPlaying could also be undefined
196
+ // Define position of mouse and which note to set
197
+ var pospt = coordinates.transformToDOMMatrixCoordinates(e.clientX, e.clientY, this.vrvSVG);
198
+ var posx = pospt.x;
199
+ var posy = pospt.y;
200
+ var target = e.target;
201
+ var options = {};
202
+ if (target.classList.contains("staffLine")) {
203
+ options["staffLineId"] = target.id;
204
+ }
205
+ if ((_a = this.interactionOverlay.querySelector("#phantomNote")) === null || _a === void 0 ? void 0 : _a.classList.contains("onChord")) {
206
+ options["targetChord"] = this.findScoreTarget(posx, posy);
207
+ }
208
+ //this.m2s.defineNote(e.pageX, e.pageY, options);
209
+ this.m2s.defineNote(posx, posy, options);
210
+ var newNote = this.m2s.getNewNote();
211
+ if (newNote == undefined)
212
+ return; //Eingabemaske in Chrome: zusätzliche Notenlinien in Noteneditor #10
213
+ var meiDoc = this.m2s.getCurrentMei();
214
+ var pitchExists = false;
215
+ // do not insert same note more than once in chord
216
+ if (newNote.chordElement) {
217
+ var chordEl = meiDoc.getElementById(newNote.chordElement.id);
218
+ if (chordEl.getAttribute("pname") === newNote.pname && chordEl.getAttribute("oct") === newNote.oct) {
219
+ pitchExists = true;
220
+ }
221
+ else {
222
+ for (let c of chordEl.children) {
223
+ if (c.getAttribute("pname") === newNote.pname && c.getAttribute("oct") === newNote.oct) {
224
+ pitchExists = true;
225
+ break;
226
+ }
227
+ }
228
+ }
229
+ }
230
+ if (!pitchExists) {
231
+ var replace = true; //(this.container.querySelector("#insertToggle") as HTMLInputElement).checked && newNote.chordElement == undefined
232
+ this.insertCallback(this.m2s.getNewNote(), replace).then(() => {
233
+ this.musicPlayer.generateTone(this.m2s.getNewNote());
234
+ }).catch(() => {
235
+ //alert("Your bar is to small")
236
+ });
237
+ }
238
+ }
239
+ /**
240
+ * Check if mouse is over a chord to snap cursor and define new note within a chord elemnt
241
+ */
242
+ mouseOverChord(e) {
243
+ var _a;
244
+ if (!this.phantomElementHandler.getIsTrackingMouse()) {
245
+ return;
246
+ }
247
+ var coords = coordinates.transformToDOMMatrixCoordinates(e.clientX, e.clientY, this.interactionOverlay);
248
+ var posx = coords.x;
249
+ var posy = coords.y;
250
+ var elementToHighlight = this.findScoreTarget(posx, posy);
251
+ if (elementToHighlight == undefined || elementToHighlight.closest(".mRest") !== null || elementToHighlight.closest(".rest") !== null) {
252
+ return;
253
+ }
254
+ //if (this.currentElementToHighlight !== elementToHighlight) {
255
+ //update focussed layer if element and layer do not match
256
+ if (elementToHighlight.closest(".layer").id !== ((_a = this.m2s.getMouseEnterElementByName("layer")) === null || _a === void 0 ? void 0 : _a.id) && this.m2s.getMouseEnterElementByName("layer") !== null) {
257
+ this.m2s.setMouseEnterElements(elementToHighlight);
258
+ }
259
+ //snap note to closest Chord
260
+ var phantom = this.interactionOverlay.querySelector("#phantomNote");
261
+ var cx = parseFloat(phantom.getAttribute("cx"));
262
+ var bboxElement = this.interactionOverlay.querySelector("[refId=" + elementToHighlight.id + "]");
263
+ var ptLeft = new DOMPoint(bboxElement.getBoundingClientRect().left, 0);
264
+ var ptRight = new DOMPoint(bboxElement.getBoundingClientRect().right, 0);
265
+ var vrvSVG = this.interactionOverlay;
266
+ var left = ptLeft.matrixTransform(vrvSVG.getScreenCTM().inverse()).x;
267
+ var right = ptRight.matrixTransform(vrvSVG.getScreenCTM().inverse()).x;
268
+ //snap only when within boundaries of target Chord
269
+ if (cx > left && cx < right) {
270
+ var snapTarget;
271
+ var snapTargetBBox;
272
+ var phantomSnapX;
273
+ var targetwidth;
274
+ var snapCoord;
275
+ snapTarget = bboxElement;
276
+ snapTargetBBox = snapTarget.getBoundingClientRect();
277
+ snapCoord = snapTargetBBox.x + snapTargetBBox.width / 2;
278
+ let snappt = new DOMPoint(snapCoord, 0);
279
+ phantomSnapX = snappt.matrixTransform(vrvSVG.getScreenCTM().inverse()).x;
280
+ // if (elementToHighlight.querySelector(".chord") !== null) {
281
+ // console.log(phantomSnapX)
282
+ // }
283
+ phantom.setAttribute("cx", phantomSnapX.toString());
284
+ if (!phantom.classList.contains("onChord")) {
285
+ phantom.classList.add("onChord");
286
+ phantom.classList.add("l" + elementToHighlight.closest(".layer").getAttribute("n"));
287
+ if (!elementToHighlight.classList.contains("chord")) {
288
+ elementToHighlight.classList.add("highlighted");
289
+ }
290
+ else {
291
+ elementToHighlight.querySelectorAll(".note").forEach((c) => {
292
+ c.classList.add("highlighted");
293
+ });
294
+ }
295
+ }
296
+ }
297
+ else {
298
+ for (const [key, value] of phantom.classList.entries()) {
299
+ if (value.indexOf("l") === 0) {
300
+ phantom.classList.remove(value);
301
+ }
302
+ }
303
+ phantom.classList.remove("onChord");
304
+ phantom.setAttribute("fill", "black");
305
+ this.container.querySelectorAll(".highlighted").forEach(h => {
306
+ h.classList.remove("highlighted");
307
+ });
308
+ }
309
+ this.currentElementToHighlight = elementToHighlight;
310
+ //}
311
+ }
214
312
  /**
215
313
  * Find Score Element nearest to given Position (e.g. Mouse)
216
314
  * @param posx client position
@@ -219,8 +317,8 @@ class ClickModeHandler {
219
317
  */
220
318
  findScoreTarget(posx, posy) {
221
319
  var _a;
222
- var nextNote = this.m2s.findScoreTarget(posx, posy);
223
- if (nextNote != undefined) {
320
+ const nextNote = this.m2s.findScoreTarget(posx, posy);
321
+ if (nextNote) {
224
322
  var el = ((_a = this.vrvSVG.querySelector("#" + nextNote.id)) === null || _a === void 0 ? void 0 : _a.closest(".chord")) || this.vrvSVG.querySelector("#" + nextNote.id);
225
323
  if (el.classList.contains("notehead")) {
226
324
  el = el.parentElement;
@@ -229,6 +327,171 @@ class ClickModeHandler {
229
327
  }
230
328
  return;
231
329
  }
330
+ clickSelect(e) {
331
+ var _a;
332
+ if (!this.shiftPressed) {
333
+ cq.getVrvSVG(this.containerId).querySelectorAll(".marked").forEach(m => m.classList.remove(marked));
334
+ }
335
+ const t = e.target;
336
+ const notehead = cq.getVrvSVG(this.containerId).querySelector(`#${t.parentElement.getAttribute("refId")}`);
337
+ notehead === null || notehead === void 0 ? void 0 : notehead.classList.add(marked);
338
+ (_a = notehead.closest(".note")) === null || _a === void 0 ? void 0 : _a.classList.add(marked);
339
+ }
340
+ selStart(e) {
341
+ e.preventDefault();
342
+ if (!this.isSelecting)
343
+ return;
344
+ if (cq.getContainer(this.containerId).classList.contains("annotMode")) {
345
+ this.selEnd(e);
346
+ return;
347
+ }
348
+ //var pt = coordinates.transformToDOMMatrixCoordinates(d3.event.sourceEvent.clientX, d3.event.sourceEvent.clientY, cq.getInteractOverlay(this.containerId))
349
+ var pt = coordinates.transformToDOMMatrixCoordinates(e.clientX, e.clientY, cq.getInteractOverlay(this.containerId));
350
+ this.initialSelectX = pt.x; //d3.event.x
351
+ this.initialSelectY = pt.y; //d3.event.y
352
+ if (!document.getElementById(this.containerId).classList.contains("harmonyMode") && !this.shiftPressed) { //!this.harmonyHandler.getGlobal()){
353
+ this.m2s.getNoteBBoxes().forEach(bb => {
354
+ let note = this.vrvSVG.querySelector("#" + bb.id);
355
+ note.classList.remove(marked);
356
+ });
357
+ }
358
+ this.initRect(this.initialSelectX, this.initialSelectY);
359
+ this.isSelecting = true;
360
+ }
361
+ selecting(e) {
362
+ e.preventDefault();
363
+ if (document.getElementById(this.containerId).classList.contains("annotMode"))
364
+ return; // prevent selecting when resizing annotation objects
365
+ if (!this.isSelecting)
366
+ return;
367
+ const pt = coordinates.transformToDOMMatrixCoordinates(e.clientX, e.clientY, cq.getInteractOverlay(this.containerId));
368
+ const curX = pt.x;
369
+ const curY = pt.y;
370
+ const newX = curX < this.initialSelectX ? curX : this.initialSelectX;
371
+ const newY = curY < this.initialSelectY ? curY : this.initialSelectY;
372
+ const width = curX < this.initialSelectX ? this.initialSelectX - curX : curX - this.initialSelectX;
373
+ const height = curY < this.initialSelectY ? this.initialSelectY - curY : curY - this.initialSelectY;
374
+ this.updateRect(newX, newY, width, height);
375
+ const rect = this.interactionOverlay.querySelector("#selectRect");
376
+ const rectpt = coordinates.getDOMMatrixCoordinates(rect, this.vrvSVG);
377
+ const rectHeightpt = rectpt.height;
378
+ const rectWidthpt = rectpt.width;
379
+ const rx = rectpt.x;
380
+ const ry = rectpt.y;
381
+ const noteBBoxes = this.m2s.getNoteBBoxes();
382
+ noteBBoxes.forEach(bb => {
383
+ const note = cq.getVrvSVG(this.containerId).querySelector("#" + bb.id);
384
+ const stem = note.querySelector(".stem");
385
+ const accid = note.querySelector(".accid");
386
+ if (bb.x >= rx &&
387
+ //bb.x <= rx + rectBBox.width &&
388
+ bb.x <= rx + rectWidthpt &&
389
+ bb.y >= ry &&
390
+ //bb.y <= ry + rectBBox.height
391
+ bb.y <= ry + rectHeightpt) {
392
+ note.classList.add(marked);
393
+ if (stem !== null)
394
+ stem.classList.add(marked);
395
+ const chord = note.closest(".chord");
396
+ if (chord !== null) {
397
+ //if(!chord.classList.contains(marked))
398
+ const noteArr = Array.from(chord.querySelectorAll(".note"));
399
+ if (noteArr.every(c => c.classList.contains(marked)) && noteArr.length > 0) {
400
+ chord.classList.add(marked);
401
+ }
402
+ }
403
+ }
404
+ else if (!this.shiftPressed) {
405
+ note.classList.remove(marked);
406
+ stem === null || stem === void 0 ? void 0 : stem.classList.remove(marked);
407
+ accid === null || accid === void 0 ? void 0 : accid.classList.remove(marked);
408
+ const chord = note.closest(".chord");
409
+ chord === null || chord === void 0 ? void 0 : chord.classList.remove(marked);
410
+ }
411
+ });
412
+ }
413
+ /**
414
+ * Ends selection. Resets all flags and delets selectRect
415
+ * @param e
416
+ * @returns
417
+ */
418
+ selEnd(e) {
419
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
420
+ e.preventDefault();
421
+ this.dragOnce = false;
422
+ this.selectStartX = undefined;
423
+ this.selectStartY = undefined;
424
+ this.selectDist = 0;
425
+ this.isSelecting = false;
426
+ if (document.getElementById(this.containerId).classList.contains("annotMode"))
427
+ return; // prevent selecting when resizing annotation objects
428
+ var selectRect = cq.getInteractOverlay(this.containerId).querySelector("#selectRect");
429
+ if (selectRect !== null && (selectRect === null || selectRect === void 0 ? void 0 : selectRect.getAttribute("width")) !== "0" && (selectRect === null || selectRect === void 0 ? void 0 : selectRect.getAttribute("height")) !== "0") {
430
+ document.dispatchEvent(this.selectEndEvent);
431
+ }
432
+ selectRect === null || selectRect === void 0 ? void 0 : selectRect.remove();
433
+ var firstMarkedNote = (_a = this.vrvSVG.querySelector(".chord.marked, .note.marked, .rest.marked")) === null || _a === void 0 ? void 0 : _a.id;
434
+ var meiNote = this.m2s.getCurrentMei().getElementById(firstMarkedNote);
435
+ (_c = (_b = document.getElementById(this.containerId)) === null || _b === void 0 ? void 0 : _b.querySelectorAll(".lastAdded")) === null || _c === void 0 ? void 0 : _c.forEach(la => la.classList.remove("lastAdded"));
436
+ if ((firstMarkedNote === null || firstMarkedNote === void 0 ? void 0 : firstMarkedNote.length) > 0) {
437
+ (_d = document.getElementById(this.containerId)) === null || _d === void 0 ? void 0 : _d.querySelectorAll("#noteGroup *, #dotGroup *, #modGroup *, #articGroup *").forEach(b => b.classList.remove("selected"));
438
+ //select buttons for given note state
439
+ var modBtnId = this.container.querySelector("#customToolbar #articGroup") !== null ? "artic" : "accid";
440
+ (_f = (_e = document.getElementById(this.containerId)) === null || _e === void 0 ? void 0 : _e.querySelector("#" + mappings_1.attrToModButtonId.get(meiNote === null || meiNote === void 0 ? void 0 : meiNote.getAttribute(modBtnId)))) === null || _f === void 0 ? void 0 : _f.classList.add("selected");
441
+ if ((meiNote === null || meiNote === void 0 ? void 0 : meiNote.closest("chord")) !== null) {
442
+ meiNote = meiNote.closest("chord");
443
+ }
444
+ (_h = (_g = document.getElementById(this.containerId)) === null || _g === void 0 ? void 0 : _g.querySelector("#" + mappings_1.numToNoteButtonId.get(meiNote === null || meiNote === void 0 ? void 0 : meiNote.getAttribute("dur")))) === null || _h === void 0 ? void 0 : _h.classList.add("selected");
445
+ (_k = (_j = document.getElementById(this.containerId)) === null || _j === void 0 ? void 0 : _j.querySelector("#" + mappings_1.numToDotButtonId.get(meiNote === null || meiNote === void 0 ? void 0 : meiNote.getAttribute("dots")))) === null || _k === void 0 ? void 0 : _k.classList.add("selected");
446
+ }
447
+ }
448
+ /**
449
+ * Initialize selectRect.
450
+ * @param x start x
451
+ * @param y start y
452
+ */
453
+ initRect(x, y) {
454
+ const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
455
+ rect.setAttribute("x", x.toString());
456
+ rect.setAttribute("y", y.toString());
457
+ rect.setAttribute("width", "0");
458
+ rect.setAttribute("height", "0");
459
+ rect.setAttribute("id", "selectRect");
460
+ rect.setAttribute("stroke", "black");
461
+ rect.setAttribute("stroke-width", "1px");
462
+ rect.setAttribute("fill", "none");
463
+ this.interactionOverlay.appendChild(rect);
464
+ }
465
+ /**
466
+ * Update size of selectRect.
467
+ * @param newX x posiiton of mouse cursor
468
+ * @param newY y position of mouse cursor
469
+ * @param currentWidth
470
+ * @param currentHeight
471
+ */
472
+ updateRect(newX, newY, currentWidth, currentHeight) {
473
+ const rect = this.interactionOverlay.querySelector("#selectRect");
474
+ rect.setAttribute("x", newX.toString());
475
+ rect.setAttribute("y", newY.toString());
476
+ rect.setAttribute("width", currentWidth.toString());
477
+ rect.setAttribute("height", currentHeight.toString());
478
+ }
479
+ /**
480
+ * Compute coordinates to determine if a selection should be initialized.
481
+ * Starts selection once if distance > 10.
482
+ * This is important to distinguish between inserting a note, drawing the selectRect and selecting one element by click.
483
+ * @param e MouseEvent
484
+ */
485
+ getSelectCoords(e) {
486
+ this.selectDist = Math.sqrt(Math.abs(e.clientX - this.selectStartX) ** 2 + Math.abs(e.clientY - this.selectStartY) ** 2);
487
+ if (this.selectDist > 10) {
488
+ this.isSelecting = true;
489
+ if (!this.dragOnce) {
490
+ this.dragOnce = true;
491
+ this.selStart(e);
492
+ }
493
+ }
494
+ }
232
495
  ///// GETTER / SETTER////////////////
233
496
  setm2s(m2s) {
234
497
  this.m2s = m2s;