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.
- package/LICENSE +1 -1
- package/README.md +12 -9
- package/package.json +11 -5
- package/src/scripts/js/Core.js +212 -186
- package/src/scripts/js/MusicProcessor.js +286 -128
- package/src/scripts/js/{VerovioScoreEditor.js → VIBE.js} +62 -28
- package/src/scripts/js/assets/mei_template.js +5 -1
- package/src/scripts/js/datastructures/MeasureMatrix.js +6 -85
- package/src/scripts/js/datastructures/ScoreGraph.js +1 -1
- package/src/scripts/js/entry.js +3 -2
- package/src/scripts/js/gui/Annotations.js +188 -111
- package/src/scripts/js/gui/HarmonyLabel.js +26 -2
- package/src/scripts/js/gui/ScoreManipulator.js +61 -31
- package/src/scripts/js/gui/Tabbar.js +41 -21
- package/src/scripts/js/gui/Toolbar.js +4 -4
- package/src/scripts/js/handlers/AnnotationChangeHandler.js +131 -60
- package/src/scripts/js/handlers/ClickModeHandler.js +406 -143
- package/src/scripts/js/handlers/CustomToolbarHandler.js +26 -24
- package/src/scripts/js/handlers/GlobalKeyboardHandler.js +12 -7
- package/src/scripts/js/handlers/InsertModeHandler.js +26 -32
- package/src/scripts/js/handlers/KeyModeHandler.js +12 -86
- package/src/scripts/js/handlers/LabelHandler.js +3 -2
- package/src/scripts/js/handlers/PhantomElementHandler.js +1 -1
- package/src/scripts/js/handlers/ScoreManipulatorHandler.js +101 -14
- package/src/scripts/js/handlers/SelectionHandler.js +80 -36
- package/src/scripts/js/handlers/SideBarHandler.js +10 -3
- package/src/scripts/js/handlers/WindowHandler.js +25 -4
- package/src/scripts/js/utils/DOMCreator.js +1 -1
- package/src/scripts/js/utils/MEIConverter.js +13 -1
- package/src/scripts/js/utils/MEIOperations.js +180 -187
- package/src/scripts/js/utils/Mouse2SVG.js +200 -43
- package/src/scripts/js/utils/ReactWrapper.js +46 -0
- package/src/scripts/js/utils/RectWrapper.js +10 -0
- package/src/scripts/js/utils/SVGEditor.js +94 -3
- package/src/scripts/js/utils/VerovioWrapper.js +6 -1
- package/src/scripts/js/utils/convenienceQueries.js +2 -0
- package/src/scripts/js/utils/mappings.js +322 -56
- 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.
|
11
|
-
|
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")
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
148
|
-
|
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
|
-
|
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('
|
113
|
+
element.addEventListener('mouseup', this.insertNoteHandler);
|
182
114
|
});
|
183
115
|
}
|
184
116
|
removeListeners() {
|
185
|
-
this.interactionOverlay.removeEventListener(
|
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('
|
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
|
-
|
223
|
-
if (nextNote
|
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;
|