abcjs 6.1.9 → 6.2.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 (135) hide show
  1. package/LICENSE.md +1 -1
  2. package/RELEASE.md +58 -0
  3. package/dist/abcjs-basic-min.js +2 -2
  4. package/dist/abcjs-basic-min.js.LICENSE +1 -1
  5. package/dist/abcjs-basic.js +4505 -4556
  6. package/dist/abcjs-basic.js.map +1 -1
  7. package/dist/abcjs-plugin-min.js +2 -2
  8. package/dist/abcjs-plugin-min.js.LICENSE +1 -1
  9. package/index.js +2 -2
  10. package/license.js +1 -1
  11. package/package.json +1 -1
  12. package/plugin.js +2 -2
  13. package/src/api/abc_timing_callbacks.js +8 -4
  14. package/src/api/abc_tunebook_svg.js +1 -2
  15. package/src/data/abc_tune.js +3 -3
  16. package/src/parse/abc_common.js +0 -47
  17. package/src/parse/abc_parse.js +16 -16
  18. package/src/parse/abc_parse_book.js +3 -3
  19. package/src/parse/abc_parse_directive.js +26 -7
  20. package/src/parse/abc_parse_header.js +11 -9
  21. package/src/parse/abc_parse_key_voice.js +17 -17
  22. package/src/parse/abc_parse_music.js +88 -105
  23. package/src/parse/abc_tokenizer.js +60 -60
  24. package/src/parse/tune-builder.js +19 -14
  25. package/src/synth/abc_midi_flattener.js +25 -9
  26. package/src/synth/create-synth.js +71 -4
  27. package/src/synth/load-note.js +1 -1
  28. package/src/synth/note-to-midi.js +50 -0
  29. package/src/synth/place-note.js +10 -2
  30. package/src/tablatures/instruments/guitar/tab-guitar.js +0 -2
  31. package/src/tablatures/instruments/string-patterns.js +47 -29
  32. package/src/tablatures/instruments/tab-note.js +26 -103
  33. package/src/tablatures/instruments/violin/tab-violin.js +0 -2
  34. package/src/tablatures/tab-absolute-elements.js +9 -31
  35. package/src/tablatures/tab-renderer.js +2 -2
  36. package/src/test/abc_parser_lint.js +7 -4
  37. package/src/write/README.md +31 -0
  38. package/src/write/creation/abstract-engraver.js +1036 -0
  39. package/src/write/creation/add-chord.js +102 -0
  40. package/src/write/{add-text-if.js → creation/add-text-if.js} +6 -6
  41. package/src/write/{calcHeight.js → creation/calc-height.js} +2 -2
  42. package/src/write/creation/create-clef.js +72 -0
  43. package/src/write/creation/create-key-signature.js +31 -0
  44. package/src/write/creation/create-note-head.js +107 -0
  45. package/src/write/creation/create-time-signature.js +55 -0
  46. package/src/write/creation/decoration.js +357 -0
  47. package/src/write/{abc_absolute_element.js → creation/elements/absolute-element.js} +14 -15
  48. package/src/write/creation/elements/beam-element.js +113 -0
  49. package/src/write/{bottom-text.js → creation/elements/bottom-text.js} +14 -15
  50. package/src/write/{abc_brace_element.js → creation/elements/brace-element.js} +5 -5
  51. package/src/write/creation/elements/free-text.js +41 -0
  52. package/src/write/{abc_relative_element.js → creation/elements/relative-element.js} +7 -7
  53. package/src/write/{separator.js → creation/elements/separator.js} +2 -2
  54. package/src/write/{abc_staff_group_element.js → creation/elements/staff-group-element.js} +4 -4
  55. package/src/write/{subtitle.js → creation/elements/subtitle.js} +3 -3
  56. package/src/write/creation/elements/tempo-element.js +63 -0
  57. package/src/write/{abc_tie_element.js → creation/elements/tie-element.js} +15 -11
  58. package/src/write/{top-text.js → creation/elements/top-text.js} +12 -12
  59. package/src/write/creation/elements/triplet-element.js +28 -0
  60. package/src/write/{abc_voice_element.js → creation/elements/voice-element.js} +3 -3
  61. package/src/write/creation/glyphs.js +226 -0
  62. package/src/write/creation/translate-chord.js +37 -0
  63. package/src/write/draw/absolute.js +5 -5
  64. package/src/write/draw/beam.js +8 -8
  65. package/src/write/draw/brace.js +33 -33
  66. package/src/write/draw/crescendo.js +4 -4
  67. package/src/write/draw/debug-box.js +1 -1
  68. package/src/write/draw/draw.js +7 -7
  69. package/src/write/draw/dynamics.js +2 -2
  70. package/src/write/draw/ending.js +6 -6
  71. package/src/write/draw/glissando.js +17 -17
  72. package/src/write/draw/group-elements.js +51 -51
  73. package/src/write/draw/horizontal-line.js +9 -9
  74. package/src/write/draw/non-music.js +1 -1
  75. package/src/write/draw/print-line.js +25 -16
  76. package/src/write/draw/print-stem.js +15 -5
  77. package/src/write/draw/print-symbol.js +12 -12
  78. package/src/write/draw/print-vertical-line.js +8 -8
  79. package/src/write/draw/relative.js +17 -16
  80. package/src/write/draw/selectables.js +5 -5
  81. package/src/write/draw/separator.js +4 -4
  82. package/src/write/draw/set-paper-size.js +2 -2
  83. package/src/write/draw/sprintf.js +31 -31
  84. package/src/write/draw/staff-group.js +36 -30
  85. package/src/write/draw/staff-line.js +2 -2
  86. package/src/write/draw/staff.js +4 -4
  87. package/src/write/draw/tab-line.js +26 -26
  88. package/src/write/draw/tempo.js +30 -30
  89. package/src/write/draw/text.js +5 -5
  90. package/src/write/draw/tie.js +18 -18
  91. package/src/write/draw/triplet.js +6 -6
  92. package/src/write/draw/voice.js +16 -17
  93. package/src/write/{abc_engraver_controller.js → engraver-controller.js} +58 -51
  94. package/src/write/{classes.js → helpers/classes.js} +6 -6
  95. package/src/write/{get-font-and-attr.js → helpers/get-font-and-attr.js} +9 -7
  96. package/src/write/{get-text-size.js → helpers/get-text-size.js} +5 -5
  97. package/src/write/{set-class.js → helpers/set-class.js} +1 -1
  98. package/src/write/{abc_spacing.js → helpers/spacing.js} +1 -1
  99. package/src/write/{highlight.js → interactive/highlight.js} +1 -1
  100. package/src/write/{selection.js → interactive/selection.js} +34 -31
  101. package/src/write/{unhighlight.js → interactive/unhighlight.js} +1 -1
  102. package/src/write/layout/beam.js +13 -13
  103. package/src/write/layout/get-left-edge-of-staff.js +4 -4
  104. package/src/write/layout/layout.js +74 -74
  105. package/src/write/layout/{setUpperAndLowerElements.js → set-upper-and-lower-elements.js} +8 -8
  106. package/src/write/layout/{staffGroup.js → staff-group.js} +32 -32
  107. package/src/write/layout/triplet.js +4 -4
  108. package/src/write/layout/{VoiceElements.js → voice-elements.js} +23 -23
  109. package/src/write/layout/voice.js +6 -6
  110. package/src/write/{abc_renderer.js → renderer.js} +36 -32
  111. package/src/write/svg.js +35 -35
  112. package/test.js +2 -2
  113. package/types/index.d.ts +37 -8
  114. package/version.js +1 -1
  115. package/src/tablatures/instruments/guitar/guitar-fonts.js +0 -19
  116. package/src/tablatures/instruments/violin/violin-fonts.js +0 -19
  117. package/src/tablatures/transposer.js +0 -110
  118. package/src/write/abc_abstract_engraver.js +0 -1026
  119. package/src/write/abc_beam_element.js +0 -113
  120. package/src/write/abc_create_clef.js +0 -72
  121. package/src/write/abc_create_key_signature.js +0 -33
  122. package/src/write/abc_create_note_head.js +0 -107
  123. package/src/write/abc_create_time_signature.js +0 -55
  124. package/src/write/abc_decoration.js +0 -357
  125. package/src/write/abc_glyphs.js +0 -224
  126. package/src/write/abc_tempo_element.js +0 -63
  127. package/src/write/abc_triplet_element.js +0 -28
  128. package/src/write/add-chord.js +0 -103
  129. package/src/write/format-jazz-chord.js +0 -15
  130. package/src/write/free-text.js +0 -41
  131. /package/src/write/{abc_crescendo_element.js → creation/elements/crescendo-element.js} +0 -0
  132. /package/src/write/{abc_dynamic_decoration.js → creation/elements/dynamic-decoration.js} +0 -0
  133. /package/src/write/{abc_ending_element.js → creation/elements/ending-element.js} +0 -0
  134. /package/src/write/{abc_glissando_element.js → creation/elements/glissando-element.js} +0 -0
  135. /package/src/write/layout/{getBarYAt.js → get-bar-y-at.js} +0 -0
