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.
@@ -203,10 +203,6 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
203
203
  var self = this;
204
204
  if (!params) params = {};
205
205
  self.qpm = params.qpm ? parseInt(params.qpm, 10) : null;
206
- if (!self.qpm) {
207
- var tempo = target.metaText ? target.metaText.tempo : null;
208
- self.qpm = target.getBpm(tempo);
209
- }
210
206
  self.extraMeasuresAtBeginning = params.extraMeasuresAtBeginning ? parseInt(params.extraMeasuresAtBeginning, 10) : 0;
211
207
  self.beatCallback = params.beatCallback; // This is called for each beat.
212
208
  self.eventCallback = params.eventCallback; // This is called for each note or rest encountered.
@@ -216,6 +212,10 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
216
212
  if (!self.beatSubdivisions) self.beatSubdivisions = 1;
217
213
  self.joggerTimer = null;
218
214
  self.replaceTarget = function (newTarget) {
215
+ if (!params.qpm) {
216
+ var tempo = newTarget.metaText ? newTarget.metaText.tempo : null;
217
+ self.qpm = newTarget.getBpm(tempo);
218
+ }
219
219
  self.noteTimings = newTarget.setTiming(self.qpm, self.extraMeasuresAtBeginning);
220
220
  if (newTarget.noteTimings.length === 0) self.noteTimings = newTarget.setTiming(0, 0);
221
221
  if (self.lineEndCallback) {
@@ -442,14 +442,40 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
442
442
  startTime: self.startTime
443
443
  };
444
444
  }
445
- var thisStartTime = self.startTime; // the beat callback can call seek and change the position from beneath us.
446
- self.beatCallback(self.beatStarts[self.currentBeat].b, self.totalBeats / self.beatSubdivisions, self.lastMoment, position, debugInfo);
447
- if (thisStartTime !== self.startTime) {
448
- return timestamp - self.startTime;
449
- } // else
450
- // self.currentBeat++;
451
- }
452
445
 
446
+ // there is a case where self.beatStarts[self.currentBeat] doesn't exist, but I don't know how. Give more info in that case for better debugging.
447
+ if (self.currentBeat < 0 || self.currentBeat >= self.beatStarts.length || !self.beatStarts[self.currentBeat]) {
448
+ var obj = {
449
+ currentBeat: self.currentBeat,
450
+ beatStartLength: self.beatStarts.length,
451
+ totalBeats: self.totalBeats,
452
+ startTime: self.startTime,
453
+ currentTime: self.currentTime,
454
+ lastMoment: self.lastMoment,
455
+ lastTimestamp: self.lastTimestamp,
456
+ qpm: self.qpm,
457
+ millisecondsPerBeat: self.millisecondsPerBeat,
458
+ beatSubdivisions: self.beatSubdivisions,
459
+ currentEvent: self.currentEvent,
460
+ currentLine: self.currentLine,
461
+ isPaused: self.isPaused,
462
+ isRunning: self.isRunning,
463
+ pausedPercent: self.pausedPercent,
464
+ justUnpaused: self.justUnpaused,
465
+ newSeekPercent: self.newSeekPercent
466
+ };
467
+ setTimeout(function () {
468
+ // throw the error outside of the normal processing so that everything else continues working.
469
+ throw new Error("abcjs-timing-callback error: " + JSON.stringify(obj));
470
+ }, 1);
471
+ } else {
472
+ var thisStartTime = self.startTime; // the beat callback can call seek and change the position from beneath us.
473
+ self.beatCallback(self.beatStarts[self.currentBeat].b, self.totalBeats / self.beatSubdivisions, self.lastMoment, position, debugInfo);
474
+ if (thisStartTime !== self.startTime) {
475
+ return timestamp - self.startTime;
476
+ }
477
+ }
478
+ }
453
479
  return null;
454
480
  };
455
481
 
@@ -3220,6 +3246,8 @@ var Parse = function Parse() {
3220
3246
  }
3221
3247
  strTune = arr.join(" "); //. the split removed two characters, so this puts them back
3222
3248
  }
3249
+ // If there is an escaped percent, then temporarily change it so it doesn't affect the processing.
3250
+ strTune = strTune.replace(/\\%/g, "\u200B\uFF05");
3223
3251
  // take care of line continuations right away, but keep the same number of characters
3224
3252
  strTune = strTune.replace(/\\([ \t]*)(%.*)*\n/g, function (all, backslash, comment) {
3225
3253
  var padding = comment ? Array(comment.length + 1).join(' ') : "";
@@ -5211,7 +5239,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5211
5239
  return [e - i + 1 + ws];
5212
5240
  case "[M:":
5213
5241
  var meter = this.setMeter(line.substring(i + 3, e));
5214
- if (tuneBuilder.hasBeginMusic() && meter) tuneBuilder.appendStartingElement('meter', startChar, endChar, meter);else multilineVars.meter = meter;
5242
+ if (startLine && multilineVars.currentVoice && meter) multilineVars.staves[multilineVars.currentVoice.staffNum].meter = meter;else if (tuneBuilder.hasBeginMusic() && meter) tuneBuilder.appendStartingElement('meter', startChar, endChar, meter);else multilineVars.meter = meter;
5215
5243
  return [e - i + 1 + ws];
5216
5244
  case "[K:":
5217
5245
  var result = parseKeyVoice.parseKey(line.substring(i + 3, e), true);
@@ -5682,8 +5710,17 @@ var parseKeyVoice = {};
5682
5710
  key.accidentals.forEach(function (k) {
5683
5711
  ret.accidentals.push(Object.assign({}, k));
5684
5712
  });
5713
+ if (key.explicitAccidentals) {
5714
+ ret.explicitAccidentals = [];
5715
+ key.explicitAccidentals.forEach(function (k) {
5716
+ ret.explicitAccidentals.push(Object.assign({}, k));
5717
+ });
5718
+ }
5685
5719
  return ret;
5686
5720
  };
5721
+ var setVoiceKey = function setVoiceKey() {
5722
+ if (multilineVars.currentVoice) multilineVars.currentVoice.key = parseKeyVoice.deepCopyKey(multilineVars.key);
5723
+ };
5687
5724
  var pitches = {
5688
5725
  A: 5,
5689
5726
  B: 6,
@@ -5814,6 +5851,10 @@ var parseKeyVoice = {};
5814
5851
  };
5815
5852
  ret.foundKey = true;
5816
5853
  tokens.shift();
5854
+ if (!isInline) {
5855
+ multilineVars.globalKey = parseKeyVoice.deepCopyKey(multilineVars.key);
5856
+ }
5857
+ setVoiceKey();
5817
5858
  break;
5818
5859
  case 'Hp':
5819
5860
  parseDirective.addDirective("bagpipes");
@@ -5834,6 +5875,10 @@ var parseKeyVoice = {};
5834
5875
  };
5835
5876
  ret.foundKey = true;
5836
5877
  tokens.shift();
5878
+ if (!isInline) {
5879
+ multilineVars.globalKey = parseKeyVoice.deepCopyKey(multilineVars.key);
5880
+ }
5881
+ setVoiceKey();
5837
5882
  break;
5838
5883
  case 'none':
5839
5884
  // we got the none key - that's the same as C to us
@@ -5845,6 +5890,10 @@ var parseKeyVoice = {};
5845
5890
  };
5846
5891
  ret.foundKey = true;
5847
5892
  tokens.shift();
5893
+ if (!isInline) {
5894
+ multilineVars.globalKey = parseKeyVoice.deepCopyKey(multilineVars.key);
5895
+ }
5896
+ setVoiceKey();
5848
5897
  break;
5849
5898
  default:
5850
5899
  var retPitch = tokenizer.getKeyPitch(tokens[0].token);
@@ -5905,6 +5954,10 @@ var parseKeyVoice = {};
5905
5954
  }
5906
5955
  }
5907
5956
  }
5957
+ if (!isInline) {
5958
+ multilineVars.globalKey = parseKeyVoice.deepCopyKey(multilineVars.key);
5959
+ }
5960
+ setVoiceKey();
5908
5961
  }
5909
5962
  break;
5910
5963
  }
@@ -5956,6 +6009,10 @@ var parseKeyVoice = {};
5956
6009
  }
5957
6010
  }
5958
6011
  }
6012
+ if (!isInline) {
6013
+ multilineVars.globalKey = parseKeyVoice.deepCopyKey(multilineVars.key);
6014
+ }
6015
+ setVoiceKey();
5959
6016
  }
5960
6017
 
5961
6018
  // Now see if any optional parameters are present. They have the form "key=value", except that "clef=" is optional
@@ -6183,6 +6240,7 @@ var parseKeyVoice = {};
6183
6240
  }
6184
6241
 
6185
6242
  multilineVars.currentVoice = currentVoice;
6243
+ if (currentVoice.key) multilineVars.key = parseKeyVoice.deepCopyKey(currentVoice.key);else if (multilineVars.globalKey) multilineVars.key = parseKeyVoice.deepCopyKey(multilineVars.globalKey);
6186
6244
  return tuneBuilder.setCurrentVoice(currentVoice.staffNum, currentVoice.index, id);
6187
6245
  };
6188
6246
  parseKeyVoice.parseVoice = function (line, i, e) {
@@ -14231,9 +14289,13 @@ ChordTrack.prototype.interpretChord = function (name) {
14231
14289
  };
14232
14290
  };
