abcjs 6.2.1 → 6.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +3 -0
  2. package/RELEASE.md +54 -0
  3. package/abc2xml_239/abc2xml.html +769 -0
  4. package/abc2xml_239/abc2xml.py +2248 -0
  5. package/abc2xml_239/abc2xml_changelog.html +124 -0
  6. package/abc2xml_239/lazy-river.abc +26 -0
  7. package/abc2xml_239/lazy-river.xml +3698 -0
  8. package/abc2xml_239/mean-to-me.abc +22 -0
  9. package/abc2xml_239/mean-to-me.xml +2954 -0
  10. package/abc2xml_239/pyparsing.py +3672 -0
  11. package/abc2xml_239/pyparsing.pyc +0 -0
  12. package/dist/abcjs-basic-min.js +2 -2
  13. package/dist/abcjs-basic.js +205 -91
  14. package/dist/abcjs-basic.js.map +1 -1
  15. package/dist/abcjs-plugin-min.js +2 -2
  16. package/package.json +1 -1
  17. package/src/api/abc_tablatures.js +5 -0
  18. package/src/api/abc_tunebook_svg.js +5 -3
  19. package/src/parse/abc_parse_music.js +18 -53
  20. package/src/parse/abc_parse_settings.js +165 -0
  21. package/src/synth/create-synth.js +4 -0
  22. package/src/synth/place-note.js +6 -0
  23. package/src/synth/play-event.js +7 -5
  24. package/src/tablatures/tab-absolute-elements.js +3 -2
  25. package/src/tablatures/tab-renderer.js +3 -1
  26. package/src/test/abc_parser_lint.js +12 -12
  27. package/src/write/creation/abstract-engraver.js +6 -0
  28. package/src/write/creation/decoration.js +2 -0
  29. package/src/write/creation/glyphs.js +1 -1
  30. package/src/write/draw/glissando.js +1 -0
  31. package/src/write/draw/print-line.js +16 -7
  32. package/src/write/draw/print-stem.js +16 -8
  33. package/src/write/draw/set-paper-size.js +1 -1
  34. package/src/write/draw/tie.js +9 -1
  35. package/src/write/engraver-controller.js +12 -4
  36. package/src/write/interactive/selection.js +6 -0
  37. package/src/write/layout/layout.js +33 -3
  38. package/src/write/svg.js +10 -0
  39. package/types/index.d.ts +29 -21
  40. package/version.js +1 -1