@@ -0,0 +1,357 @@
1
+ // abc_decoration.js: Creates a data structure suitable for printing a line of abc
2
+
3
+ var DynamicDecoration = require('./elements/dynamic-decoration');
4
+ var CrescendoElem = require('./elements/crescendo-element');
5
+ var GlissandoElem = require('./elements/glissando-element');
6
+ var glyphs = require('./glyphs');
7
+ var RelativeElement = require('./elements/relative-element');
8
+ var TieElem = require('./elements/tie-element');
9
+
10
+ var Decoration = function Decoration() {
11
+ this.startDiminuendoX = undefined;
12
+ this.startCrescendoX = undefined;
13
+ this.minTop = 12; // TODO-PER: this is assuming a 5-line staff. Pass that info in.
14
+ this.minBottom = 0;
15
+ };
16
+
17
+ var closeDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch) {
18
+ var yPos;
19
+ for (var i = 0; i < decoration.length; i++) {
20
+ if (decoration[i] === "staccato" || decoration[i] === "tenuto" || decoration[i] === "accent") {
21
+ var symbol = "scripts." + decoration[i];
22
+ if (decoration[i] === "accent") symbol = "scripts.sforzato";
23
+ if (yPos === undefined)
24
+ yPos = (dir === "down") ? pitch + 2 : minPitch - 2;
25
+ else
26
+ yPos = (dir === "down") ? yPos + 2 : yPos - 2;
27
+ if (decoration[i] === "accent") {
28
+ // Always place the accent three pitches away, no matter whether that is a line or space.
29
+ if (dir === "up") yPos--;
30
+ else yPos++;
31
+ } else {
32
+ // don't place on a stave line. The stave lines are 2,4,6,8,10
33
+ switch (yPos) {
34
+ case 2:
35
+ case 4:
36
+ case 6:
37
+ case 8:
38
+ case 10:
39
+ if (dir === "up") yPos--;
40
+ else yPos++;
41
+ break;
42
+ }
43
+ }
44
+ if (pitch > 9) yPos++; // take up some room of those that are above
45
+ var deltaX = width / 2;
46
+ if (glyphs.getSymbolAlign(symbol) !== "center") {
47
+ deltaX -= (glyphs.getSymbolWidth(symbol) / 2);
48
+ }
49
+ abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), yPos));
50
+ }
51
+ if (decoration[i] === "slide" && abselem.heads[0]) {
52
+ var yPos2 = abselem.heads[0].pitch;
53
+ yPos2 -= 2; // TODO-PER: not sure what this fudge factor is.
54
+ var blank1 = new RelativeElement("", -roomtaken - 15, 0, yPos2 - 1);
55
+ var blank2 = new RelativeElement("", -roomtaken - 5, 0, yPos2 + 1);
56
+ abselem.addFixedX(blank1);
57
+ abselem.addFixedX(blank2);
58
+ voice.addOther(new TieElem({ anchor1: blank1, anchor2: blank2, fixedY: true }));
59
+ }
60
+ }
61
+ if (yPos === undefined)
62
+ yPos = pitch;
63
+
64
+ return { above: yPos, below: abselem.bottom };
65
+ };
66
+
67
+ var volumeDecoration = function (voice, decoration, abselem, positioning) {
68
+ for (var i = 0; i < decoration.length; i++) {
69
+ switch (decoration[i]) {
70
+ case "p":
71
+ case "mp":
72
+ case "pp":
73
+ case "ppp":
74
+ case "pppp":
75
+ case "f":
76
+ case "ff":
77
+ case "fff":
78
+ case "ffff":
79
+ case "sfz":
80
+ case "mf":
81
+ var elem = new DynamicDecoration(abselem, decoration[i], positioning);
82
+ voice.addOther(elem);
83
+ }
84
+ }
85
+ };
86
+
87
+ var compoundDecoration = function (decoration, pitch, width, abselem, dir) {
88
+ function highestPitch() {
89
+ if (abselem.heads.length === 0)
90
+ return 10; // TODO-PER: I don't know if this can happen, but we'll return the top of the staff if so.
91
+ var pitch = abselem.heads[0].pitch;
92
+ for (var i = 1; i < abselem.heads.length; i++)
93
+ pitch = Math.max(pitch, abselem.heads[i].pitch);
94
+ return pitch;
95
+ }
96
+ function lowestPitch() {
97
+ if (abselem.heads.length === 0)
98
+ return 2; // TODO-PER: I don't know if this can happen, but we'll return the bottom of the staff if so.
99
+ var pitch = abselem.heads[0].pitch;
100
+ for (var i = 1; i < abselem.heads.length; i++)
101
+ pitch = Math.min(pitch, abselem.heads[i].pitch);
102
+ return pitch;
103
+ }
104
+ function compoundDecoration(symbol, count) {
105
+ var placement = (dir === 'down') ? lowestPitch() + 1 : highestPitch() + 9;
106
+ if (dir !== 'down' && count === 1)
107
+ placement--;
108
+ var deltaX = width / 2;
109
+ deltaX += (dir === 'down') ? -5 : 3;
110
+ for (var i = 0; i < count; i++) {
111
+ placement -= 1;
112
+ abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), placement));
113
+ }
114
+ }
115
+
116
+ for (var i = 0; i < decoration.length; i++) {
117
+ switch (decoration[i]) {
118
+ case "/": compoundDecoration("flags.ugrace", 1); break;
119
+ case "//": compoundDecoration("flags.ugrace", 2); break;
120
+ case "///": compoundDecoration("flags.ugrace", 3); break;
121
+ case "////": compoundDecoration("flags.ugrace", 4); break;
122
+ }
123
+ }
124
+ };
125
+
126
+ var stackedDecoration = function (decoration, width, abselem, yPos, positioning, minTop, minBottom) {
127
+ function incrementPlacement(placement, height) {
128
+ if (placement === 'above')
129
+ yPos.above += height;
130
+ else
131
+ yPos.below -= height;
132
+ }
133
+ function getPlacement(placement) {
134
+ var y;
135
+ if (placement === 'above') {
136
+ y = yPos.above;
137
+ if (y < minTop)
138
+ y = minTop;
139
+ } else {
140
+ y = yPos.below;
141
+ if (y > minBottom)
142
+ y = minBottom;
143
+ }
144
+ return y;
145
+ }
146
+ function textDecoration(text, placement, anchor) {
147
+ var y = getPlacement(placement);
148
+ var textFudge = 2;
149
+ var textHeight = 5;
150
+ // TODO-PER: Get the height of the current font and use that for the thickness.
151
+ abselem.addFixedX(new RelativeElement(text, width / 2, 0, y + textFudge, { type: "decoration", klass: 'ornament', thickness: 3, anchor: anchor }));
152
+
153
+ incrementPlacement(placement, textHeight);
154
+ }
155
+ function symbolDecoration(symbol, placement) {
156
+ var deltaX = width / 2;
157
+ if (glyphs.getSymbolAlign(symbol) !== "center") {
158
+ deltaX -= (glyphs.getSymbolWidth(symbol) / 2);
159
+ }
160
+ var height = glyphs.symbolHeightInPitches(symbol) + 1; // adding a little padding so nothing touches.
161
+ var y = getPlacement(placement);
162
+ y = (placement === 'above') ? y + height / 2 : y - height / 2;// Center the element vertically.
163
+ abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), y, { klass: 'ornament', thickness: glyphs.symbolHeightInPitches(symbol) }));
164
+
165
+ incrementPlacement(placement, height);
166
+ }
167
+
168
+ var symbolList = {
169
+ "+": "scripts.stopped",
170
+ "open": "scripts.open",
171
+ "snap": "scripts.snap",
172
+ "wedge": "scripts.wedge",
173
+ "thumb": "scripts.thumb",
174
+ "shortphrase": "scripts.shortphrase",
175
+ "mediumphrase": "scripts.mediumphrase",
176
+ "longphrase": "scripts.longphrase",
177
+ "trill": "scripts.trill",
178
+ "roll": "scripts.roll",
179
+ "irishroll": "scripts.roll",
180
+ "marcato": "scripts.umarcato",
181
+ "dmarcato": "scripts.dmarcato",
182
+ "umarcato": "scripts.umarcato",
183
+ "turn": "scripts.turn",
184
+ "uppermordent": "scripts.prall",
185
+ "pralltriller": "scripts.prall",
186
+ "mordent": "scripts.mordent",
187
+ "lowermordent": "scripts.mordent",
188
+ "downbow": "scripts.downbow",
189
+ "upbow": "scripts.upbow",
190
+ "fermata": "scripts.ufermata",
191
+ "invertedfermata": "scripts.dfermata",
192
+ "breath": ",",
193
+ "coda": "scripts.coda",
194
+ "segno": "scripts.segno"
195
+ };
196
+
197
+ var hasOne = false;
198
+ for (var i = 0; i < decoration.length; i++) {
199
+ switch (decoration[i]) {
200
+ case "0":
201
+ case "1":
202
+ case "2":
203
+ case "3":
204
+ case "4":
205
+ case "5":
206
+ case "D.C.":
207
+ case "D.S.":
208
+ textDecoration(decoration[i], positioning, 'middle');
209
+ hasOne = true;
210
+ break;
211
+ case "D.C.alcoda":
212
+ textDecoration("D.C. al coda", positioning, 'end');
213
+ hasOne = true;
214
+ break;
215
+ case "D.C.alfine":
216
+ textDecoration("D.C. al fine", positioning, 'end');
217
+ hasOne = true;
218
+ break;
219
+ case "D.S.alcoda":
220
+ textDecoration("D.S. al coda", positioning, 'end');
221
+ hasOne = true;
222
+ break;
223
+ case "D.S.alfine":
224
+ textDecoration("D.S. al fine", positioning, 'end');
225
+ hasOne = true;
226
+ break;
227
+ case "fine":
228
+ textDecoration("FINE", positioning, 'middle');
229
+ hasOne = true;
230
+ break;
231
+ case "+":
232
+ case "open":
233
+ case "snap":
234
+ case "wedge":
235
+ case "thumb":
236
+ case "shortphrase":
237
+ case "mediumphrase":
238
+ case "longphrase":
239
+ case "trill":
240
+ case "roll":
241
+ case "irishroll":
242
+ case "marcato":
243
+ case "dmarcato":
244
+ case "turn":
245
+ case "uppermordent":
246
+ case "pralltriller":
247
+ case "mordent":
248
+ case "lowermordent":
249
+ case "downbow":
250
+ case "upbow":
251
+ case "fermata":
252
+ case "breath":
253
+ case "umarcato":
254
+ case "coda":
255
+ case "segno":
256
+ symbolDecoration(symbolList[decoration[i]], positioning);
257
+ hasOne = true;
258
+ break;
259
+ case "invertedfermata":
260
+ symbolDecoration(symbolList[decoration[i]], 'below');
261
+ hasOne = true;
262
+ break;
263
+ case "mark":
264
+ abselem.klass = "mark";
265
+ break;
266
+ }
267
+ }
268
+ return hasOne;
269
+ };
270
+
271
+ function leftDecoration(decoration, abselem, roomtaken) {
272
+ for (var i = 0; i < decoration.length; i++) {
273
+ switch (decoration[i]) {
274
+ case "arpeggio":
275
+ // The arpeggio symbol is the height of a note (that is, two Y units). This stacks as many as we need to go from the
276
+ // top note to the bottom note. The arpeggio should also be a little taller than the stacked notes, so there is an extra
277
+ // one drawn and it is offset by half of a note height (that is, one Y unit).
278
+ for (var j = abselem.abcelem.minpitch - 1; j <= abselem.abcelem.maxpitch; j += 2) {
279
+ abselem.addExtra(
280
+ new RelativeElement(
281
+ "scripts.arpeggio",
282
+ -glyphs.getSymbolWidth("scripts.arpeggio") * 2 - roomtaken,
283
+ 0,
284
+ j + 2,
285
+ { klass: 'ornament', thickness: glyphs.symbolHeightInPitches("scripts.arpeggio") }
286
+ )
287
+ );
288
+ }
289
+ break;
290
+ }
291
+ }
292
+ }
293
+
294
+ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, positioning) {
295
+ var diminuendo;
296
+ var crescendo;
297
+ var glissando;
298
+ for (var i = 0; i < decoration.length; i++) {
299
+ switch (decoration[i]) {
300
+ case "diminuendo(":
301
+ this.startDiminuendoX = abselem;
302
+ diminuendo = undefined;
303
+ break;
304
+ case "diminuendo)":
305
+ diminuendo = { start: this.startDiminuendoX, stop: abselem };
306
+ this.startDiminuendoX = undefined;
307
+ break;
308
+ case "crescendo(":
309
+ this.startCrescendoX = abselem;
310
+ crescendo = undefined;
311
+ break;
312
+ case "crescendo)":
313
+ crescendo = { start: this.startCrescendoX, stop: abselem };
314
+ this.startCrescendoX = undefined;
315
+ break;
316
+ case "glissando(":
317
+ this.startGlissandoX = abselem;
318
+ glissando = undefined;
319
+ break;
320
+ case "glissando)":
321
+ glissando = { start: this.startGlissandoX, stop: abselem };
322
+ this.startGlissandoX = undefined;
323
+ break;
324
+ }
325
+ }
326
+ if (diminuendo) {
327
+ voice.addOther(new CrescendoElem(diminuendo.start, diminuendo.stop, ">", positioning));
328
+ }
329
+ if (crescendo) {
330
+ voice.addOther(new CrescendoElem(crescendo.start, crescendo.stop, "<", positioning));
331
+ }
332
+ if (glissando) {
333
+ voice.addOther(new GlissandoElem(glissando.start, glissando.stop));
334
+ }
335
+ };
336
+
337
+ Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals) {
338
+ if (!positioning)
339
+ positioning = { ornamentPosition: 'above', volumePosition: hasVocals ? 'above' : 'below', dynamicPosition: hasVocals ? 'above' : 'below' };
340
+ // These decorations don't affect the placement of other decorations
341
+ volumeDecoration(voice, decoration, abselem, positioning.volumePosition);
342
+ this.dynamicDecoration(voice, decoration, abselem, positioning.dynamicPosition);
343
+ compoundDecoration(decoration, pitch, width, abselem, dir);
344
+
345
+ // treat staccato, accent, and tenuto first (may need to shift other markers)
346
+ var yPos = closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch);
347
+ // yPos is an object containing 'above' and 'below'. That is the placement of the next symbol on either side.
348
+
349
+ yPos.above = Math.max(yPos.above, this.minTop);
350
+ var hasOne = stackedDecoration(decoration, width, abselem, yPos, positioning.ornamentPosition, this.minTop, this.minBottom);
351
+ if (hasOne) {
352
+ // abselem.top = Math.max(yPos.above + 3, abselem.top); // TODO-PER: Not sure why we need this fudge factor.
353
+ }
354
+ leftDecoration(decoration, abselem, roomtaken);
355
+ };
356
+
357
+ module.exports = Decoration;
@@ -1,8 +1,7 @@
1
1
  // abc_absolute_element.js: Definition of the AbsoluteElement class.