14233
14291
  ChordTrack.prototype.chordNotes = function (bass, modifier) {
14234
- var intervals = this.chordIntervals[modifier];
14292
+ // accept either chord spelling
14293
+ modifier = modifier.replace(/♭/g, 'b').replace(/♯/g, '#');
14294
+ var intervals = chordIntervals[modifier];
14235
14295
  if (!intervals) {
14236
- if (modifier.slice(0, 2).toLowerCase() === 'ma' || modifier[0] === 'M') intervals = this.chordIntervals.M;else if (modifier[0] === 'm' || modifier[0] === '-') intervals = this.chordIntervals.m;else intervals = this.chordIntervals.M;
14296
+ // If the chord isn't in our list, we can at least guess
14297
+ // whether it is major or minor.
14298
+ if (modifier.slice(0, 2).toLowerCase() === 'ma' || modifier[0] === 'M') intervals = chordIntervals.M;else if (modifier[0] === 'm' || modifier[0] === '-') intervals = chordIntervals.m;else intervals = chordIntervals.M;
14237
14299
  }
14238
14300
  bass += 12; // the chord is an octave above the bass note.
14239
14301
 
@@ -14396,7 +14458,7 @@ function extractNote(chord, index) {
14396
14458
  // This creates an arpeggio note no matter how many notes are in the chord - if it runs out of notes it continues in the next octave
14397
14459
  var octave = Math.floor(index / chord.chick.length);
14398
14460
  var note = chord.chick[index % chord.chick.length];
14399
- //console.log(chord.chick, {index, octave, note}, index % chord.chick.length)
14461
+ //console.log(chord.chick, {index, octave, note, index % chord.chick.length)
14400
14462
  return note + octave * 12;
14401
14463
  }
14402
14464
  function parseGChord(gchord) {
@@ -14488,118 +14550,366 @@ ChordTrack.prototype.basses = {
14488
14550
  'F': 41,
14489
14551
  'G': 43
14490
14552
  };
14491
- ChordTrack.prototype.chordIntervals = {
14492
- // diminished (all flat 5 chords)
14493
- 'dim': [0, 3, 6],
14494
- '°': [0, 3, 6],
14495
- '˚': [0, 3, 6],
14496
- 'dim7': [0, 3, 6, 9],
14497
- '°7': [0, 3, 6, 9],
14498
- '˚7': [0, 3, 6, 9],
14499
- 'ø7': [0, 3, 6, 10],
14500
- 'm7(b5)': [0, 3, 6, 10],
14501
- 'm7b5': [0, 3, 6, 10],
14502
- 'm7♭5': [0, 3, 6, 10],
14503
- '-7(b5)': [0, 3, 6, 10],
14504
- '-7b5': [0, 3, 6, 10],
14505
- '7b5': [0, 4, 6, 10],
14506
- '7(b5)': [0, 4, 6, 10],
14507
- '7♭5': [0, 4, 6, 10],
14508
- '7(b9,b5)': [0, 4, 6, 10, 13],
14509
- '7b9,b5': [0, 4, 6, 10, 13],
14510
- '7(#9,b5)': [0, 4, 6, 10, 15],
14511
- '7#9b5': [0, 4, 6, 10, 15],
14512
- 'maj7(b5)': [0, 4, 6, 11],
14513
- 'maj7b5': [0, 4, 6, 11],
14514
- '13(b5)': [0, 4, 6, 10, 14, 21],
14515
- '13b5': [0, 4, 6, 10, 14, 21],
14516
- // minor (all normal 5, minor 3 chords)
14517
- 'm': [0, 3, 7],
14518
- '-': [0, 3, 7],
14519
- 'm6': [0, 3, 7, 9],
14520
- '-6': [0, 3, 7, 9],
14521
- 'm7': [0, 3, 7, 10],
14522
- '-7': [0, 3, 7, 10],
14523
- '-(b6)': [0, 3, 7, 8],
14524
- '-b6': [0, 3, 7, 8],
14525
- '-6/9': [0, 3, 7, 9, 14],
14526
- '-7(b9)': [0, 3, 7, 10, 13],
14527
- '-7b9': [0, 3, 7, 10, 13],
14528
- '-maj7': [0, 3, 7, 11],
14529
- '-9+7': [0, 3, 7, 11, 13],
14530
- '-11': [0, 3, 7, 11, 14, 17],
14531
- 'm11': [0, 3, 7, 11, 14, 17],
14532
- '-maj9': [0, 3, 7, 11, 14],
14533
- '-∆9': [0, 3, 7, 11, 14],
14534
- 'mM9': [0, 3, 7, 11, 14],
14535
- // major (all normal 5, major 3 chords)
14536
- 'M': [0, 4, 7],
14537
- '6': [0, 4, 7, 9],
14538
- '6/9': [0, 4, 7, 9, 14],
14539
- '6add9': [0, 4, 7, 9, 14],
14540
- '69': [0, 4, 7, 9, 14],
14541
- '7': [0, 4, 7, 10],
14542
- '9': [0, 4, 7, 10, 14],
14543
- '11': [0, 7, 10, 14, 17],
14544
- '13': [0, 4, 7, 10, 14, 21],
14545
- '7b9': [0, 4, 7, 10, 13],
14546
- '7♭9': [0, 4, 7, 10, 13],
14547
- '7(b9)': [0, 4, 7, 10, 13],
14548
- '7(#9)': [0, 4, 7, 10, 15],
14549
- '7#9': [0, 4, 7, 10, 15],
14550
- '(13)': [0, 4, 7, 10, 14, 21],
14551
- '7(9,13)': [0, 4, 7, 10, 14, 21],
14552
- '7(#9,b13)': [0, 4, 7, 10, 15, 20],
14553
- '7(#11)': [0, 4, 7, 10, 14, 18],
14554
- '7#11': [0, 4, 7, 10, 14, 18],
14555
- '7(b13)': [0, 4, 7, 10, 20],
14556
- '7b13': [0, 4, 7, 10, 20],
14557
- '9(#11)': [0, 4, 7, 10, 14, 18],
14558
- '9#11': [0, 4, 7, 10, 14, 18],
14559
- '13(#11)': [0, 4, 7, 10, 18, 21],
14560
- '13#11': [0, 4, 7, 10, 18, 21],
14561
- 'maj7': [0, 4, 7, 11],
14562
- '∆7': [0, 4, 7, 11],
14563
- 'Δ7': [0, 4, 7, 11],
14564
- 'maj9': [0, 4, 7, 11, 14],
14565
- 'maj7(9)': [0, 4, 7, 11, 14],
14566
- 'maj7(11)': [0, 4, 7, 11, 17],
14567
- 'maj7(#11)': [0, 4, 7, 11, 18],
14568
- 'maj7(13)': [0, 4, 7, 14, 21],
14569
- 'maj7(9,13)': [0, 4, 7, 11, 14, 21],
14570
- '7sus4': [0, 5, 7, 10],
14571
- 'm7sus4': [0, 3, 7, 10, 17],
14572
- 'sus4': [0, 5, 7],
14573
- 'sus2': [0, 2, 7],
14574
- '7sus2': [0, 2, 7, 10],
14575
- '9sus4': [0, 5, 7, 10, 14],
14576
- '13sus4': [0, 5, 7, 10, 14, 21],
14577
- // augmented (all sharp 5 chords)
14578
- 'aug7': [0, 4, 8, 10],
14579
- '+7': [0, 4, 8, 10],
14580
- '+': [0, 4, 8],
14581
- '7#5': [0, 4, 8, 10],
14582
- '7♯5': [0, 4, 8, 10],
14583
- '7+5': [0, 4, 8, 10],
14584
- '9#5': [0, 4, 8, 10, 14],
14585
- '9♯5': [0, 4, 8, 10, 14],
14586
- '9+5': [0, 4, 8, 10, 14],
14587
- '-7(#5)': [0, 3, 8, 10],
14588
- '-7#5': [0, 3, 8, 10],
14589
- '7(#5)': [0, 4, 8, 10],
14590
- '7(b9,#5)': [0, 4, 8, 10, 13],
14591
- '7b9#5': [0, 4, 8, 10, 13],
14592
- 'maj7(#5)': [0, 4, 8, 11],
14593
- 'maj7#5': [0, 4, 8, 11],
14594
- 'maj7(#5,#11)': [0, 4, 8, 11, 18],
14595
- 'maj7#5#11': [0, 4, 8, 11, 18],
14596
- '9(#5)': [0, 4, 8, 10, 14],
14597
- '13(#5)': [0, 4, 8, 10, 14, 21],
14598
- '13#5': [0, 4, 8, 10, 14, 21],
14599
- // MAE Power chords added 10 April 2024
14600
- '5': [0, 7],
14601
- '5(8)': [0, 7, 12],
14602
- '5add8': [0, 7, 12]
14553
+ var chordIntervals = {
14554
+ // unusual chords
14555
+ "-addb2": [0, 1, 3, 7],
14556
+ "maddb2": [0, 1, 3, 7],
14557
+ "addb2": [0, 1, 4, 7],
14558
+ "susb2": [0, 1, 7],
14559
+ "-add2": [0, 2, 3, 7],
14560
+ "madd2": [0, 2, 3, 7],
14561
+ "add2": [0, 2, 4, 7],
14562
+ "sus2": [0, 2, 7],
14563
+ "-7sus2": [0, 2, 7, 10],
14564
+ "7sus2": [0, 2, 7, 10],
14565
+ "m7sus2": [0, 2, 7, 10],
14566
+ "min7sus2": [0, 2, 7, 10],
14567
+ "7sus2/9": [0, 2, 7, 10, 14],
14568
+ "add#2": [0, 3, 4, 7],
14569
+ "-add4": [0, 3, 5, 7],
14570
+ "madd4": [0, 3, 5, 7],
14571
+ // diminished chords
14572
+ "dim": [0, 3, 6],
14573
+ "m(b5)": [0, 3, 6],
14574
+ "°": [0, 3, 6],
14575
+ "˚": [0, 3, 6],
14576
+ "-add#4": [0, 3, 6, 7],
14577
+ "madd#4": [0, 3, 6, 7],
14578
+ "dim7": [0, 3, 6, 9],
14579
+ "°7": [0, 3, 6, 9],
14580
+ "˚7": [0, 3, 6, 9],
14581
+ "-7(b5)": [0, 3, 6, 10],
14582
+ "-7b5": [0, 3, 6, 10],
14583
+ "m7(b5)": [0, 3, 6, 10],
14584
+ "m7b5": [0, 3, 6, 10],
14585
+ "ø": [0, 3, 6, 10],
14586
+ "ø7": [0, 3, 6, 10],
14587
+ // minor chords
14588
+ "-": [0, 3, 7],
14589
+ "m": [0, 3, 7],
14590
+ "-(b6)": [0, 3, 7, 8],
14591
+ "-b6": [0, 3, 7, 8],
14592
+ "m(b6)": [0, 3, 7, 8],
14593
+ "mb6": [0, 3, 7, 8],
14594
+ "-6": [0, 3, 7, 9],
14595
+ "m6": [0, 3, 7, 9],
14596
+ "-6(9)": [0, 3, 7, 9, 14],
14597
+ "-6/9": [0, 3, 7, 9, 14],
14598
+ "-69": [0, 3, 7, 9, 14],
14599
+ "m6(9)": [0, 3, 7, 9, 14],
14600
+ "m6/9": [0, 3, 7, 9, 14],
14601
+ "m69": [0, 3, 7, 9, 14],
14602
+ "-7": [0, 3, 7, 10],
14603
+ "m7": [0, 3, 7, 10],
14604
+ "-7(b9)": [0, 3, 7, 10, 13],
14605
+ "-7b9": [0, 3, 7, 10, 13],
14606
+ "-7(9)": [0, 3, 7, 10, 14],
14607
+ "-7/9": [0, 3, 7, 10, 14],
14608
+ "-9": [0, 3, 7, 10, 14],
14609
+ "m7(9)": [0, 3, 7, 10, 14],
14610
+ "m7/9": [0, 3, 7, 10, 14],
14611
+ "m9": [0, 3, 7, 10, 14],
14612
+ "-7(9,11)": [0, 3, 7, 10, 14, 17],
14613
+ "-7/9/11": [0, 3, 7, 10, 14, 17],
14614
+ "m7(9,11)": [0, 3, 7, 10, 14, 17],
14615
+ "m7/9/11": [0, 3, 7, 10, 14, 17],
14616
+ "-13": [0, 3, 7, 10, 14, 17, 21],
14617
+ "-7(9,11,13)": [0, 3, 7, 10, 14, 17, 21],
14618
+ "-7/9/11/13": [0, 3, 7, 10, 14, 17, 21],
14619
+ "m13": [0, 3, 7, 10, 14, 17, 21],
14620
+ "m7(9,11,13)": [0, 3, 7, 10, 14, 17, 21],
14621
+ "m7/9/11/13": [0, 3, 7, 10, 14, 17, 21],
14622
+ "-7(9,13)": [0, 3, 7, 10, 14, 21],
14623
+ "-7/9/13": [0, 3, 7, 10, 14, 21],
14624
+ "m7(9,13)": [0, 3, 7, 10, 14, 21],
14625
+ "m7/9/13": [0, 3, 7, 10, 14, 21],
14626
+ "-7(11)": [0, 3, 7, 10, 17],
14627
+ "-7/11": [0, 3, 7, 10, 17],
14628
+ "m7(11)": [0, 3, 7, 10, 17],
14629
+ "m7/11": [0, 3, 7, 10, 17],
14630
+ "-7(11,13)": [0, 3, 7, 10, 17, 21],
14631
+ "-7/11/13": [0, 3, 7, 10, 17, 21],
14632
+ "m7(11,13)": [0, 3, 7, 10, 17, 21],
14633
+ "m7/11/13": [0, 3, 7, 10, 17, 21],
14634
+ "-(maj7)": [0, 3, 7, 11],
14635
+ "-7M": [0, 3, 7, 11],
14636
+ "-maj7": [0, 3, 7, 11],
14637
+ "-∆7": [0, 3, 7, 11],
14638
+ "m(maj7)": [0, 3, 7, 11],
14639
+ "mM7": [0, 3, 7, 11],
14640
+ "min(maj7)": [0, 3, 7, 11],
14641
+ "-9+7": [0, 3, 7, 11, 13],
14642
+ "-(maj9)": [0, 3, 7, 11, 14],
14643
+ "-7M(9)": [0, 3, 7, 11, 14],
14644
+ "-maj9": [0, 3, 7, 11, 14],
14645
+ "-∆9": [0, 3, 7, 11, 14],
14646
+ "m(maj9)": [0, 3, 7, 11, 14],
14647
+ "mM9": [0, 3, 7, 11, 14],
14648
+ "min(maj9)": [0, 3, 7, 11, 14],
14649
+ "-11": [0, 3, 7, 11, 14, 17],
14650
+ "m11": [0, 3, 7, 11, 14, 17],
14651
+ "-addb9": [0, 3, 7, 13],
14652
+ "maddb9": [0, 3, 7, 13],
14653
+ "-add9": [0, 3, 7, 14],
14654
+ "madd9": [0, 3, 7, 14],
14655
+ "-add11": [0, 3, 7, 17],
14656
+ "madd11": [0, 3, 7, 17],
14657
+ "-add#11": [0, 3, 7, 18],
14658
+ "madd#11": [0, 3, 7, 18],
14659
+ // altered minor chords
14660
+ "-#5": [0, 3, 8],
14661
+ "-(#5)": [0, 3, 8],
14662
+ "m#5": [0, 3, 8],
14663
+ "m(#5)": [0, 3, 8],
14664
+ "-7#5": [0, 3, 8, 10],
14665
+ "-7(#5)": [0, 3, 8, 10],
14666
+ "dim7(b13)": [0, 3, 9, 20],
14667
+ "°7(b13)": [0, 3, 9, 20],
14668
+ "˚7(b13)": [0, 3, 9, 20],
14669
+ // altered major chords
14670
+ "add4": [0, 4, 5, 7],
14671
+ "maj(b5)": [0, 4, 6],
14672
+ "majb5": [0, 4, 6],
14673
+ "add#4": [0, 4, 6, 7],
14674
+ "7(b5)": [0, 4, 6, 10],
14675
+ "7b5": [0, 4, 6, 10],
14676
+ "7(b5,b9)": [0, 4, 6, 10, 13],
14677
+ "7(b9,b5)": [0, 4, 6, 10, 13],
14678
+ "7b5(b9)": [0, 4, 6, 10, 13],
14679
+ "7b9,b5": [0, 4, 6, 10, 13],
14680
+ "7b9b5": [0, 4, 6, 10, 13],
14681
+ "7(b5,b9,b13)": [0, 4, 6, 10, 13, 20],
14682
+ "7/b5/b9/b13": [0, 4, 6, 10, 13, 20],
14683
+ "7b5/b9/b13": [0, 4, 6, 10, 13, 20],
14684
+ "7(b5,b9,13)": [0, 4, 6, 10, 13, 21],
14685
+ "7/b5/b9/13": [0, 4, 6, 10, 13, 21],
14686
+ "7b5/b9/13": [0, 4, 6, 10, 13, 21],
14687
+ "7(9,b5)": [0, 4, 6, 10, 14],
14688
+ "7(b5,9)": [0, 4, 6, 10, 14],
14689
+ "7b5(9)": [0, 4, 6, 10, 14],
14690
+ "9b5": [0, 4, 6, 10, 14],
14691
+ "13(b5)": [0, 4, 6, 10, 14, 21],
14692
+ "13b5": [0, 4, 6, 10, 14, 21],
14693
+ "7(b5,9,13)": [0, 4, 6, 10, 14, 21],
14694
+ "7/b5/9/13": [0, 4, 6, 10, 14, 21],
14695
+ "7b5/9/13": [0, 4, 6, 10, 14, 21],
14696
+ "7#9b5": [0, 4, 6, 10, 15],
14697
+ "7(#9,b5)": [0, 4, 6, 10, 15],
14698
+ "7(b5,#9)": [0, 4, 6, 10, 15],
14699
+ "7b5(#9)": [0, 4, 6, 10, 15],
14700
+ "7(b5,#9,b13)": [0, 4, 6, 10, 15, 20],
14701
+ "7/b5/#9/b13": [0, 4, 6, 10, 15, 20],
14702
+ "7b5/#9/b13": [0, 4, 6, 10, 15, 20],
14703
+ "7(b5,#9,13)": [0, 4, 6, 10, 15, 21],
14704
+ "7/b5/#9/13": [0, 4, 6, 10, 15, 21],
14705
+ "7b5/#9/13": [0, 4, 6, 10, 15, 21],
14706
+ "7(b5,b13)": [0, 4, 6, 10, 20],
14707
+ "7/b5/b13": [0, 4, 6, 10, 20],
14708
+ "7b5/b13": [0, 4, 6, 10, 20],
14709
+ "7(b5,13)": [0, 4, 6, 10, 21],
14710
+ "7/b5/13": [0, 4, 6, 10, 21],
14711
+ "7b5(13)": [0, 4, 6, 10, 21],
14712
+ "7b5/13": [0, 4, 6, 10, 21],
14713
+ "7M(b5)": [0, 4, 6, 11],
14714
+ "maj7(b5)": [0, 4, 6, 11],
14715
+ "maj7b5": [0, 4, 6, 11],
14716
+ "7M(b5,9)": [0, 4, 6, 11, 14],
14717
+ "maj7(b5,9)": [0, 4, 6, 11, 14],
14718
+ "maj7b5/9": [0, 4, 6, 11, 14],
14719
+ "7M(b5,9,13)": [0, 4, 6, 11, 14, 21],
14720
+ "maj7(b5,9,13)": [0, 4, 6, 11, 14, 21],
14721
+ "maj7b5/9/13": [0, 4, 6, 11, 14, 21],
14722
+ "7M(b5,13)": [0, 4, 6, 11, 21],
14723
+ "maj7(b5,13)": [0, 4, 6, 11, 21],
14724
+ "maj7b5/13": [0, 4, 6, 11, 21],
14725
+ // major chords
14726
+ "M": [0, 4, 7],
14727
+ "Δ": [0, 4, 7],
14728
+ "∆": [0, 4, 7],
14729
+ "6": [0, 4, 7, 9],
14730
+ "6(9)": [0, 4, 7, 9, 14],
14731
+ "6/9": [0, 4, 7, 9, 14],
14732
+ "69": [0, 4, 7, 9, 14],
14733
+ "6add9": [0, 4, 7, 9, 14],
14734
+ "6(9,11)": [0, 4, 7, 9, 14, 17],
14735
+ "6/9/11": [0, 4, 7, 9, 14, 17],
14736
+ "6911": [0, 4, 7, 9, 14, 17],
14737
+ "6(9,#11)": [0, 4, 7, 9, 14, 18],
14738
+ "6/9/#11": [0, 4, 7, 9, 14, 18],
14739
+ "69#11": [0, 4, 7, 9, 14, 18],
14740
+ "6(11)": [0, 4, 7, 9, 17],
14741
+ "6/11": [0, 4, 7, 9, 17],
14742
+ "611": [0, 4, 7, 9, 17],
14743
+ "6#11": [0, 4, 7, 9, 18],
14744
+ "6(#11)": [0, 4, 7, 9, 18],
14745
+ "6/#11": [0, 4, 7, 9, 18],
14746
+ "7": [0, 4, 7, 10],
14747
+ "7(b9)": [0, 4, 7, 10, 13],
14748
+ "7b9": [0, 4, 7, 10, 13],
14749
+ "7(9)": [0, 4, 7, 10, 14],
14750
+ "7/9": [0, 4, 7, 10, 14],
14751
+ "9": [0, 4, 7, 10, 14],
14752
+ "7(9,11)": [0, 4, 7, 10, 14, 17],
14753
+ "7/9/11": [0, 4, 7, 10, 14, 17],
14754
+ "7(9,11,13)": [0, 4, 7, 10, 14, 17, 21],
14755
+ "7/9/11/13": [0, 4, 7, 10, 14, 17, 21],
14756
+ "7#11": [0, 4, 7, 10, 14, 18],
14757
+ "7(#11)": [0, 4, 7, 10, 14, 18],
14758
+ "9#11": [0, 4, 7, 10, 14, 18],
14759
+ "9(#11)": [0, 4, 7, 10, 14, 18],
14760
+ "(13)": [0, 4, 7, 10, 14, 21],
14761
+ "13": [0, 4, 7, 10, 14, 21],
14762
+ "7(9,13)": [0, 4, 7, 10, 14, 21],
14763
+ "7/9/13": [0, 4, 7, 10, 14, 21],
14764
+ "9/13": [0, 4, 7, 10, 14, 21],
14765
+ "7#9": [0, 4, 7, 10, 15],
14766
+ "7(#9)": [0, 4, 7, 10, 15],
14767
+ "7(#9,b13)": [0, 4, 7, 10, 15, 20],
14768
+ "7(11)": [0, 4, 7, 10, 17],
14769
+ "7/11": [0, 4, 7, 10, 17],
14770
+ "7(11,13)": [0, 4, 7, 10, 17, 21],
14771
+ "7/11/13": [0, 4, 7, 10, 17, 21],
14772
+ "13#11": [0, 4, 7, 10, 18, 21],
14773
+ "13(#11)": [0, 4, 7, 10, 18, 21],
14774
+ "7(b13)": [0, 4, 7, 10, 20],
14775
+ "7b13": [0, 4, 7, 10, 20],
14776
+ "7(13)": [0, 4, 7, 10, 21],
14777
+ "7/13": [0, 4, 7, 10, 21],
14778
+ "7M": [0, 4, 7, 11],
14779
+ "maj7": [0, 4, 7, 11],
14780
+ "Δ7": [0, 4, 7, 11],
14781
+ "∆7": [0, 4, 7, 11],
14782
+ "7M(9)": [0, 4, 7, 11, 14],
14783
+ "7M/9": [0, 4, 7, 11, 14],
14784
+ "maj7(9)": [0, 4, 7, 11, 14],
14785
+ "maj7/9": [0, 4, 7, 11, 14],
14786
+ "maj9": [0, 4, 7, 11, 14],
14787
+ "Δ9": [0, 4, 7, 11, 14],
14788
+ "∆9": [0, 4, 7, 11, 14],
14789
+ "7M(9,11)": [0, 4, 7, 11, 14, 17],
14790
+ "7M/9/11": [0, 4, 7, 11, 14, 17],
14791
+ "maj11": [0, 4, 7, 11, 14, 17],
14792
+ "maj7(9,11)": [0, 4, 7, 11, 14, 17],
14793
+ "maj7/9/11": [0, 4, 7, 11, 14, 17],
14794
+ "Δ11": [0, 4, 7, 11, 14, 17],
14795
+ "∆11": [0, 4, 7, 11, 14, 17],
14796
+ "7M(9,11,13)": [0, 4, 7, 11, 14, 17, 21],
14797
+ "7M/9/11/13": [0, 4, 7, 11, 14, 17, 21],
14798
+ "maj13": [0, 4, 7, 11, 14, 17, 21],
14799
+ "maj7(9,11,13)": [0, 4, 7, 11, 14, 17, 21],
14800
+ "maj7/9/11/13": [0, 4, 7, 11, 14, 17, 21],
14801
+ "Δ11(13)": [0, 4, 7, 11, 14, 17, 21],
14802
+ "Δ13": [0, 4, 7, 11, 14, 17, 21],
14803
+ "∆11(13)": [0, 4, 7, 11, 14, 17, 21],
14804
+ "7M(9,#11)": [0, 4, 7, 11, 14, 18],
14805
+ "7M/9/#11": [0, 4, 7, 11, 14, 18],
14806
+ "maj7(9,#11)": [0, 4, 7, 11, 14, 18],
14807
+ "maj7/9/#11": [0, 4, 7, 11, 14, 18],
14808
+ "maj9#11": [0, 4, 7, 11, 14, 18],
14809
+ "maj9(#11)": [0, 4, 7, 11, 14, 18],
14810
+ "maj9/#11": [0, 4, 7, 11, 14, 18],
14811
+ "Δ9(#11)": [0, 4, 7, 11, 14, 18],
14812
+ "7M(9,#11,13)": [0, 4, 7, 11, 14, 18, 21],
14813
+ "7M/9/#11/13": [0, 4, 7, 11, 14, 18, 21],
14814
+ "maj13#11": [0, 4, 7, 11, 14, 18, 21],
14815
+ "maj13(#11)": [0, 4, 7, 11, 14, 18, 21],
14816
+ "maj13/#11": [0, 4, 7, 11, 14, 18, 21],
14817
+ "Δ13(#11)": [0, 4, 7, 11, 14, 18, 21],
14818
+ "∆13(#11)": [0, 4, 7, 11, 14, 18, 21],
14819
+ "7M(9,13)": [0, 4, 7, 11, 14, 21],
14820
+ "7M/9/13": [0, 4, 7, 11, 14, 21],
14821
+ "maj7(9,13)": [0, 4, 7, 11, 14, 21],
14822
+ "maj7/9/13": [0, 4, 7, 11, 14, 21],
14823
+ "maj9(13)": [0, 4, 7, 11, 14, 21],
14824
+ "Δ9(13)": [0, 4, 7, 11, 14, 21],
14825
+ "∆9(13)": [0, 4, 7, 11, 14, 21],
14826
+ "7M(11)": [0, 4, 7, 11, 17],
14827
+ "7M/11": [0, 4, 7, 11, 17],
14828
+ "maj7(11)": [0, 4, 7, 11, 17],
14829
+ "maj7/11": [0, 4, 7, 11, 17],
14830
+ "Δ7(11)": [0, 4, 7, 11, 17],
14831
+ "∆7(11)": [0, 4, 7, 11, 17],
14832
+ "7M(#11)": [0, 4, 7, 11, 18],
14833
+ "7M/#11": [0, 4, 7, 11, 18],
14834
+ "maj7(#11)": [0, 4, 7, 11, 18],
14835
+ "maj7/#11": [0, 4, 7, 11, 18],
14836
+ "Δ7(#11)": [0, 4, 7, 11, 18],
14837
+ "∆7(#11)": [0, 4, 7, 11, 18],
14838
+ "7M(13)": [0, 4, 7, 11, 21],
14839
+ "7M/13": [0, 4, 7, 11, 21],
14840
+ "maj7(13)": [0, 4, 7, 11, 21],
14841
+ "maj7/13": [0, 4, 7, 11, 21],
14842
+ "Δ7(13)": [0, 4, 7, 11, 21],
14843
+ "∆7(13)": [0, 4, 7, 11, 21],
14844
+ "addb9": [0, 4, 7, 13],
14845
+ "add9": [0, 4, 7, 14],
14846
+ "add#9": [0, 4, 7, 15],
14847
+ "add11": [0, 4, 7, 17],
14848
+ "add#11": [0, 4, 7, 18],
14849
+ // augmented chords
14850
+ "+": [0, 4, 8],
14851
+ "aug": [0, 4, 8],
14852
+ "+7": [0, 4, 8, 10],
14853
+ "7#5": [0, 4, 8, 10],
14854
+ "7(#5)": [0, 4, 8, 10],
14855
+ "7+5": [0, 4, 8, 10],
14856
+ "aug7": [0, 4, 8, 10],
14857
+ "7(b9,#5)": [0, 4, 8, 10, 13],
14858
+ "7b9#5": [0, 4, 8, 10, 13],
14859
+ "9#5": [0, 4, 8, 10, 14],
14860
+ "9(#5)": [0, 4, 8, 10, 14],
14861
+ "9+5": [0, 4, 8, 10, 14],
14862
+ "13#5": [0, 4, 8, 10, 14, 21],
14863
+ "13(#5)": [0, 4, 8, 10, 14, 21],
14864
+ "7M(#5)": [0, 4, 8, 11],
14865
+ "maj7#5": [0, 4, 8, 11],
14866
+ "maj7(#5)": [0, 4, 8, 11],
14867
+ "7M(#5,9)": [0, 4, 8, 11, 14],
14868
+ "maj7#5/9": [0, 4, 8, 11, 14],
14869
+ "maj7(#5,9)": [0, 4, 8, 11, 14],
14870
+ "maj7#5#11": [0, 4, 8, 11, 18],
14871
+ "maj7(#5,#11)": [0, 4, 8, 11, 18],
14872
+ // sus4 chords
14873
+ "sus": [0, 5, 7],
14874
+ "sus4": [0, 5, 7],
14875
+ "-7sus4": [0, 5, 7, 10],
14876
+ "7sus": [0, 5, 7, 10],
14877
+ "7sus4": [0, 5, 7, 10],
14878
+ "m7sus4": [0, 5, 7, 10],
14879
+ "min7sus4": [0, 5, 7, 10],
14880
+ "7sus(b9)": [0, 5, 7, 10, 13],
14881
+ "7sus4(b9)": [0, 5, 7, 10, 13],
14882
+ "-9sus4": [0, 5, 7, 10, 14],
14883
+ "7sus4(9)": [0, 5, 7, 10, 14],
14884
+ "7sus4/9": [0, 5, 7, 10, 14],
14885
+ "9sus": [0, 5, 7, 10, 14],
14886
+ "9sus4": [0, 5, 7, 10, 14],
14887
+ "m9sus4": [0, 5, 7, 10, 14],
14888
+ "min9sus4": [0, 5, 7, 10, 14],
14889
+ "11sus4": [0, 5, 7, 10, 14, 17],
14890
+ "13sus4": [0, 5, 7, 10, 14, 21],
14891
+ "7sus(#9)": [0, 5, 7, 10, 15],
14892
+ "7sus4(#9)": [0, 5, 7, 10, 15],
14893
+ "7sus4(11)": [0, 5, 7, 10, 17],
14894
+ "7sus4(13)": [0, 5, 7, 10, 21],
14895
+ "7Msus": [0, 5, 7, 11],
14896
+ "7Msus4": [0, 5, 7, 11],
14897
+ "maj7sus": [0, 5, 7, 11],
14898
+ "maj7sus4": [0, 5, 7, 11],
14899
+ // power chords and unusual chords
14900
+ "#4": [0, 6],
14901
+ "b5": [0, 6],
14902
+ "sus#4": [0, 6, 7],
14903
+ "7Msus#4": [0, 6, 11],
14904
+ "maj7sus#4": [0, 6, 11],
14905
+ "5": [0, 7],
14906
+ "11": [0, 7, 10, 14, 17],
14907
+ "5(8)": [0, 7, 12],
14908
+ "5add8": [0, 7, 12],
14909
+ "5(9)": [0, 7, 14],
14910
+ "5add9": [0, 7, 14],
14911
+ "#5": [0, 8],
14912
+ "b6": [0, 8]
14603
14913
  };
14604
14914
  ChordTrack.prototype.rhythmPatterns = {
14605
14915
  "2/2": ['boom', '', '', '', 'chick', '', '', ''],
@@ -15180,7 +15490,14 @@ function CreateSynth() {
15180
15490
  // There might be a previous run that needs to be turned off.
15181
15491
  self.stop();
15182
15492
  var noteMapTracks = createNoteMap(self.flattened);
15183
- if (self.options.swing) addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength);
15493
+ if (self.options.swing) {
15494
+ // If we have a drum intro, then the pickup is already incorporated into the beat.
15495
+ // That is, without a drum intro, the first note starts at 0 whether it
15496
+ // is a pickup or not. With a drum intro, the first note will be in its
15497
+ // proper place.
15498
+ var pickupLength = self.options.drumIntro ? 0 : self.pickupLength;
15499
+ addSwing(noteMapTracks, self.options.swing, self.meterFraction, pickupLength);
15500
+ }
15184
15501
  if (self.sequenceCallback) self.sequenceCallback(noteMapTracks, self.callbackContext);
15185
15502
  var panDistances = setPan(noteMapTracks.length, self.pan);
15186
15503
 
@@ -15393,7 +15710,7 @@ function CreateSynth() {
15393
15710
  };
15394
15711
  function addSwing(noteMapTracks, swing, meterFraction, pickupLength) {
15395
15712
  // we can only swing in X/4 and X/8 meters.
15396
- if (meterFraction.den != 4 && meterFraction.den != 8) return;
15713
+ if (meterFraction.den !== 4 && meterFraction.den !== 8) return;
15397
15714
  swing = parseFloat(swing);
15398
15715
 
15399
15716
  // 50 (or less) is no swing,
@@ -15411,7 +15728,7 @@ function CreateSynth() {
15411
15728
  // could be also in the settings. Try out values such 0.1, 0.2
15412
15729
  var volumeIncrease = 0.0;
15413
15730
 
15414
- // the beatLength in X/8 meters
15731
+ // the beatLength in X/4 meters
15415
15732
  var beatLength = 0.25;
15416
15733
 
15417
15734
  // in X/8 meters the 16s swing so the beatLength is halved
@@ -15428,11 +15745,11 @@ function CreateSynth() {
15428
15745
  var event = track[i];
15429
15746
  if (
15430
15747
  // is halfbeat
15431
- (event.start - pickupLength) % halfbeatLength == 0 && (event.start - pickupLength) % beatLength != 0 && (
15748
+ (event.start - pickupLength) % halfbeatLength === 0 && (event.start - pickupLength) % beatLength !== 0 && (
15432
15749
  // the previous note is on the beat or before OR there is no previous note
15433
- i == 0 || track[i - 1].start <= track[i].start - halfbeatLength) && (
15750
+ i === 0 || track[i - 1].start <= track[i].start - halfbeatLength) && (
15434
15751
  // the next note is on the beat or after OR there is no next note
15435
- i == track.length - 1 || track[i + 1].start >= track[i].start + halfbeatLength)) {
15752
+ i === track.length - 1 || track[i + 1].start >= track[i].start + halfbeatLength)) {
15436
15753
  var oldEventStart = event.start;
15437
15754
  event.start += swingDuration;
15438
15755
 
@@ -15441,7 +15758,7 @@ function CreateSynth() {
15441
15758
 
15442
15759
  // if there is a previous note ending at the start of this note, extend its end
15443
15760
  // and decrease its volume
15444
- if (i > 0 && track[i - 1].end == oldEventStart) {
15761
+ if (i > 0 && track[i - 1].end === oldEventStart) {
15445
15762
  track[i - 1].end = event.start;
15446
15763
  track[i - 1].volume *= 1 - volumeIncrease;
15447
15764
  }
@@ -16179,15 +16496,15 @@ function Repeats(voice) {
16179
16496
  index: thisIndex
16180
16497
  });
16181
16498
  }
16182
- if (isStartRepeat) this.sections.push({
16183
- type: "startRepeat",
16184
- index: thisIndex
16185
- });
16186
16499
  if (startEnding) this.sections.push({
16187
16500
  type: "startEnding",
16188
16501
  index: thisIndex,
16189
16502
  endings: startEnding
16190
16503
  });
16504
+ if (isStartRepeat) this.sections.push({
16505
+ type: "startRepeat",
16506
+ index: thisIndex
16507
+ });
16191
16508
  };
16192
16509
  this.resolveRepeats = function () {
16193
16510
  // this.sections contain all the interesting bars - start and end repeats.
@@ -19014,6 +19331,7 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
19014
19331
  }
19015
19332
  }
19016
19333
  var hasStem = !nostem && durlog <= -1;
19334
+ var chordPos = pp > 1 ? p + 1 : null;
19017
19335
  var ret = createNoteHead(abselem, c, elem.pitches[p], {
19018
19336
  dir: dir,
19019
19337
  extrax: -roomTaken,
@@ -19023,7 +19341,8 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
19023
19341
  scale: this.voiceScale,
19024
19342
  accidentalSlot: accidentalSlot,
19025
19343
  shouldExtendStem: !stemdir,
19026
- printAccidentals: !voice.isPercussion
19344
+ printAccidentals: !voice.isPercussion,
19345
+ chordPos: chordPos
19027
19346
  });
19028
19347
  symbolWidth = Math.max(glyphs.getSymbolWidth(c), symbolWidth);
19029
19348
  abselem.extraw -= ret.extraLeft;
@@ -19074,7 +19393,7 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
19074
19393
  symbolWidth: symbolWidth
19075
19394
  };
19076
19395
  };
19077
- AbstractEngraver.prototype.addLyric = function (abselem, elem) {
19396
+ AbstractEngraver.prototype.addLyric = function (abselem, elem, voiceNumber) {
19078
19397
  var lyricStr = "";
19079
19398
  elem.lyric.forEach(function (ly) {
19080
19399
  var div = ly.divider === ' ' ? "" : ly.divider;
@@ -19086,7 +19405,8 @@ AbstractEngraver.prototype.addLyric = function (abselem, elem) {
19086
19405
  type: "lyric",
19087
19406
  position: position,
19088
19407
  height: lyricDim.height / spacing.STEP,
19089
- dim: this.getTextSize.attr('vocalfont', "lyric")
19408
+ dim: this.getTextSize.attr('vocalfont', "lyric"),
19409
+ voiceNumber: voiceNumber
19090
19410
  }));
19091
19411
  };
19092
19412
  AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaff, voice) {
@@ -19138,7 +19458,7 @@ AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaf
19138
19458
  symbolWidth = ret2.symbolWidth;
19139
19459
  }
19140
19460
  if (elem.lyric !== undefined) {
19141
- this.addLyric(abselem, elem);
19461
+ this.addLyric(abselem, elem, voice.voicenumber);
19142
19462
  }
19143
19463
  if (elem.gracenotes !== undefined) {
19144
19464
  roomtaken += this.addGraceNotes(elem, voice, abselem, notehead, this.stemHeight * this.voiceScale, this.isBagpipes, roomtaken);
@@ -19784,6 +20104,7 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19784
20104
  var accidentalSlot = options.accidentalSlot !== undefined ? options.accidentalSlot : [];
19785
20105
  var shouldExtendStem = options.shouldExtendStem !== undefined ? options.shouldExtendStem : false;
19786
20106
  var printAccidentals = options.printAccidentals !== undefined ? options.printAccidentals : true;
20107
+ var chordPos = options.chordPos;
19787
20108
 
19788
20109
  // TODO scale the dot as well
19789
20110
  var pitch = pitchelem.verticalPos;
@@ -19794,7 +20115,9 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19794
20115
  if (c === undefined) abselem.addFixed(new RelativeElement("pitch is undefined", 0, 0, 0, {
19795
20116
  type: "debug"
19796
20117
  }));else if (c === "") {
19797
- notehead = new RelativeElement(null, 0, 0, pitch);
20118
+ notehead = new RelativeElement(null, 0, 0, pitch, {
20119
+ chordPos: chordPos
20120
+ });
19798
20121
  } else {
19799
20122
  var shiftheadx = headx;
19800
20123
  if (pitchelem.printer_shift) {
@@ -19805,7 +20128,8 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19805
20128
  scalex: scale,
19806
20129
  scaley: scale,
19807
20130
  thickness: glyphs.symbolHeightInPitches(c) * scale,
19808
- name: pitchelem.name
20131
+ name: pitchelem.name,
20132
+ chordPos: chordPos
19809
20133
  };
19810
20134
  notehead = new RelativeElement(c, shiftheadx, glyphs.getSymbolWidth(c) * scale, pitch, opts);
19811
20135
  notehead.stemDir = dir;
@@ -19820,13 +20144,16 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19820
20144
  var xdelta = dir === "down" ? headx : headx + notehead.w - 0.6;
19821
20145
  abselem.addRight(new RelativeElement(flag, xdelta, glyphs.getSymbolWidth(flag) * scale, pos, {
19822
20146
  scalex: scale,
19823
- scaley: scale
20147
+ scaley: scale,
20148
+ chordPos: chordPos
19824
20149
  }));
19825
20150
  }
19826
20151
  newDotShiftX = notehead.w + dotshiftx - 2 + 5 * dot;
19827
20152
  for (; dot > 0; dot--) {
19828
20153
  var dotadjusty = 1 - Math.abs(pitch) % 2; //PER: take abs value of the pitch. And the shift still happens on ledger lines.
19829
- abselem.addRight(new RelativeElement("dots.dot", notehead.w + dotshiftx - 2 + 5 * dot, glyphs.getSymbolWidth("dots.dot"), pitch + dotadjusty));
20154
+ abselem.addRight(new RelativeElement("dots.dot", notehead.w + dotshiftx - 2 + 5 * dot, glyphs.getSymbolWidth("dots.dot"), pitch + dotadjusty, {
20155
+ chordPos: chordPos
20156
+ }));
19830
20157
  }
19831
20158
  }
19832
20159
  if (notehead) notehead.highestVert = pitchelem.highestVert;
@@ -19875,7 +20202,8 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19875
20202
  scalex: scale,
19876
20203
  scaley: scale,
19877
20204
  top: pitch + h / 2,
19878
- bottom: pitch - h / 2
20205
+ bottom: pitch - h / 2,
20206
+ chordPos: chordPos
19879
20207
  }));
19880
20208
  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.
19881
20209
  }
