abcjs 6.4.4 → 6.5.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.
@@ -4319,11 +4319,24 @@ var parseDirective = {};
4319
4319
  }
4320
4320
  multilineVars.currBarNumber = tuneBuilder.setBarNumberImmediate(tokens[0].intt);
4321
4321
  break;
4322
+ case "keywarn":
4323
+ if (tokens.length !== 1 || tokens[0].type !== 'number' || tokens[0].intt !== 1 && tokens[0].intt !== 0) {
4324
+ return 'Directive ' + cmd + ' requires 0 or 1 as a parameter.';
4325
+ }
4326
+ multilineVars[cmd] = tokens[0].intt === 1;
4327
+ break;
4322
4328
  case "begintext":
4323
4329
  var textBlock = '';
4324
4330
  line = tokenizer.nextLine();
4325
4331
  while (line && line.indexOf('%%endtext') !== 0) {
4326
- if (parseCommon.startsWith(line, "%%")) textBlock += line.substring(2) + "\n";else textBlock += line + "\n";
4332
+ // MAE 9 May 2025 - for text blocks with just white space
4333
+ if (parseCommon.startsWith(line, "%%")) {
4334
+ var theLine = line.substring(2);
4335
+ theLine = theLine.trim() + "\n";
4336
+ textBlock += theLine;
4337
+ } else {
4338
+ textBlock += line.trim() + "\n";
4339
+ }
4327
4340
  line = tokenizer.nextLine();
4328
4341
  }
4329
4342
  tuneBuilder.addText(textBlock, {
@@ -4508,6 +4521,14 @@ var parseDirective = {};
4508
4521
  }
4509
4522
  }
4510
4523
  break;
4524
+ case "maxstaves":
4525
+ var nStaves = tokenizer.getInt(restOfString);
4526
+ if (nStaves.digits === 0) warn("Expected number of staves in maxstaves");else {
4527
+ if (nStaves.value > 0) {
4528
+ tune.formatting.maxStaves = nStaves.value;
4529
+ }
4530
+ }
4531
+ break;
4511
4532
  case "newpage":
4512
4533
  var pgNum = tokenizer.getInt(restOfString);
4513
4534
  tuneBuilder.addNewPage(pgNum.digits === 0 ? -1 : pgNum.value);
@@ -4568,6 +4589,10 @@ var parseDirective = {};
4568
4589
  tune.formatting.percmap[percmap.key] = percmap.value;
4569
4590
  }
4570
4591
  break;
4592
+ case "visualtranspose":
4593
+ var halfSteps = tokenizer.getInt(restOfString);
4594
+ if (halfSteps.digits === 0) warn("Expected number of half steps in visualTranspose");else multilineVars.globalTranspose = halfSteps.value;
4595
+ break;
4571
4596
  case "map":
4572
4597
  case "playtempo":
4573
4598
  case "auquality":
@@ -5109,8 +5134,8 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5109
5134
  return [line.length];
5110
5135
  case "K:":
5111
5136
  var result = parseKeyVoice.parseKey(line.substring(i + 2), tuneBuilder.hasBeginMusic());
