abcjs 6.0.0-beta.8 → 6.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 (204) hide show
  1. package/.github/workflows/tests.yml +29 -0
  2. package/CODE_OF_CONDUCT.md +76 -0
  3. package/CONTRIBUTING.md +1 -0
  4. package/LICENSE.md +1 -1
  5. package/README.md +92 -3
  6. package/RELEASE.md +959 -1
  7. package/abcjs-audio.css +14 -5
  8. package/dist/.gitignore +1 -2
  9. package/dist/abcjs-basic-min.js +3 -0
  10. package/dist/abcjs-basic-min.js.LICENSE +23 -0
  11. package/dist/abcjs-basic.js +28232 -0
  12. package/dist/abcjs-basic.js.map +1 -0
  13. package/dist/abcjs-plugin-min.js +3 -0
  14. package/dist/abcjs-plugin-min.js.LICENSE +23 -0
  15. package/dist/report-basic.html +37 -0
  16. package/dist/report-before-glyph-compress.html +37 -0
  17. package/dist/report-brown-ts-target-es5.html +37 -0
  18. package/dist/report-dev-orig-no-babel.html +37 -0
  19. package/dist/report-synth.html +37 -0
  20. package/docker-build.sh +1 -0
  21. package/glyphs.json +1 -0
  22. package/index.js +27 -2
  23. package/{static-wrappers/license.js → license.js} +1 -1
  24. package/package.json +26 -29
  25. package/{src/plugin/abc_plugin.js → plugin.js} +31 -19
  26. package/src/api/abc_animation.js +1 -17
  27. package/src/api/abc_tablatures.js +144 -0
  28. package/src/api/abc_timing_callbacks.js +239 -116
  29. package/src/api/abc_tunebook.js +18 -67
  30. package/src/api/abc_tunebook_svg.js +38 -46
  31. package/src/data/abc_tune.js +232 -972
  32. package/src/data/deline-tune.js +199 -0
  33. package/src/edit/abc_editarea.js +112 -0
  34. package/src/edit/abc_editor.js +95 -221
  35. package/src/midi/abc_midi_create.js +48 -50
  36. package/src/parse/abc_common.js +0 -14
  37. package/src/parse/abc_parse.js +167 -1321
  38. package/src/parse/abc_parse_book.js +62 -0
  39. package/src/parse/abc_parse_directive.js +164 -41
  40. package/src/parse/abc_parse_header.js +116 -145
  41. package/src/parse/abc_parse_key_voice.js +26 -20
  42. package/src/parse/abc_parse_music.js +1337 -0
  43. package/src/parse/abc_tokenizer.js +21 -15
  44. package/src/parse/abc_transpose.js +3 -15
  45. package/src/parse/tune-builder.js +896 -0
  46. package/src/parse/wrap_lines.js +205 -453
  47. package/src/synth/abc_midi_flattener.js +1292 -0
  48. package/src/{midi → synth}/abc_midi_renderer.js +44 -17
  49. package/src/synth/abc_midi_sequencer.js +648 -0
  50. package/src/synth/active-audio-context.js +3 -14
  51. package/src/synth/cents-to-factor.js +10 -0
  52. package/src/synth/create-note-map.js +21 -32
  53. package/src/synth/create-synth-control.js +20 -103
  54. package/src/synth/create-synth.js +185 -77
  55. package/src/synth/download-buffer.js +7 -21
  56. package/src/synth/get-midi-file.js +13 -20
  57. package/src/synth/images/{loading.svg → loading.svg.js} +4 -0
  58. package/src/synth/images/loop.svg.js +65 -0
  59. package/src/synth/images/pause.svg.js +10 -0
  60. package/src/synth/images/play.svg.js +9 -0
  61. package/src/synth/images/{reset.svg → reset.svg.js} +5 -1
  62. package/src/synth/instrument-index-to-name.js +1 -16
  63. package/src/synth/load-note.js +37 -76
  64. package/src/synth/pitch-to-note-name.js +0 -15
  65. package/src/synth/pitches-to-perc.js +64 -0
  66. package/src/synth/place-note.js +78 -68
  67. package/src/synth/play-event.js +17 -18
  68. package/src/synth/register-audio-context.js +11 -23
  69. package/src/synth/sounds-cache.js +0 -15
  70. package/src/synth/supports-audio.js +9 -23
  71. package/src/synth/synth-controller.js +80 -49
  72. package/src/synth/synth-sequence.js +20 -34
  73. package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
  74. package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
  75. package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
  76. package/src/tablatures/instruments/string-patterns.js +277 -0
  77. package/src/tablatures/instruments/string-tablature.js +56 -0
  78. package/src/tablatures/instruments/tab-note.js +282 -0
  79. package/src/tablatures/instruments/tab-notes.js +41 -0
  80. package/src/tablatures/instruments/violin/tab-violin.js +47 -0
  81. package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
  82. package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
  83. package/src/tablatures/tab-absolute-elements.js +310 -0
  84. package/src/tablatures/tab-common.js +29 -0
  85. package/src/tablatures/tab-renderer.js +243 -0
  86. package/src/tablatures/transposer.js +110 -0
  87. package/src/test/abc_midi_lint.js +5 -22
  88. package/src/test/abc_midi_sequencer_lint.js +11 -14
  89. package/src/test/abc_parser_lint.js +136 -32
  90. package/src/test/abc_vertical_lint.js +94 -32
  91. package/src/test/rendering-lint.js +38 -5
  92. package/src/write/abc_absolute_element.js +112 -120
  93. package/src/write/abc_abstract_engraver.js +102 -253
  94. package/src/write/abc_beam_element.js +30 -290
  95. package/src/write/abc_brace_element.js +12 -121
  96. package/src/write/abc_create_clef.js +21 -32
  97. package/src/write/abc_create_key_signature.js +8 -26
  98. package/src/write/abc_create_note_head.js +107 -0
  99. package/src/write/abc_create_time_signature.js +2 -21
  100. package/src/write/abc_crescendo_element.js +3 -50
  101. package/src/write/abc_decoration.js +7 -30
  102. package/src/write/abc_dynamic_decoration.js +3 -37
  103. package/src/write/abc_ending_element.js +1 -57
  104. package/src/write/abc_engraver_controller.js +111 -234
  105. package/src/write/abc_glyphs.js +8 -18
  106. package/src/write/abc_relative_element.js +57 -97
  107. package/src/write/abc_renderer.js +10 -832
  108. package/src/write/abc_spacing.js +0 -15
  109. package/src/write/abc_staff_group_element.js +14 -349
  110. package/src/write/abc_tempo_element.js +9 -117
  111. package/src/write/abc_tie_element.js +5 -68
  112. package/src/write/abc_triplet_element.js +6 -124
  113. package/src/write/abc_voice_element.js +7 -222
  114. package/src/write/add-chord.js +103 -0
  115. package/src/write/add-text-if.js +33 -0
  116. package/src/write/bottom-text.js +79 -0
  117. package/src/write/calcHeight.js +17 -0
  118. package/src/write/classes.js +100 -0
  119. package/src/write/draw/absolute.js +68 -0
  120. package/src/write/draw/beam.js +56 -0
  121. package/src/write/draw/brace.js +106 -0
  122. package/src/write/draw/crescendo.js +38 -0
  123. package/src/write/draw/debug-box.js +8 -0
  124. package/src/write/draw/draw.js +56 -0
  125. package/src/write/draw/dynamics.js +20 -0
  126. package/src/write/draw/ending.js +46 -0
  127. package/src/write/draw/group-elements.js +66 -0
  128. package/src/write/draw/horizontal-line.js +25 -0
  129. package/src/write/draw/non-music.js +50 -0
  130. package/src/write/draw/print-line.js +24 -0
  131. package/src/write/draw/print-path.js +7 -0
  132. package/src/write/draw/print-stem.js +30 -0
  133. package/src/write/draw/print-symbol.js +59 -0
  134. package/src/write/draw/print-vertical-line.js +18 -0
  135. package/src/write/draw/relative.js +77 -0
  136. package/src/write/draw/round-number.js +5 -0
  137. package/src/write/draw/selectables.js +59 -0
  138. package/src/write/draw/separator.js +16 -0
  139. package/src/write/draw/set-paper-size.js +45 -0
  140. package/src/write/{sprintf.js → draw/sprintf.js} +0 -0
  141. package/src/write/draw/staff-group.js +226 -0
  142. package/src/write/draw/staff-line.js +9 -0
  143. package/src/write/draw/staff.js +33 -0
  144. package/src/write/draw/tab-line.js +40 -0
  145. package/src/write/draw/tempo.js +45 -0
  146. package/src/write/draw/text.js +71 -0
  147. package/src/write/draw/tie.js +97 -0
  148. package/src/write/draw/triplet.js +46 -0
  149. package/src/write/draw/voice.js +102 -0
  150. package/src/write/format-jazz-chord.js +15 -0
  151. package/src/write/free-text.js +41 -0
  152. package/src/write/get-font-and-attr.js +37 -0
  153. package/src/write/get-text-size.js +56 -0
  154. package/src/write/highlight.js +11 -0
  155. package/src/write/layout/VoiceElements.js +121 -0
  156. package/src/write/layout/beam.js +213 -0
  157. package/src/write/layout/get-left-edge-of-staff.js +56 -0
  158. package/src/write/layout/getBarYAt.js +6 -0
  159. package/src/write/layout/layout.js +94 -0
  160. package/src/write/layout/setUpperAndLowerElements.js +232 -0
  161. package/src/write/layout/staffGroup.js +146 -0
  162. package/src/write/layout/triplet.js +75 -0
  163. package/src/write/layout/voice.js +137 -0
  164. package/src/write/selection.js +188 -70
  165. package/src/write/separator.js +10 -0
  166. package/src/write/set-class.js +21 -0
  167. package/src/write/subtitle.js +12 -0
  168. package/src/write/svg.js +95 -43
  169. package/src/write/top-text.js +54 -0
  170. package/src/write/unhighlight.js +11 -0
  171. package/temp.txt +15256 -0
  172. package/test.js +27 -64
  173. package/types/index.d.ts +1095 -0
  174. package/version.js +1 -1
  175. package/.babelrc +0 -5
  176. package/.eslintrc +0 -3
  177. package/.gitmodules +0 -3
  178. package/abcjs-midi.css +0 -166
  179. package/build-utils/loadPresets.js +0 -14
  180. package/build-utils/presets/webpack.analyze.js +0 -6
  181. package/build-utils/presets/webpack.optimize.js +0 -30
  182. package/build-utils/webpack.development.js +0 -14
  183. package/build-utils/webpack.production.js +0 -35
  184. package/deploy-docs.sh +0 -25
  185. package/docs/README.md +0 -33
  186. package/fix-versions.sh +0 -23
  187. package/mei.js +0 -43
  188. package/midi.js +0 -62
  189. package/src/api/abc_tunebook_midi.js +0 -116
  190. package/src/midi/abc_midi_controls.js +0 -701
  191. package/src/midi/abc_midi_flattener.js +0 -1119
  192. package/src/midi/abc_midi_js_preparer.js +0 -243
  193. package/src/midi/abc_midi_sequencer.js +0 -401
  194. package/src/midi/abc_midi_ui_generator.js +0 -86
  195. package/src/plugin/abc_plugin_midi.js +0 -220
  196. package/src/synth/images/loop.svg +0 -61
  197. package/src/synth/images/pause.svg +0 -6
  198. package/src/synth/images/play.svg +0 -5
  199. package/src/transform/abc2abc_write.js +0 -395
  200. package/static-wrappers/basic.js +0 -2
  201. package/static-wrappers/midi.js +0 -2
  202. package/static-wrappers/plugin-midi.js +0 -6
  203. package/static-wrappers/plugin.js +0 -6
  204. package/webpack.config.js +0 -29