2
2
 
3
- var setClass = require('./set-class');
4
- var highlight = require("./highlight");
5
- var unhighlight = require("./unhighlight");
3
+ var highlight = require("../../interactive/highlight");
4
+ var unhighlight = require("../../interactive/unhighlight");
6
5
 
7
6
  // Everything that is placed in the SVG is first created as an absolute element. This is one unit of graphic information.
8
7
  // That is, it embodies a concept: a clef, a time signature, a bar line,etc. or most complexly:
@@ -32,7 +31,7 @@ var unhighlight = require("./unhighlight");
32
31
  // minspacing - spacing which must be taken on top of the width defined by the duration
33
32
  // type is a meta-type for the element. It is not necessary for drawing, but it is useful to make semantic sense of the element. For instance, it can be used in the element's class name.
34
33
  var AbsoluteElement = function AbsoluteElement(abcelem, duration, minspacing, type, tuneNumber, options) {
35
- // console.log("Absolute:",abcelem, duration, minspacing, type, tuneNumber, options);
34
+ // console.log("Absolute:",abcelem, duration, minspacing, type, tuneNumber, options);
36
35
  if (!options)
37
36
  options = {};
38
37
  this.tuneNumber = tuneNumber;
@@ -92,16 +91,16 @@ AbsoluteElement.prototype.addExtra = function (extra) {
92
91
  // )
93
92
  // console.log("extra", extra);
94
93
 
95
- this.fixed.w = Math.max(this.fixed.w, extra.dx+extra.w);
94
+ this.fixed.w = Math.max(this.fixed.w, extra.dx + extra.w);
96
95
  if (this.fixed.t === undefined) this.fixed.t = extra.top; else this.fixed.t = Math.max(this.fixed.t, extra.top);
97
96
  if (this.fixed.b === undefined) this.fixed.b = extra.bottom; else this.fixed.b = Math.min(this.fixed.b, extra.bottom);
98
- if (extra.dx<this.extraw) this.extraw = extra.dx;
97
+ if (extra.dx < this.extraw) this.extraw = extra.dx;
99
98
  this.extra[this.extra.length] = extra;
100
99
  this._addChild(extra);
101
100
  };
102
101
 
103
102
  AbsoluteElement.prototype.addHead = function (head) {
104
- if (head.dx<this.extraw) this.extraw = head.dx;
103
+ if (head.dx < this.extraw) this.extraw = head.dx;
105
104
  this.heads[this.heads.length] = head;
106
105
  this.addRight(head);
107
106
  };
@@ -128,7 +127,7 @@ AbsoluteElement.prototype.addRight = function (right) {
128
127
  // )
129
128
  // console.log("right", right);
130
129
  // These are the elements that are the fixed part.
131
- this.fixed.w = Math.max(this.fixed.w, right.dx+right.w);
130
+ this.fixed.w = Math.max(this.fixed.w, right.dx + right.w);
132
131
  if (right.top !== undefined) {
133
132
  if (this.fixed.t === undefined) this.fixed.t = right.top; else this.fixed.t = Math.max(this.fixed.t, right.top);
134
133
  }
@@ -137,7 +136,7 @@ AbsoluteElement.prototype.addRight = function (right) {
137
136
  }
138
137
  // if (isNaN(this.fixed.t) || isNaN(this.fixed.b))
139
138
  // debugger;
140
- if (right.dx+right.w>this.w) this.w = right.dx+right.w;
139
+ if (right.dx + right.w > this.w) this.w = right.dx + right.w;
141
140
  this.right[this.right.length] = right;
142
141
  this._addChild(right);
143
142
  };
@@ -161,15 +160,15 @@ AbsoluteElement.prototype.addCentered = function (elem) {
161
160
  // elem.type !== 'lyric'
162
161
  // )
163
162
  // console.log("centered", elem);
164
- var half = elem.w/2;
165
- if (-half<this.extraw) this.extraw = -half;
163
+ var half = elem.w / 2;
164
+ if (-half < this.extraw) this.extraw = -half;
166
165
  this.extra[this.extra.length] = elem;
167
- if (elem.dx+half>this.w) this.w = elem.dx+half;
166
+ if (elem.dx + half > this.w) this.w = elem.dx + half;
168
167
  this.right[this.right.length] = elem;
169
168
  this._addChild(elem);
170
169
  };
171
170
 
172
- AbsoluteElement.prototype.setLimit = function(member, child) {
171
+ AbsoluteElement.prototype.setLimit = function (member, child) {
173
172
  if (!child[member]) return;
174
173
  if (!this.specialY[member])
175
174
  this.specialY[member] = child[member];
@@ -178,7 +177,7 @@ AbsoluteElement.prototype.setLimit = function(member, child) {
178
177
  };
179
178
 
180
179
  AbsoluteElement.prototype._addChild = function (child) {
181
- // console.log("Relative:",child);
180
+ // console.log("Relative:",child);
182
181
  child.parent = this;
183
182
  this.children[this.children.length] = child;
184
183
  this.pushTop(child.top);
@@ -216,7 +215,7 @@ AbsoluteElement.prototype.pushBottom = function (bottom) {
216
215
 
217
216
  AbsoluteElement.prototype.setX = function (x) {
218
217
  this.x = x;
219
- for (var i=0; i<this.children.length; i++)
218
+ for (var i = 0; i < this.children.length; i++)
220
219
  this.children[i].setX(x);
221
220
  };
222
221
 
@@ -0,0 +1,113 @@
1
+ // abc_beam_element.js: Definition of the BeamElem class.
2
+
3
+ // Most elements on the page are related to a particular absolute element -- notes, rests, bars, etc. Beams, however, span multiple elements.
4
+ // This means that beams can't be laid out until the absolute elements are placed. There is the further complication that the stems for beamed
5
+ // notes can't be laid out until the beams are because we don't know how long they will be until we know the slope of the beam and the horizontal
6
+ // spacing of the absolute elements.
7
+ //
8
+ // So, when a beam is detected, a BeamElem is created, then all notes belonging to that beam are added to it. These notes are not given stems at that time.
9
+ // Then, after the horizontal layout is complete, all of the BeamElem are iterated to set the beam position, then all of the notes that are beamed are given
10
+ // stems. After that, we are ready for the drawing step.
11
+
12
+ // There are three phases: the setup phase, when new elements are being discovered, the layout phase, when everything is calculated, and the drawing phase,
13
+ // when the object is not changed, but is used to put the elements on the page.
14
+
15
+ //
16
+ // Setup phase
17
+ //
18
+ var BeamElem = function BeamElem(stemHeight, type, flat, firstElement) {
19
+ // type is "grace", "up", "down", or undefined. flat is used to force flat beams, as it commonly found in the grace notes of bagpipe music.
20
+ this.type = "BeamElem";
21
+ this.isflat = !!flat;
22
+ this.isgrace = !!(type && type === "grace");
23
+ this.forceup = !!(this.isgrace || (type && type === "up"));
24
+ this.forcedown = !!(type && type === "down");
25
+ this.elems = []; // all the AbsoluteElements that this beam touches. It may include embedded rests.
26
+ this.total = 0;
27
+ this.average = 6; // use middle line as start for average.
28
+ this.allrests = true;
29
+ this.stemHeight = stemHeight;
30
+ this.beams = []; // During the layout phase, this will become a list of the beams that need to be drawn.
31
+ if (firstElement && firstElement.duration) {
32
+ this.duration = firstElement.duration;
33
+ if (firstElement.startTriplet) {
34
+ this.duration *= firstElement.tripletMultiplier;
35
+ }
36
+ this.duration = Math.round(this.duration * 1000) / 1000;
37
+ } else
38
+ this.duration = 0;
39
+ };
40
+
41
+ BeamElem.prototype.setHint = function () {
42
+ this.hint = true;
43
+ };
44
+
45
+ BeamElem.prototype.runningDirection = function (abcelem) {
46
+ var pitch = abcelem.averagepitch;
47
+ if (pitch === undefined) return; // don't include elements like spacers in beams
48
+ this.total = Math.round(this.total + pitch);
49
+ if (!this.count)
50
+ this.count = 0;
51
+ this.count++
52
+ };
53
+
54
+ BeamElem.prototype.add = function (abselem) {
55
+ var pitch = abselem.abcelem.averagepitch;
56
+ if (pitch === undefined) return; // don't include elements like spacers in beams
57
+ if (!abselem.abcelem.rest)
58
+ this.allrests = false;
59
+ abselem.beam = this;
60
+ this.elems.push(abselem);
61
+ this.total = Math.round(this.total + pitch);
62
+ if (this.min === undefined || abselem.abcelem.minpitch < this.min) {
63
+ this.min = abselem.abcelem.minpitch;
64
+ }
65
+ if (this.max === undefined || abselem.abcelem.maxpitch > this.max) {
66
+ this.max = abselem.abcelem.maxpitch;
67
+ }
68
+ };
69
+
70
+ BeamElem.prototype.addBeam = function (beam) {
71
+ this.beams.push(beam);
72
+ };
73
+
74
+ BeamElem.prototype.setStemDirection = function () {
75
+ // Have to figure this out before the notes are placed because placing the notes also places the decorations.
76
+ this.average = calcAverage(this.total, this.count);
77
+ if (this.forceup) {
78
+ this.stemsUp = true;
79
+ } else if (this.forcedown) {
80
+ this.stemsUp = false;
81
+ } else {
82
+ var middleLine = 6; // hardcoded 6 is B
83
+ this.stemsUp = this.average < middleLine; // true is up, false is down;
84
+ }
85
+ delete this.count;
86
+ this.total = 0;
87
+ };
88
+
89
+ BeamElem.prototype.calcDir = function () {
90
+ this.average = calcAverage(this.total, this.elems.length);
91
+ if (this.forceup) {
92
+ this.stemsUp = true;
93
+ } else if (this.forcedown) {
94
+ this.stemsUp = false;
95
+ } else {
96
+ var middleLine = 6; // hardcoded 6 is B
97
+ this.stemsUp = this.average < middleLine; // true is up, false is down;
98
+ }
99
+ var dir = this.stemsUp ? 'up' : 'down';
100
+ for (var i = 0; i < this.elems.length; i++) {
101
+ for (var j = 0; j < this.elems[i].heads.length; j++) {
102
+ this.elems[i].heads[j].stemDir = dir;
103
+ }
104
+ }
105
+ };
106
+
107
+ function calcAverage(total, numElements) {
108
+ if (!numElements)
109
+ return 0;
110
+ return total / numElements;
111
+ }
112
+
113
+ module.exports = BeamElem;
@@ -1,4 +1,4 @@
1
- const addTextIf = require("./add-text-if");
1
+ const addTextIf = require("../add-text-if");
2
2
 
3
3
  function BottomText(metaText, width, isPrint, paddingLeft, spacing, getTextSize) {
4
4
  this.rows = [];
@@ -10,19 +10,18 @@ function BottomText(metaText, width, isPrint, paddingLeft, spacing, getTextSize)
10
10
  }
11
11
 
12
12
  BottomText.prototype.unalignedWords = function (unalignedWords, paddingLeft, spacing, getTextSize) {
13
- var indent = 50;
14
13
  var klass = 'meta-bottom unaligned-words';
15
14
  var defFont = 'wordsfont';
16
- this.rows.push({startGroup: "unalignedWords", klass: 'abcjs-meta-bottom abcjs-unaligned-words', name: "words"});
15
+ this.rows.push({ startGroup: "unalignedWords", klass: 'abcjs-meta-bottom abcjs-unaligned-words', name: "words" });
17
16
  var space = getTextSize.calc("i", defFont, klass);
18
17
 
19
- this.rows.push({move: spacing.words});
18
+ this.rows.push({ move: spacing.words });
20
19
 
21
20
  for (var j = 0; j < unalignedWords.length; j++) {
22
21
  if (unalignedWords[j] === '')
23
- this.rows.push({move: space.height});
22
+ this.rows.push({ move: space.height });
24
23
  else if (typeof unalignedWords[j] === 'string') {
25
- addTextIf(this.rows, { marginLeft: paddingLeft + indent, text: unalignedWords[j], font: defFont, klass: klass, inGroup: true, name: "words"}, getTextSize);
24
+ addTextIf(this.rows, { marginLeft: paddingLeft, text: unalignedWords[j], font: defFont, klass: klass, inGroup: true, name: "words" }, getTextSize);
26
25
  } else {
27
26
  var largestY = 0;
28
27
  var offsetX = 0;
@@ -30,7 +29,7 @@ BottomText.prototype.unalignedWords = function (unalignedWords, paddingLeft, spa
30
29
  var thisWord = unalignedWords[j][k];
31
30
  var font = (thisWord.font) ? thisWord.font : defFont;
32
31
  this.rows.push({
33
- left: paddingLeft + indent + offsetX,
32
+ left: paddingLeft + offsetX,
34
33
  text: thisWord.text,
35
34
  font: font,
36
35
  anchor: 'start'
@@ -43,11 +42,11 @@ BottomText.prototype.unalignedWords = function (unalignedWords, paddingLeft, spa
43
42
  offsetX += space.width;
44
43
  }
45
44
  }
46
- this.rows.push({move: largestY});
45
+ this.rows.push({ move: largestY });
47
46
  }
48
47
  }
49
- this.rows.push({move: space.height * 2});
50
- this.rows.push({endGroup: "unalignedWords", absElemType: "unalignedWords", startChar: -1, endChar: -1, name: "unalignedWords"});
48
+ this.rows.push({ move: space.height * 2 });
49
+ this.rows.push({ endGroup: "unalignedWords", absElemType: "unalignedWords", startChar: -1, endChar: -1, name: "unalignedWords" });
51
50
  }
52
51
 
53
52
  BottomText.prototype.extraText = function (metaText, marginLeft, spacing, getTextSize) {
@@ -62,18 +61,18 @@ BottomText.prototype.extraText = function (metaText, marginLeft, spacing, getTex
62
61
  if (metaText['abc-creator']) extraText += "Creator: " + metaText['abc-creator'] + "\n";
63
62
  if (metaText['abc-edited-by']) extraText += "Edited By: " + metaText['abc-edited-by'] + "\n";
64
63
  if (extraText.length > 0) {
65
- addTextIf(this.rows, { marginLeft: marginLeft, text: extraText, font: 'historyfont', klass: 'meta-bottom extra-text', marginTop: spacing.info, absElemType: "extraText", name: "description"}, getTextSize);
64
+ addTextIf(this.rows, { marginLeft: marginLeft, text: extraText, font: 'historyfont', klass: 'meta-bottom extra-text', marginTop: spacing.info, absElemType: "extraText", name: "description" }, getTextSize);
66
65
  }
67
66
  }
68
67
 
69
68
  BottomText.prototype.footer = function (footer, width, paddingLeft, getTextSize) {
70
69
  var klass = 'header meta-bottom';
71
70
  var font = "footerfont";
72
- this.rows.push({startGroup: "footer", klass: klass});
71
+ this.rows.push({ startGroup: "footer", klass: klass });
73
72
  // Note: whether there is a footer or not doesn't change any other positioning, so this doesn't change the Y-coordinate.
74
- addTextIf(this.rows, { marginLeft: paddingLeft, text: footer.left, font: font, klass: klass, name: "footer"}, getTextSize);
75
- addTextIf(this.rows, { marginLeft: paddingLeft + width / 2, text: footer.center, font: font, klass: klass, anchor: 'middle', name: "footer"} , getTextSize);
76
- addTextIf(this.rows, { marginLeft: paddingLeft + width, text: footer.right, font: font, klass: klass, anchor: 'end', name: "footer"}, getTextSize);
73
+ addTextIf(this.rows, { marginLeft: paddingLeft, text: footer.left, font: font, klass: klass, name: "footer" }, getTextSize);
74
+ addTextIf(this.rows, { marginLeft: paddingLeft + width / 2, text: footer.center, font: font, klass: klass, anchor: 'middle', name: "footer" }, getTextSize);
75
+ addTextIf(this.rows, { marginLeft: paddingLeft + width, text: footer.right, font: font, klass: klass, anchor: 'end', name: "footer" }, getTextSize);
77
76
  }
78
77
 
79
78
  module.exports = BottomText;
@@ -1,11 +1,11 @@
1
1
  // abc_brace_element.js: Definition of the BraceElement class.
2
2
 
3
3
  var BraceElem = function BraceElem(voice, type) {
4
- this.startVoice = voice;
5
- this.type = type;
4
+ this.startVoice = voice;
5
+ this.type = type;
6
6
  };
7
7
 
8
- BraceElem.prototype.setBottomStaff = function(voice) {
8
+ BraceElem.prototype.setBottomStaff = function (voice) {
9
9
  this.endVoice = voice;
10
10
  // If only the start brace has a name then the name belongs to the brace instead of the staff.
11
11
  if (this.startVoice.header && !this.endVoice.header) {
@@ -14,12 +14,12 @@ BraceElem.prototype.setBottomStaff = function(voice) {
14
14
  }
15
15
  };
16
16
 
17
- BraceElem.prototype.continuing = function(voice) {
17
+ BraceElem.prototype.continuing = function (voice) {
18
18
  // If the final staff isn't present, then use the last one we saw.
19
19
  this.lastContinuedVoice = voice;
20
20
  };
21
21
 
22
- BraceElem.prototype.getWidth = function() {
22
+ BraceElem.prototype.getWidth = function () {
23
23
  return 10; // TODO-PER: right now the drawing function doesn't vary the width at all. If it does in the future then this will change.
24
24
  };
25
25