5112
- if (result.foundClef && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('clef', multilineVars.iChar + i, multilineVars.iChar + line.length, multilineVars.clef);
5113
- if (result.foundKey && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('key', multilineVars.iChar + i, multilineVars.iChar + line.length, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
5137
+ if (result.foundClef && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false) tuneBuilder.appendStartingElement('clef', multilineVars.iChar + i, multilineVars.iChar + line.length, multilineVars.clef);
5138
+ if (result.foundKey && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false) tuneBuilder.appendStartingElement('key', multilineVars.iChar + i, multilineVars.iChar + line.length, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
5114
5139
  return [line.length];
5115
5140
  case "P:":
5116
5141
  if (tuneBuilder.hasBeginMusic()) tuneBuilder.appendElement('part', multilineVars.iChar + i, multilineVars.iChar + line.length, {
@@ -5192,7 +5217,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5192
5217
  // since the key is the last thing that can happen in the header, we can resolve the tempo now
5193
5218
  this.resolveTempo();
5194
5219
  var result = parseKeyVoice.parseKey(line.substring(2), false);
5195
- if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic()) {
5220
+ if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false) {
5196
5221
  if (result.foundClef) tuneBuilder.appendStartingElement('clef', startChar, endChar, multilineVars.clef);
5197
5222
  if (result.foundKey) tuneBuilder.appendStartingElement('key', startChar, endChar, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
5198
5223
  }
@@ -5724,7 +5749,7 @@ var parseKeyVoice = {};
5724
5749
  multilineVars.key = parseKeyVoice.deepCopyKey(parseKeyVoice.standardKey(key, retPitch.token, acc, keyCompensate));
5725
5750
  if (isInline) multilineVars.globalTransposeOrigKeySig = savedOrigKey;
5726
5751
  multilineVars.key.mode = mode;
5727
- if (oldKey) {
5752
+ if (oldKey && multilineVars.keywarn !== false) {
5728
5753
  // Add natural in all places that the old key had an accidental.
5729
5754
  var kk;
5730
5755
  for (var k = 0; k < multilineVars.key.accidentals.length; k++) {
@@ -6135,12 +6160,47 @@ var parseKeyVoice = {};
6135
6160
  case 'tenor,,':
6136
6161
  case 'alto,,':
6137
6162
  case 'none,,':
6163
+ // MAE 26 May 2025 Start of additional clefs
6164
+ case 'treble+8':
6165
+ case 'treble-8':
6166
+ case 'treble^8':
6167
+ case 'treble_8':
6168
+ case 'treble1':
6169
+ case 'treble2':
6170
+ case 'treble3':
6171
+ case 'treble4':
6172
+ case 'treble5':
6173
+ case 'bass+8':
6174
+ case 'bass-8':
6175
+ case 'bass^8':
6176
+ case 'bass_8':
6177
+ case 'bass+16':
6178
+ case 'bass-16':
6179
+ case 'bass^16':
6180
+ case 'bass_16':
6181
+ case 'bass1':
6182
+ case 'bass2':
6183
+ case 'bass3':
6184
+ case 'bass4':
6185
+ case 'bass5':
6186
+ case 'tenor1':
6187
+ case 'tenor2':
6188
+ case 'tenor3':
6189
+ case 'tenor4':
6190
+ case 'tenor5':
6191
+ case 'alto1':
6192
+ case 'alto2':
6193
+ case 'alto3':
6194
+ case 'alto4':
6195
+ case 'alto5':
6196
+ case 'alto+8':
6197
+ case 'alto-8':
6198
+ case 'alto^8':
6199
+ case 'alto_8':
6200
+ // MAE 26 May 2025 End of additional clefs
6201
+
6138
6202
  // TODO-PER: handle the octave indicators on the clef by changing the middle property
6139
6203
  var oct2 = 0;
6140
- // for (var iii = 0; iii < token.token.length; iii++) {
6141
- // if (token.token[iii] === ',') oct2 -= 7;
6142
- // else if (token.token[iii] === "'") oct2 += 7;
6143
- // }
6144
6204
  staffInfo.clef = token.token.replace(/[',]/g, ""); //'//comment for emacs formatting of regexp
6145
6205
  staffInfo.verticalPos = calcMiddle(staffInfo.clef, oct2);
6146
6206
  multilineVars.voices[id].clef = token.token;
@@ -7036,6 +7096,8 @@ var letter_to_accent = function letter_to_accent(line, i) {
7036
7096
  return [1, 'segno'];
7037
7097
  case 'T':
7038
7098
  return [1, 'trill'];
7099
+ case 't':
7100
+ return [1, 'trillh'];
7039
7101
  }
7040
7102
  return [0, 0];
7041
7103
  };
@@ -7513,7 +7575,7 @@ module.exports = MusicParser;
7513
7575
  \*****************************************/
7514
7576
  /***/ (function(module) {
7515
7577
 
7516
- module.exports.legalAccents = ['trill', 'lowermordent', 'uppermordent', 'mordent', 'pralltriller', 'accent', 'fermata', 'invertedfermata', 'tenuto', '0', '1', '2', '3', '4', '5', '+', 'wedge', 'open', 'thumb', 'snap', 'turn', 'roll', 'breath', 'shortphrase', 'mediumphrase', 'longphrase', 'segno', 'coda', 'D.S.', 'D.C.', 'fine', 'beambr1', 'beambr2', 'slide', 'marcato', 'upbow', 'downbow', '/', '//', '///', '////', 'trem1', 'trem2', 'trem3', 'trem4', 'turnx', 'invertedturn', 'invertedturnx', 'trill(', 'trill)', 'arpeggio', 'xstem', 'mark', 'umarcato', 'style=normal', 'style=harmonic', 'style=rhythm', 'style=x', 'style=triangle', 'D.C.alcoda', 'D.C.alfine', 'D.S.alcoda', 'D.S.alfine', 'editorial', 'courtesy'];
7578
+ module.exports.legalAccents = ['trill', 'trillh', 'lowermordent', 'uppermordent', 'mordent', 'pralltriller', 'accent', 'fermata', 'invertedfermata', 'tenuto', '0', '1', '2', '3', '4', '5', '+', 'wedge', 'open', 'thumb', 'snap', 'turn', 'roll', 'breath', 'shortphrase', 'mediumphrase', 'longphrase', 'segno', 'coda', 'D.S.', 'D.C.', 'fine', 'beambr1', 'beambr2', 'slide', 'marcato', 'upbow', 'downbow', '/', '//', '///', '////', 'trem1', 'trem2', 'trem3', 'trem4', 'turnx', 'invertedturn', 'invertedturnx', 'trill(', 'trill)', 'arpeggio', 'xstem', 'mark', 'umarcato', 'style=normal', 'style=harmonic', 'style=rhythm', 'style=x', 'style=triangle', 'D.C.alcoda', 'D.C.alfine', 'D.S.alcoda', 'D.S.alfine', 'editorial', 'courtesy'];
7517
7579
  module.exports.volumeDecorations = ['p', 'pp', 'f', 'ff', 'mf', 'mp', 'ppp', 'pppp', 'fff', 'ffff', 'sfz'];
7518
7580
  module.exports.dynamicDecorations = ['crescendo(', 'crescendo)', 'diminuendo(', 'diminuendo)', 'glissando(', 'glissando)', '~(', '~)'];
7519
7581
  module.exports.accentPseudonyms = [['<', 'accent'], ['>', 'accent'], ['tr', 'trill'], ['plus', '+'], ['emphasis', 'accent'], ['^', 'umarcato'], ['marcato', 'umarcato']];
@@ -8731,14 +8793,38 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
8731
8793
  }
8732
8794
  }
8733
8795
  var thePatterns = [{
8734
- match: /,\s*[Tt]he$/,
8796
+ match: /,\s*The$/,
8735
8797
  replace: "The "
8736
8798
  }, {
8737
- match: /,\s*[Aa]$/,
8799
+ match: /,\s*the$/,
8800
+ replace: "the "
8801
+ }, {
8802
+ match: /,\s*A$/,
8738
8803
  replace: "A "
8739
8804
  }, {
8740
- match: /,\s*[Aa]n$/,
8805
+ match: /,\s*a$/,
8806
+ replace: "a "
8807
+ }, {
8808
+ match: /,\s*An$/,
8741
8809
  replace: "An "
8810
+ }, {
8811
+ match: /,\s*an$/,
8812
+ replace: "an "
8813
+ }, {
8814
+ match: /,\s*Da$/,
8815
+ replace: "Da "
8816
+ }, {
8817
+ match: /,\s*La$/,
8818
+ replace: "La "
8819
+ }, {
8820
+ match: /,\s*Le$/,
8821
+ replace: "Le "
8822
+ }, {
8823
+ match: /,\s*Les$/,
8824
+ replace: "Les "
8825
+ }, {
8826
+ match: /,\s*Ye$/,
8827
+ replace: "Ye "
8742
8828
  }];
8743
8829
  this.theReverser = function (str) {
8744
8830
  for (var i = 0; i < thePatterns.length; i++) {
@@ -8988,7 +9074,9 @@ transpose.keySignature = function (multilineVars, keyName, root, acc, localTrans
8988
9074
  var newKeyName = keyName[0] === 'm' ? newKeyMinor[index] : newKey[index];
8989
9075
  var transposedKey = newKeyName + keyName;
8990
9076
  var newKeySig = keyAccidentals(transposedKey);
8991
- if (newKeySig.length > 0 && newKeySig[0].acc === 'flat') multilineVars.localTransposePreferFlats = true;
9077
+ if (newKeySig.length === 0 || newKeySig[0].acc === 'flat')
9078
+ // key of C and all keys with flats should have chords with flats
9079
+ multilineVars.localTransposePreferFlats = true;
8992
9080
  var distance = transposedKey.charCodeAt(0) - baseKey.charCodeAt(0);
8993
9081
  if (multilineVars.localTranspose > 0) {
8994
9082
  if (distance < 0) distance += 7;else if (distance === 0) {
@@ -9160,6 +9248,13 @@ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
9160
9248
  } else {
9161
9249
  if (freeGCchord) chord = sharpChordsFree[index];else chord = sharpChords[index];
9162
9250
  }
9251
+ var isDim = extra1 && (extra1.indexOf('dim') >= 0 || extra1.indexOf('°') >= 0);
9252
+ //console.log(isDim, chord, extra1)
9253
+ // We never want A#dim or D#dim
9254
+ if (isDim && chord === 'A#') chord = 'Bb';
9255
+ if (isDim && chord === 'D#') chord = 'Eb';
9256
+ if (isDim && chord === 'A♯') chord = 'B♭';
9257
+ if (isDim && chord === 'D♯') chord = 'E♭';
9163
9258
  if (extra1) chord += extra1;
9164
9259
  if (bass) {
9165
9260
  var index = sharpChords.indexOf(bass);
@@ -9201,7 +9296,7 @@ var TuneBuilder = function TuneBuilder(tune) {
9201
9296
  var currentVoiceName = '';
9202
9297
  tune.reset();
9203
9298
  this.setVisualTranspose = function (visualTranspose) {
9204
- if (visualTranspose) tune.visualTranspose = visualTranspose;
9299
+ if (visualTranspose !== undefined) tune.visualTranspose = visualTranspose;
9205
9300
  };
9206
9301
  this.cleanUp = function (barsperstaff, staffnonote, currSlur) {
9207
9302
  closeLine(tune); // Close the last line.
@@ -10000,6 +10095,7 @@ function wrapMusicLines(lines, barsperstaff) {
10000
10095
  return false;
10001
10096
  }
10002
10097
  function getPrevMusicLine(lines, currentLine) {
10098
+ if (lines.length <= currentLine) return null;
10003
10099
  // If the current line doesn't have music, search backwards until one is found.
10004
10100
  while (currentLine >= 0) {
10005
10101
  if (lines[currentLine].staff) return lines[currentLine];
@@ -10745,16 +10841,16 @@ var strTranspose;
10745
10841
  var count = arr[0].length;
10746
10842
  for (var i = 1; i < arr.length; i++) {
10747
10843
  var segment = arr[i];
10748
- var match = segment.match(/^( *)([A-G])([#b]?)(\w*)/);
10844
+ var match = segment.match(/^( *)([A-G])([#b]?)( ?)(\w*)/);
10749
10845
  if (match) {
10750
10846
  var start = count + 2 + match[1].length; // move past the 'K:' and optional white space
10751
- var key = match[2] + match[3] + match[4]; // key name, accidental, and mode
10847
+ var key = match[2] + match[3] + match[4] + match[5]; // key name, accidental, optional space, and mode
10752
10848
  var destinationKey = newKey({
10753
10849
  root: match[2],
10754
10850
  acc: match[3],
10755
- mode: match[4]
10851
+ mode: match[5]
10756
10852
  }, steps);
10757
- var dest = destinationKey.root + destinationKey.acc + destinationKey.mode;
10853
+ var dest = destinationKey.root + destinationKey.acc + match[4] + destinationKey.mode;
10758
10854
  changes.push({
10759
10855
  start: start,
10760
10856
  end: start + key.length,
@@ -11202,6 +11298,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
11202
11298
  var stressBeat1 = 105;
11203
11299
  var stressBeatDown = 95;
11204
11300
  var stressBeatUp = 85;
11301
+ var volumesPerNotePitch = [[stressBeat1, stressBeatDown, stressBeatUp]];
11205
11302
  var beatFraction = 0.25;
11206
11303
  var nextVolume;
11207
11304
  var nextVolumeDelta;
@@ -11243,6 +11340,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
11243
11340
  stressBeat1 = 105;
11244
11341
  stressBeatDown = 95;
11245
11342
  stressBeatUp = 85;
11343
+ volumesPerNotePitch = [];
11246
11344
  beatFraction = 0.25;
11247
11345
  nextVolume = undefined;
11248
11346
  nextVolumeDelta = undefined;
@@ -11353,6 +11451,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
11353
11451
  stressBeat1 = element.beats[0];
11354
11452
  stressBeatDown = element.beats[1];
11355
11453
  stressBeatUp = element.beats[2];
11454
+ if (!element.volumesPerNotePitch) volumesPerNotePitch = [];else volumesPerNotePitch = element.volumesPerNotePitch;
11356
11455
  // TODO-PER: also use the last parameter - which changes which beats are strong.
11357
11456
  break;
11358
11457
  case "vol":
@@ -11496,21 +11595,29 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
11496
11595
  var distanceFromStart = currTime - measureStart;
11497
11596
  return distanceFromStart / beatLength;
11498
11597
  }
11499
- function processVolume(beat, voiceOff) {
11598
+ function processVolume(beat, voiceOff, pitchIndexOfNote) {
11500
11599
  if (voiceOff) return 0;
11600
+ var pitchStressBeat1 = stressBeat1;
11601
+ var pitchStressBeatDown = stressBeatDown;
11602
+ var pitchStressBeatUp = stressBeatUp;
11603
+ if (pitchIndexOfNote !== undefined && volumesPerNotePitch.length >= pitchIndexOfNote + 1) {
11604
+ pitchStressBeat1 = volumesPerNotePitch[pitchIndexOfNote][0];
11605
+ pitchStressBeatDown = volumesPerNotePitch[pitchIndexOfNote][1];
11606
+ pitchStressBeatUp = volumesPerNotePitch[pitchIndexOfNote][2];
11607
+ }
11501
11608
  var volume;
11502
11609
  // MAE 21 Jun 2024 - This previously wasn't allowing zero volume to be applied
11503
- if (nextVolume != undefined) {
11610
+ if (nextVolume !== undefined) {
11504
11611
  volume = nextVolume;
11505
11612
  nextVolume = undefined;
11506
11613
  } else if (!doBeatAccents) {
11507
- volume = stressBeatDown;
11614
+ volume = pitchStressBeatDown;
11508
11615
  } else if (pickupLength > beat) {
11509
- volume = stressBeatUp;
11616
+ volume = pitchStressBeatUp;
11510
11617
  } else {
11511
11618
  //var barLength = meter.num / meter.den;
11512
11619
  var barBeat = calcBeat(lastBarTime, getBeatFraction(meter), beat);
11513
- if (barBeat === 0) volume = stressBeat1;else if (parseInt(barBeat, 10) === barBeat) volume = stressBeatDown;else volume = stressBeatUp;
11620
+ if (barBeat === 0) volume = pitchStressBeat1;else if (parseInt(barBeat, 10) === barBeat) volume = pitchStressBeatDown;else volume = pitchStressBeatUp;
11514
11621
  }
11515
11622
  if (nextVolumeDelta) {
11516
11623
  volume += nextVolumeDelta;
@@ -11524,7 +11631,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
11524
11631
  var ret = {};
11525
11632
  if (elem.decoration) {
11526
11633
  for (var d = 0; d < elem.decoration.length; d++) {
11527
- if (elem.decoration[d] === 'staccato') ret.thisBreakBetweenNotes = 'staccato';else if (elem.decoration[d] === 'tenuto') ret.thisBreakBetweenNotes = 'tenuto';else if (elem.decoration[d] === 'accent') ret.velocity = Math.min(127, velocity * 1.5);else if (elem.decoration[d] === 'trill') ret.noteModification = "trill";else if (elem.decoration[d] === 'lowermordent') ret.noteModification = "lowermordent";else if (elem.decoration[d] === 'uppermordent') ret.noteModification = "mordent";else if (elem.decoration[d] === 'mordent') ret.noteModification = "mordent";else if (elem.decoration[d] === 'turn') ret.noteModification = "turn";else if (elem.decoration[d] === 'roll') ret.noteModification = "roll";
11634
+ if (elem.decoration[d] === 'staccato') ret.thisBreakBetweenNotes = 'staccato';else if (elem.decoration[d] === 'tenuto') ret.thisBreakBetweenNotes = 'tenuto';else if (elem.decoration[d] === 'accent') ret.velocity = Math.min(127, velocity * 1.5);else if (elem.decoration[d] === 'trill') ret.noteModification = "trill";else if (elem.decoration[d] === 'lowermordent') ret.noteModification = "lowermordent";else if (elem.decoration[d] === 'uppermordent') ret.noteModification = "pralltriller";else if (elem.decoration[d] === 'mordent') ret.noteModification = "mordent";else if (elem.decoration[d] === 'turn') ret.noteModification = "turn";else if (elem.decoration[d] === 'roll') ret.noteModification = "roll";else if (elem.decoration[d] === 'pralltriller') ret.noteModification = "pralltriller";else if (elem.decoration[d] === 'trillh') ret.noteModification = "trillh";
11528
11635
  }
11529
11636
  }
11530
11637
  return ret;
@@ -11555,7 +11662,25 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
11555
11662
  start += shortestNote;
11556
11663
  }
11557
11664
  break;
11558
- case "mordent":
11665
+ case "trillh":
11666
+ var note = 1;
11667
+ while (runningDuration > 0) {
11668
+ currentTrack.push({
11669
+ cmd: 'note',
11670
+ pitch: p.pitch + note,
11671
+ volume: p.volume,
11672
+ start: start,
11673
+ duration: shortestNote,
11674
+ gap: 0,
11675
+ instrument: currentInstrument,
11676
+ style: 'decoration'
11677
+ });
11678
+ note = note === 1 ? 0 : 1;
11679
+ runningDuration -= shortestNote;
11680
+ start += shortestNote;
11681
+ }
11682
+ break;
11683
+ case "pralltriller":
11559
11684
  currentTrack.push({
11560
11685
  cmd: 'note',
11561
11686
  pitch: p.pitch,
@@ -11590,6 +11715,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
11590
11715
  instrument: currentInstrument
11591
11716
  });
11592
11717
  break;
11718
+ case "mordent":
11593
11719
  case "lowermordent":
11594
11720
  currentTrack.push({
11595
11721
  cmd: 'note',
@@ -11754,6 +11880,11 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
11754
11880
  }
11755
11881
  if (elem.elem) elem.elem.midiPitches = [];
11756
11882
  for (var i = 0; i < ePitches.length; i++) {
11883
+ //here we can set the volume for each note in a chord, if specified
11884
+ var pitchVelocity = velocity;
11885
+ if (!ret.velocity && Array.isArray(elem.decoration) && elem.decoration.length > i) {
11886
+ pitchVelocity = processVolume(timeToRealTime(elem.time), voiceOff, i);
11887
+ }
11757
11888
  var note = ePitches[i];
11758
11889
  if (!note) continue;
11759
11890
  if (note.startSlur) slurCount += note.startSlur.length;
@@ -11766,7 +11897,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
11766
11897
  var p = {
11767
11898
  cmd: 'note',
11768
11899
  pitch: actualPitch,
11769
- volume: velocity,
11900
+ volume: pitchVelocity,
11770
11901
  start: timeToRealTime(elem.time),
11771
11902
  duration: durationRounded(note.duration),
11772
11903
  instrument: currentInstrument,
@@ -12549,6 +12680,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
12549
12680
 
12550
12681
  // visit each voice completely in turn
12551
12682
  var voices = [];
12683
+ var clefTransposeActive = [];
12552
12684
  var inCrescendo = [];
12553
12685
  var inDiminuendo = [];
12554
12686
  var durationCounter = [0];
@@ -12569,6 +12701,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
12569
12701
  if (line.staff) {
12570
12702
  var setDynamics = function setDynamics(elem) {
12571
12703
  var volumes = {
12704
+ //stressBeat1, stressBeatDown, stressBeatUp
12572
12705
  'pppp': [15, 10, 5, 1],
12573
12706
  'ppp': [30, 20, 10, 1],
12574
12707
  'pp': [45, 35, 20, 1],
@@ -12585,9 +12718,17 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
12585
12718
  if (elem.decoration.indexOf('pppp') >= 0) dynamicType = 'pppp';else if (elem.decoration.indexOf('ppp') >= 0) dynamicType = 'ppp';else if (elem.decoration.indexOf('pp') >= 0) dynamicType = 'pp';else if (elem.decoration.indexOf('p') >= 0) dynamicType = 'p';else if (elem.decoration.indexOf('mp') >= 0) dynamicType = 'mp';else if (elem.decoration.indexOf('mf') >= 0) dynamicType = 'mf';else if (elem.decoration.indexOf('f') >= 0) dynamicType = 'f';else if (elem.decoration.indexOf('ff') >= 0) dynamicType = 'ff';else if (elem.decoration.indexOf('fff') >= 0) dynamicType = 'fff';else if (elem.decoration.indexOf('ffff') >= 0) dynamicType = 'ffff';
12586
12719
  if (dynamicType) {
12587
12720
  currentVolume = volumes[dynamicType].slice(0);
12721
+ var volumesPerNotePitch = [currentVolume];
12722
+ if (Array.isArray(elem.decoration)) {
12723
+ volumesPerNotePitch = [];
12724
+ elem.decoration.forEach(function (d) {
12725
+ if (d in volumes) volumesPerNotePitch.push(volumes[d].slice(0));
12726
+ });
12727
+ }
12588
12728
  voices[voiceNumber].push({
12589
12729
  el_type: 'beat',
12590
- beats: currentVolume.slice(0)
12730
+ beats: currentVolume.slice(0),
12731
+ volumesPerNotePitch: volumesPerNotePitch
12591
12732
  });
12592
12733
  inCrescendo[k] = false;
12593
12734
  inDiminuendo[k] = false;
@@ -12665,15 +12806,31 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
12665
12806
  el_type: 'transpose',
12666
12807
  transpose: staff.clef.transpose
12667
12808
  });
12809
+ clefTransposeActive[voiceNumber] = false;
12668
12810
  }
12669
12811
  if (staff.clef && staff.clef.type) {
12670
- if (staff.clef.type.indexOf("-8") >= 0) voices[voiceNumber].push({
12671
- el_type: 'transpose',
12672
- transpose: -12
12673
- });else if (staff.clef.type.indexOf("+8") >= 0) voices[voiceNumber].push({
12674
- el_type: 'transpose',
12675
- transpose: 12
12676
- });
12812
+ if (staff.clef.type.indexOf("-8") >= 0) {
12813
+ voices[voiceNumber].push({
12814
+ el_type: 'transpose',
12815
+ transpose: -12
12816
+ });
12817
+ clefTransposeActive[voiceNumber] = true;
12818
+ } else if (staff.clef.type.indexOf("+8") >= 0) {
12819
+ voices[voiceNumber].push({
12820
+ el_type: 'transpose',
12821
+ transpose: 12
12822
+ });
12823
+ clefTransposeActive[voiceNumber] = true;
12824
+ } else {
12825
+ // if we had a previous treble+8 and now have a regular clef, then cancel the transposition
12826
+ if (clefTransposeActive[voiceNumber]) {
12827
+ voices[voiceNumber].push({
12828
+ el_type: 'transpose',
12829
+ transpose: 0
12830
+ });
12831
+ clefTransposeActive[voiceNumber] = false;
12832
+ }
12833
+ }
12677
12834
  }
12678
12835
  if (abctune.formatting.midi && abctune.formatting.midi.drumoff) {
12679
12836
  // If there is a drum off command right at the beginning it is put in the metaText instead of the stream,
@@ -13394,7 +13551,7 @@ ChordTrack.prototype.interpretChord = function (name) {
13394
13551
  };
13395
13552
  var root = name.substring(0, 1);
13396
13553
  if (root === '(') {
13397
- name = name.substring(1, name.length - 2);
13554
+ name = name.substring(1, name.length - 1);
13398
13555
  if (name.length === 0) return undefined;
13399
13556
  root = name.substring(0, 1);
13400
13557
  }
@@ -14166,7 +14323,7 @@ function CreateSynth() {
14166
14323
  self.audioBuffers = []; // cache of the buffers so starting play can be fast.
14167
14324
  self.duration = undefined; // the duration of the tune in seconds.
14168
14325
  self.isRunning = false; // whether there is currently a sound buffer running.
14169
- self.options = undefined;
14326
+ self.options = {}; // Thx tomohirohiratsuka
14170
14327
  self.pickupLength = 0;
14171
14328
 
14172
14329
  // Load and cache all needed sounds
@@ -14394,87 +14551,93 @@ function CreateSynth() {
14394
14551
  self.isRunning = false;
14395
14552
  if (!self.audioBufferPossible) return Promise.reject(new Error(notSupportedMessage));
14396
14553
  if (self.debugCallback) self.debugCallback("prime called");
14397
- return new Promise(function (resolve) {
14398
- var startTime = activeAudioContext().currentTime;
14399
- var tempoMultiplier = self.millisecondsPerMeasure / 1000 / self.meterSize;
14400
- self.duration = self.flattened.totalDuration * tempoMultiplier;
14401
- if (self.duration <= 0) {
14402
- self.audioBuffers = [];
14403
- return resolve({
14404
- status: "empty",
14405
- seconds: 0
14406
- });
14407
- }
14408
- self.duration += fadeTimeSec;
14409
- var totalSamples = Math.floor(activeAudioContext().sampleRate * self.duration);
14410
-
14411
- // There might be a previous run that needs to be turned off.
14412
- self.stop();
14413
- var noteMapTracks = createNoteMap(self.flattened);
14414
- if (self.options.swing) addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength);
14415
- if (self.sequenceCallback) self.sequenceCallback(noteMapTracks, self.callbackContext);
14416
- var panDistances = setPan(noteMapTracks.length, self.pan);
14417
-
14418
- // Create a simple list of all the unique sounds in this music and where they should be placed.
14419
- // There appears to be a limit on how many audio buffers can be created at once so this technique limits the number needed.
14420
- var uniqueSounds = {};
14421
- noteMapTracks.forEach(function (noteMap, trackNumber) {
14422
- var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
14423
- noteMap.forEach(function (note) {
14424
- var key = note.instrument + ':' + note.pitch + ':' + note.volume + ':' + Math.round((note.end - note.start) * 1000) / 1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
14425
- if (self.debugCallback) self.debugCallback("noteMapTrack " + key);
14426
- if (!uniqueSounds[key]) uniqueSounds[key] = [];
14427
- uniqueSounds[key].push(note.start);
14428
- });
14429
- });
14430
-
14431
- // Now that we know what we are trying to create, construct the audio buffer by creating each sound and placing it.
14432
- var allPromises = [];
14433
- var audioBuffer = activeAudioContext().createBuffer(2, totalSamples, activeAudioContext().sampleRate);
14434
- for (var key2 = 0; key2 < Object.keys(uniqueSounds).length; key2++) {
14435
- var k = Object.keys(uniqueSounds)[key2];
14436
- var parts = k.split(":");
14437
- var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
14438
- parts = {
14439
- instrument: parts[0],
14440
- pitch: parseInt(parts[1], 10),
14441
- volume: parseInt(parts[2], 10),
14442
- len: parseFloat(parts[3]),
14443
- pan: parseFloat(parts[4]),
14444
- tempoMultiplier: parseFloat(parts[5]),
14445
- cents: cents
14446
- };
14447
- allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000, self.debugCallback));
14448
- }
14449
- self.audioBuffers = [audioBuffer];
14450
- if (self.debugCallback) {
14451
- self.debugCallback("sampleRate = " + activeAudioContext().sampleRate);
14452
- self.debugCallback("totalSamples = " + totalSamples);
14453
- self.debugCallback("creationTime = " + Math.floor((activeAudioContext().currentTime - startTime) * 1000) + "ms");
14454
- }
14455
- function resolveData(me) {
14456
- var duration = me && me.audioBuffers && me.audioBuffers.length > 0 ? me.audioBuffers[0].duration : 0;
14457
- return {
14458
- status: activeAudioContext().state,
14459
- duration: duration
14554
+ return new Promise(function (resolve, reject) {
14555
+ try {
14556
+ var resolveData = function resolveData(me) {
14557
+ var duration = me && me.audioBuffers && me.audioBuffers.length > 0 ? me.audioBuffers[0].duration : 0;
14558
+ return {
14559
+ status: activeAudioContext().state,
14560
+ duration: duration
14561
+ };
14460
14562
  };
14461
- }
14462
- Promise.all(allPromises).then(function () {
14463
- // Safari iOS can mess with the audioContext state, so resume if needed.
14464
- if (activeAudioContext().state === "suspended") {
14465
- activeAudioContext().resume().then(function () {
14466
- resolve(resolveData(self));
14563
+ var startTime = activeAudioContext().currentTime;
14564
+ var tempoMultiplier = self.millisecondsPerMeasure / 1000 / self.meterSize;
14565
+ self.duration = self.flattened.totalDuration * tempoMultiplier;
14566
+ if (self.duration <= 0) {
14567
+ self.audioBuffers = [];
14568
+ return resolve({
14569
+ status: "empty",
14570
+ seconds: 0
14467
14571
  });
14468
- } else if (activeAudioContext().state === "interrupted") {
14469
- activeAudioContext().suspend().then(function () {
14572
+ }
14573
+ self.duration += fadeTimeSec;
14574
+ var totalSamples = Math.floor(activeAudioContext().sampleRate * self.duration);
14575
+
14576
+ // There might be a previous run that needs to be turned off.
14577
+ self.stop();
14578
+ var noteMapTracks = createNoteMap(self.flattened);
14579
+ if (self.options.swing) addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength);
14580
+ if (self.sequenceCallback) self.sequenceCallback(noteMapTracks, self.callbackContext);
14581
+ var panDistances = setPan(noteMapTracks.length, self.pan);
14582
+
14583
+ // Create a simple list of all the unique sounds in this music and where they should be placed.
14584
+ // There appears to be a limit on how many audio buffers can be created at once so this technique limits the number needed.
14585
+ var uniqueSounds = {};
14586
+ noteMapTracks.forEach(function (noteMap, trackNumber) {
14587
+ var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
14588
+ noteMap.forEach(function (note) {
14589
+ var key = note.instrument + ':' + note.pitch + ':' + note.volume + ':' + Math.round((note.end - note.start) * 1000) / 1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
14590
+ if (self.debugCallback) self.debugCallback("noteMapTrack " + key);
14591
+ if (!uniqueSounds[key]) uniqueSounds[key] = [];
14592
+ uniqueSounds[key].push(note.start);
14593
+ });
14594
+ });
14595
+
14596
+ // Now that we know what we are trying to create, construct the audio buffer by creating each sound and placing it.
14597
+ var allPromises = [];
14598
+ var audioBuffer = activeAudioContext().createBuffer(2, totalSamples, activeAudioContext().sampleRate);
14599
+ for (var key2 = 0; key2 < Object.keys(uniqueSounds).length; key2++) {
14600
+ var k = Object.keys(uniqueSounds)[key2];
14601
+ var parts = k.split(":");
14602
+ var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
14603
+ parts = {
14604
+ instrument: parts[0],
14605
+ pitch: parseInt(parts[1], 10),
14606
+ volume: parseInt(parts[2], 10),
14607
+ len: parseFloat(parts[3]),
14608
+ pan: parseFloat(parts[4]),
14609
+ tempoMultiplier: parseFloat(parts[5]),
14610
+ cents: cents
14611
+ };
14612
+ allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000, self.debugCallback));
14613
+ }
14614
+ self.audioBuffers = [audioBuffer];
14615
+ if (self.debugCallback) {
14616
+ self.debugCallback("sampleRate = " + activeAudioContext().sampleRate);
14617
+ self.debugCallback("totalSamples = " + totalSamples);
14618
+ self.debugCallback("creationTime = " + Math.floor((activeAudioContext().currentTime - startTime) * 1000) + "ms");
14619
+ }
14620
+ Promise.all(allPromises).then(function () {
14621
+ // Safari iOS can mess with the audioContext state, so resume if needed.
14622
+ if (activeAudioContext().state === "suspended") {
14470
14623
  activeAudioContext().resume().then(function () {
14471
14624
  resolve(resolveData(self));
14472
14625
  });
14473
- });
14474
- } else {
14475
- resolve(resolveData(self));
14476
- }
14477
- });
14626
+ } else if (activeAudioContext().state === "interrupted") {
14627
+ activeAudioContext().suspend().then(function () {
14628
+ activeAudioContext().resume().then(function () {
14629
+ resolve(resolveData(self));
14630
+ });
14631
+ });
14632
+ } else {
14633
+ resolve(resolveData(self));
14634
+ }
14635
+ })["catch"](function (error) {
14636
+ reject(error);
14637
+ });
14638
+ } catch (error) {
14639
+ reject(error);
14640
+ }
14478
14641
  });
14479
14642
  };
14480
14643
  function setPan(numTracks, panParam) {
@@ -15288,7 +15451,7 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
15288
15451
  });
15289
15452
  })["catch"](function (error) {
15290
15453
  if (debugCallback) debugCallback('placeNote catch: ' + error.message);
15291
- return Promise.resolve();
15454
+ return Promise.reject(error);
15292
15455
  });
15293
15456
  }
15294
15457
  var copyToChannel = function copyToChannel(toBuffer, fromBuffer, start) {
@@ -18338,13 +18501,15 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
18338
18501
  } // 2 is hardcoded
18339
18502
 
18340
18503
  if (elem.startEnding && isFirstStaff) {
18341
- // only put the first & second ending marks on the first staff
18342
- var textWidth = this.getTextSize.calc(elem.startEnding, "repeatfont", '').width;
18343
- abselem.minspacing += textWidth + 10; // Give plenty of room for the ending number.
18344
- this.partstartelem = new EndingElem(elem.startEnding, anchor, null);
18345
- voice.addOther(this.partstartelem);
18504
+ // MAE 17 May 2025 - Fixes drawing issue
18505
+ if (voice.voicenumber === 0) {
18506
+ // only put the first & second ending marks on the first staff
18507
+ var textWidth = this.getTextSize.calc(elem.startEnding, "repeatfont", '').width;
18508
+ abselem.minspacing += textWidth + 10; // Give plenty of room for the ending number.
18509
+ this.partstartelem = new EndingElem(elem.startEnding, anchor, null);
18510
+ voice.addOther(this.partstartelem);
18511
+ }
18346
18512
  }
18347
-
18348
18513
  // Add a little space to the left of the bar line so that nothing can crowd it.
18349
18514
  abselem.extraw -= 5;
18350
18515
  if (elem.chord !== undefined) {
@@ -19137,6 +19302,7 @@ var stackedDecoration = function stackedDecoration(decoration, width, abselem, y
19137
19302
  "mediumphrase": "scripts.mediumphrase",
19138
19303
  "longphrase": "scripts.longphrase",
19139
19304
  "trill": "scripts.trill",
19305
+ "trillh": "scripts.trill",
19140
19306
  "roll": "scripts.roll",
19141
19307
  "irishroll": "scripts.roll",
19142
19308
  "marcato": "scripts.umarcato",
@@ -19198,6 +19364,7 @@ var stackedDecoration = function stackedDecoration(decoration, width, abselem, y
19198
19364
  case "mediumphrase":
19199
19365
  case "longphrase":
19200
19366
  case "trill":
19367
+ case "trillh":
19201
19368
  case "roll":
19202
19369
  case "irishroll":
19203
19370
  case "marcato":
@@ -19950,6 +20117,11 @@ function FreeText(info, vskip, getFontAndAttr, paddingLeft, width, getTextSize)
19950
20117
  move: hash.attr['font-size'] * 2
19951
20118
  }); // move the distance of the line, plus the distance of the margin, which is also one line.
19952
20119
  } else if (typeof text === 'string') {
20120
+ // MAE 9 May 2025 - Force blank text lines in a text block to have height
20121
+ var replaceStandaloneNewlinesForTextBlocks = function replaceStandaloneNewlinesForTextBlocks(input) {
20122
+ return input.replace(/^[ \t]*\n/gm, 'X\n');
20123
+ ;
20124
+ };
19953
20125
  this.rows.push({
19954
20126
  move: hash.attr['font-size'] / 2
19955
20127
  }); // TODO-PER: move down some - the y location should be the top of the text, but we output text specifying the center line.
@@ -19964,7 +20136,8 @@ function FreeText(info, vskip, getFontAndAttr, paddingLeft, width, getTextSize)
19964
20136
  absElemType: "freeText",
19965
20137
  name: "free-text"
19966
20138
  });
19967
- size = getTextSize.calc(text, 'textfont', 'defined-text');
20139
+ var textForSize = replaceStandaloneNewlinesForTextBlocks(text);
20140
+ size = getTextSize.calc(textForSize, 'textfont', 'defined-text'); // was text
19968
20141
  this.rows.push({
19969
20142
  move: size.height
19970
20143
  });
@@ -21934,10 +22107,18 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
21934
22107
  renderer.paper.closeGroup();
21935
22108
  renderer.moveY(renderer.spacing.music);
21936
22109
  var staffgroups = [];
22110
+ var nStaves = 0;
21937
22111
  for (var line = 0; line < abcTune.lines.length; line++) {
21938
22112
  classes.incrLine();
21939
22113
  var abcLine = abcTune.lines[line];
21940
22114
  if (abcLine.staff) {
22115
+ // MAE 26 May 2025 - for incipits staff count limiting
22116
+ nStaves++;
22117
+ if (abcTune.formatting.maxStaves) {
22118
+ if (nStaves > abcTune.formatting.maxStaves) {
22119
+ break;
22120
+ }
22121
+ }
21941
22122
  if (classes.shouldAddClasses) groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber;
21942
22123
  renderer.paper.openGroup(groupClasses);
21943
22124
  if (abcLine.vskip) {
@@ -22044,6 +22225,8 @@ function drawEnding(renderer, params, linestartx, lineendx, selectables) {
22044
22225
  pathString += sprintf("M %f %f L %f %f ", linestartx, y, lineendx, y);
22045
22226
  renderer.paper.openGroup({
22046
22227
  klass: renderer.controller.classes.generate("ending"),
22228
+ // MAE 17 May 2025 - Ending numbers not being drawn in correct color
22229
+ fill: renderer.foregroundColor,
22047
22230
  "data-name": "ending"
22048
22231
  });
22049
22232
  printPath(renderer, {
@@ -23325,7 +23508,14 @@ function renderText(renderer, params, alreadyInGroup) {
23325
23508
  if (params.cursor) {
23326
23509
  hash.attr.cursor = params.cursor;
23327
23510
  }
23328
- var text = params.text.replace(/\n\n/g, "\n \n");
23511
+
23512
+ // MAE 9 May 2025 for free text blocks
23513
+ var text;
23514
+ if (params.name == "free-text") {
23515
+ text = params.text.replace(/^[ \t]*\n/gm, ' \n');
23516
+ } else {
23517
+ text = params.text.replace(/\n\n/g, "\n \n");
23518
+ }
23329
23519
  text = text.replace(/^\n/, "\xA0\n");
23330
23520
  if (hash.font.box) {
23331
23521
  if (!alreadyInGroup) renderer.paper.openGroup({
@@ -24548,8 +24738,13 @@ function keyboardSelection(ev) {
24548
24738
  }
24549
24739
  function findElementInHistory(selectables, el) {
24550
24740
  if (!el) return -1;
24741
+ // This should always exist, but it occasionally causes an exception, so check first.
24742
+ var dataset = el.dataset;
24743
+ if (!dataset) return -1;
24744
+ var index = dataset.index;
24551
24745
  for (var i = 0; i < selectables.length; i++) {
24552
- if (el.dataset.index === selectables[i].svgEl.dataset.index) return i;
24746
+ var svgDataset = selectables[i].svgEl.dataset;
24747
+ if (svgDataset && index === svgDataset.index) return i;
24553
24748
  }
24554
24749
  return -1;
24555
24750
  }
@@ -26353,8 +26548,13 @@ Svg.prototype.text = function (text, attr, target) {
26353
26548
  el.setAttribute(key, attr[key]);
26354
26549
  }
26355
26550
  }
26551
+ var isFreeText = attr["data-name"] == "free-text";
26356
26552
  var lines = ("" + text).split("\n");
26357
26553
  for (var i = 0; i < lines.length; i++) {
26554
+ if (isFreeText && lines[i] == "") {
26555
+ // Don't draw empty lines
26556
+ continue;
26557
+ }
26358
26558
  var line = document.createElementNS(svgNS, 'tspan');
26359
26559
  line.setAttribute("x", attr.x ? attr.x : 0);
26360
26560
  if (i !== 0) line.setAttribute("dy", "1.2em");
@@ -26376,7 +26576,19 @@ Svg.prototype.text = function (text, attr, target) {
26376
26576
  ts3.textContent = parts[2];
26377
26577
  line.appendChild(ts3);
26378
26578
  }
26379
- } else line.textContent = lines[i];
26579
+ } else {
26580
+ // MAE 9 May 2025 - For improved block text
26581
+ if (isFreeText) {
26582
+ // Fixes issue where blank lines in text blocks didn't take up any vertical
26583
+ if (lines[i].trim() == "") {
26584
+ line.innerHTML = "&nbsp;";
26585
+ } else {
26586
+ line.textContent = lines[i];
26587
+ }
26588
+ } else {
26589
+ line.textContent = lines[i];
26590
+ }
26591
+ }
26380
26592
  el.appendChild(line);
26381
26593
  }
26382
26594
  if (target) target.appendChild(el);else this.append(el);
@@ -26555,7 +26767,7 @@ module.exports = Svg;
26555
26767
  \********************/
26556
26768
  /***/ (function(module) {
26557
26769
 
26558
- var version = '6.4.4';
26770
+ var version = '6.5.1';
26559
26771
  module.exports = version;
26560
26772
 
26561
26773
  /***/ })