@@ -211,6 +211,8 @@ var GuitarTablature = __webpack_require__(/*! ../tablatures/instruments/guitar/t
211
211
  // Existing tab classes
212
212
  var pluginTab = {
213
213
  'violin': 'ViolinTab',
214
+ 'fiddle': 'ViolinTab',
215
+ 'mandolin': 'ViolinTab',
214
216
  'guitar': 'GuitarTab'
215
217
  };
216
218
  var abcTablatures = {
@@ -274,6 +276,9 @@ var abcTablatures = {
274
276
  // plugin.init(tune, tuneNumber, args, ii);
275
277
  returned.push(pluginInstance);
276
278
  nbPlugins++;
279
+ } else if (instrument === '') {
280
+ // create a placeholder - there is no tab for this staff
281
+ returned.push(null);
277
282
  } else {
278
283
  // unknown tab plugin
279
284
  //this.emit_error('Undefined tablature plugin: ' + tabName)
@@ -995,11 +1000,11 @@ var renderAbc = function renderAbc(output, abc, parserParams, engraverParams, re
995
1000
  div.setAttribute("style", "visibility: hidden;");
996
1001
  document.body.appendChild(div);
997
1002
  }
998
- if (params.afterParsing) params.afterParsing(tune, tuneNumber, abcString);
999
1003
  if (!removeDiv && params.wrap && params.staffwidth) {
1000
1004
  tune = doLineWrapping(div, tune, tuneNumber, abcString, params);
1001
1005
  return tune;
1002
1006
  }
1007
+ if (params.afterParsing) params.afterParsing(tune, tuneNumber, abcString);
1003
1008
  renderOne(div, tune, params, tuneNumber, 0);
1004
1009
  if (removeDiv) div.parentNode.removeChild(div);
1005
1010
  return null;
@@ -1017,6 +1022,7 @@ function doLineWrapping(div, tune, tuneNumber, abcString, params) {
1017
1022
  var warnings = abcParser.getWarnings();
1018
1023
  if (warnings) tune.warnings = warnings;
1019
1024
  }
1025
+ if (params.afterParsing) params.afterParsing(tune, tuneNumber, abcString);
1020
1026
  renderOne(div, tune, ret.revisedParams, tuneNumber, 0);
1021
1027
  tune.explanation = ret.explanation;
1022
1028
  return tune;
@@ -6339,6 +6345,18 @@ var multilineVars;
6339
6345
  var tune;
6340
6346
  var tuneBuilder;
6341
6347
  var header;
6348
+ var _require = __webpack_require__(/*! ./abc_parse_settings */ "./src/parse/abc_parse_settings.js"),
6349
+ legalAccents = _require.legalAccents,
6350
+ volumeDecorations = _require.volumeDecorations,
6351
+ dynamicDecorations = _require.dynamicDecorations,
6352
+ accentPseudonyms = _require.accentPseudonyms,
6353
+ accentDynamicPseudonyms = _require.accentDynamicPseudonyms,
6354
+ nonDecorations = _require.nonDecorations,
6355
+ durations = _require.durations,
6356
+ pitches = _require.pitches,
6357
+ rests = _require.rests,
6358
+ accMap = _require.accMap,
6359
+ tripletQ = _require.tripletQ;
6342
6360
  var MusicParser = function MusicParser(_tokenizer, _warn, _multilineVars, _tune, _tuneBuilder, _header) {
6343
6361
  tokenizer = _tokenizer;
6344
6362
  warn = _warn;
@@ -6406,7 +6424,6 @@ var MusicParser = function MusicParser(_tokenizer, _warn, _multilineVars, _tune,
6406
6424
  // double-quote: chord symbol
6407
6425
  // less-than, greater-than, slash: duration
6408
6426
  // back-tick, space, tab: space
6409
- var nonDecorations = "ABCDEFGabcdefgxyzZ[]|^_{"; // use this to prescreen so we don't have to look for a decoration at every note.
6410
6427
 
6411
6428
  var isInTie = function isInTie(multilineVars, overlayLevel, el) {
6412
6429
  if (multilineVars.inTie[overlayLevel] === undefined) return false;
@@ -6815,7 +6832,7 @@ MusicParser.prototype.parseMusic = function (line) {
6815
6832
  // Create a warning if this is not a displayable duration.
6816
6833
  // The first item on a line is a regular note value, each item after that represents a dot placed after the previous note.
6817
6834
  // Only durations less than a whole note are tested because whole note durations have some tricky rules.
6818
- var durations = [0.5, 0.75, 0.875, 0.9375, 0.96875, 0.984375, 0.25, 0.375, 0.4375, 0.46875, 0.484375, 0.4921875, 0.125, 0.1875, 0.21875, 0.234375, 0.2421875, 0.24609375, 0.0625, 0.09375, 0.109375, 0.1171875, 0.12109375, 0.123046875, 0.03125, 0.046875, 0.0546875, 0.05859375, 0.060546875, 0.0615234375, 0.015625, 0.0234375, 0.02734375, 0.029296875, 0.0302734375, 0.03076171875];
6835
+
6819
6836
  if (el.duration < 1 && durations.indexOf(el.duration) === -1 && el.duration !== 0) {
6820
6837
  if (!el.rest || el.rest.type !== 'spacer') warn("Duration not representable: " + line.substring(startI, i), line, i);
6821
6838
  }
@@ -6963,11 +6980,6 @@ function durationOfMeasure(multilineVars) {
6963
6980
  if (!meter.value || meter.value.length === 0) return 1;
6964
6981
  return parseInt(meter.value[0].num, 10) / parseInt(meter.value[0].den, 10);
6965
6982
  }
6966
- var 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"];
6967
- var volumeDecorations = ["p", "pp", "f", "ff", "mf", "mp", "ppp", "pppp", "fff", "ffff", "sfz"];
6968
- var dynamicDecorations = ["crescendo(", "crescendo)", "diminuendo(", "diminuendo)", "glissando(", "glissando)"];
6969
- var accentPseudonyms = [["<", "accent"], [">", "accent"], ["tr", "trill"], ["plus", "+"], ["emphasis", "accent"], ["^", "umarcato"], ["marcato", "umarcato"]];
6970
- var accentDynamicPseudonyms = [["<(", "crescendo("], ["<)", "crescendo)"], [">(", "diminuendo("], [">)", "diminuendo)"]];
6971
6983
  var letter_to_accent = function letter_to_accent(line, i) {
6972
6984
  var macro = multilineVars.macros[line[i]];
6973
6985
  if (macro !== undefined) {
@@ -7094,19 +7106,6 @@ var letter_to_bar = function letter_to_bar(line, curr_pos) {
7094
7106
  if (retRep.len === 0 || retRep.token[0] === '-') return [orig_bar_len, ret.token];
7095
7107
  return [ret.len + retRep.len, ret.token, retRep.token];
7096
7108
  };
7097
- var tripletQ = {
7098
- 2: 3,
7099
- 3: 2,
7100
- 4: 3,
7101
- 5: 2,
7102
- // TODO-PER: not handling 6/8 rhythm yet
7103
- 6: 2,
7104
- 7: 2,
7105
- // TODO-PER: not handling 6/8 rhythm yet
7106
- 8: 3,
7107
- 9: 2 // TODO-PER: not handling 6/8 rhythm yet
7108
- };
7109
-
7110
7109
  var letter_to_open_slurs_and_triplets = function letter_to_open_slurs_and_triplets(line, i) {
7111
7110
  // consume spaces, and look for all the open parens. If there is a number after the open paren,
7112
7111
  // that is a triplet. Otherwise that is a slur. Collect all the slurs and the first triplet.
@@ -7240,38 +7239,6 @@ var addEndBeam = function addEndBeam(el) {
7240
7239
  if (el.duration !== undefined && el.duration < 0.25) el.end_beam = true;
7241
7240
  return el;
7242
7241
  };
7243
- var pitches = {
7244
- A: 5,
7245
- B: 6,
7246
- C: 0,
7247
- D: 1,
7248
- E: 2,
7249
- F: 3,
7250
- G: 4,
7251
- a: 12,
7252
- b: 13,
7253
- c: 7,
7254
- d: 8,
7255
- e: 9,
7256
- f: 10,
7257
- g: 11
7258
- };
7259
- var rests = {
7260
- x: 'invisible',
7261
- X: 'invisible-multimeasure',
7262
- y: 'spacer',
7263
- z: 'rest',
7264
- Z: 'multimeasure'
7265
- };
7266
- var accMap = {
7267
- 'dblflat': '__',
7268
- 'flat': '_',
7269
- 'natural': '=',
7270
- 'sharp': '^',
7271
- 'dblsharp': '^^',
7272
- 'quarterflat': '_/',
7273
- 'quartersharp': '^/'
7274
- };
7275
7242
  var getCoreNote = function getCoreNote(line, index, el, canHaveBrokenRhythm) {
7276
7243
  //var el = { startChar: index };
7277
7244
  var isComplete = function isComplete(state) {
@@ -7559,6 +7526,67 @@ module.exports = MusicParser;
7559
7526
 
7560
7527
  /***/ }),
7561
7528
 
7529
+ /***/ "./src/parse/abc_parse_settings.js":
7530
+ /*!*****************************************!*\
7531
+ !*** ./src/parse/abc_parse_settings.js ***!
7532
+ \*****************************************/
7533
+ /***/ (function(module) {
7534
+
7535
+ 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'];
7536
+ module.exports.volumeDecorations = ['p', 'pp', 'f', 'ff', 'mf', 'mp', 'ppp', 'pppp', 'fff', 'ffff', 'sfz'];
7537
+ module.exports.dynamicDecorations = ['crescendo(', 'crescendo)', 'diminuendo(', 'diminuendo)', 'glissando(', 'glissando)', '~(', '~)'];
7538
+ module.exports.accentPseudonyms = [['<', 'accent'], ['>', 'accent'], ['tr', 'trill'], ['plus', '+'], ['emphasis', 'accent'], ['^', 'umarcato'], ['marcato', 'umarcato']];
7539
+ module.exports.accentDynamicPseudonyms = [['<(', 'crescendo('], ['<)', 'crescendo)'], ['>(', 'diminuendo('], ['>)', 'diminuendo)']];
7540
+ module.exports.nonDecorations = 'ABCDEFGabcdefgxyzZ[]|^_{'; // use this to prescreen so we don't have to look for a decoration at every note.
7541
+
7542
+ module.exports.durations = [0.5, 0.75, 0.875, 0.9375, 0.96875, 0.984375, 0.25, 0.375, 0.4375, 0.46875, 0.484375, 0.4921875, 0.125, 0.1875, 0.21875, 0.234375, 0.2421875, 0.24609375, 0.0625, 0.09375, 0.109375, 0.1171875, 0.12109375, 0.123046875, 0.03125, 0.046875, 0.0546875, 0.05859375, 0.060546875, 0.0615234375, 0.015625, 0.0234375, 0.02734375, 0.029296875, 0.0302734375, 0.03076171875];
7543
+ module.exports.pitches = {
7544
+ A: 5,
7545
+ B: 6,
7546
+ C: 0,
7547
+ D: 1,
7548
+ E: 2,
7549
+ F: 3,
7550
+ G: 4,
7551
+ a: 12,
7552
+ b: 13,
7553
+ c: 7,
7554
+ d: 8,
7555
+ e: 9,
7556
+ f: 10,
7557
+ g: 11
7558
+ };
7559
+ module.exports.rests = {
7560
+ x: 'invisible',
7561
+ X: 'invisible-multimeasure',
7562
+ y: 'spacer',
7563
+ z: 'rest',
7564
+ Z: 'multimeasure'
7565
+ };
7566
+ module.exports.accMap = {
7567
+ dblflat: '__',
7568
+ flat: '_',
7569
+ natural: '=',
7570
+ sharp: '^',
7571
+ dblsharp: '^^',
7572
+ quarterflat: '_/',
7573
+ quartersharp: '^/'
7574
+ };
7575
+ module.exports.tripletQ = {
7576
+ 2: 3,
7577
+ 3: 2,
7578
+ 4: 3,
7579
+ 5: 2,
7580
+ // TODO-PER: not handling 6/8 rhythm yet
7581
+ 6: 2,
7582
+ 7: 2,
7583
+ // TODO-PER: not handling 6/8 rhythm yet
7584
+ 8: 3,
7585
+ 9: 2 // TODO-PER: not handling 6/8 rhythm yet
7586
+ };
7587
+
7588
+ /***/ }),
7589
+
7562
7590
  /***/ "./src/parse/abc_tokenizer.js":
7563
7591
  /*!************************************!*\
7564
7592
  !*** ./src/parse/abc_tokenizer.js ***!
@@ -14186,6 +14214,9 @@ function CreateSynth() {
14186
14214
  self.getAudioBuffer = function () {
14187
14215
  return self.audioBuffers[0];
14188
14216
  };
14217
+ self.getIsRunning = function () {
14218
+ return self.isRunning;
14219
+ };
14189
14220
 
14190
14221
  /////////////// Private functions //////////////
14191
14222
 
@@ -14782,6 +14813,11 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
14782
14813
  if (len < 0) len = 0.005; // Have some small audible length no matter how short the note is.
14783
14814
  var offlineCtx = new OfflineAC(2, Math.floor((len + fadeTimeSec) * sampleRate), sampleRate);
14784
14815
  var noteName = pitchToNoteName[sound.pitch];
14816
+ if (!soundsCache[sound.instrument]) {
14817
+ // It shouldn't happen that the entire instrument cache wasn't created, but this has been seen in practice, so guard against it.
14818
+ if (debugCallback) debugCallback('placeNote skipped (instrument empty): ' + sound.instrument + ':' + noteName);
14819
+ return Promise.resolve();
14820
+ }
14785
14821
  var noteBufferPromise = soundsCache[sound.instrument][noteName];
14786
14822
  if (!noteBufferPromise) {
14787
14823
  // if the note isn't present then just skip it - it will leave a blank spot in the audio.
@@ -14877,7 +14913,7 @@ module.exports = placeNote;
14877
14913
  var SynthSequence = __webpack_require__(/*! ./synth-sequence */ "./src/synth/synth-sequence.js");
14878
14914
  var CreateSynth = __webpack_require__(/*! ./create-synth */ "./src/synth/create-synth.js");
14879
14915
  var activeAudioContext = __webpack_require__(/*! ./active-audio-context */ "./src/synth/active-audio-context.js");
14880
- function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure) {
14916
+ function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure, soundFontUrl, debugCallback) {
14881
14917
  var sequence = new SynthSequence();
14882
14918
  for (var i = 0; i < midiPitches.length; i++) {
14883
14919
  var note = midiPitches[i];
@@ -14894,17 +14930,21 @@ function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure) {
14894
14930
  var ac = activeAudioContext();
14895
14931
  if (ac.state === "suspended") {
14896
14932
  return ac.resume().then(function () {
14897
- return doPlay(sequence, millisecondsPerMeasure);
14933
+ return doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback);
14898
14934
  });
14899
14935
  } else {
14900
- return doPlay(sequence, millisecondsPerMeasure);
14936
+ return doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback);
14901
14937
  }
14902
14938
  }
14903
- function doPlay(sequence, millisecondsPerMeasure) {
14939
+ function doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback) {
14904
14940
  var buffer = new CreateSynth();
14905
14941
  return buffer.init({
14906
14942
  sequence: sequence,
14907
- millisecondsPerMeasure: millisecondsPerMeasure
14943
+ millisecondsPerMeasure: millisecondsPerMeasure,
14944
+ options: {
14945
+ soundFontUrl: soundFontUrl
14946
+ },
14947
+ debugCallback: debugCallback
14908
14948
  }).then(function () {
14909
14949
  return buffer.prime();
14910
14950
  }).then(function () {
@@ -16250,10 +16290,10 @@ function buildGraceRelativesForRest(plugin, abs, absChild, graceNotes, tabVoice)
16250
16290
  * Build tab absolutes by scanning current staff line absolute array
16251
16291
  * @param {*} staffAbsolute
16252
16292
  */
16253
- TabAbsoluteElements.prototype.build = function (plugin, staffAbsolute, tabVoice, voiceIndex, staffIndex, keySig) {
16293
+ TabAbsoluteElements.prototype.build = function (plugin, staffAbsolute, tabVoice, voiceIndex, staffIndex, keySig, tabVoiceIndex) {
16254
16294
  var staffSize = getInitialStaffSize(staffAbsolute);
16255
16295
  var source = staffAbsolute[staffIndex + voiceIndex];
16256
- var dest = staffAbsolute[staffSize + staffIndex + voiceIndex];
16296
+ var dest = staffAbsolute[tabVoiceIndex];
16257
16297
  var tabPos = null;
16258
16298
  var defNote = null;
16259
16299
  if (source.children[0].abcelem.el_type != 'clef') {
@@ -16632,15 +16672,17 @@ TabRenderer.prototype.doLayout = function () {
16632
16672
  this.tabStaff.voices = [];
16633
16673
  for (var ii = 0; ii < nbVoices; ii++) {
16634
16674
  var tabVoice = new VoiceElement(0, 0);
16675
+ if (ii > 0) tabVoice.duplicate = true;
16635
16676
  var nameHeight = buildTabName(this, tabVoice) / spacing.STEP;
16636
16677
  nameHeight = Math.max(nameHeight, 1); // If there is no label for the tab line, then there needs to be a little padding
16637
16678
  staffGroup.staffs[this.staffIndex].top += nameHeight;
16638
16679
  staffGroup.height += nameHeight * spacing.STEP;
16639
16680
  tabVoice.staff = staffGroupInfos;
16681
+ var tabVoiceIndex = voices.length;
16640
16682
  voices.splice(voices.length, 0, tabVoice);
16641
16683
  var keySig = checkVoiceKeySig(voices, ii + this.staffIndex);
16642
16684
  this.tabStaff.voices[ii] = [];
16643
- this.absolutes.build(this.plugin, voices, this.tabStaff.voices[ii], ii, this.staffIndex, keySig);
16685
+ this.absolutes.build(this.plugin, voices, this.tabStaff.voices[ii], ii, this.staffIndex, keySig, tabVoiceIndex);
16644
16686
  }
16645
16687
  linkStaffAndTabs(staffGroup.staffs); // crossreference tabs and staff
16646
16688
  };
@@ -17484,6 +17526,9 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
17484
17526
  if (noteHead && noteHead.c === 'noteheads.slash.quarter') {
17485
17527
  if (dir === 'down') p2 -= 1;else p1 += 1;
17486
17528
  }
17529
+ if (noteHead && noteHead.c === 'noteheads.triangle.quarter') {
17530
+ if (dir === 'down') p2 -= 0.7;else p1 -= 1.2;
17531
+ }
17487
17532
  abselem.addRight(new RelativeElement(null, dx, 0, p1, {
17488
17533
  "type": "stem",
17489
17534
  "pitch2": p2,
@@ -18696,10 +18741,12 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
18696
18741
  };
18697
18742
  this.startCrescendoX = undefined;
18698
18743
  break;
18744
+ case '~(':
18699
18745
  case "glissando(":
18700
18746
  this.startGlissandoX = abselem;
18701
18747
  glissando = undefined;
18702
18748
  break;
18749
+ case '~)':
18703
18750
  case "glissando)":
18704
18751
  glissando = {
18705
18752
  start: this.startGlissandoX,
@@ -20692,7 +20739,7 @@ glyphs['noteheads.harmonic.quarter'] = {
20692
20739
  h: 8.165
20693
20740
  };
20694
20741
  glyphs['noteheads.triangle.quarter'] = {
20695
- d: [['M', 0, 0], ['l', 9, 0], ['l', -4.5, -9], ['z']],
20742
+ d: [['M', 0, 4], ['l', 9, 0], ['l', -4.5, -9], ['z']],
20696
20743
  w: 9,
20697
20744
  h: 9
20698
20745
  };
@@ -21565,14 +21612,20 @@ function printLine(renderer, x1, x2, y, klass, name, dy) {
21565
21612
  x2 = roundNumber(x2);
21566
21613
  var y1 = roundNumber(y - dy);
21567
21614
  var y2 = roundNumber(y + dy);
21568
- // TODO-PER: This fixes a firefox bug where a path needs to go over the 0.5 mark or it isn't displayed
21569
- if (renderer.firefox112 && dy < 1) {
21570
- var _int = Math.floor(y2);
21571
- var distToHalf = 0.52 - (y2 - _int);
21572
- if (distToHalf > 0) {
21573
- y1 += distToHalf;
21574
- y2 += distToHalf;
21575
- }
21615
+ // TODO-PER: This fixes a firefox bug where it isn't displayed
21616
+ if (renderer.firefox112) {
21617
+ y += dy / 2; // Because the y coordinate is the edge of where the line goes but the width widens from the middle.
21618
+ var attr = {
21619
+ x1: x1,
21620
+ x2: x2,
21621
+ y1: y,
21622
+ y2: y,
21623
+ stroke: renderer.foregroundColor,
21624
+ 'stroke-width': Math.abs(dy * 2)
21625
+ };
21626
+ if (klass) attr['class'] = klass;
21627
+ if (name) attr['data-name'] = name;
21628
+ return renderer.paper.lineToBack(attr);
21576
21629
  }
21577
21630
  var pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y1, x2, y1, x2, y2, x1, y2);
21578
21631
  var options = {
@@ -21623,15 +21676,20 @@ function printStem(renderer, x, dx, y1, y2, klass, name) {
21623
21676
  }
21624
21677
  x = roundNumber(x);
21625
21678
  var x2 = roundNumber(x + dx);
21626
- // TODO-PER: This fixes a firefox bug where a path needs to go over the 0.5 mark or it isn't displayed
21627
- if (renderer.firefox112 && Math.abs(dx) < 1) {
21628
- var higher = Math.max(x, x2);
21629
- var _int = Math.floor(higher);
21630
- var distToHalf = 0.52 - (higher - _int);
21631
- if (distToHalf > 0) {
21632
- x += distToHalf;
21633
- x2 += distToHalf;
21634
- }
21679
+ // TODO-PER: This fixes a firefox bug where it isn't displayed
21680
+ if (renderer.firefox112) {
21681
+ x += dx / 2; // Because the x coordinate is the edge of where the line goes but the width widens from the middle.
21682
+ var attr = {
21683
+ x1: x,
21684
+ x2: x,
21685
+ y1: y1,
21686
+ y2: y2,
21687
+ stroke: renderer.foregroundColor,
21688
+ 'stroke-width': Math.abs(dx)
21689
+ };
21690
+ if (klass) attr['class'] = klass;
21691
+ if (name) attr['data-name'] = name;
21692
+ return renderer.paper.lineToBack(attr);
21635
21693
  }
21636
21694
  var pathArray = [["M", x, y1], ["L", x, y2], ["L", x2, y2], ["L", x2, y1], ["z"]];
21637
21695
  var attr = {
@@ -22025,7 +22083,7 @@ module.exports = drawSeparator;
22025
22083
  /***/ (function(module) {
22026
22084
 
22027
22085
  function setPaperSize(renderer, maxwidth, scale, responsive) {
22028
- var w = (maxwidth + renderer.padding.right) * scale;
22086
+ var w = (maxwidth + renderer.padding.left + renderer.padding.right) * scale;
22029
22087
  var h = (renderer.y + renderer.padding.bottom) * scale;
22030
22088
  if (renderer.isPrint) h = Math.max(h, 1056); // 11in x 72pt/in x 1.33px/pt
22031
22089
  // TODO-PER: We are letting the page get as long as it needs now, but eventually that should go to a second page.
@@ -22638,10 +22696,15 @@ function drawTie(renderer, params, linestartx, lineendx, selectables) {
22638
22696
  if (params.hint) klass = "abcjs-hint";
22639
22697
  var fudgeY = params.fixedY ? 1.5 : 0; // TODO-PER: This just compensates for drawArc, which contains too much knowledge of ties and slurs.
22640
22698
  var el = drawArc(renderer, params.startX, params.endX, params.startY + fudgeY, params.endY + fudgeY, params.above, klass, params.isTie, params.dotted);
22699
+ var startChar = -1;
22700
+ // This gets the start and end points of the contents of the slur. We assume that the parenthesis are just to the outside of that.
22701
+ if (params.anchor1 && !params.isTie) startChar = params.anchor1.parent.abcelem.startChar - 1;
22702
+ var endChar = -1;
22703
+ if (params.anchor2 && !params.isTie) endChar = params.anchor2.parent.abcelem.endChar + 1;
22641
22704
  selectables.wrapSvgEl({
22642
22705
  el_type: "slur",
22643
- startChar: -1,
22644
- endChar: -1
22706
+ startChar: startChar,
22707
+ endChar: endChar
22645
22708
  }, el);
22646
22709
  return [el];
22647
22710
  }
@@ -22951,6 +23014,7 @@ var EngraverController = function EngraverController(paper, params) {
22951
23014
  this.responsive = params.responsive;
22952
23015
  this.space = 3 * spacing.SPACE;
22953
23016
  this.initialClef = params.initialClef;
23017
+ this.expandToWidest = !!params.expandToWidest;
22954
23018
  this.scale = params.scale ? parseFloat(params.scale) : 0;
22955
23019
  this.classes = new Classes({
22956
23020
  shouldAddClasses: params.add_classes
@@ -23134,7 +23198,12 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
23134
23198
  this.constructTuneElements(abcTune);
23135
23199
 
23136
23200
  // Do all the positioning, both horizontally and vertically
23137
- var maxWidth = layout(this.renderer, abcTune, this.width, this.space);
23201
+ var maxWidth = layout(this.renderer, abcTune, this.width, this.space, this.expandToWidest);
23202
+
23203
+ //Set the top text now that we know the width
23204
+ if (this.expandToWidest && maxWidth > this.width + 1) {
23205
+ abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, maxWidth, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.getTextSize);
23206
+ }
23138
23207
 
23139
23208
  // Deal with tablature for staff
23140
23209
  if (abcTune.tablatures) {
@@ -23147,13 +23216,13 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
23147
23216
  this.selectables = ret.selectables;
23148
23217
  if (this.oneSvgPerLine) {
23149
23218
  var div = this.renderer.paper.svg.parentNode;
23150
- this.svgs = splitSvgIntoLines(div, abcTune.metaText.title, this.responsive);
23219
+ this.svgs = splitSvgIntoLines(this.renderer, div, abcTune.metaText.title, this.responsive);
23151
23220
  } else {
23152
23221
  this.svgs = [this.renderer.paper.svg];
23153
23222
  }
23154
23223
  setupSelection(this, this.svgs);
23155
23224
  };
23156
- function splitSvgIntoLines(output, title, responsive) {
23225
+ function splitSvgIntoLines(renderer, output, title, responsive) {
23157
23226
  // Each line is a top level <g> in the svg. To split it into separate
23158
23227
  // svgs iterate through each of those and put them in a new svg. Since
23159
23228
  // they are placed absolutely, the viewBox needs to be manipulated to
@@ -23183,7 +23252,9 @@ function splitSvgIntoLines(output, title, responsive) {
23183
23252
  svg.setAttribute("aria-label", fullTitle);
23184
23253
  if (responsive !== 'resize') svg.setAttribute("height", height);
23185
23254
  if (responsive === 'resize') svg.style.position = '';
23186
- svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + height);
23255
+ // TODO-PER: Hack! Not sure why this is needed.
23256
+ var viewBoxHeight = renderer.firefox112 ? height + 1 : height;
23257
+ svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + viewBoxHeight);
23187
23258
  svg.appendChild(style.cloneNode(true));
23188
23259
  var titleEl = document.createElement("title");
23189
23260
  titleEl.innerText = fullTitle;
@@ -23635,6 +23706,7 @@ function keyboardSelection(ev) {
23635
23706
  if (handled) ev.preventDefault();
23636
23707
  }
23637
23708
  function findElementInHistory(selectables, el) {
23709
+ if (!el) return -1;
23638
23710
  for (var i = 0; i < selectables.length; i++) {
23639
23711
  if (el.dataset.index === selectables[i].svgEl.dataset.index) return i;
23640
23712
  }
@@ -23693,7 +23765,9 @@ function getBestMatchCoordinates(dim, ev, scale) {
23693
23765
  }
23694
23766
  function getTarget(target) {
23695
23767
  // This searches up the dom for the first item containing the attribute "selectable", or stopping at the SVG.
23768
+ if (!target) return null;
23696
23769
  if (target.tagName === "svg") return target;
23770
+ if (!target.getAttribute) return null;
23697
23771
  var found = target.getAttribute("selectable");
23698
23772
  while (!found) {
23699
23773
  if (!target.parentElement) found = true;else {
@@ -24232,7 +24306,7 @@ var layoutVoice = __webpack_require__(/*! ./voice */ "./src/write/layout/voice.j
24232
24306
  var setUpperAndLowerElements = __webpack_require__(/*! ./set-upper-and-lower-elements */ "./src/write/layout/set-upper-and-lower-elements.js");
24233
24307
  var layoutStaffGroup = __webpack_require__(/*! ./staff-group */ "./src/write/layout/staff-group.js");
24234
24308
  var getLeftEdgeOfStaff = __webpack_require__(/*! ./get-left-edge-of-staff */ "./src/write/layout/get-left-edge-of-staff.js");
24235
- var layout = function layout(renderer, abctune, width, space) {
24309
+ var layout = function layout(renderer, abctune, width, space, expandToWidest) {
24236
24310
  var i;
24237
24311
  var abcLine;
24238
24312
  // Adjust the x-coordinates to their absolute positions
@@ -24240,8 +24314,14 @@ var layout = function layout(renderer, abctune, width, space) {
24240
24314
  for (i = 0; i < abctune.lines.length; i++) {
24241
24315
  abcLine = abctune.lines[i];
24242
24316
  if (abcLine.staff) {
24243
- setXSpacing(renderer, width, space, abcLine.staffGroup, abctune.formatting, i === abctune.lines.length - 1, false);
24244
- if (abcLine.staffGroup.w > maxWidth) maxWidth = abcLine.staffGroup.w;
24317
+ // console.log("=== line", i)
24318
+ var thisWidth = setXSpacing(renderer, maxWidth, space, abcLine.staffGroup, abctune.formatting, i === abctune.lines.length - 1, false);
24319
+ // console.log(thisWidth, maxWidth)
24320
+ if (Math.round(thisWidth) > Math.round(maxWidth)) {
24321
+ // to take care of floating point weirdness
24322
+ maxWidth = thisWidth;
24323
+ if (expandToWidest) i = -1; // do the calculations over with the new width
24324
+ }
24245
24325
  }
24246
24326
  }
24247
24327
 
@@ -24272,13 +24352,38 @@ var setXSpacing = function setXSpacing(renderer, width, space, staffGroup, forma
24272
24352
  var newspace = space;
24273
24353
  for (var it = 0; it < 8; it++) {
24274
24354
  // TODO-PER: shouldn't need multiple passes, but each pass gets it closer to the right spacing. (Only affects long lines: normal lines break out of this loop quickly.)
24355
+ // console.log("iteration", it)
24356
+ // dumpGroup("before", staffGroup)
24275
24357
  var ret = layoutStaffGroup(newspace, renderer, debug, staffGroup, leftEdge);
24358
+ // dumpGroup("after",staffGroup)
24276
24359
  newspace = calcHorizontalSpacing(isLastLine, formatting.stretchlast, width + renderer.padding.left, staffGroup.w, newspace, ret.spacingUnits, ret.minSpace, renderer.padding.left + renderer.padding.right);
24277
24360
  if (debug) console.log("setXSpace", it, staffGroup.w, newspace, staffGroup.minspace);
24278
24361
  if (newspace === null) break;
24279
24362
  }
24280
24363
  centerWholeRests(staffGroup.voices);
24281
- };
24364
+ return staffGroup.w - leftEdge;
24365
+ };
24366
+
24367
+ // function dumpGroup(label, staffGroup) {
24368
+ // var output = {
24369
+ // line: staffGroup.line,
24370
+ // w: staffGroup.w,
24371
+ // voice: {
24372
+ // i: staffGroup.voices[0].i,
24373
+ // minx: staffGroup.voices[0].minx,
24374
+ // nextx: staffGroup.voices[0].nextx,
24375
+ // spacingduration: staffGroup.voices[0].spacingduration,
24376
+ // w: staffGroup.voices[0].w,
24377
+ // children: [],
24378
+ // }
24379
+ // }
24380
+ // for (var i = 0; i < staffGroup.voices[0].children.length; i++) {
24381
+ // var child = staffGroup.voices[0].children[i]
24382
+ // output.voice.children.push({ fixedW: child.fixed.w, w: child.w, x: child.x, type: child.type })
24383
+ // }
24384
+ // console.log(label,output)
24385
+ // }
24386
+
24282
24387
  function calcHorizontalSpacing(isLastLine, stretchLast, targetWidth, lineWidth, spacing, spacingUnits, minSpace, padding) {
24283
24388
  if (isLastLine) {
24284
24389
  if (stretchLast === undefined) {
@@ -25486,6 +25591,15 @@ Svg.prototype.pathToBack = function (attr) {
25486
25591
  this.prepend(el);
25487
25592
  return el;
25488
25593
  };
25594
+ Svg.prototype.lineToBack = function (attr) {
25595
+ var el = document.createElementNS(svgNS, 'line');
25596
+ var keys = Object.keys(attr);
25597
+ for (var i = 0; i < keys.length; i++) {
25598
+ el.setAttribute(keys[i], attr[keys[i]]);
25599
+ }
25600
+ this.prepend(el);
25601
+ return el;
25602
+ };
25489
25603
  Svg.prototype.append = function (el) {
25490
25604
  if (this.currentGroup.length > 0) this.currentGroup[0].appendChild(el);else this.svg.appendChild(el);
25491
25605
  };
@@ -25521,7 +25635,7 @@ module.exports = Svg;
25521
25635
  \********************/
25522
25636
  /***/ (function(module) {
25523
25637
 
25524
- var version = '6.2.1';
25638
+ var version = '6.2.3';
25525
25639
  module.exports = version;
25526
25640
 
25527
25641
  /***/ })