@@ -1,26 +1,11 @@
1
- // Copyright (C) 2020 Paul Rosen (paul at paulrosen dot net)
2
- //
3
- // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
- // documentation files (the "Software"), to deal in the Software without restriction, including without limitation
5
- // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
6
- // to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
- //
8
- // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
- //
10
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
11
- // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
12
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13
- // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
14
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15
-
16
1
  var spacing = require('./abc_spacing');
17
2
 
18
3
  function setupSelection(engraver) {
19
4
  engraver.rangeHighlight = rangeHighlight;
20
5
  if (engraver.dragging) {
21
- for (var h = 0; h < engraver.history.length; h++) {
22
- var hist = engraver.history[h];
23
- if (hist.selectable) {
6
+ for (var h = 0; h < engraver.selectables.length; h++) {
7
+ var hist = engraver.selectables[h];
8
+ if (hist.svgEl.getAttribute("selectable") === "true") {
24
9
  hist.svgEl.setAttribute("tabindex", 0);
25
10
  hist.svgEl.setAttribute("data-index", h);
26
11
  hist.svgEl.addEventListener("keydown", keyboardDown.bind(engraver));
@@ -34,26 +19,42 @@ function setupSelection(engraver) {
34
19
  engraver.renderer.paper.svg.addEventListener('mouseup', mouseUp.bind(engraver));
35
20
  }
36
21
 
37
- function getCoord(ev) {
38
- var x = ev.offsetX;
39
- var y = ev.offsetY;
40
- // The target might be the SVG that we want, or it could be an item in the SVG (usually a path). If it is not the SVG then
41
- // add an offset to the coordinates.
42
- // if (ev.target.tagName.toLowerCase() !== 'svg') {
43
- // var box = ev.target.getBBox();
44
- // var absRect = ev.target.getBoundingClientRect();
45
- // var offsetX = ev.clientX - absRect.left;
46
- // var offsetY = ev.clientY - absRect.top;
47
- // x = offsetX + box.x;
48
- // y = offsetY + box.y;
49
- // }
50
- return [x,y];
22
+ function getCoord(ev, svg) {
23
+ var scaleX = 1;
24
+ var scaleY = 1;
25
+
26
+ // when renderer.options.responsive === 'resize' the click coords are in relation to the HTML
27
+ // element, we need to convert to the SVG viewBox coords
28
+ if (svg.viewBox.baseVal) { // Firefox passes null to this when no viewBox is given
29
+ // Chrome makes these values null when no viewBox is given.
30
+ if (svg.viewBox.baseVal.width !== 0)
31
+ scaleX = svg.viewBox.baseVal.width / svg.clientWidth
32
+ if (svg.viewBox.baseVal.height !== 0)
33
+ scaleY = svg.viewBox.baseVal.height / svg.clientHeight
34
+ }
35
+
36
+ var svgClicked = ev.target.tagName === "svg";
37
+ var x;
38
+ var y;
39
+ if (svgClicked) {
40
+ x = ev.offsetX;
41
+ y = ev.offsetY;
42
+ } else {
43
+ x = ev.layerX;
44
+ y = ev.layerY;
45
+ }
46
+
47
+ x = x * scaleX;
48
+ y = y * scaleY;
49
+ //console.log(x, y)
50
+
51
+ return [x, y];
51
52
  }
52
53
 
53
54
  function elementFocused(ev) {
54
55
  // If there had been another element focused and is being dragged, then report that before setting the new element up.
55
56
  if (this.dragMechanism === "keyboard" && this.dragYStep !== 0 && this.dragTarget)
56
- notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.history.length, this.dragIndex);
57
+ notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, ev);
57
58
 
58
59
  this.dragYStep = 0;
59
60
  }
@@ -75,14 +76,14 @@ function keyboardSelection(ev) {
75
76
  case 13:
76
77
  case 32:
77
78
  handled = true;
78
- this.dragTarget = this.history[index];
79
+ this.dragTarget = this.selectables[index];
79
80
  this.dragIndex = index;
80
81
  this.dragMechanism = "keyboard";
81
- mouseUp.bind(this)();
82
+ mouseUp.bind(this)(ev);
82
83
  break;
83
84
  case 38: // arrow up
84
85
  handled = true;
85
- this.dragTarget = this.history[index];
86
+ this.dragTarget = this.selectables[index];
86
87
  this.dragIndex = index;
87
88
  if (this.dragTarget.isDraggable) {
88
89
  if (this.dragging && this.dragTarget.isDraggable)
@@ -93,7 +94,7 @@ function keyboardSelection(ev) {
93
94
  break;
94
95
  case 40: // arrow down
95
96
  handled = true;
96
- this.dragTarget = this.history[index];
97
+ this.dragTarget = this.selectables[index];
97
98
  this.dragIndex = index;
98
99
  this.dragMechanism = "keyboard";
99
100
  if (this.dragTarget.isDraggable) {
@@ -106,7 +107,7 @@ function keyboardSelection(ev) {
106
107
  case 9: // tab
107
108
  // This is losing focus - if there had been dragging, then do the callback
108
109
  if (this.dragYStep !== 0) {
109
- mouseUp.bind(this)();
110
+ mouseUp.bind(this)(ev);
110
111
  }
111
112
  break;
112
113
  default:
@@ -117,22 +118,21 @@ function keyboardSelection(ev) {
117
118
  ev.preventDefault();
118
119
  }
119
120
 
120
- function mouseDown(ev) {
121
- // "this" is the EngraverController because of the bind(this) when setting the event listener.
122
-
123
- var box = getCoord(ev);
124
- var x = box[0];
125
- var y = box[1];
121
+ function findElementInHistory(selectables, el) {
122
+ for (var i = 0; i < selectables.length; i++) {
123
+ if (el === selectables[i].svgEl)
124
+ return i;
125
+ }
126
+ return -1;
127
+ }
126
128
 
129
+ function findElementByCoord(self, x, y) {
127
130
  var minDistance = 9999999;
128
131
  var closestIndex = -1;
129
- var chosenEl;
130
- for (var i = 0; i < this.history.length && minDistance > 0; i++) {
131
- var el = this.history[i];
132
- if (!el.selectable)
133
- continue;
132
+ for (var i = 0; i < self.selectables.length && minDistance > 0; i++) {
133
+ var el = self.selectables[i];
134
134
 
135
- this.getDim(el);
135
+ self.getDim(el);
136
136
  if (el.dim.left < x && el.dim.right > x && el.dim.top < y && el.dim.bottom > y) {
137
137
  // See if it is a direct hit on an element - if so, definitely take it (there are no overlapping elements)
138
138
  closestIndex = i;
@@ -162,13 +162,82 @@ function mouseDown(ev) {
162
162
  }
163
163
  }
164
164
  }
165
- if (closestIndex >= 0 && minDistance <= 12) {
166
- this.dragTarget = this.history[closestIndex];
167
- this.dragIndex = closestIndex;
165
+ return (closestIndex >= 0 && minDistance <= 12) ? closestIndex : -1;
166
+ }
167
+
168
+ function getBestMatchCoordinates(dim, ev, scale) {
169
+ // Different browsers have conflicting meanings for the coordinates that are returned.
170
+ // If the item we want is clicked on directly, then we will just see what is the best match.
171
+ // This seems like less of a hack than browser sniffing.
172
+ if (dim.x <= ev.offsetX && dim.x+dim.width >= ev.offsetX &&
173
+ dim.y <= ev.offsetY && dim.y+dim.height >= ev.offsetY)
174
+ return [ ev.offsetX, ev.offsetY];
175
+ // Firefox returns a weird value for offset, but layer is correct.
176
+ // Safari and Chrome return the correct value for offset, but layer is multiplied by the scale (that is, if it were rendered with { scale: 2 })
177
+ // For instance (if scale is 2):
178
+ // Firefox: { offsetY: 5, layerY: 335 }
179
+ // Others: {offsetY: 335, layerY: 670} (there could be a little rounding, so the number might not be exactly 2x)
180
+ // So, if layerY/scale is approx. offsetY, then use offsetY, otherwise use layerY
181
+ var epsilon = Math.abs(ev.layerY/scale - ev.offsetY);
182
+ if (epsilon < 3)
183
+ return [ ev.offsetX, ev.offsetY];
184
+ else
185
+ return [ ev.layerX, ev.layerY];
186
+ }
187
+
188
+ function getTarget(target) {
189
+ // This searches up the dom for the first item containing the attribute "selectable", or stopping at the SVG.
190
+ if (target.tagName === "svg")
191
+ return target;
192
+
193
+ var found = target.getAttribute("selectable");
194
+ while (!found) {
195
+ target = target.parentElement;
196
+ if (target.tagName === "svg")
197
+ found = true;
198
+ else
199
+ found = target.getAttribute("selectable");
200
+ }
201
+ return target;
202
+ }
203
+
204
+ function getMousePosition(self, ev) {
205
+ // if the user clicked exactly on an element that we're interested in, then we already have the answer.
206
+ // This is more reliable than the calculations because firefox returns different coords for offsetX, offsetY
207
+ var x;
208
+ var y;
209
+ var box;
210
+ var clickedOn = findElementInHistory(self.selectables, getTarget(ev.target));
211
+ if (clickedOn >= 0) {
212
+ // There was a direct hit on an element.
213
+ box = getBestMatchCoordinates(self.selectables[clickedOn].svgEl.getBBox(), ev, self.scale);
214
+ x = box[0];
215
+ y = box[1];
216
+ //console.log("clicked on", clickedOn, x, y, self.selectables[clickedOn].svgEl.getBBox(), ev.target.getBBox());
217
+ } else {
218
+ // See if they clicked close to an element.
219
+ box = getCoord(ev, self.renderer.paper.svg);
220
+ x = box[0];
221
+ y = box[1];
222
+ clickedOn = findElementByCoord(self, x, y);
223
+ //console.log("clicked near", clickedOn, x, y, printEl(ev.target));
224
+ }
225
+ return { x: x, y: y, clickedOn: clickedOn };
226
+ }
227
+
228
+ function mouseDown(ev) {
229
+ // "this" is the EngraverController because of the bind(this) when setting the event listener.
230
+
231
+ var positioning = getMousePosition(this, ev);
232
+
233
+ // Only start dragging if the user clicked close enough to an element and clicked with the main mouse button.
234
+ if (positioning.clickedOn >= 0 && ev.button === 0) {
235
+ this.dragTarget = this.selectables[positioning.clickedOn];
236
+ this.dragIndex = positioning.clickedOn;
168
237
  this.dragMechanism = "mouse";
169
- this.dragMouseStart = { x: x, y: y };
238
+ this.dragMouseStart = { x: positioning.x, y: positioning.y };
170
239
  if (this.dragging && this.dragTarget.isDraggable) {
171
- this.renderer.addGlobalClass("abcjs-dragging-in-progress");
240
+ addGlobalClass(this.renderer.paper, "abcjs-dragging-in-progress");
172
241
  this.dragTarget.absEl.highlight(undefined, this.dragColor);
173
242
  }
174
243
  }
@@ -180,11 +249,9 @@ function mouseMove(ev) {
180
249
  if (!this.dragTarget || !this.dragging || !this.dragTarget.isDraggable || this.dragMechanism !== 'mouse')
181
250
  return;
182
251
 
183
- var box = getCoord(ev);
184
- var x = box[0];
185
- var y = box[1];
252
+ var positioning = getMousePosition(this, ev);
186
253
 
187
- var yDist = Math.round((y - this.dragMouseStart.y)/spacing.STEP);
254
+ var yDist = Math.round((positioning.y - this.dragMouseStart.y)/spacing.STEP);
188
255
  if (yDist !== this.dragYStep) {
189
256
  this.dragYStep = yDist;
190
257
  this.dragTarget.svgEl.setAttribute("transform", "translate(0," + (yDist * spacing.STEP) + ")");
@@ -203,23 +270,25 @@ function mouseUp(ev) {
203
270
  this.dragTarget.absEl.highlight(undefined, this.selectionColor);
204
271
  }
205
272
 
206
- notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.history.length, this.dragIndex);
207
- this.dragTarget.svgEl.focus();
208
- this.dragTarget = null;
209
- this.dragIndex = -1;
210
- this.renderer.removeGlobalClass("abcjs-dragging-in-progress");
273
+ notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, ev);
274
+ if (this.dragTarget.svgEl && this.dragTarget.svgEl.focus) {
275
+ this.dragTarget.svgEl.focus();
276
+ this.dragTarget = null;
277
+ this.dragIndex = -1;
278
+ }
279
+ removeGlobalClass(this.renderer.svg, "abcjs-dragging-in-progress");
211
280
  }
212
281
 
213
282
  function setSelection(dragIndex) {
214
- if (dragIndex >= 0 && dragIndex < this.history.length) {
215
- this.dragTarget = this.history[dragIndex];
283
+ if (dragIndex >= 0 && dragIndex < this.selectables.length) {
284
+ this.dragTarget = this.selectables[dragIndex];
216
285
  this.dragIndex = dragIndex;
217
286
  this.dragMechanism = "keyboard";
218
- mouseUp.bind(this)();
287
+ mouseUp.bind(this)({target: this.dragTarget.svgEl});
219
288
  }
220
289
  }
221
290
 
222
- function notifySelect(target, dragStep, dragMax, dragIndex) {
291
+ function notifySelect(target, dragStep, dragMax, dragIndex, ev) {
223
292
  var classes = [];
224
293
  if (target.absEl.elemset) {
225
294
  var classObj = {};
@@ -240,9 +309,22 @@ function notifySelect(target, dragStep, dragMax, dragIndex) {
240
309
  findNumber(classes[ii], "abcjs-l", analysis, "line");
241
310
  findNumber(classes[ii], "abcjs-m", analysis, "measure");
242
311
  }
312
+ if (target.staffPos)
313
+ analysis.staffPos = target.staffPos;
314
+ var closest = ev.target;
315
+ while (!closest.dataset.name && closest.tagName.toLowerCase() !== 'svg')
316
+ closest = closest.parentNode;
317
+ var parent = ev.target;
318
+ while (!parent.dataset.index && parent.tagName.toLowerCase() !== 'svg')
319
+ parent = parent.parentNode;
320
+ analysis.name = parent.dataset.name;
321
+ analysis.clickedName = closest.dataset.name;
322
+ analysis.parentClasses = parent.classList;
323
+ analysis.clickedClasses = closest.classList;
324
+ analysis.selectableElement = target.svgEl;
243
325
 
244
326
  for (var i=0; i<this.listeners.length;i++) {
245
- this.listeners[i](target.absEl.abcelem, target.absEl.tuneNumber, classes.join(' '), analysis, { step: dragStep, max: dragMax, index: dragIndex, setSelection: setSelection.bind(this)});
327
+ this.listeners[i](target.absEl.abcelem, target.absEl.tuneNumber, classes.join(' '), analysis, { step: dragStep, max: dragMax, index: dragIndex, setSelection: setSelection.bind(this)}, ev);
246
328
  }
247
329
  }
248
330
 
@@ -257,7 +339,7 @@ function findNumber(klass, match, target, name) {
257
339
 
258
340
  function clearSelection() {
259
341
  for (var i=0;i<this.selected.length;i++) {
260
- this.selected[i].unhighlight();
342
+ this.selected[i].unhighlight(undefined, this.renderer.foregroundColor);
261
343
  }
262
344
  this.selected = [];
263
345
  }
@@ -283,4 +365,40 @@ function rangeHighlight(start,end) {
283
365
  }
284
366
  }
285
367
 
368
+ function getClassSet(el) {
369
+ var oldClass = el.getAttribute('class');
370
+ if (!oldClass)
371
+ oldClass = "";
372
+ var klasses = oldClass.split(" ");
373
+ var obj = {};
374
+ for (var i = 0; i < klasses.length; i++)
375
+ obj[klasses[i]] = true;
376
+ return obj;
377
+ }
378
+
379
+ function setClassSet(el, klassSet) {
380
+ var klasses = [];
381
+ for (var key in klassSet) {
382
+ if (klassSet.hasOwnProperty(key))
383
+ klasses.push(key);
384
+ }
385
+ el.setAttribute('class', klasses.join(' '));
386
+ }
387
+
388
+ function addGlobalClass(svg, klass) {
389
+ if (svg) {
390
+ var obj = getClassSet(svg.svg);
391
+ obj[klass] = true;
392
+ setClassSet(svg.svg, obj);
393
+ }
394
+ }
395
+
396
+ function removeGlobalClass(svg, klass) {
397
+ if (svg) {
398
+ var obj = getClassSet(svg.svg);
399
+ delete obj[klass];
400
+ setClassSet(svg.svg, obj);
401
+ }
402
+ }
403
+
286
404
  module.exports = setupSelection;
@@ -0,0 +1,10 @@
1
+ function Separator(spaceAbove, lineLength, spaceBelow) {
2
+ this.rows = [];
3
+ if (spaceAbove)
4
+ this.rows.push({move: spaceAbove});
5
+ this.rows.push({ separator: lineLength, absElemType: "separator" });
6
+ if (spaceBelow)
7
+ this.rows.push({move: spaceBelow});
8
+ }
9
+
10
+ module.exports = Separator;
@@ -0,0 +1,21 @@
1
+ var setClass = function (elemset, addClass, removeClass, color) {
2
+ if (!elemset)
3
+ return;
4
+ for (var i = 0; i < elemset.length; i++) {
5
+ var el = elemset[i];
6
+ var attr = el.getAttribute("highlight");
7
+ if (!attr) attr = "fill";
8
+ el.setAttribute(attr, color);
9
+ var kls = el.getAttribute("class");
10
+ if (!kls) kls = "";
11
+ kls = kls.replace(removeClass, "");
12
+ kls = kls.replace(addClass, "");
13
+ if (addClass.length > 0) {
14
+ if (kls.length > 0 && kls.charAt(kls.length - 1) !== ' ') kls += " ";
15
+ kls += addClass;
16
+ }
17
+ el.setAttribute("class", kls);
18
+ }
19
+ };
20
+
21
+ module.exports = setClass;
@@ -0,0 +1,12 @@
1
+ function Subtitle(spaceAbove, formatting, info, center, paddingLeft, getTextSize) {
2
+ this.rows = [];
3
+ if (spaceAbove)
4
+ this.rows.push({move: spaceAbove});
5
+ var tAnchor = formatting.titleleft ? 'start' : 'middle';
6
+ var tLeft = formatting.titleleft ? paddingLeft : center;
7
+ this.rows.push({left: tLeft, text: info.text, font: 'subtitlefont', klass: 'text subtitle', anchor: tAnchor, startChar: info.startChar, endChar: info.endChar, absElemType: "subtitle", name: "subtitle"});
8
+ var size = getTextSize.calc(info.text, 'subtitlefont', 'text subtitle');
9
+ this.rows.push({move: size.height});
10
+ }
11
+
12
+ module.exports = Subtitle;
package/src/write/svg.js CHANGED
@@ -1,18 +1,4 @@
1
1
  // abc_voice_element.js: Definition of the VoiceElement class.
2
- // Copyright (C) 2010-2020 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
3
- //
4
- // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
- // documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6
- // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
7
- // to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
- //
9
- // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10
- //
11
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
12
- // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
13
- // NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
14
- // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
2
 
17
3
  /*global module */
18
4
 
@@ -20,6 +6,7 @@ var svgNS = "http://www.w3.org/2000/svg";
20
6
 
21
7
  function Svg(wrapper) {
22
8
  this.svg = createSvg();
9
+ this.currentGroup = [];
23
10
  wrapper.appendChild(this.svg);
24
11
  }
25
12
 
@@ -27,6 +14,7 @@ Svg.prototype.clear = function() {
27
14
  if (this.svg) {
28
15
  var wrapper = this.svg.parentNode;
29
16
  this.svg = createSvg();
17
+ this.currentGroup = [];
30
18
  if (wrapper) {
31
19
  // TODO-PER: If the wrapper is not present, then the underlying div was pulled out from under this instance. It's possible that is still useful (for creating the music off page?)
32
20
  wrapper.innerHTML = "";
@@ -74,14 +62,10 @@ Svg.prototype.setResponsiveWidth = function(w, h) {
74
62
  Svg.prototype.setSize = function(w, h) {
75
63
  this.svg.setAttribute('width', w);
76
64
  this.svg.setAttribute('height', h);
77
- // TODO-PER: Is this hack still needed?
78
- // Correct for IE problem in calculating height
79
- // var isIE = /*@cc_on!@*/false;//IE detector
80
- // if (isIE) {
81
- // this.paper.canvas.parentNode.style.width = w + "px";
82
- // this.paper.canvas.parentNode.style.height = "" + h + "px";
83
- // } else
84
- // this.paper.canvas.parentNode.setAttribute("style", "width:" + w + "px");
65
+ };
66
+
67
+ Svg.prototype.setAttribute = function(attr, value) {
68
+ this.svg.setAttribute(attr, value);
85
69
  };
86
70
 
87
71
  Svg.prototype.setScale = function(scale) {
@@ -126,15 +110,19 @@ Svg.prototype.setParentStyles = function(attr) {
126
110
  };
127
111
 
128
112
  function constructHLine(x1, y1, x2) {
129
- return "M " + x1 + " " + y1 + " L " + x2 + ' ' + y1 +
130
- " L " + x2 + " " + (y1+1) + " " +
131
- " L " + x1 + " " + (y1+1) + " " + " z ";
113
+ var len = x2 - x1;
114
+ return "M " + x1 + " " + y1 +
115
+ " l " + len + ' ' + 0 +
116
+ " l " + 0 + " " + 1 + " " +
117
+ " l " + (-len) + " " + 0 + " " + " z ";
132
118
  }
133
119
 
134
120
  function constructVLine(x1, y1, y2) {
135
- return "M " + x1 + " " + y1 + " L " + x1 + ' ' + y2 +
136
- " L " + (x1+1) + " " + y2 + " " +
137
- " L " + (x1+1) + " " + y1 + " " + " z ";
121
+ var len = y2 - y1;
122
+ return "M " + x1 + " " + y1 +
123
+ " l " + 0 + ' ' + len +
124
+ " l " + 1 + " " + 0 + " " +
125
+ " l " + 0 + " " + (-len) + " " + " z ";
138
126
  }
139
127
 
140
128
  Svg.prototype.rect = function(attr) {
@@ -146,14 +134,43 @@ Svg.prototype.rect = function(attr) {
146
134
  var y2 = attr.y + attr.height;
147
135
  lines.push(constructHLine(x1, y1, x2));
148
136
  lines.push(constructHLine(x1, y2, x2));
149
- lines.push(constructVLine(x1, y1, y2));
150
137
  lines.push(constructVLine(x2, y1, y2));
138
+ lines.push(constructVLine(x1, y2, y1));
139
+
140
+ return this.path({ path: lines.join(" "), stroke: "none", "data-name": attr["data-name"] });
141
+ };
151
142
 
152
- return this.path({ path: lines.join(" "), stroke: "none"});
143
+ Svg.prototype.dottedLine = function(attr) {
144
+ var el = document.createElementNS(svgNS, 'line');
145
+ el.setAttribute("x1", attr.x1);
146
+ el.setAttribute("x2", attr.x2);
147
+ el.setAttribute("y1", attr.y1);
148
+ el.setAttribute("y2", attr.y2);
149
+ el.setAttribute("stroke", attr.stroke);
150
+ el.setAttribute("stroke-dasharray", "5,5");
151
+ this.svg.insertBefore(el, this.svg.firstChild);
152
+ };
153
+
154
+ Svg.prototype.rectBeneath = function(attr) {
155
+ var el = document.createElementNS(svgNS, 'rect');
156
+ el.setAttribute("x", attr.x);
157
+ el.setAttribute("width", attr.width);
158
+ el.setAttribute("y", attr.y);
159
+ el.setAttribute("height", attr.height);
160
+ if (attr.stroke)
161
+ el.setAttribute("stroke", attr.stroke);
162
+ if (attr['stroke-opacity'])
163
+ el.setAttribute("stroke-opacity", attr['stroke-opacity']);
164
+ if (attr.fill)
165
+ el.setAttribute("fill", attr.fill);
166
+ if (attr['fill-opacity'])
167
+ el.setAttribute("fill-opacity", attr['fill-opacity']);
168
+ this.svg.insertBefore(el, this.svg.firstChild);
153
169
  };
154
170
 
155
171
  Svg.prototype.text = function(text, attr, target) {
156
172
  var el = document.createElementNS(svgNS, 'text');
173
+ el.setAttribute("stroke", "none");
157
174
  for (var key in attr) {
158
175
  if (attr.hasOwnProperty(key)) {
159
176
  el.setAttribute(key, attr[key]);
@@ -162,10 +179,29 @@ Svg.prototype.text = function(text, attr, target) {
162
179
  var lines = (""+text).split("\n");
163
180
  for (var i = 0; i < lines.length; i++) {
164
181
  var line = document.createElementNS(svgNS, 'tspan');
165
- line.textContent = lines[i];
166
182
  line.setAttribute("x", attr.x ? attr.x : 0);
167
183
  if (i !== 0)
168
184
  line.setAttribute("dy", "1.2em");
185
+ if (lines[i].indexOf("\x03") !== -1) {
186
+ var parts = lines[i].split('\x03')
187
+ line.textContent = parts[0];
188
+ if (parts[1]) {
189
+ var ts2 = document.createElementNS(svgNS, 'tspan');
190
+ ts2.setAttribute("dy", "-0.3em");
191
+ ts2.setAttribute("style", "font-size:0.7em");
192
+ ts2.textContent = parts[1];
193
+ line.appendChild(ts2);
194
+ }
195
+ if (parts[2]) {
196
+ var dist = parts[1] ? "0.4em" : "0.1em";
197
+ var ts3 = document.createElementNS(svgNS, 'tspan');
198
+ ts3.setAttribute("dy", dist);
199
+ ts3.setAttribute("style", "font-size:0.7em");
200
+ ts3.textContent = parts[2];
201
+ line.appendChild(ts3);
202
+ }
203
+ } else
204
+ line.textContent = lines[i];
169
205
  el.appendChild(line);
170
206
  }
171
207
  if (target)
@@ -237,8 +273,8 @@ Svg.prototype.getTextSize = function(text, attr, el) {
237
273
  size = this.guessWidth(text, attr);
238
274
  }
239
275
  if (removeLater) {
240
- if (this.currentGroup)
241
- this.currentGroup.removeChild(el);
276
+ if (this.currentGroup.length > 0)
277
+ this.currentGroup[0].removeChild(el);
242
278
  else
243
279
  this.svg.removeChild(el);
244
280
  }
@@ -256,18 +292,24 @@ Svg.prototype.openGroup = function(options) {
256
292
  el.setAttribute("fill", options.fill);
257
293
  if (options.stroke)
258
294
  el.setAttribute("stroke", options.stroke);
295
+ if (options['data-name'])
296
+ el.setAttribute("data-name", options['data-name']);
259
297
 
260
298
  if (options.prepend)
261
- this.svg.insertBefore(el, this.svg.firstChild);
299
+ this.prepend(el);
262
300
  else
263
- this.svg.appendChild(el);
264
- this.currentGroup = el;
301
+ this.append(el);
302
+ this.currentGroup.unshift(el);
265
303
  return el;
266
304
  };
267
305
 
268
306
  Svg.prototype.closeGroup = function() {
269
- var g = this.currentGroup;
270
- this.currentGroup = null;
307
+ var g = this.currentGroup.shift();
308
+ if (g && g.children.length === 0) {
309
+ // If nothing was added to the group it is because all the elements were invisible. We don't need the group, then.
310
+ this.svg.removeChild(g);
311
+ return null;
312
+ }
271
313
  return g;
272
314
  };
273
315
 
@@ -277,7 +319,9 @@ Svg.prototype.path = function(attr) {
277
319
  if (attr.hasOwnProperty(key)) {
278
320
  if (key === 'path')
279
321
  el.setAttributeNS(null, 'd', attr.path);
280
- else
322
+ else if (key === 'klass')
323
+ el.setAttributeNS(null, "class", attr[key]);
324
+ else if (attr[key] !== undefined)
281
325
  el.setAttributeNS(null, key, attr[key]);
282
326
  }
283
327
  }
@@ -291,6 +335,8 @@ Svg.prototype.pathToBack = function(attr) {
291
335
  if (attr.hasOwnProperty(key)) {
292
336
  if (key === 'path')
293
337
  el.setAttributeNS(null, 'd', attr.path);
338
+ else if (key === 'klass')
339
+ el.setAttributeNS(null, "class", attr[key]);
294
340
  else
295
341
  el.setAttributeNS(null, key, attr[key]);
296
342
  }
@@ -300,16 +346,16 @@ Svg.prototype.pathToBack = function(attr) {
300
346
  };
301
347
 
302
348
  Svg.prototype.append = function(el) {
303
- if (this.currentGroup)
304
- this.currentGroup.appendChild(el);
349
+ if (this.currentGroup.length > 0)
350
+ this.currentGroup[0].appendChild(el);
305
351
  else
306
352
  this.svg.appendChild(el);
307
353
  };
308
354
 
309
355
  Svg.prototype.prepend = function(el) {
310
356
  // The entire group is prepended, so don't prepend the individual elements.
311
- if (this.currentGroup)
312
- this.currentGroup.appendChild(el);
357
+ if (this.currentGroup.length > 0)
358
+ this.currentGroup[0].appendChild(el);
313
359
  else
314
360
  this.svg.insertBefore(el, this.svg.firstChild);
315
361
  };
@@ -322,10 +368,16 @@ Svg.prototype.setAttributeOnElement = function(el, attr) {
322
368
  }
323
369
  };
324
370
 
371
+ Svg.prototype.moveElementToChild = function(parent, child) {
372
+ parent.appendChild(child);
373
+ };
374
+
325
375
  function createSvg() {
326
376
  var svg = document.createElementNS(svgNS, "svg");
327
377
  svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
328
378
  svg.setAttribute('role', 'img'); // for accessibility
379
+ svg.setAttribute('fill', 'currentColor'); // for automatically picking up dark mode and high contrast
380
+ svg.setAttribute('stroke', 'currentColor'); // for automatically picking up dark mode and high contrast
329
381
  return svg;
330
382
  }
331
383