abcjs 6.6.2 → 6.6.4

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.
@@ -13,6 +13,7 @@ var createNoteHead = function (abselem, c, pitchelem, options) {
13
13
  var accidentalSlot = (options.accidentalSlot !== undefined) ? options.accidentalSlot : [];
14
14
  var shouldExtendStem = (options.shouldExtendStem !== undefined) ? options.shouldExtendStem : false;
15
15
  var printAccidentals = (options.printAccidentals !== undefined) ? options.printAccidentals : true;
16
+ var chordPos = options.chordPos
16
17
 
17
18
  // TODO scale the dot as well
18
19
  var pitch = pitchelem.verticalPos;
@@ -23,14 +24,14 @@ var createNoteHead = function (abselem, c, pitchelem, options) {
23
24
  if (c === undefined)
24
25
  abselem.addFixed(new RelativeElement("pitch is undefined", 0, 0, 0, { type: "debug" }));
25
26
  else if (c === "") {
26
- notehead = new RelativeElement(null, 0, 0, pitch);
27
+ notehead = new RelativeElement(null, 0, 0, pitch, {chordPos:chordPos});
27
28
  } else {
28
29
  var shiftheadx = headx;
29
30
  if (pitchelem.printer_shift) {
30
31
  var adjust = (pitchelem.printer_shift === "same") ? 1 : 0;
31
32
  shiftheadx = (dir === "down") ? -glyphs.getSymbolWidth(c) * scale + adjust : glyphs.getSymbolWidth(c) * scale - adjust;
32
33
  }
33
- var opts = { scalex: scale, scaley: scale, thickness: glyphs.symbolHeightInPitches(c) * scale, name: pitchelem.name };
34
+ var opts = { scalex: scale, scaley: scale, thickness: glyphs.symbolHeightInPitches(c) * scale, name: pitchelem.name, chordPos: chordPos };
34
35
  notehead = new RelativeElement(c, shiftheadx, glyphs.getSymbolWidth(c) * scale, pitch, opts);
35
36
  notehead.stemDir = dir;
36
37
  if (flag) {
@@ -44,12 +45,12 @@ var createNoteHead = function (abselem, c, pitchelem, options) {
44
45
  }
45
46
  //if (scale===1 && (dir==="down")?(pos>6):(pos<6)) pos=6;
46
47
  var xdelta = (dir === "down") ? headx : headx + notehead.w - 0.6;
47
- abselem.addRight(new RelativeElement(flag, xdelta, glyphs.getSymbolWidth(flag) * scale, pos, { scalex: scale, scaley: scale }));
48
+ abselem.addRight(new RelativeElement(flag, xdelta, glyphs.getSymbolWidth(flag) * scale, pos, { scalex: scale, scaley: scale, chordPos: chordPos }));
48
49
  }
49
50
  newDotShiftX = notehead.w + dotshiftx - 2 + 5 * dot;
50
51
  for (; dot > 0; dot--) {
51
52
  var dotadjusty = (1 - Math.abs(pitch) % 2); //PER: take abs value of the pitch. And the shift still happens on ledger lines.
52
- abselem.addRight(new RelativeElement("dots.dot", notehead.w + dotshiftx - 2 + 5 * dot, glyphs.getSymbolWidth("dots.dot"), pitch + dotadjusty));
53
+ abselem.addRight(new RelativeElement("dots.dot", notehead.w + dotshiftx - 2 + 5 * dot, glyphs.getSymbolWidth("dots.dot"), pitch + dotadjusty, {chordPos:chordPos}));
53
54
  }
54
55
  }
55
56
  if (notehead)
@@ -96,7 +97,7 @@ var createNoteHead = function (abselem, c, pitchelem, options) {
96
97
  accidentalshiftx = (glyphs.getSymbolWidth(symb) * scale + 2);
97
98
  }
98
99
  var h = glyphs.symbolHeightInPitches(symb);
99
- abselem.addExtra(new RelativeElement(symb, accPlace, glyphs.getSymbolWidth(symb), pitch, { scalex: scale, scaley: scale, top: pitch + h / 2, bottom: pitch - h / 2 }));
100
+ abselem.addExtra(new RelativeElement(symb, accPlace, glyphs.getSymbolWidth(symb), pitch, { scalex: scale, scaley: scale, top: pitch + h / 2, bottom: pitch - h / 2, chordPos: chordPos }));
100
101
  extraLeft = glyphs.getSymbolWidth(symb) / 2; // TODO-PER: We need a little extra width if there is an accidental, but I'm not sure why it isn't the full width of the accidental.
101
102
  }
102
103
 
@@ -13,6 +13,7 @@ var RelativeElement = function RelativeElement(c, dx, w, pitch, opt) {
13
13
  this.pitch2 = opt.pitch2;
14
14
  this.linewidth = opt.linewidth;
15
15
  this.klass = opt.klass;
16
+ this.chordPos = opt.chordPos;
16
17
  this.anchor = opt.anchor ? opt.anchor : 'middle'
17
18
  this.top = pitch;
18
19
  if (this.pitch2 !== undefined && this.pitch2 > this.top) this.top = this.pitch2;
@@ -32,6 +33,8 @@ var RelativeElement = function RelativeElement(c, dx, w, pitch, opt) {
32
33
  this.dim = opt.dim;
33
34
  if (opt.position)
34
35
  this.position = opt.position;
36
+ if (opt.voiceNumber !== undefined)
37
+ this.voiceNumber = opt.voiceNumber
35
38
  this.height = opt.height ? opt.height : 4; // The +1 is to give a little bit of padding.
36
39
  if (opt.top)
37
40
  this.top = opt.top;
@@ -20,6 +20,12 @@ function drawAbsolute(renderer, params, bartop, selectables, staffPos) {
20
20
  if (child.type === "symbol" && child.c && child.c.indexOf('notehead') >= 0) {
21
21
  el.setAttribute('class', 'abcjs-notehead')
22
22
  }
23
+ if (el && child.chordPos && child.name.indexOf('flags.') !== 0) {
24
+ var klass = el.getAttribute("class")
25
+ if (klass) klass = klass + ' abcjs-chord-pos-'+child.chordPos
26
+ else klass = 'abcjs-chord-pos-'+child.chordPos
27
+ el.setAttribute('class', klass)
28
+ }
23
29
  }
24
30
  }
25
31
  var klass = params.type;
@@ -8,7 +8,7 @@ function printLine(renderer, x1, x2, y, klass, name, dy) {
8
8
  var y1 = roundNumber(y - dy);
9
9
  var y2 = roundNumber(y + dy);
10
10
  // TODO-PER: This fixes a firefox bug where it isn't displayed
11
- if (renderer.firefox112) {
11
+ if (renderer.firefox) {
12
12
  y += dy / 2; // Because the y coordinate is the edge of where the line goes but the width widens from the middle.
13
13
  var attr = {
14
14
  x1: x1,
@@ -39,4 +39,3 @@ function printLine(renderer, x1, x2, y, klass, name, dy) {
39
39
  }
40
40
 
41
41
  module.exports = printLine;
42
-
@@ -13,7 +13,7 @@ function printStem(renderer, x, dx, y1, y2, klass, name) {
13
13
  x = roundNumber(x);
14
14
  var x2 = roundNumber(x + dx);
15
15
  // TODO-PER: This fixes a firefox bug where it isn't displayed
16
- if (renderer.firefox112) {
16
+ if (renderer.firefox) {
17
17
  x += dx / 2; // Because the x coordinate is the edge of where the line goes but the width widens from the middle.
18
18
  var attr = {
19
19
  x1: x,
@@ -6,7 +6,7 @@ var roundNumber = require("./round-number");
6
6
  function drawTriplet(renderer, params, selectables) {
7
7
  renderer.paper.openGroup({ klass: renderer.controller.classes.generate('triplet ' + params.durationClass), "data-name": "triplet" });
8
8
  if (!params.hasBeam) {
9
- drawBracket(renderer, params.anchor1.x, params.startNote, params.anchor2.x + params.anchor2.w, params.endNote);
9
+ drawBracket(renderer, params.anchor1.x, params.startNote, params.anchor2.x + params.anchor2.w, params.endNote, params.up);
10
10
  }
11
11
  // HACK: adjust the position of "3". It is too high in all cases so we fudge it by subtracting 1 here.
12
12
  renderText(renderer, { x: params.xTextPos, y: renderer.calcY(params.yTextPos - 1), text: "" + params.number, type: 'tripletfont', anchor: "middle", centerVertically: true, noClass: true, name: "" + params.number }, true);
@@ -19,10 +19,10 @@ function drawLine(l, t, r, b) {
19
19
  return sprintf("M %f %f L %f %f", roundNumber(l), roundNumber(t), roundNumber(r), roundNumber(b));
20
20
  }
21
21
 
22
- function drawBracket(renderer, x1, y1, x2, y2) {
22
+ function drawBracket(renderer, x1, y1, x2, y2, up) {
23
23
  y1 = renderer.calcY(y1);
24
24
  y2 = renderer.calcY(y2);
25
- var bracketHeight = 5;
25
+ var bracketHeight = up ? 5 : -5;
26
26
 
27
27
  // Draw vertical lines at the beginning and end
28
28
  var pathString = "";
@@ -351,7 +351,7 @@ function splitSvgIntoLines(renderer, output, title, responsive, scale) {
351
351
  if (responsive === 'resize')
352
352
  svg.style.position = ''
353
353
  // TODO-PER: Hack! Not sure why this is needed.
354
- var viewBoxHeight = renderer.firefox112 ? height+1 : height
354
+ var viewBoxHeight = renderer.firefox ? height+1 : height
355
355
  svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + viewBoxHeight)
356
356
  svg.appendChild(style.cloneNode(true))
357
357
  var titleEl = document.createElement("title")
@@ -3,6 +3,7 @@ var setUpperAndLowerElements = require('./set-upper-and-lower-elements');
3
3
  var layoutStaffGroup = require('./staff-group');
4
4
  var getLeftEdgeOfStaff = require('./get-left-edge-of-staff');
5
5
  var layoutInGrid = require('./layout-in-grid');
6
+ var toTimeAndStaffBased = require("./to-time-and-staff-based");
6
7
 
7
8
  // This sets the "x" attribute on all the children in abctune.lines
8
9
  // It also sets the "w" and "startx" attributes on "voices"
@@ -40,6 +41,16 @@ var layout = function (renderer, abctune, width, space, expandToWidest, timeBase
40
41
  }
41
42
  }
42
43
 
44
+ // See if there are collisions between voices that need to be tweaked
45
+ var timeBased = toTimeAndStaffBased(abctune.lines)
46
+ for (i = 0; i < abctune.lines.length; i++) {
47
+ abcLine = abctune.lines[i];
48
+ if (abcLine.staffGroup) {
49
+ fixVoiceCollisions(timeBased[i])
50
+ //setUpperAndLowerElements(renderer, abcLine.staffGroup);
51
+ }
52
+ }
53
+
43
54
  // Set the staff spacing
44
55
  // TODO-PER: we should have been able to do this by the time we called setUpperAndLowerElements, but for some reason the "bottom" element seems to be set as a side effect of setting the X spacing.
45
56
  for (i = 0; i < abctune.lines.length; i++) {
@@ -126,4 +137,83 @@ function centerWholeRests(voices) {
126
137
  }
127
138
  }
128
139
 
140
+ function fixVoiceCollisions(timeBasedLine) {
141
+ for (var s = 0; s < timeBasedLine.length; s++) {
142
+ var timeSlot = timeBasedLine[s]
143
+ // If there is more than one thing happening at the same time,
144
+ // and one of those things is a rest, then:
145
+ // If the rest is in the first element, check to see if the bottom bumps into the top of any of the rest of the elements
146
+ // If the rest is in the last element, check to see if the top bumps into the bottom of any of the rest of the elements.
147
+ // Note: if there are more than two voices the staff will get sloppy, so there is a limit to how much that can be improved, but this should be fine when there are two voices.
148
+ // If there is a collision, move the rest up or down to fix that.
149
+ var keys = Object.keys(timeSlot)
150
+ for (var z = 0; z < keys.length; z++) {
151
+ var slot = timeSlot[keys[z]] // slot is an array of all the things happening at a particular time
152
+ var lastIndex = slot.length - 1
153
+ if (slot.length > 1) {
154
+ var isRealRest = slot[0].abcelem.rest && slot[0].abcelem.rest.type === 'rest' // weed out invisible rests
155
+ var isRealRest2 = slot[lastIndex].abcelem.rest && slot[lastIndex].abcelem.rest.type === 'rest' // weed out invisible rests
156
+ if (isRealRest && !slot[lastIndex].abcelem.rest) {
157
+ // the first voice has a rest and the second doesn't
158
+ var restTop = slot[0].children.find(function (ch) { return ch.name.includes('rest') })
159
+ var otherTop = closeTop(slot[lastIndex])
160
+ if (restTop) {
161
+ var distance1 = restTop.bottom - otherTop
162
+ distance1 -= 2 // give some room between the rest and the note
163
+ if (distance1 < 0 && slot[0].children.length > 0) {
164
+ slot[0].bottom -= distance1
165
+ slot[0].top -= distance1
166
+ slot[0].children[0].bottom -= distance1
167
+ slot[0].children[0].top -= distance1
168
+ slot[0].children[0].pitch -= distance1
169
+ }
170
+ }
171
+ } else if (isRealRest2 && !slot[0].abcelem.rest) {
172
+ // the last voice has a rest and the first doesn't
173
+ var restBottom = slot[lastIndex].children.find(function (ch) { return ch.name.includes('rest') })
174
+ if (restBottom) {
175
+ var distance2 = restBottom.top - closeBottom(slot[0])
176
+ distance2 += 2 // give some room between the rest and the note
177
+ if (distance2 > 0 && slot[lastIndex].children.length > 0) {
178
+ slot[lastIndex].bottom -= distance2
179
+ slot[lastIndex].top -= distance2
180
+ slot[lastIndex].children[0].bottom -= distance2
181
+ slot[lastIndex].children[0].top -= distance2
182
+ slot[lastIndex].children[0].pitch -= distance2
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ function closeTop(absElem) {
192
+ if (absElem.children) {
193
+ var max = -90 // This is clearly way lower than the max calculated below
194
+ for (var i = 0; i < absElem.children.length; i++) {
195
+ var child = absElem.children[i]
196
+ if (child.type !== 'chord')
197
+ max = Math.max(max, child.top)
198
+ }
199
+ if (max > -90)
200
+ return max
201
+ }
202
+ return absElem.top
203
+ }
204
+
205
+ function closeBottom(absElem) {
206
+ if (absElem.children) {
207
+ var min = 90 // This is clearly way higher than the min calculated below
208
+ for (var i = 0; i < absElem.children.length; i++) {
209
+ var child = absElem.children[i]
210
+ if (child.type !== 'lyric')
211
+ min = Math.min(min, child.bottom)
212
+ }
213
+ if (min < 90)
214
+ return min
215
+ }
216
+ return absElem.bottom
217
+ }
218
+
129
219
  module.exports = layout;
@@ -75,7 +75,8 @@ var setUpperAndLowerElements = function (renderer, staffGroup) {
75
75
 
76
76
  for (var j = 0; j < staff.voices.length; j++) {
77
77
  var voice = staffGroup.voices[staff.voices[j]];
78
- setUpperAndLowerVoiceElements(positionY, voice, renderer.spacing);
78
+ var diff = setUpperAndLowerVoiceElements(positionY, voice, renderer.spacing);
79
+ staff.bottom -= diff//
79
80
  }
80
81
  // We might need a little space in between staves if the staves haven't been pushed far enough apart by notes or extra vertical stuff.
81
82
  // Only try to put in extra space if this isn't the top staff.
@@ -112,9 +113,16 @@ function incTop(staff, positionY, item, count) {
112
113
  function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
113
114
  var i;
114
115
  var abselem;
116
+ var diff = 0
115
117
  for (i = 0; i < voice.children.length; i++) {
116
118
  abselem = voice.children[i];
117
- setUpperAndLowerAbsoluteElements(positionY, abselem, spacing);
119
+ var bottom = setUpperAndLowerAbsoluteElements(positionY, abselem, spacing);
120
+ if (bottom < abselem.bottom) {
121
+ // We're moving things down so tell the staff that it needs to be taller
122
+ diff = abselem.bottom - bottom
123
+ abselem.bottom = bottom//
124
+ voice.bottom = bottom//
125
+ }
118
126
  }
119
127
  for (i = 0; i < voice.otherchildren.length; i++) {
120
128
  abselem = voice.otherchildren[i];
@@ -138,6 +146,7 @@ function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
138
146
  break;
139
147
  }
140
148
  }
149
+ return diff
141
150
  }
142
151
 
143
152
  // For each of the relative elements that can't be placed in advance (because their vertical placement depends on everything
@@ -145,6 +154,7 @@ function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
145
154
  // hash with the vertical placement (in pitch units) for each type.
146
155
  // TODO-PER: I think this needs to be separated by "above" and "below". How do we know that for dynamics at the point where they are being defined, though? We need a pass through all the relative elements to set "above" and "below".
147
156
  function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
157
+ var bottom = element.bottom
148
158
  // specialYResolved contains the actual pitch for each of the classes of elements.
149
159
  for (var i = 0; i < element.children.length; i++) {
150
160
  var child = element.children[i];
@@ -152,6 +162,11 @@ function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
152
162
  if (element.specialY.hasOwnProperty(key)) {
153
163
  if (child[key]) { // If this relative element has defined a height for this class of element
154
164
  child.pitch = specialYResolved[key];
165
+ if (key === 'lyricHeightBelow' && child.type === 'lyric' && child.voiceNumber) {
166
+ // TODO-PER: This can result in extra unused vertical space if there are lyrics only on the second but not the first voice.
167
+ child.pitch -= child.voiceNumber*child[key]//
168
+ bottom = Math.min(element.bottom, child.pitch)//
169
+ }
155
170
  if (child.top === undefined) { // TODO-PER: HACK! Not sure this is the right place to do this.
156
171
  if (child.type === 'TempoElement') {
157
172
  setUpperAndLowerTempoElement(specialYResolved, child);
@@ -165,6 +180,7 @@ function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
165
180
  }
166
181
  }
167
182
  }
183
+ return bottom
168
184
  }
169
185
 
170
186
  function setUpperAndLowerCrescendoElements(positionY, element) {
@@ -0,0 +1,35 @@
1
+ function toTimeAndStaffBased(abcLines) {
2
+ var results = []
3
+ for (var lin = 0; lin < abcLines.length; lin++) {
4
+ var line = abcLines[lin]
5
+ var staffGroup = line.staffGroup
6
+
7
+ var group = []
8
+ if (staffGroup && staffGroup && staffGroup.staffs) {
9
+ for (var s = 0; s < staffGroup.staffs.length; s++) {
10
+ var staff = staffGroup.staffs[s]
11
+ var timeSlot = {}
12
+ for (var i = 0; i < staff.voices.length; i++) {
13
+ var voice = staffGroup.voices[staff.voices[i]]
14
+ var time = 0
15
+ for (var k = 0; k < voice.children.length; k++) {
16
+ var index = 'T' + Math.round(time*1000) // There can be inexactness when calculating triplets, so we'll round, but we'll make sure that no make sure that we don't lose necessary precision by making it a shorter time than would ever happen
17
+ if (!timeSlot[index])
18
+ timeSlot[index] = []
19
+ if (voice.children[k].abcelem.el_type === 'note') {
20
+ timeSlot[index].push(voice.children[k])
21
+ time += voice.children[k].duration
22
+ }
23
+ }
24
+ }
25
+ // Now timeSlot is an object with all the voices on a particular staff that
26
+ // happen at the same time as an array.
27
+ group.push(timeSlot)
28
+ }
29
+ }
30
+ results.push(group)
31
+ }
32
+ return results
33
+ }
34
+
35
+ module.exports = toTimeAndStaffBased;
@@ -21,34 +21,60 @@ function layoutTriplet(element) {
21
21
  if (isAbove(beam))
22
22
  element.endingHeightAbove = 4;
23
23
  } else {
24
- // If there isn't a beam, then we need to draw the bracket and the text. The bracket is always above.
24
+ // If there isn't a beam, then we need to draw the bracket and the text. The bracket is either above or below depending on the stem direction of the notes.
25
+ // Above:
25
26
  // The bracket is never lower than the 'a' line, but is 4 pitches above the first and last notes. If there is
26
27
  // a tall note in the middle, the bracket is horizontal and above the highest note.
27
- element.startNote = Math.max(element.anchor1.parent.top, 9) + 4;
28
- element.endNote = Math.max(element.anchor2.parent.top, 9) + 4;
28
+ // Below: The bracket is never higher than the 'C' line, and is 4 pitches below.
29
+
30
+ // To decide if the bracket goes above or below, go in the direction of the most stems. If there are the same number it will put the bracket above.
31
+ var up = stemDirectionUp(element)
32
+ element.up = up
33
+ element.startNote = up ? Math.max(element.anchor1.parent.top, 9) + 4 : Math.min(element.anchor1.parent.bottom, 0) - 2
34
+ element.endNote = up ? Math.max(element.anchor2.parent.top, 9) + 4 : Math.min(element.anchor2.parent.bottom, 0) - 2
35
+
29
36
  // If it starts or ends on a rest, make the beam horizontal
30
37
  if (element.anchor1.parent.type === "rest" && element.anchor2.parent.type !== "rest")
31
38
  element.startNote = element.endNote;
32
39
  else if (element.anchor2.parent.type === "rest" && element.anchor1.parent.type !== "rest")
33
40
  element.endNote = element.startNote;
34
- // See if the middle note is really high.
35
- var max = 0;
36
- for (var i = 0; i < element.middleElems.length; i++) {
37
- max = Math.max(max, element.middleElems[i].top);
38
- }
39
- max += 4;
40
- if (max > element.startNote || max > element.endNote) {
41
- element.startNote = max;
42
- element.endNote = max;
41
+ if (up) {
42
+ // See if the middle note is really high.
43
+ var max = 0;
44
+ for (var i = 0; i < element.middleElems.length; i++) {
45
+ max = Math.max(max, element.middleElems[i].top);
46
+ }
47
+ max += 4;
48
+ if (max > element.startNote || max > element.endNote) {
49
+ element.startNote = max + 3;
50
+ element.endNote = max + 3;
51
+ }
52
+ } else {
53
+ // See if the middle note is really low.
54
+ var min = 0;
55
+ for (var i = 0; i < element.middleElems.length; i++) {
56
+ min = Math.min(min, element.middleElems[i].bottom-element.middleElems[i].height);
57
+ }
58
+ min -= 3;
59
+ if (min < element.startNote && min < element.endNote) {
60
+ element.startNote = Math.min(min, element.startNote) - 2;
61
+ element.endNote = Math.min(min, element.endNote) - 2;
62
+ }
43
63
  }
44
64
  if (element.flatBeams) {
45
- element.startNote = Math.max(element.startNote, element.endNote);
46
- element.endNote = Math.max(element.startNote, element.endNote);
65
+ if (up) {
66
+ element.startNote = Math.max(element.startNote, element.endNote);
67
+ element.endNote = Math.max(element.startNote, element.endNote);
68
+ } else {
69
+ element.startNote = Math.min(element.startNote, element.endNote);
70
+ element.endNote = Math.min(element.startNote, element.endNote);
71
+ }
47
72
  }
48
73
 
49
74
  element.yTextPos = element.startNote + (element.endNote - element.startNote) / 2;
50
75
  element.xTextPos = element.anchor1.x + (element.anchor2.x + element.anchor2.w - element.anchor1.x) / 2;
51
76
  element.top = element.yTextPos + 1;
77
+ element.bottom = element.yTextPos - 2;
52
78
  }
53
79
  }
54
80
  delete element.middleElems;
@@ -59,6 +85,27 @@ function isAbove(beam) {
59
85
  return beam.stemsUp;
60
86
  }
61
87
 
88
+ function stemDirectionUp(element) {
89
+ var up = 0
90
+ var down = 0
91
+ if (element.anchor1) {
92
+ if (element.anchor1.stemDir === 'up') up++
93
+ if (element.anchor1.stemDir === 'down') down++
94
+ }
95
+ if (element.anchor2) {
96
+ if (element.anchor2.stemDir === 'up') up++
97
+ if (element.anchor2.stemDir === 'down') down++
98
+ }
99
+ if (element.middleElems) {
100
+ for (var i = 0; i < element.middleElems.length; i++) {
101
+ var elem = element.middleElems[i]
102
+ if (elem.stemDir === 'up') up++
103
+ if (elem.stemDir === 'down') down++
104
+ }
105
+ }
106
+ return up >= down
107
+ }
108
+
62
109
  // We can't just use the entire beam for the calculation. The range has to be passed in, because the beam might extend into some unrelated notes. for instance, (3_a'f'e'f'2 when L:16
63
110
  function heightAtMidpoint(startX, endX, beam) {
64
111
  if (beam.beams.length === 0)
@@ -16,7 +16,7 @@ var Renderer = function (paper) {
16
16
  this.space = 3 * spacing.SPACE;
17
17
  this.padding = {}; // renderer's padding is managed by the controller
18
18
  this.reset();
19
- this.firefox112 = navigator.userAgent.indexOf('Firefox/112.0') >= 0
19
+ this.firefox = navigator.userAgent.indexOf('Firefox/') >= 0
20
20
  };
21
21
 
22
22
  Renderer.prototype.reset = function () {
package/types/index.d.ts CHANGED
@@ -142,7 +142,7 @@ declare module 'abcjs' {
142
142
 
143
143
  export interface ChordProperties {
144
144
  name: string;
145
- chord: {
145
+ chord?: {
146
146
  root: ChordRoot;
147
147
  type: ChordType;
148
148
  },
@@ -237,8 +237,6 @@ declare module 'abcjs' {
237
237
 
238
238
  export type AbstractEngraver = any;
239
239
 
240
- export type NoteProperties = any; // TODO
241
-
242
240
  export type AudioTrackCommand = 'program' | 'text' | 'note';
243
241
  //
244
242
  // Input Types
@@ -750,7 +748,7 @@ declare module 'abcjs' {
750
748
  el_type: "overlay";
751
749
  startChar: number;
752
750
  endChar: number;
753
- overlay: Array<NoteProperties>;
751
+ overlay: Array<VoiceItemNote>;
754
752
  }
755
753
 
756
754
  export interface VoiceItemPart {
@@ -786,12 +784,21 @@ declare module 'abcjs' {
786
784
  steps: number;
787
785
  }
788
786
 
789
- export interface VoiceItemNote extends NoteProperties {
787
+ export interface VoiceItemNote {
790
788
  el_type: "note";
791
789
  startChar: number;
792
790
  endChar: number;
793
791
  duration: number;
794
- pitches?: Array<any>; // TODO-PER
792
+ pitches?: Array<{
793
+ accidental?: AccidentalName;
794
+ pitch: number;
795
+ name: string;
796
+ startSlur?: Array<{label: number}>;
797
+ endSlur?: Array<number>;
798
+ startTie?: {};
799
+ endTie?: boolean;
800
+ verticalPos: number;
801
+ }>;
795
802
  rest?: { type: 'rest' | 'spacer' | 'invisible' | 'invisible-multimeasure' | 'multimeasure', text? : number};
796
803
  }
797
804
  export type VoiceItem = VoiceItemClef | VoiceItemBar | VoiceItemGap | VoiceItemKey | VoiceItemMeter | VoiceItemMidi | VoiceItemOverlay | VoiceItemPart | VoiceItemScale | VoiceItemStem | VoiceItemStyle | VoiceItemTempo | VoiceItemTranspose | VoiceItemNote;
@@ -932,41 +939,133 @@ declare module 'abcjs' {
932
939
 
933
940
  export type TuneObjectArray = [TuneObject]
934
941
 
942
+ export type AbcElemType = VoiceItem["el_type"] | DragTypes | "break" | "color" | "font" | "hint" | "bracket" |
943
+ "glissando" | "keySignature" | "separator" | "tab" | "tabNumber" | "timeSignature" | "triplet";
944
+
945
+ export type SlurDirection = "up" | "down";
946
+
947
+ export interface SlurProperties {
948
+ label: number;
949
+ direction?: SlurDirection;
950
+ style?: "dotted";
951
+ }
952
+
953
+ export interface TieProperties {
954
+ direction?: SlurDirection;
955
+ style?: "dotted";
956
+ }
957
+
958
+ export interface AbcElemPitch {
959
+ accidental?: AccidentalName;
960
+ endSlur?: Array<number>;
961
+ endTie?: true;
962
+ highestVert?: number;
963
+ midipitch?: number;
964
+ name: string;
965
+ pitch: number;
966
+ startSlur?: Array<SlurProperties>;
967
+ startTie?: TieProperties;
968
+ style?: NoteHeadType;
969
+ verticalPos: number;
970
+ }
971
+
972
+ export interface AbcElemGraceNote extends AbcElemPitch {
973
+ acciaccatura?: true;
974
+ duration: number;
975
+ endBeam?: true;
976
+ startBeam?: true;
977
+ }
978
+
979
+ export interface AbcElemRest {
980
+ type: "invisible" | "spacer" | "rest" | "multimeasure" | "invisible-multimeasure" | "whole";
981
+ text?: number;
982
+ endTie?: true;
983
+ startTie?: TieProperties;
984
+ }
985
+
986
+ export interface AbcElemPositioning {
987
+ chordPosition?: Placement | "hidden";
988
+ dynamicPosition?: Placement | "hidden";
989
+ ornamentPosition?: Placement | "hidden";
990
+ vocalPosition?: Placement | "hidden";
991
+ volumePosition?: Placement | "hidden";
992
+ }
993
+
994
+ export interface AbcElemFonts {
995
+ annotationfont?: Font;
996
+ gchordfont?: Font;
997
+ measurefont?: Font;
998
+ repeatfont?: Font;
999
+ tripletfont?: Font;
1000
+ vocalfont?: Font;
1001
+ }
1002
+
935
1003
  export interface AbcElem {
936
- el_type: string; //TODO enumerate these
937
- abselem: AbsoluteElement;
1004
+ el_type: AbcElemType;
1005
+ abselem?: AbsoluteElement;
1006
+ accidentals?: Array<Accidental>
1007
+ acc?: KeyAccidentalName
1008
+ averagepitch?: number;
1009
+ barNumber?: number;
1010
+ beat_division?: Array<MeterFraction>;
1011
+ bpm?: number
1012
+ clefPos?: number
1013
+ cmd?: MidiCommands;
1014
+ maxpitch?: number;
1015
+ minpitch?: number;
1016
+ mode?: Mode
1017
+ postString?: string
1018
+ preString?: string
1019
+ root?: KeyRoot
1020
+ stafflines?: number
1021
+ staffscale?: number
1022
+ suppress?: true
1023
+ suppressBpm?: true
1024
+ value?: Array<MeterFraction>
1025
+ verticalPos?: number
938
1026
  beambr?: number;
939
- chord?: Array<{name: string; position: ChordPlacement}>
940
- decoration: Array<string> //TODO enumerate these
941
- duration: number
942
- endBeam?: boolean
943
- endSlur?: number
1027
+ chord?: Array<ChordProperties>
1028
+ color?: string;
1029
+ decoration?: Array<Decorations>
1030
+ duration?: number | Array<number>
1031
+ endBeam?: true
1032
+ endEnding?: true
1033
+ endSlur?: Array<number>
944
1034
  endTriplet?: true
945
- gracenotes?: Array<{duration: number; name:string; pitch: number; verticalPosition: number;}>
1035
+ fonts?: AbcElemFonts;
1036
+ gap?: number;
1037
+ gracenotes?: Array<AbcElemGraceNote>
1038
+ head?: NoteHeadType
946
1039
  lyric?: Array<{syllable: string; divider: ' ' | '-' | '_';}>
947
- noStem?: boolean
948
- midiPitches?: MidiPitches;
949
- midiGraceNotePitches?: MidiGracePitches;
950
- pitches?: Array<{
951
- pitch: number;
952
- name: string;
953
- startSlur?: Array<{label: number}>;
954
- endSlur?: Array<number>;
955
- startTie?: {};
956
- endTie?: boolean;
957
- verticalPos: number;
958
- highestVert: number;
959
- }>
960
- positioning?: any
961
- rest?: {"type": "rest"}
962
- startBeam?: boolean
1040
+ noStem?: true
1041
+ notes?: Array<any>
1042
+ overlay?: Array<VoiceItemNote>
1043
+ params?: Array<string|number>;
1044
+ pitches?: Array<AbcElemPitch>
1045
+ positioning?: AbcElemPositioning
1046
+ rest?: AbcElemRest
1047
+ size?: number
1048
+ startBeam?: true
1049
+ startEnding?: string
1050
+ startSlur?: Array<SlurProperties>
963
1051
  startTriplet?: number
964
- tripletMultiplier?: number
965
- tripletR?: number
1052
+ steps?: number
966
1053
  stemConnectsToAbove?: true
967
1054
  style?: NoteHeadType
968
- startChar: number
969
- endChar: number
1055
+ title?: string
1056
+ transpose?: number
1057
+ tripletMultiplier?: number
1058
+ tripletR?: number
1059
+ type?: string
1060
+ direction?: StemDirection;
1061
+ grace?: true
1062
+ text?: string | number;
1063
+ currentTrackMilliseconds?: number
1064
+ currentTrackWholeNotes?: number | Array<number>
1065
+ midiPitches?: MidiPitches;
1066
+ midiGraceNotePitches?: MidiGracePitches;
1067
+ startChar?: number
1068
+ endChar?: number
970
1069
  }
971
1070
 
972
1071
  export interface ClickListenerDrag {