@@ -21112,6 +21440,7 @@ var RelativeElement = function RelativeElement(c, dx, w, pitch, opt) {
21112
21440
  this.pitch2 = opt.pitch2;
21113
21441
  this.linewidth = opt.linewidth;
21114
21442
  this.klass = opt.klass;
21443
+ this.chordPos = opt.chordPos;
21115
21444
  this.anchor = opt.anchor ? opt.anchor : 'middle';
21116
21445
  this.top = pitch;
21117
21446
  if (this.pitch2 !== undefined && this.pitch2 > this.top) this.top = this.pitch2;
@@ -21126,6 +21455,7 @@ var RelativeElement = function RelativeElement(c, dx, w, pitch, opt) {
21126
21455
  }
21127
21456
  if (opt.dim) this.dim = opt.dim;
21128
21457
  if (opt.position) this.position = opt.position;
21458
+ if (opt.voiceNumber !== undefined) this.voiceNumber = opt.voiceNumber;
21129
21459
  this.height = opt.height ? opt.height : 4; // The +1 is to give a little bit of padding.
21130
21460
  if (opt.top) this.top = opt.top;
21131
21461
  if (opt.bottom) this.bottom = opt.bottom;
@@ -22679,6 +23009,11 @@ function drawAbsolute(renderer, params, bartop, selectables, staffPos) {
22679
23009
  if (child.type === "symbol" && child.c && child.c.indexOf('notehead') >= 0) {
22680
23010
  el.setAttribute('class', 'abcjs-notehead');
22681
23011
  }
23012
+ if (el && child.chordPos && child.name.indexOf('flags.') !== 0) {
23013
+ var klass = el.getAttribute("class");
23014
+ if (klass) klass = klass + ' abcjs-chord-pos-' + child.chordPos;else klass = 'abcjs-chord-pos-' + child.chordPos;
23015
+ el.setAttribute('class', klass);
23016
+ }
22682
23017
  }
22683
23018
  }
22684
23019
  var klass = params.type;
@@ -23644,7 +23979,7 @@ function printLine(renderer, x1, x2, y, klass, name, dy) {
23644
23979
  var y1 = roundNumber(y - dy);
23645
23980
  var y2 = roundNumber(y + dy);
23646
23981
  // TODO-PER: This fixes a firefox bug where it isn't displayed
23647
- if (renderer.firefox112) {
23982
+ if (renderer.firefox) {
23648
23983
  y += dy / 2; // Because the y coordinate is the edge of where the line goes but the width widens from the middle.
23649
23984
  var attr = {
23650
23985
  x1: x1,
@@ -23708,7 +24043,7 @@ function printStem(renderer, x, dx, y1, y2, klass, name) {
23708
24043
  x = roundNumber(x);
23709
24044
  var x2 = roundNumber(x + dx);
23710
24045
  // TODO-PER: This fixes a firefox bug where it isn't displayed
23711
- if (renderer.firefox112) {
24046
+ if (renderer.firefox) {
23712
24047
  x += dx / 2; // Because the x coordinate is the edge of where the line goes but the width widens from the middle.
23713
24048
  var attr = {
23714
24049
  x1: x,
@@ -24842,7 +25177,7 @@ function drawTriplet(renderer, params, selectables) {
24842
25177
  "data-name": "triplet"
24843
25178
  });
24844
25179
  if (!params.hasBeam) {
24845
- drawBracket(renderer, params.anchor1.x, params.startNote, params.anchor2.x + params.anchor2.w, params.endNote);
25180
+ drawBracket(renderer, params.anchor1.x, params.startNote, params.anchor2.x + params.anchor2.w, params.endNote, params.up);
24846
25181
  }
24847
25182
  // HACK: adjust the position of "3". It is too high in all cases so we fudge it by subtracting 1 here.
24848
25183
  renderText(renderer, {
@@ -24866,10 +25201,10 @@ function drawTriplet(renderer, params, selectables) {
24866
25201
  function drawLine(l, t, r, b) {
24867
25202
  return sprintf("M %f %f L %f %f", roundNumber(l), roundNumber(t), roundNumber(r), roundNumber(b));
24868
25203
  }
24869
- function drawBracket(renderer, x1, y1, x2, y2) {
25204
+ function drawBracket(renderer, x1, y1, x2, y2, up) {
24870
25205
  y1 = renderer.calcY(y1);
24871
25206
  y2 = renderer.calcY(y2);
24872
- var bracketHeight = 5;
25207
+ var bracketHeight = up ? 5 : -5;
24873
25208
 
24874
25209
  // Draw vertical lines at the beginning and end
24875
25210
  var pathString = "";
@@ -25338,7 +25673,7 @@ function splitSvgIntoLines(renderer, output, title, responsive, scale) {
25338
25673
  if (responsive !== 'resize') svg.setAttribute("height", height);
25339
25674
  if (responsive === 'resize') svg.style.position = '';
25340
25675
  // TODO-PER: Hack! Not sure why this is needed.
25341
- var viewBoxHeight = renderer.firefox112 ? height + 1 : height;
25676
+ var viewBoxHeight = renderer.firefox ? height + 1 : height;
25342
25677
  svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + viewBoxHeight);
25343
25678
  svg.appendChild(style.cloneNode(true));
25344
25679
  var titleEl = document.createElement("title");
@@ -26592,6 +26927,7 @@ var setUpperAndLowerElements = __webpack_require__(/*! ./set-upper-and-lower-ele
26592
26927
  var layoutStaffGroup = __webpack_require__(/*! ./staff-group */ "./src/write/layout/staff-group.js");
26593
26928
  var getLeftEdgeOfStaff = __webpack_require__(/*! ./get-left-edge-of-staff */ "./src/write/layout/get-left-edge-of-staff.js");
26594
26929
  var layoutInGrid = __webpack_require__(/*! ./layout-in-grid */ "./src/write/layout/layout-in-grid.js");
26930
+ var toTimeAndStaffBased = __webpack_require__(/*! ./to-time-and-staff-based */ "./src/write/layout/to-time-and-staff-based.js");
26595
26931
 
26596
26932
  // This sets the "x" attribute on all the children in abctune.lines
26597
26933
  // It also sets the "w" and "startx" attributes on "voices"
@@ -26627,6 +26963,16 @@ var layout = function layout(renderer, abctune, width, space, expandToWidest, ti
26627
26963
  }
26628
26964
  }
26629
26965
 
26966
+ // See if there are collisions between voices that need to be tweaked
26967
+ var timeBased = toTimeAndStaffBased(abctune.lines);
26968
+ for (i = 0; i < abctune.lines.length; i++) {
26969
+ abcLine = abctune.lines[i];
26970
+ if (abcLine.staffGroup) {
26971
+ fixVoiceCollisions(timeBased[i]);
26972
+ //setUpperAndLowerElements(renderer, abcLine.staffGroup);
26973
+ }
26974
+ }
26975
+
26630
26976
  // Set the staff spacing
26631
26977
  // 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.
26632
26978
  for (i = 0; i < abctune.lines.length; i++) {
@@ -26709,6 +27055,82 @@ function centerWholeRests(voices) {
26709
27055
  }
26710
27056
  }
26711
27057
  }
27058
+ function fixVoiceCollisions(timeBasedLine) {
27059
+ for (var s = 0; s < timeBasedLine.length; s++) {
27060
+ var timeSlot = timeBasedLine[s];
27061
+ // If there is more than one thing happening at the same time,
27062
+ // and one of those things is a rest, then:
27063
+ // 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
27064
+ // 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.
27065
+ // 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.
27066
+ // If there is a collision, move the rest up or down to fix that.
27067
+ var keys = Object.keys(timeSlot);
27068
+ for (var z = 0; z < keys.length; z++) {
27069
+ var slot = timeSlot[keys[z]]; // slot is an array of all the things happening at a particular time
27070
+ var lastIndex = slot.length - 1;
27071
+ if (slot.length > 1) {
27072
+ var isRealRest = slot[0].abcelem.rest && slot[0].abcelem.rest.type === 'rest'; // weed out invisible rests
27073
+ var isRealRest2 = slot[lastIndex].abcelem.rest && slot[lastIndex].abcelem.rest.type === 'rest'; // weed out invisible rests
27074
+ if (isRealRest && !slot[lastIndex].abcelem.rest) {
27075
+ // the first voice has a rest and the second doesn't
27076
+ var restTop = slot[0].children.find(function (ch) {
27077
+ return ch.name.includes('rest');
27078
+ });
27079
+ var otherTop = closeTop(slot[lastIndex]);
27080
+ if (restTop) {
27081
+ var distance1 = restTop.bottom - otherTop;
27082
+ distance1 -= 2; // give some room between the rest and the note
27083
+ if (distance1 < 0 && slot[0].children.length > 0) {
27084
+ slot[0].bottom -= distance1;
27085
+ slot[0].top -= distance1;
27086
+ slot[0].children[0].bottom -= distance1;
27087
+ slot[0].children[0].top -= distance1;
27088
+ slot[0].children[0].pitch -= distance1;
27089
+ }
27090
+ }
27091
+ } else if (isRealRest2 && !slot[0].abcelem.rest) {
27092
+ // the last voice has a rest and the first doesn't
27093
+ var restBottom = slot[lastIndex].children.find(function (ch) {
27094
+ return ch.name.includes('rest');
27095
+ });
27096
+ if (restBottom) {
27097
+ var distance2 = restBottom.top - closeBottom(slot[0]);
27098
+ distance2 += 2; // give some room between the rest and the note
27099
+ if (distance2 > 0 && slot[lastIndex].children.length > 0) {
27100
+ slot[lastIndex].bottom -= distance2;
27101
+ slot[lastIndex].top -= distance2;
27102
+ slot[lastIndex].children[0].bottom -= distance2;
27103
+ slot[lastIndex].children[0].top -= distance2;
27104
+ slot[lastIndex].children[0].pitch -= distance2;
27105
+ }
27106
+ }
27107
+ }
27108
+ }
27109
+ }
27110
+ }
27111
+ }
27112
+ function closeTop(absElem) {
27113
+ if (absElem.children) {
27114
+ var max = -90; // This is clearly way lower than the max calculated below
27115
+ for (var i = 0; i < absElem.children.length; i++) {
27116
+ var child = absElem.children[i];
27117
+ if (child.type !== 'chord') max = Math.max(max, child.top);
27118
+ }
27119
+ if (max > -90) return max;
27120
+ }
27121
+ return absElem.top;
27122
+ }
27123
+ function closeBottom(absElem) {
27124
+ if (absElem.children) {
27125
+ var min = 90; // This is clearly way higher than the min calculated below
27126
+ for (var i = 0; i < absElem.children.length; i++) {
27127
+ var child = absElem.children[i];
27128
+ if (child.type !== 'lyric') min = Math.min(min, child.bottom);
27129
+ }
27130
+ if (min < 90) return min;
27131
+ }
27132
+ return absElem.bottom;
27133
+ }
26712
27134
  module.exports = layout;
26713
27135
 
26714
27136
  /***/ }),
@@ -26788,7 +27210,8 @@ var setUpperAndLowerElements = function setUpperAndLowerElements(renderer, staff
26788
27210
 
26789
27211
  for (var j = 0; j < staff.voices.length; j++) {
26790
27212
  var voice = staffGroup.voices[staff.voices[j]];
26791
- setUpperAndLowerVoiceElements(positionY, voice, renderer.spacing);
27213
+ var diff = setUpperAndLowerVoiceElements(positionY, voice, renderer.spacing);
27214
+ staff.bottom -= diff; //
26792
27215
  }
26793
27216
  // 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.
26794
27217
  // Only try to put in extra space if this isn't the top staff.
@@ -26821,10 +27244,18 @@ function incTop(staff, positionY, item, count) {
26821
27244
  function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
26822
27245
  var i;
26823
27246
  var abselem;
27247
+ var diff = 0;
26824
27248
  for (i = 0; i < voice.children.length; i++) {
26825
27249
  abselem = voice.children[i];
26826
- setUpperAndLowerAbsoluteElements(positionY, abselem, spacing);
27250
+ var bottom = setUpperAndLowerAbsoluteElements(positionY, abselem, spacing);
27251
+ if (bottom < abselem.bottom) {
27252
+ // We're moving things down so tell the staff that it needs to be taller
27253
+ diff = abselem.bottom - bottom;
27254
+ abselem.bottom = bottom; //
27255
+ voice.bottom = bottom; //
27256
+ }
26827
27257
  }
27258
+
26828
27259
  for (i = 0; i < voice.otherchildren.length; i++) {
26829
27260
  abselem = voice.otherchildren[i];
26830
27261
  switch (abselem.type) {
@@ -26847,6 +27278,7 @@ function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
26847
27278
  break;
26848
27279
  }
26849
27280
  }
27281
+ return diff;
26850
27282
  }
26851
27283
 
26852
27284
  // For each of the relative elements that can't be placed in advance (because their vertical placement depends on everything
@@ -26854,6 +27286,7 @@ function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
26854
27286
  // hash with the vertical placement (in pitch units) for each type.
26855
27287
  // 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".
26856
27288
  function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
27289
+ var bottom = element.bottom;
26857
27290
  // specialYResolved contains the actual pitch for each of the classes of elements.
26858
27291
  for (var i = 0; i < element.children.length; i++) {
26859
27292
  var child = element.children[i];
@@ -26863,6 +27296,12 @@ function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
26863
27296
  if (child[key]) {
26864
27297
  // If this relative element has defined a height for this class of element
26865
27298
  child.pitch = specialYResolved[key];
27299
+ if (key === 'lyricHeightBelow' && child.type === 'lyric' && child.voiceNumber) {
27300
+ // TODO-PER: This can result in extra unused vertical space if there are lyrics only on the second but not the first voice.
27301
+ child.pitch -= child.voiceNumber * child[key]; //
27302
+ bottom = Math.min(element.bottom, child.pitch); //
27303
+ }
27304
+
26866
27305
  if (child.top === undefined) {
26867
27306
  // TODO-PER: HACK! Not sure this is the right place to do this.
26868
27307
  if (child.type === 'TempoElement') {
@@ -26877,6 +27316,7 @@ function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
26877
27316
  }
26878
27317
  }
26879
27318
  }
27319
+ return bottom;
26880
27320
  }
26881
27321
  function setUpperAndLowerCrescendoElements(positionY, element) {
26882
27322
  if (element.dynamicHeightAbove) element.pitch = positionY.dynamicHeightAbove;else element.pitch = positionY.dynamicHeightBelow;
@@ -27083,6 +27523,47 @@ module.exports = layoutStaffGroup;
27083
27523
 
27084
27524
  /***/ }),
27085
27525
 
27526
+ /***/ "./src/write/layout/to-time-and-staff-based.js":
27527
+ /*!*****************************************************!*\
27528
+ !*** ./src/write/layout/to-time-and-staff-based.js ***!
27529
+ \*****************************************************/
27530
+ /***/ (function(module) {
27531
+
27532
+ function toTimeAndStaffBased(abcLines) {
27533
+ var results = [];
27534
+ for (var lin = 0; lin < abcLines.length; lin++) {
27535
+ var line = abcLines[lin];
27536
+ var staffGroup = line.staffGroup;
27537
+ var group = [];
27538
+ if (staffGroup && staffGroup && staffGroup.staffs) {
27539
+ for (var s = 0; s < staffGroup.staffs.length; s++) {
27540
+ var staff = staffGroup.staffs[s];
27541
+ var timeSlot = {};
27542
+ for (var i = 0; i < staff.voices.length; i++) {
27543
+ var voice = staffGroup.voices[staff.voices[i]];
27544
+ var time = 0;
27545
+ for (var k = 0; k < voice.children.length; k++) {
27546
+ 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
27547
+ if (!timeSlot[index]) timeSlot[index] = [];
27548
+ if (voice.children[k].abcelem.el_type === 'note') {
27549
+ timeSlot[index].push(voice.children[k]);
27550
+ time += voice.children[k].duration;
27551
+ }
27552
+ }
27553
+ }
27554
+ // Now timeSlot is an object with all the voices on a particular staff that
27555
+ // happen at the same time as an array.
27556
+ group.push(timeSlot);
27557
+ }
27558
+ }
27559
+ results.push(group);
27560
+ }
27561
+ return results;
27562
+ }
27563
+ module.exports = toTimeAndStaffBased;
27564
+
27565
+ /***/ }),
27566
+
27086
27567
  /***/ "./src/write/layout/triplet.js":
27087
27568
  /*!*************************************!*\
27088
27569
  !*** ./src/write/layout/triplet.js ***!
@@ -27108,30 +27589,56 @@ function layoutTriplet(element) {
27108
27589
  element.bottom = element.yTextPos - 2;
27109
27590
  if (isAbove(beam)) element.endingHeightAbove = 4;
27110
27591
  } else {
27111
- // If there isn't a beam, then we need to draw the bracket and the text. The bracket is always above.
27592
+ // 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.
27593
+ // Above:
27112
27594
  // The bracket is never lower than the 'a' line, but is 4 pitches above the first and last notes. If there is
27113
27595
  // a tall note in the middle, the bracket is horizontal and above the highest note.
27114
- element.startNote = Math.max(element.anchor1.parent.top, 9) + 4;
27115
- element.endNote = Math.max(element.anchor2.parent.top, 9) + 4;
27596
+ // Below: The bracket is never higher than the 'C' line, and is 4 pitches below.
27597
+
27598
+ // 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.
27599
+ var up = stemDirectionUp(element);
27600
+ element.up = up;
27601
+ element.startNote = up ? Math.max(element.anchor1.parent.top, 9) + 4 : Math.min(element.anchor1.parent.bottom, 0) - 2;
27602
+ element.endNote = up ? Math.max(element.anchor2.parent.top, 9) + 4 : Math.min(element.anchor2.parent.bottom, 0) - 2;
27603
+
27116
27604
  // If it starts or ends on a rest, make the beam horizontal
27117
27605
  if (element.anchor1.parent.type === "rest" && element.anchor2.parent.type !== "rest") element.startNote = element.endNote;else if (element.anchor2.parent.type === "rest" && element.anchor1.parent.type !== "rest") element.endNote = element.startNote;
27118
- // See if the middle note is really high.
27119
- var max = 0;
27120
- for (var i = 0; i < element.middleElems.length; i++) {
27121
- max = Math.max(max, element.middleElems[i].top);
27122
- }
27123
- max += 4;
27124
- if (max > element.startNote || max > element.endNote) {
27125
- element.startNote = max;
27126
- element.endNote = max;
27606
+ if (up) {
27607
+ // See if the middle note is really high.
27608
+ var max = 0;
27609
+ for (var i = 0; i < element.middleElems.length; i++) {
27610
+ max = Math.max(max, element.middleElems[i].top);
27611
+ }
27612
+ max += 4;
27613
+ if (max > element.startNote || max > element.endNote) {
27614
+ element.startNote = max + 3;
27615
+ element.endNote = max + 3;
27616
+ }
27617
+ } else {
27618
+ // See if the middle note is really low.
27619
+ var min = 0;
27620
+ for (var i = 0; i < element.middleElems.length; i++) {
27621
+ min = Math.min(min, element.middleElems[i].bottom - element.middleElems[i].height);
27622
+ }
27623
+ min -= 3;
27624
+ if (min < element.startNote && min < element.endNote) {
27625
+ element.startNote = Math.min(min, element.startNote) - 2;
27626
+ element.endNote = Math.min(min, element.endNote) - 2;
27627
+ }
27127
27628
  }
27128
27629
  if (element.flatBeams) {
27129
- element.startNote = Math.max(element.startNote, element.endNote);
27130
- element.endNote = Math.max(element.startNote, element.endNote);
27630
+ if (up) {
27631
+ element.startNote = Math.max(element.startNote, element.endNote);
27632
+ element.endNote = Math.max(element.startNote, element.endNote);
27633
+ } else {
27634
+ element.startNote = Math.min(element.startNote, element.endNote);
27635
+ element.endNote = Math.min(element.startNote, element.endNote);
27636
+ }
27131
27637
  }
27132
27638
  element.yTextPos = element.startNote + (element.endNote - element.startNote) / 2;
27133
27639
  element.xTextPos = element.anchor1.x + (element.anchor2.x + element.anchor2.w - element.anchor1.x) / 2;
27134
27640
  element.top = element.yTextPos + 1;
27641
+ element.bottom = element.yTextPos - 2;
27135
27642
  }
27136
27643
  }
27137
27644
  delete element.middleElems;
@@ -27140,6 +27647,26 @@ function layoutTriplet(element) {
27140
27647
  function isAbove(beam) {
27141
27648
  return beam.stemsUp;
27142
27649
  }
27650
+ function stemDirectionUp(element) {
27651
+ var up = 0;
27652
+ var down = 0;
27653
+ if (element.anchor1) {
27654
+ if (element.anchor1.stemDir === 'up') up++;
27655
+ if (element.anchor1.stemDir === 'down') down++;
27656
+ }
27657
+ if (element.anchor2) {
27658
+ if (element.anchor2.stemDir === 'up') up++;
27659
+ if (element.anchor2.stemDir === 'down') down++;
27660
+ }
27661
+ if (element.middleElems) {
27662
+ for (var i = 0; i < element.middleElems.length; i++) {
27663
+ var elem = element.middleElems[i];
27664
+ if (elem.stemDir === 'up') up++;
27665
+ if (elem.stemDir === 'down') down++;
27666
+ }
27667
+ }
27668
+ return up >= down;
27669
+ }
27143
27670
 
27144
27671
  // 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
27145
27672
  function heightAtMidpoint(startX, endX, beam) {
@@ -27432,7 +27959,7 @@ var Renderer = function Renderer(paper) {
27432
27959
  this.space = 3 * spacing.SPACE;
27433
27960
  this.padding = {}; // renderer's padding is managed by the controller
27434
27961
  this.reset();
27435
- this.firefox112 = navigator.userAgent.indexOf('Firefox/112.0') >= 0;
27962
+ this.firefox = navigator.userAgent.indexOf('Firefox/') >= 0;
27436
27963
  };
27437
27964
  Renderer.prototype.reset = function () {
27438
27965
  this.paper.clear();
@@ -27980,7 +28507,7 @@ module.exports = Svg;
27980
28507
  \********************/
27981
28508
  /***/ (function(module) {
27982
28509
 
27983
- var version = '6.6.2';
28510
+ var version = '6.6.4';
27984
28511
  module.exports = version;
27985
28512
 
27986
28513
  /***/ })