abcjs 6.2.2 → 6.3.0

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 (66) hide show
  1. package/README.md +4 -0
  2. package/RELEASE.md +78 -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 +909 -485
  14. package/dist/abcjs-basic.js.map +1 -1
  15. package/dist/abcjs-plugin-min.js +2 -2
  16. package/index.js +2 -0
  17. package/package.json +1 -1
  18. package/plugin.js +1 -1
  19. package/src/api/abc_tablatures.js +51 -13
  20. package/src/api/abc_tunebook_svg.js +5 -3
  21. package/src/parse/abc_parse_directive.js +17 -16
  22. package/src/parse/abc_parse_header.js +22 -19
  23. package/src/parse/abc_parse_music.js +18 -53
  24. package/src/parse/abc_parse_settings.js +165 -0
  25. package/src/parse/abc_tokenizer.js +72 -7
  26. package/src/parse/tune-builder.js +60 -1
  27. package/src/synth/create-synth.js +4 -0
  28. package/src/synth/place-note.js +6 -0
  29. package/src/synth/play-event.js +7 -5
  30. package/src/synth/synth-controller.js +6 -2
  31. package/src/tablatures/instruments/string-patterns.js +11 -0
  32. package/src/tablatures/instruments/{guitar/guitar-patterns.js → tab-string-patterns.js} +6 -6
  33. package/src/tablatures/instruments/{violin/tab-violin.js → tab-string.js} +13 -11
  34. package/src/tablatures/tab-absolute-elements.js +19 -9
  35. package/src/tablatures/tab-renderer.js +24 -10
  36. package/src/test/abc_parser_lint.js +1 -0
  37. package/src/write/creation/abstract-engraver.js +9 -2
  38. package/src/write/creation/add-chord.js +102 -82
  39. package/src/write/creation/add-text-if.js +2 -2
  40. package/src/write/creation/decoration.js +16 -8
  41. package/src/write/creation/elements/bottom-text.js +62 -47
  42. package/src/write/creation/elements/rich-text.js +51 -0
  43. package/src/write/creation/elements/top-text.js +37 -11
  44. package/src/write/creation/glyphs.js +2 -2
  45. package/src/write/draw/absolute.js +4 -1
  46. package/src/write/draw/draw.js +13 -4
  47. package/src/write/draw/glissando.js +1 -0
  48. package/src/write/draw/non-music.js +3 -1
  49. package/src/write/draw/relative.js +1 -1
  50. package/src/write/draw/set-paper-size.js +1 -1
  51. package/src/write/draw/tempo.js +1 -1
  52. package/src/write/draw/text.js +10 -0
  53. package/src/write/draw/tie.js +9 -1
  54. package/src/write/engraver-controller.js +58 -11
  55. package/src/write/helpers/classes.js +1 -1
  56. package/src/write/helpers/get-font-and-attr.js +8 -1
  57. package/src/write/helpers/get-text-size.js +8 -1
  58. package/src/write/interactive/selection.js +6 -0
  59. package/src/write/layout/layout.js +33 -3
  60. package/src/write/svg.js +30 -0
  61. package/temp.txt +50 -0
  62. package/types/index.d.ts +74 -26
  63. package/version.js +1 -1
  64. package/.github/workflows/tests.yml +0 -29
  65. package/src/tablatures/instruments/guitar/tab-guitar.js +0 -48
  66. package/src/tablatures/instruments/violin/violin-patterns.js +0 -23
@@ -69,6 +69,7 @@ var supportsAudio = __webpack_require__(/*! ./src/synth/supports-audio */ "./src
69
69
  var playEvent = __webpack_require__(/*! ./src/synth/play-event */ "./src/synth/play-event.js");
70
70
  var SynthController = __webpack_require__(/*! ./src/synth/synth-controller */ "./src/synth/synth-controller.js");
71
71
  var getMidiFile = __webpack_require__(/*! ./src/synth/get-midi-file */ "./src/synth/get-midi-file.js");
72
+ var midiRenderer = __webpack_require__(/*! ./src/synth/abc_midi_renderer */ "./src/synth/abc_midi_renderer.js");
72
73
  abcjs.synth = {
73
74
  CreateSynth: CreateSynth,
74
75
  instrumentIndexToName: instrumentIndexToName,
@@ -81,7 +82,8 @@ abcjs.synth = {
81
82
  supportsAudio: supportsAudio,
82
83
  playEvent: playEvent,
83
84
  getMidiFile: getMidiFile,
84
- sequence: sequence
85
+ sequence: sequence,
86
+ midiRenderer: midiRenderer
85
87
  };
86
88
  abcjs['Editor'] = __webpack_require__(/*! ./src/edit/abc_editor */ "./src/edit/abc_editor.js");
87
89
  abcjs['EditArea'] = __webpack_require__(/*! ./src/edit/abc_editarea */ "./src/edit/abc_editarea.js");
@@ -203,17 +205,42 @@ module.exports = animation;
203
205
  * where plugin represents a plugin instance
204
206
  *
205
207
  */
206
- var ViolinTablature = __webpack_require__(/*! ../tablatures/instruments/violin/tab-violin */ "./src/tablatures/instruments/violin/tab-violin.js");
207
- var GuitarTablature = __webpack_require__(/*! ../tablatures/instruments/guitar/tab-guitar */ "./src/tablatures/instruments/guitar/tab-guitar.js");
208
+ var StringTablature = __webpack_require__(/*! ../tablatures/instruments/tab-string */ "./src/tablatures/instruments/tab-string.js");
208
209
 
209
210
  /* extend the table below when adding a new instrument plugin */
210
211
 
211
212
  // Existing tab classes
212
213
  var pluginTab = {
213
- 'violin': 'ViolinTab',
214
- 'fiddle': 'ViolinTab',
215
- 'mandolin': 'ViolinTab',
216
- 'guitar': 'GuitarTab'
214
+ 'violin': {
215
+ name: 'StringTab',
216
+ defaultTuning: ['G,', 'D', 'A', 'e'],
217
+ isTabBig: false,
218
+ tabSymbolOffset: 0
219
+ },
220
+ 'fiddle': {
221
+ name: 'StringTab',
222
+ defaultTuning: ['G,', 'D', 'A', 'e'],
223
+ isTabBig: false,
224
+ tabSymbolOffset: 0
225
+ },
226
+ 'mandolin': {
227
+ name: 'StringTab',
228
+ defaultTuning: ['G,', 'D', 'A', 'e'],
229
+ isTabBig: false,
230
+ tabSymbolOffset: 0
231
+ },
232
+ 'guitar': {
233
+ name: 'StringTab',
234
+ defaultTuning: ['E,', 'A,', 'D', 'G', 'B', 'e'],
235
+ isTabBig: true,
236
+ tabSymbolOffset: 0
237
+ },
238
+ 'fiveString': {
239
+ name: 'StringTab',
240
+ defaultTuning: ['C,', 'G,', 'D', 'A', 'e'],
241
+ isTabBig: false,
242
+ tabSymbolOffset: -.95
243
+ }
217
244
  };
218
245
  var abcTablatures = {
219
246
  inited: false,
@@ -258,7 +285,7 @@ var abcTablatures = {
258
285
  var tabName = pluginTab[instrument];
259
286
  var plugin = null;
260
287
  if (tabName) {
261
- plugin = this.plugins[tabName];
288
+ plugin = this.plugins[tabName.name];
262
289
  }
263
290
  if (plugin) {
264
291
  if (params.visualTranspose != 0) {
@@ -270,12 +297,16 @@ var abcTablatures = {
270
297
  classz: plugin,
271
298
  tuneNumber: tuneNumber,
272
299
  params: args,
273
- instance: null
300
+ instance: null,
301
+ tabType: tabName
274
302
  };
275
303
  // proceed with tab plugin init
276
304
  // plugin.init(tune, tuneNumber, args, ii);
277
305
  returned.push(pluginInstance);
278
306
  nbPlugins++;
307
+ } else if (instrument === '') {
308
+ // create a placeholder - there is no tab for this staff
309
+ returned.push(null);
279
310
  } else {
280
311
  // unknown tab plugin
281
312
  //this.emit_error('Undefined tablature plugin: ' + tabName)
@@ -293,20 +324,50 @@ var abcTablatures = {
293
324
  */
294
325
  layoutTablatures: function layoutTablatures(renderer, abcTune) {
295
326
  var tabs = abcTune.tablatures;
327
+
296
328
  // chack tabs request for each staffs
329
+ var staffLineCount = 0;
330
+
331
+ // Clear the suppression flag
332
+ if (tabs && tabs.length > 0) {
333
+ var nTabs = tabs.length;
334
+ for (var kk = 0; kk < nTabs; ++kk) {
335
+ if (tabs[kk] && tabs[kk].params.firstStaffOnly) {
336
+ tabs[kk].params.suppress = false;
337
+ }
338
+ }
339
+ }
297
340
  for (var ii = 0; ii < abcTune.lines.length; ii++) {
298
341
  var line = abcTune.lines[ii];
342
+ if (line.staff) {
343
+ staffLineCount++;
344
+ }
345
+
346
+ // MAE 27Nov2023
347
+ // If tab param "firstStaffOnly", remove the tab label after the first staff
348
+ if (staffLineCount > 1) {
349
+ if (tabs && tabs.length > 0) {
350
+ var nTabs = tabs.length;
351
+ for (var kk = 0; kk < nTabs; ++kk) {
352
+ if (tabs[kk].params.firstStaffOnly) {
353
+ // Set the staff draw suppression flag
354
+ tabs[kk].params.suppress = true;
355
+ }
356
+ }
357
+ }
358
+ }
299
359
  var curStaff = line.staff;
300
360
  if (curStaff) {
361
+ var maxStaves = curStaff.length;
301
362
  for (var jj = 0; jj < curStaff.length; jj++) {
302
- if (tabs[jj]) {
363
+ if (tabs[jj] && jj < maxStaves) {
303
364
  // tablature requested for staff
304
365
  var tabPlugin = tabs[jj];
305
366
  if (tabPlugin.instance == null) {
306
367
  tabPlugin.instance = new tabPlugin.classz();
307
368
  // plugin.init(tune, tuneNumber, args, ii);
308
369
  // call initer first
309
- tabPlugin.instance.init(abcTune, tabPlugin.tuneNumber, tabPlugin.params, jj);
370
+ tabPlugin.instance.init(abcTune, tabPlugin.tuneNumber, tabPlugin.params, jj, tabPlugin.tabType);
310
371
  }
311
372
  // render next
312
373
  tabPlugin.instance.render(renderer, line, jj);
@@ -321,8 +382,7 @@ var abcTablatures = {
321
382
  init: function init() {
322
383
  // just register plugin hosted by abcjs
323
384
  if (!this.inited) {
324
- this.register(new ViolinTablature());
325
- this.register(new GuitarTablature());
385
+ this.register(new StringTablature());
326
386
  this.inited = true;
327
387
  }
328
388
  }
@@ -997,11 +1057,11 @@ var renderAbc = function renderAbc(output, abc, parserParams, engraverParams, re
997
1057
  div.setAttribute("style", "visibility: hidden;");
998
1058
  document.body.appendChild(div);
999
1059
  }
1000
- if (params.afterParsing) params.afterParsing(tune, tuneNumber, abcString);
1001
1060
  if (!removeDiv && params.wrap && params.staffwidth) {
1002
1061
  tune = doLineWrapping(div, tune, tuneNumber, abcString, params);
1003
1062
  return tune;
1004
1063
  }
1064
+ if (params.afterParsing) params.afterParsing(tune, tuneNumber, abcString);
1005
1065
  renderOne(div, tune, params, tuneNumber, 0);
1006
1066
  if (removeDiv) div.parentNode.removeChild(div);
1007
1067
  return null;
@@ -1019,6 +1079,7 @@ function doLineWrapping(div, tune, tuneNumber, abcString, params) {
1019
1079
  var warnings = abcParser.getWarnings();
1020
1080
  if (warnings) tune.warnings = warnings;
1021
1081
  }
1082
+ if (params.afterParsing) params.afterParsing(tune, tuneNumber, abcString);
1022
1083
  renderOne(div, tune, ret.revisedParams, tuneNumber, 0);
1023
1084
  tune.explanation = ret.explanation;
1024
1085
  return tune;
@@ -4165,31 +4226,30 @@ var parseDirective = {};
4165
4226
  }
4166
4227
  };
4167
4228
  parseDirective.parseFontChangeLine = function (textstr) {
4229
+ // We don't want to match two dollar signs, so change those temporarily
4230
+ textstr = textstr.replace(/\$\$/g, "\x03");
4168
4231
  var textParts = textstr.split('$');
4169
4232
  if (textParts.length > 1 && multilineVars.setfont) {
4170
- var textarr = [{
4171
- text: textParts[0]
4172
- }];
4233
+ var textarr = [];
4234
+ if (textParts[0] !== '')
4235
+ // did the original string start with `$`?
4236
+ textarr.push({
4237
+ text: textParts[0]
4238
+ });
4173
4239
  for (var i = 1; i < textParts.length; i++) {
4174
4240
  if (textParts[i][0] === '0') textarr.push({
4175
- text: textParts[i].substring(1)
4176
- });else if (textParts[i][0] === '1' && multilineVars.setfont[1]) textarr.push({
4177
- font: multilineVars.setfont[1],
4178
- text: textParts[i].substring(1)
4179
- });else if (textParts[i][0] === '2' && multilineVars.setfont[2]) textarr.push({
4180
- font: multilineVars.setfont[2],
4181
- text: textParts[i].substring(1)
4182
- });else if (textParts[i][0] === '3' && multilineVars.setfont[3]) textarr.push({
4183
- font: multilineVars.setfont[3],
4184
- text: textParts[i].substring(1)
4185
- });else if (textParts[i][0] === '4' && multilineVars.setfont[4]) textarr.push({
4186
- font: multilineVars.setfont[4],
4187
- text: textParts[i].substring(1)
4188
- });else textarr[textarr.length - 1].text += '$' + textParts[i];
4189
- }
4190
- if (textarr.length > 1) return textarr;
4191
- }
4192
- return textstr;
4241
+ text: textParts[i].substring(1).replace(/\x03/g, "$$")
4242
+ });else {
4243
+ var whichFont = parseInt(textParts[i][0], 10);
4244
+ if (multilineVars.setfont[whichFont]) textarr.push({
4245
+ font: multilineVars.setfont[whichFont],
4246
+ text: textParts[i].substring(1).replace(/\x03/g, "$$")
4247
+ });else textarr[textarr.length - 1].text += '$' + textParts[i].replace(/\x03/g, "$$");
4248
+ }
4249
+ }
4250
+ return textarr;
4251
+ }
4252
+ return textstr.replace(/\x03/g, "$$");
4193
4253
  };
4194
4254
  var positionChoices = ['auto', 'above', 'below', 'hidden'];
4195
4255
  parseDirective.addDirective = function (str) {
@@ -4237,6 +4297,9 @@ var parseDirective = {};
4237
4297
  case "jazzchords":
4238
4298
  tune.formatting.jazzchords = true;
4239
4299
  break;
4300
+ case "accentAbove":
4301
+ tune.formatting.accentAbove = true;
4302
+ break;
4240
4303
  case "germanAlphabet":
4241
4304
  tune.formatting.germanAlphabet = true;
4242
4305
  break;
@@ -4428,7 +4491,7 @@ var parseDirective = {};
4428
4491
  if (sfTokens.length >= 4) {
4429
4492
  if (sfTokens[0].token === '-' && sfTokens[1].type === 'number') {
4430
4493
  var sfNum = parseInt(sfTokens[1].token);
4431
- if (sfNum >= 1 && sfNum <= 4) {
4494
+ if (sfNum >= 1 && sfNum <= 9) {
4432
4495
  if (!multilineVars.setfont) multilineVars.setfont = [];
4433
4496
  sfTokens.shift();
4434
4497
  sfTokens.shift();
@@ -4746,17 +4809,15 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
4746
4809
  parseDirective.initialize(tokenizer, warn, multilineVars, tune, tuneBuilder);
4747
4810
  };
4748
4811
  this.reset(tokenizer, warn, multilineVars, tune);
4749
- this.setTitle = function (title) {
4750
- if (multilineVars.hasMainTitle) tuneBuilder.addSubtitle(tokenizer.translateString(tokenizer.stripComment(title)), {
4812
+ this.setTitle = function (title, origSize) {
4813
+ if (multilineVars.hasMainTitle) tuneBuilder.addSubtitle(title, {
4751
4814
  startChar: multilineVars.iChar,
4752
- endChar: multilineVars.iChar + title.length + 2
4815
+ endChar: multilineVars.iChar + origSize + 2
4753
4816
  }); // display secondary title
4754
4817
  else {
4755
- var titleStr = tokenizer.translateString(tokenizer.theReverser(tokenizer.stripComment(title)));
4756
- if (multilineVars.titlecaps) titleStr = titleStr.toUpperCase();
4757
- tuneBuilder.addMetaText("title", titleStr, {
4818
+ tuneBuilder.addMetaText("title", title, {
4758
4819
  startChar: multilineVars.iChar,
4759
- endChar: multilineVars.iChar + title.length + 2
4820
+ endChar: multilineVars.iChar + origSize + 2
4760
4821
  });
4761
4822
  multilineVars.hasMainTitle = true;
4762
4823
  }
@@ -5116,12 +5177,13 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5116
5177
  if (result.foundKey && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('key', startChar, endChar, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
5117
5178
  return [e - i + 1 + ws];
5118
5179
  case "[P:":
5180
+ var part = parseDirective.parseFontChangeLine(line.substring(i + 3, e));
5119
5181
  if (startLine || tune.lines.length <= tune.lineNum) multilineVars.partForNextLine = {
5120
- title: line.substring(i + 3, e),
5182
+ title: part,
5121
5183
  startChar: startChar,
5122
5184
  endChar: endChar
5123
5185
  };else tuneBuilder.appendElement('part', startChar, endChar, {
5124
- title: line.substring(i + 3, e)
5186
+ title: part
5125
5187
  });
5126
5188
  return [e - i + 1 + ws];
5127
5189
  case "[L:":
@@ -5212,28 +5274,34 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5212
5274
  };
5213
5275
  this.parseHeader = function (line) {
5214
5276
  var field = metaTextHeaders[line[0]];
5215
- if (field !== undefined) {
5216
- if (field === 'unalignedWords') tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line.substring(2)))), {
5277
+ var origSize = line.length - 2;
5278
+ var restOfLine = tokenizer.translateString(tokenizer.stripComment(line.substring(2)));
5279
+ if (field === 'unalignedWords' || field === 'notes') {
5280
+ // These fields can be multi-line
5281
+ tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(restOfLine), {
5217
5282
  startChar: multilineVars.iChar,
5218
5283
  endChar: multilineVars.iChar + line.length
5219
- });else tuneBuilder.addMetaText(field, tokenizer.translateString(tokenizer.stripComment(line.substring(2))), {
5284
+ });
5285
+ } else if (field !== undefined) {
5286
+ // these fields are single line
5287
+ tuneBuilder.addMetaText(field, parseDirective.parseFontChangeLine(restOfLine), {
5220
5288
  startChar: multilineVars.iChar,
5221
5289
  endChar: multilineVars.iChar + line.length
5222
5290
  });
5223
- return {};
5224
5291
  } else {
5225
5292
  var startChar = multilineVars.iChar;
5226
5293
  var endChar = startChar + line.length;
5227
5294
  switch (line[0]) {
5228
5295
  case 'H':
5229
- tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line.substring(2))), {
5296
+ // History is a little different because once it starts it continues until another header field is encountered
5297
+ tuneBuilder.addMetaTextArray("history", parseDirective.parseFontChangeLine(restOfLine), {
5230
5298
  startChar: multilineVars.iChar,
5231
5299
  endChar: multilineVars.iChar + line.length
5232
5300
  });
5233
5301
  line = tokenizer.peekLine();
5234
5302
  while (line && line[1] !== ':') {
5235
5303
  tokenizer.nextLine();
5236
- tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line)), {
5304
+ tuneBuilder.addMetaTextArray("history", parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line))), {
5237
5305
  startChar: multilineVars.iChar,
5238
5306
  endChar: multilineVars.iChar + line.length
5239
5307
  });
@@ -5258,11 +5326,11 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5258
5326
  break;
5259
5327
  case 'P':
5260
5328
  // TODO-PER: There is more to do with parts, but the writer doesn't care.
5261
- if (multilineVars.is_in_header) tuneBuilder.addMetaText("partOrder", tokenizer.translateString(tokenizer.stripComment(line.substring(2))), {
5329
+ if (multilineVars.is_in_header) tuneBuilder.addMetaText("partOrder", parseDirective.parseFontChangeLine(restOfLine), {
5262
5330
  startChar: multilineVars.iChar,
5263
5331
  endChar: multilineVars.iChar + line.length
5264
5332
  });else multilineVars.partForNextLine = {
5265
- title: tokenizer.translateString(tokenizer.stripComment(line.substring(2))),
5333
+ title: restOfLine,
5266
5334
  startChar: startChar,
5267
5335
  endChar: endChar
5268
5336
  };
@@ -5274,7 +5342,8 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
5274
5342
  }
5275
5343
  break;
5276
5344
  case 'T':
5277
- this.setTitle(line.substring(2));
5345
+ if (multilineVars.titlecaps) restOfLine = restOfLine.toUpperCase();
5346
+ this.setTitle(parseDirective.parseFontChangeLine(tokenizer.theReverser(restOfLine)), origSize);
5278
5347
  break;
5279
5348
  case 'U':
5280
5349
  this.addUserDefinition(line, 2, line.length);
@@ -6341,6 +6410,18 @@ var multilineVars;
6341
6410
  var tune;
6342
6411
  var tuneBuilder;
6343
6412
  var header;
6413
+ var _require = __webpack_require__(/*! ./abc_parse_settings */ "./src/parse/abc_parse_settings.js"),
6414
+ legalAccents = _require.legalAccents,
6415
+ volumeDecorations = _require.volumeDecorations,
6416
+ dynamicDecorations = _require.dynamicDecorations,
6417
+ accentPseudonyms = _require.accentPseudonyms,
6418
+ accentDynamicPseudonyms = _require.accentDynamicPseudonyms,
6419
+ nonDecorations = _require.nonDecorations,
6420
+ durations = _require.durations,
6421
+ pitches = _require.pitches,
6422
+ rests = _require.rests,
6423
+ accMap = _require.accMap,
6424
+ tripletQ = _require.tripletQ;
6344
6425
  var MusicParser = function MusicParser(_tokenizer, _warn, _multilineVars, _tune, _tuneBuilder, _header) {
6345
6426
  tokenizer = _tokenizer;
6346
6427
  warn = _warn;
@@ -6408,7 +6489,6 @@ var MusicParser = function MusicParser(_tokenizer, _warn, _multilineVars, _tune,
6408
6489
  // double-quote: chord symbol
6409
6490
  // less-than, greater-than, slash: duration
6410
6491
  // back-tick, space, tab: space
6411
- var nonDecorations = "ABCDEFGabcdefgxyzZ[]|^_{"; // use this to prescreen so we don't have to look for a decoration at every note.
6412
6492
 
6413
6493
  var isInTie = function isInTie(multilineVars, overlayLevel, el) {
6414
6494
  if (multilineVars.inTie[overlayLevel] === undefined) return false;
@@ -6817,7 +6897,7 @@ MusicParser.prototype.parseMusic = function (line) {
6817
6897
  // Create a warning if this is not a displayable duration.
6818
6898
  // The first item on a line is a regular note value, each item after that represents a dot placed after the previous note.
6819
6899
  // Only durations less than a whole note are tested because whole note durations have some tricky rules.
6820
- 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];
6900
+
6821
6901
  if (el.duration < 1 && durations.indexOf(el.duration) === -1 && el.duration !== 0) {
6822
6902
  if (!el.rest || el.rest.type !== 'spacer') warn("Duration not representable: " + line.substring(startI, i), line, i);
6823
6903
  }
@@ -6965,11 +7045,6 @@ function durationOfMeasure(multilineVars) {
6965
7045
  if (!meter.value || meter.value.length === 0) return 1;
6966
7046
  return parseInt(meter.value[0].num, 10) / parseInt(meter.value[0].den, 10);
6967
7047
  }
6968
- 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"];
6969
- var volumeDecorations = ["p", "pp", "f", "ff", "mf", "mp", "ppp", "pppp", "fff", "ffff", "sfz"];
6970
- var dynamicDecorations = ["crescendo(", "crescendo)", "diminuendo(", "diminuendo)", "glissando(", "glissando)"];
6971
- var accentPseudonyms = [["<", "accent"], [">", "accent"], ["tr", "trill"], ["plus", "+"], ["emphasis", "accent"], ["^", "umarcato"], ["marcato", "umarcato"]];
6972
- var accentDynamicPseudonyms = [["<(", "crescendo("], ["<)", "crescendo)"], [">(", "diminuendo("], [">)", "diminuendo)"]];
6973
7048
  var letter_to_accent = function letter_to_accent(line, i) {
6974
7049
  var macro = multilineVars.macros[line[i]];
6975
7050
  if (macro !== undefined) {
@@ -7096,19 +7171,6 @@ var letter_to_bar = function letter_to_bar(line, curr_pos) {
7096
7171
  if (retRep.len === 0 || retRep.token[0] === '-') return [orig_bar_len, ret.token];
7097
7172
  return [ret.len + retRep.len, ret.token, retRep.token];
7098
7173
  };
7099
- var tripletQ = {
7100
- 2: 3,
7101
- 3: 2,
7102
- 4: 3,
7103
- 5: 2,
7104
- // TODO-PER: not handling 6/8 rhythm yet
7105
- 6: 2,
7106
- 7: 2,
7107
- // TODO-PER: not handling 6/8 rhythm yet
7108
- 8: 3,
7109
- 9: 2 // TODO-PER: not handling 6/8 rhythm yet
7110
- };
7111
-
7112
7174
  var letter_to_open_slurs_and_triplets = function letter_to_open_slurs_and_triplets(line, i) {
7113
7175
  // consume spaces, and look for all the open parens. If there is a number after the open paren,
7114
7176
  // that is a triplet. Otherwise that is a slur. Collect all the slurs and the first triplet.
@@ -7242,38 +7304,6 @@ var addEndBeam = function addEndBeam(el) {
7242
7304
  if (el.duration !== undefined && el.duration < 0.25) el.end_beam = true;
7243
7305
  return el;
7244
7306
  };
7245
- var pitches = {
7246
- A: 5,
7247
- B: 6,
7248
- C: 0,
7249
- D: 1,
7250
- E: 2,
7251
- F: 3,
7252
- G: 4,
7253
- a: 12,
7254
- b: 13,
7255
- c: 7,
7256
- d: 8,
7257
- e: 9,
7258
- f: 10,
7259
- g: 11
7260
- };
7261
- var rests = {
7262
- x: 'invisible',
7263
- X: 'invisible-multimeasure',
7264
- y: 'spacer',
7265
- z: 'rest',
7266
- Z: 'multimeasure'
7267
- };
7268
- var accMap = {
7269
- 'dblflat': '__',
7270
- 'flat': '_',
7271
- 'natural': '=',
7272
- 'sharp': '^',
7273
- 'dblsharp': '^^',
7274
- 'quarterflat': '_/',
7275
- 'quartersharp': '^/'
7276
- };
7277
7307
  var getCoreNote = function getCoreNote(line, index, el, canHaveBrokenRhythm) {
7278
7308
  //var el = { startChar: index };
7279
7309
  var isComplete = function isComplete(state) {
@@ -7561,6 +7591,67 @@ module.exports = MusicParser;
7561
7591
 
7562
7592
  /***/ }),
7563
7593
 
7594
+ /***/ "./src/parse/abc_parse_settings.js":
7595
+ /*!*****************************************!*\
7596
+ !*** ./src/parse/abc_parse_settings.js ***!
7597
+ \*****************************************/
7598
+ /***/ (function(module) {
7599
+
7600
+ 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'];
7601
+ module.exports.volumeDecorations = ['p', 'pp', 'f', 'ff', 'mf', 'mp', 'ppp', 'pppp', 'fff', 'ffff', 'sfz'];
7602
+ module.exports.dynamicDecorations = ['crescendo(', 'crescendo)', 'diminuendo(', 'diminuendo)', 'glissando(', 'glissando)', '~(', '~)'];
7603
+ module.exports.accentPseudonyms = [['<', 'accent'], ['>', 'accent'], ['tr', 'trill'], ['plus', '+'], ['emphasis', 'accent'], ['^', 'umarcato'], ['marcato', 'umarcato']];
7604
+ module.exports.accentDynamicPseudonyms = [['<(', 'crescendo('], ['<)', 'crescendo)'], ['>(', 'diminuendo('], ['>)', 'diminuendo)']];
7605
+ module.exports.nonDecorations = 'ABCDEFGabcdefgxyzZ[]|^_{'; // use this to prescreen so we don't have to look for a decoration at every note.
7606
+
7607
+ 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];
7608
+ module.exports.pitches = {
7609
+ A: 5,
7610
+ B: 6,
7611
+ C: 0,
7612
+ D: 1,
7613
+ E: 2,
7614
+ F: 3,
7615
+ G: 4,
7616
+ a: 12,
7617
+ b: 13,
7618
+ c: 7,
7619
+ d: 8,
7620
+ e: 9,
7621
+ f: 10,
7622
+ g: 11
7623
+ };
7624
+ module.exports.rests = {
7625
+ x: 'invisible',
7626
+ X: 'invisible-multimeasure',
7627
+ y: 'spacer',
7628
+ z: 'rest',
7629
+ Z: 'multimeasure'
7630
+ };
7631
+ module.exports.accMap = {
7632
+ dblflat: '__',
7633
+ flat: '_',
7634
+ natural: '=',
7635
+ sharp: '^',
7636
+ dblsharp: '^^',
7637
+ quarterflat: '_/',
7638
+ quartersharp: '^/'
7639
+ };
7640
+ module.exports.tripletQ = {
7641
+ 2: 3,
7642
+ 3: 2,
7643
+ 4: 3,
7644
+ 5: 2,
7645
+ // TODO-PER: not handling 6/8 rhythm yet
7646
+ 6: 2,
7647
+ 7: 2,
7648
+ // TODO-PER: not handling 6/8 rhythm yet
7649
+ 8: 3,
7650
+ 9: 2 // TODO-PER: not handling 6/8 rhythm yet
7651
+ };
7652
+
7653
+ /***/ }),
7654
+
7564
7655
  /***/ "./src/parse/abc_tokenizer.js":
7565
7656
  /*!************************************!*\
7566
7657
  !*** ./src/parse/abc_tokenizer.js ***!
@@ -8692,9 +8783,67 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
8692
8783
  index: index
8693
8784
  };
8694
8785
  };
8786
+
8787
+ //
8788
+ // MAE 10 Jan 2023 - For better handling of tunes that have tune numbers in front of them.
8789
+ //
8790
+ // Previous version would take:
8791
+ // 21. Woman of the House, The
8792
+ // and return:
8793
+ // The 21. Woman of the House
8794
+ //
8795
+ // This fix results in:
8796
+ // 21. The Woman of the House
8797
+ //
8798
+ // Also added additional checks and handlers for lower case ", the" and ", a" since I found several tune collections with those tune name constructs
8799
+ //
8800
+ // Find an optional title number at the start of a tune title
8801
+ function getTitleNumber(str) {
8802
+ var regex = /^(\d+)\./;
8803
+
8804
+ // Use the exec method to search for the pattern in the string
8805
+ var match = regex.exec(str);
8806
+
8807
+ // Check if a match is found
8808
+ if (match) {
8809
+ // The matched number is captured in the first group (index 1)
8810
+ var foundNumber = match[1];
8811
+ return foundNumber;
8812
+ } else {
8813
+ // Return null if no match is found
8814
+ return null;
8815
+ }
8816
+ }
8817
+ var thePatterns = [{
8818
+ match: /,\s*[Tt]he$/,
8819
+ replace: "The "
8820
+ }, {
8821
+ match: /,\s*[Aa]$/,
8822
+ replace: "A "
8823
+ }, {
8824
+ match: /,\s*[Aa]n$/,
8825
+ replace: "An "
8826
+ }];
8695
8827
  this.theReverser = function (str) {
8696
- if (parseCommon.endsWith(str, ", The")) return "The " + str.substring(0, str.length - 5);
8697
- if (parseCommon.endsWith(str, ", A")) return "A " + str.substring(0, str.length - 3);
8828
+ for (var i = 0; i < thePatterns.length; i++) {
8829
+ var thisPattern = thePatterns[i];
8830
+ var match = str.match(thisPattern.match);
8831
+ if (match) {
8832
+ var theTitleNumber = getTitleNumber(str);
8833
+ if (theTitleNumber) {
8834
+ //console.log("theReverser The titlenumber:"+theTitleNumber);
8835
+
8836
+ str = str.replace(theTitleNumber + ".", "");
8837
+ str = str.trim();
8838
+ }
8839
+ var len = match[0].length;
8840
+ var result = thisPattern.replace + str.substring(0, str.length - len);
8841
+ if (theTitleNumber) {
8842
+ result = theTitleNumber + ". " + result;
8843
+ }
8844
+ return result;
8845
+ }
8846
+ }
8698
8847
  return str;
8699
8848
  };
8700
8849
  this.stripComment = function (str) {
@@ -9128,6 +9277,7 @@ module.exports = transposeChordName;
9128
9277
 
9129
9278
  var parseKeyVoice = __webpack_require__(/*! ../parse/abc_parse_key_voice */ "./src/parse/abc_parse_key_voice.js");
9130
9279
  var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/abc_common.js");
9280
+ var parseDirective = __webpack_require__(/*! ./abc_parse_directive */ "./src/parse/abc_parse_directive.js");
9131
9281
  var TuneBuilder = function TuneBuilder(tune) {
9132
9282
  var self = this;
9133
9283
  this.setVisualTranspose = function (visualTranspose) {
@@ -9287,6 +9437,8 @@ var TuneBuilder = function TuneBuilder(tune) {
9287
9437
  this.cleanUp = function (barsperstaff, staffnonote, currSlur) {
9288
9438
  this.closeLine(); // Close the last line.
9289
9439
  delete tune.runningFonts;
9440
+ simplifyMetaText(tune);
9441
+ //addRichTextToAnnotationsAndLyrics(tune)
9290
9442
 
9291
9443
  // If the tempo was created with a string like "Allegro", then the duration of a beat needs to be set at the last moment, when it is most likely known.
9292
9444
  if (tune.metaText.tempo && tune.metaText.tempo.bpm && !tune.metaText.tempo.duration) tune.metaText.tempo.duration = [tune.getBeatLength()];
@@ -10021,7 +10173,15 @@ var TuneBuilder = function TuneBuilder(tune) {
10021
10173
  tune.metaText[key] = value;
10022
10174
  tune.metaTextInfo[key] = info;
10023
10175
  } else {
10024
- tune.metaText[key] += "\n" + value;
10176
+ if (typeof tune.metaText[key] === 'string' && typeof value === 'string') tune.metaText[key] += "\n" + value;else {
10177
+ if (tune.metaText[key] === 'string') tune.metaText[key] = [{
10178
+ text: tune.metaText[key]
10179
+ }];
10180
+ if (typeof value === 'string') value = [{
10181
+ text: value
10182
+ }];
10183
+ tune.metaText[key] = tune.metaText[key].concat(value);
10184
+ }
10025
10185
  tune.metaTextInfo[key].endChar = info.endChar;
10026
10186
  }
10027
10187
  };
@@ -10039,6 +10199,46 @@ var TuneBuilder = function TuneBuilder(tune) {
10039
10199
  tune.metaTextInfo[key] = info;
10040
10200
  };
10041
10201
  };
10202
+ function isArrayOfStrings(arr) {
10203
+ if (!arr) return false;
10204
+ if (typeof arr === "string") return false;
10205
+ var str = '';
10206
+ for (var i = 0; i < arr.length; i++) {
10207
+ if (typeof arr[i] !== 'string') return false;
10208
+ }
10209
+ return true;
10210
+ }
10211
+ function simplifyMetaText(tune) {
10212
+ if (isArrayOfStrings(tune.metaText.notes)) tune.metaText.notes = tune.metaText.notes.join("\n");
10213
+ if (isArrayOfStrings(tune.metaText.history)) tune.metaText.history = tune.metaText.history.join("\n");
10214
+ }
10215
+ function addRichTextToAnnotationsAndLyrics(tune) {
10216
+ var lines = tune.lines;
10217
+ for (var i = 0; i < lines.length; i++) {
10218
+ if (lines[i].staff !== undefined) {
10219
+ for (var s = 0; s < lines[i].staff.length; s++) {
10220
+ for (var v = 0; v < lines[i].staff[s].voices.length; v++) {
10221
+ var voice = lines[i].staff[s].voices[v];
10222
+ for (var n = 0; n < voice.length; n++) {
10223
+ var element = voice[n];
10224
+ if (element.chord) {
10225
+ for (var c = 0; c < element.chord.length; c++) {
10226
+ element.chord[c].name = parseDirective.parseFontChangeLine(element.chord[c].name);
10227
+ console.log(element.chord[c].name);
10228
+ }
10229
+ }
10230
+ if (element.lyric) {
10231
+ for (var l = 0; l < element.lyric.length; l++) {
10232
+ element.lyric[l].syllable = parseDirective.parseFontChangeLine(element.lyric[l].syllable);
10233
+ console.log(element.lyric[l].syllable);
10234
+ }
10235
+ }
10236
+ }
10237
+ }
10238
+ }
10239
+ }
10240
+ }
10241
+ }
10042
10242
  module.exports = TuneBuilder;
10043
10243
 
10044
10244
  /***/ }),
@@ -14188,6 +14388,9 @@ function CreateSynth() {
14188
14388
  self.getAudioBuffer = function () {
14189
14389
  return self.audioBuffers[0];
14190
14390
  };
14391
+ self.getIsRunning = function () {
14392
+ return self.isRunning;
14393
+ };
14191
14394
 
14192
14395
  /////////////// Private functions //////////////
14193
14396
 
@@ -14784,6 +14987,11 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
14784
14987
  if (len < 0) len = 0.005; // Have some small audible length no matter how short the note is.
14785
14988
  var offlineCtx = new OfflineAC(2, Math.floor((len + fadeTimeSec) * sampleRate), sampleRate);
14786
14989
  var noteName = pitchToNoteName[sound.pitch];
14990
+ if (!soundsCache[sound.instrument]) {
14991
+ // It shouldn't happen that the entire instrument cache wasn't created, but this has been seen in practice, so guard against it.
14992
+ if (debugCallback) debugCallback('placeNote skipped (instrument empty): ' + sound.instrument + ':' + noteName);
14993
+ return Promise.resolve();
14994
+ }
14787
14995
  var noteBufferPromise = soundsCache[sound.instrument][noteName];
14788
14996
  if (!noteBufferPromise) {
14789
14997
  // if the note isn't present then just skip it - it will leave a blank spot in the audio.
@@ -14879,7 +15087,7 @@ module.exports = placeNote;
14879
15087
  var SynthSequence = __webpack_require__(/*! ./synth-sequence */ "./src/synth/synth-sequence.js");
14880
15088
  var CreateSynth = __webpack_require__(/*! ./create-synth */ "./src/synth/create-synth.js");
14881
15089
  var activeAudioContext = __webpack_require__(/*! ./active-audio-context */ "./src/synth/active-audio-context.js");
14882
- function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure) {
15090
+ function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure, soundFontUrl, debugCallback) {
14883
15091
  var sequence = new SynthSequence();
14884
15092
  for (var i = 0; i < midiPitches.length; i++) {
14885
15093
  var note = midiPitches[i];
@@ -14896,17 +15104,21 @@ function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure) {
14896
15104
  var ac = activeAudioContext();
14897
15105
  if (ac.state === "suspended") {
14898
15106
  return ac.resume().then(function () {
14899
- return doPlay(sequence, millisecondsPerMeasure);
15107
+ return doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback);
14900
15108
  });
14901
15109
  } else {
14902
- return doPlay(sequence, millisecondsPerMeasure);
15110
+ return doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback);
14903
15111
  }
14904
15112
  }
14905
- function doPlay(sequence, millisecondsPerMeasure) {
15113
+ function doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback) {
14906
15114
  var buffer = new CreateSynth();
14907
15115
  return buffer.init({
14908
15116
  sequence: sequence,
14909
- millisecondsPerMeasure: millisecondsPerMeasure
15117
+ millisecondsPerMeasure: millisecondsPerMeasure,
15118
+ options: {
15119
+ soundFontUrl: soundFontUrl
15120
+ },
15121
+ debugCallback: debugCallback
14910
15122
  }).then(function () {
14911
15123
  return buffer.prime();
14912
15124
  }).then(function () {
@@ -15008,6 +15220,8 @@ function SynthController() {
15008
15220
  self.isLoading = false;
15009
15221
  self.load = function (selector, cursorControl, visualOptions) {
15010
15222
  if (!visualOptions) visualOptions = {};
15223
+ if (visualOptions.displayPlay === undefined) visualOptions.displayPlay = true;
15224
+ if (visualOptions.displayProgress === undefined) visualOptions.displayProgress = true;
15011
15225
  self.control = new CreateSynthControl(selector, {
15012
15226
  loopHandler: visualOptions.displayLoop ? self.toggleLoop : undefined,
15013
15227
  restartHandler: visualOptions.displayRestart ? self.restart : undefined,
@@ -15025,7 +15239,7 @@ function SynthController() {
15025
15239
  self.setTune = function (visualObj, userAction, audioParams) {
15026
15240
  self.visualObj = visualObj;
15027
15241
  self.disable(false);
15028
- self.options = audioParams;
15242
+ self.options = audioParams ? audioParams : {};
15029
15243
  if (self.control) {
15030
15244
  self.pause();
15031
15245
  self.setProgress(0, 1);
@@ -15157,7 +15371,7 @@ function SynthController() {
15157
15371
  };
15158
15372
  self._randomAccess = function (ev) {
15159
15373
  var background = ev.target.classList.contains('abcjs-midi-progress-indicator') ? ev.target.parentNode : ev.target;
15160
- var percent = (ev.x - background.offsetLeft) / background.offsetWidth;
15374
+ var percent = (ev.x - background.getBoundingClientRect().left) / background.offsetWidth;
15161
15375
  if (percent < 0) percent = 0;
15162
15376
  if (percent > 1) percent = 1;
15163
15377
  self.seek(percent);
@@ -15299,87 +15513,6 @@ module.exports = SynthSequence;
15299
15513
 
15300
15514
  /***/ }),
15301
15515
 
15302
- /***/ "./src/tablatures/instruments/guitar/guitar-patterns.js":
15303
- /*!**************************************************************!*\
15304
- !*** ./src/tablatures/instruments/guitar/guitar-patterns.js ***!
15305
- \**************************************************************/
15306
- /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
15307
-
15308
- var StringPatterns = __webpack_require__(/*! ../string-patterns */ "./src/tablatures/instruments/string-patterns.js");
15309
- function GuitarPatterns(plugin) {
15310
- this.tuning = plugin._super.params.tuning;
15311
- if (!this.tuning) {
15312
- this.tuning = ['E,', 'A,', 'D', 'G', 'B', 'e'];
15313
- }
15314
- plugin.tuning = this.tuning;
15315
- this.strings = new StringPatterns(plugin);
15316
- }
15317
- GuitarPatterns.prototype.notesToNumber = function (notes, graces) {
15318
- var converter = this.strings;
15319
- return converter.notesToNumber(notes, graces);
15320
- };
15321
- GuitarPatterns.prototype.stringToPitch = function (stringNumber) {
15322
- var converter = this.strings;
15323
- return converter.stringToPitch(stringNumber);
15324
- };
15325
- module.exports = GuitarPatterns;
15326
-
15327
- /***/ }),
15328
-
15329
- /***/ "./src/tablatures/instruments/guitar/tab-guitar.js":
15330
- /*!*********************************************************!*\
15331
- !*** ./src/tablatures/instruments/guitar/tab-guitar.js ***!
15332
- \*********************************************************/
15333
- /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
15334
-
15335
- /*
15336
- Emit tab for Guitar staff
15337
- */
15338
- var StringTablature = __webpack_require__(/*! ../string-tablature */ "./src/tablatures/instruments/string-tablature.js");
15339
- var TabCommon = __webpack_require__(/*! ../../tab-common */ "./src/tablatures/tab-common.js");
15340
- var TabRenderer = __webpack_require__(/*! ../../tab-renderer */ "./src/tablatures/tab-renderer.js");
15341
- var GuitarPatterns = __webpack_require__(/*! ./guitar-patterns */ "./src/tablatures/instruments/guitar/guitar-patterns.js");
15342
-
15343
- /**
15344
- * upon init mainly store provided instances for later usage
15345
- * @param {*} abcTune the parsed tune AST tree
15346
- * @param {*} tuneNumber the parsed tune AST tree
15347
- * @param {*} params complementary args provided to Tablature Plugin
15348
- */
15349
- Plugin.prototype.init = function (abcTune, tuneNumber, params) {
15350
- var _super = new TabCommon(abcTune, tuneNumber, params);
15351
- this._super = _super;
15352
- this.abcTune = abcTune;
15353
- this.linePitch = 3;
15354
- this.nbLines = 6;
15355
- this.isTabBig = true;
15356
- this.capo = params.capo;
15357
- this.transpose = params.visualTranspose;
15358
- this.tablature = new StringTablature(this.nbLines, this.linePitch);
15359
- var semantics = new GuitarPatterns(this);
15360
- this.semantics = semantics;
15361
- };
15362
- Plugin.prototype.render = function (renderer, line, staffIndex) {
15363
- if (this._super.inError) return;
15364
- if (this.tablature.bypass(line)) return;
15365
- var rndrer = new TabRenderer(this, renderer, line, staffIndex);
15366
- rndrer.doLayout();
15367
- };
15368
- function Plugin() {}
15369
-
15370
- //
15371
- // Tablature plugin definition
15372
- //
15373
- var AbcGuitarTab = function AbcGuitarTab() {
15374
- return {
15375
- name: 'GuitarTab',
15376
- tablature: Plugin
15377
- };
15378
- };
15379
- module.exports = AbcGuitarTab;
15380
-
15381
- /***/ }),
15382
-
15383
15516
  /***/ "./src/tablatures/instruments/string-patterns.js":
15384
15517
  /*!*******************************************************!*\
15385
15518
  !*** ./src/tablatures/instruments/string-patterns.js ***!
@@ -15620,6 +15753,17 @@ StringPatterns.prototype.tabInfos = function (plugin) {
15620
15753
  return '';
15621
15754
  };
15622
15755
 
15756
+ // MAE 27 Nov 2023
15757
+ StringPatterns.prototype.suppress = function (plugin) {
15758
+ var _super = plugin._super;
15759
+ var suppress = _super.params.suppress;
15760
+ if (suppress) {
15761
+ return true;
15762
+ }
15763
+ return false;
15764
+ };
15765
+ // MAE 27 Nov 2023 End
15766
+
15623
15767
  /**
15624
15768
  * Common patterns for all string instruments
15625
15769
  * @param {} plugin
@@ -15994,16 +16138,43 @@ module.exports = TabNotes;
15994
16138
 
15995
16139
  /***/ }),
15996
16140
 
15997
- /***/ "./src/tablatures/instruments/violin/tab-violin.js":
15998
- /*!*********************************************************!*\
15999
- !*** ./src/tablatures/instruments/violin/tab-violin.js ***!
16000
- \*********************************************************/
16141
+ /***/ "./src/tablatures/instruments/tab-string-patterns.js":
16142
+ /*!***********************************************************!*\
16143
+ !*** ./src/tablatures/instruments/tab-string-patterns.js ***!
16144
+ \***********************************************************/
16001
16145
  /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
16002
16146
 
16003
- var StringTablature = __webpack_require__(/*! ../string-tablature */ "./src/tablatures/instruments/string-tablature.js");
16004
- var TabCommon = __webpack_require__(/*! ../../tab-common */ "./src/tablatures/tab-common.js");
16005
- var TabRenderer = __webpack_require__(/*! ../../tab-renderer */ "./src/tablatures/tab-renderer.js");
16006
- var ViolinPatterns = __webpack_require__(/*! ./violin-patterns */ "./src/tablatures/instruments/violin/violin-patterns.js");
16147
+ var StringPatterns = __webpack_require__(/*! ./string-patterns */ "./src/tablatures/instruments/string-patterns.js");
16148
+ function TabStringPatterns(plugin, defaultTuning) {
16149
+ this.tuning = plugin._super.params.tuning;
16150
+ if (!this.tuning) {
16151
+ this.tuning = defaultTuning;
16152
+ }
16153
+ plugin.tuning = this.tuning;
16154
+ this.strings = new StringPatterns(plugin);
16155
+ }
16156
+ TabStringPatterns.prototype.notesToNumber = function (notes, graces) {
16157
+ var converter = this.strings;
16158
+ return converter.notesToNumber(notes, graces);
16159
+ };
16160
+ TabStringPatterns.prototype.stringToPitch = function (stringNumber) {
16161
+ var converter = this.strings;
16162
+ return converter.stringToPitch(stringNumber);
16163
+ };
16164
+ module.exports = TabStringPatterns;
16165
+
16166
+ /***/ }),
16167
+
16168
+ /***/ "./src/tablatures/instruments/tab-string.js":
16169
+ /*!**************************************************!*\
16170
+ !*** ./src/tablatures/instruments/tab-string.js ***!
16171
+ \**************************************************/
16172
+ /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
16173
+
16174
+ var StringTablature = __webpack_require__(/*! ./string-tablature */ "./src/tablatures/instruments/string-tablature.js");
16175
+ var TabCommon = __webpack_require__(/*! ../tab-common */ "./src/tablatures/tab-common.js");
16176
+ var TabRenderer = __webpack_require__(/*! ../tab-renderer */ "./src/tablatures/tab-renderer.js");
16177
+ var TabStringPatterns = __webpack_require__(/*! ./tab-string-patterns */ "./src/tablatures/instruments/tab-string-patterns.js");
16007
16178
 
16008
16179
  /**
16009
16180
  * upon init mainly store provided instances for later usage
@@ -16011,17 +16182,19 @@ var ViolinPatterns = __webpack_require__(/*! ./violin-patterns */ "./src/tablatu
16011
16182
  * @param {*} tuneNumber the parsed tune AST tree
16012
16183
  * @param {*} params complementary args provided to Tablature Plugin
16013
16184
  */
16014
- Plugin.prototype.init = function (abcTune, tuneNumber, params) {
16185
+ Plugin.prototype.init = function (abcTune, tuneNumber, params, staffNumber, tabSettings) {
16015
16186
  var _super = new TabCommon(abcTune, tuneNumber, params);
16016
16187
  this.abcTune = abcTune;
16017
16188
  this._super = _super;
16018
16189
  this.linePitch = 3;
16019
- this.nbLines = 4;
16020
- this.isTabBig = false;
16190
+ this.nbLines = tabSettings.defaultTuning.length;
16191
+ this.isTabBig = tabSettings.isTabBig;
16192
+ this.tabSymbolOffset = tabSettings.tabSymbolOffset;
16021
16193
  this.capo = params.capo;
16022
16194
  this.transpose = params.visualTranspose;
16195
+ this.hideTabSymbol = params.hideTabSymbol;
16023
16196
  this.tablature = new StringTablature(this.nbLines, this.linePitch);
16024
- var semantics = new ViolinPatterns(this);
16197
+ var semantics = new TabStringPatterns(this, tabSettings.defaultTuning);
16025
16198
  this.semantics = semantics;
16026
16199
  };
16027
16200
  Plugin.prototype.render = function (renderer, line, staffIndex) {
@@ -16035,40 +16208,13 @@ function Plugin() {}
16035
16208
  //
16036
16209
  // Tablature plugin definition
16037
16210
  //
16038
- var AbcViolinTab = function AbcViolinTab() {
16211
+ var AbcStringTab = function AbcStringTab() {
16039
16212
  return {
16040
- name: 'ViolinTab',
16213
+ name: 'StringTab',
16041
16214
  tablature: Plugin
16042
16215
  };
16043
16216
  };
16044
- module.exports = AbcViolinTab;
16045
-
16046
- /***/ }),
16047
-
16048
- /***/ "./src/tablatures/instruments/violin/violin-patterns.js":
16049
- /*!**************************************************************!*\
16050
- !*** ./src/tablatures/instruments/violin/violin-patterns.js ***!
16051
- \**************************************************************/
16052
- /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
16053
-
16054
- var StringPatterns = __webpack_require__(/*! ../string-patterns */ "./src/tablatures/instruments/string-patterns.js");
16055
- function ViolinPatterns(plugin) {
16056
- this.tuning = plugin._super.params.tuning;
16057
- if (!this.tuning) {
16058
- this.tuning = ['G,', 'D', 'A', 'e'];
16059
- }
16060
- plugin.tuning = this.tuning;
16061
- this.strings = new StringPatterns(plugin);
16062
- }
16063
- ViolinPatterns.prototype.notesToNumber = function (notes, graces) {
16064
- var converter = this.strings;
16065
- return converter.notesToNumber(notes, graces);
16066
- };
16067
- ViolinPatterns.prototype.stringToPitch = function (stringNumber) {
16068
- var converter = this.strings;
16069
- return converter.stringToPitch(stringNumber);
16070
- };
16071
- module.exports = ViolinPatterns;
16217
+ module.exports = AbcStringTab;
16072
16218
 
16073
16219
  /***/ }),
16074
16220
 
@@ -16137,13 +16283,20 @@ function buildTabAbsolute(plugin, absX, relX) {
16137
16283
  icon: tabIcon,
16138
16284
  Ypos: tabYPos
16139
16285
  };
16140
- var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0);
16141
- tabAbsolute.x = absX;
16142
- var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab");
16143
- tabRelative.x = relX;
16144
- tabAbsolute.children.push(tabRelative);
16145
- if (tabAbsolute.abcelem.el_type == 'tab') {
16146
- tabRelative.pitch = tabYPos;
16286
+
16287
+ // Offset the TAB symbol position if specified in the tab description
16288
+ tabYPos += plugin.tabSymbolOffset;
16289
+
16290
+ // For tablature like whistle tab where you want the TAB symbol hidden
16291
+ if (!plugin.hideTabSymbol) {
16292
+ var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0);
16293
+ tabAbsolute.x = absX;
16294
+ var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab");
16295
+ tabRelative.x = relX;
16296
+ tabAbsolute.children.push(tabRelative);
16297
+ if (tabAbsolute.abcelem.el_type == 'tab') {
16298
+ tabRelative.pitch = tabYPos;
16299
+ }
16147
16300
  }
16148
16301
  return tabAbsolute;
16149
16302
  }
@@ -16252,10 +16405,10 @@ function buildGraceRelativesForRest(plugin, abs, absChild, graceNotes, tabVoice)
16252
16405
  * Build tab absolutes by scanning current staff line absolute array
16253
16406
  * @param {*} staffAbsolute
16254
16407
  */
16255
- TabAbsoluteElements.prototype.build = function (plugin, staffAbsolute, tabVoice, voiceIndex, staffIndex, keySig) {
16408
+ TabAbsoluteElements.prototype.build = function (plugin, staffAbsolute, tabVoice, voiceIndex, staffIndex, keySig, tabVoiceIndex) {
16256
16409
  var staffSize = getInitialStaffSize(staffAbsolute);
16257
16410
  var source = staffAbsolute[staffIndex + voiceIndex];
16258
- var dest = staffAbsolute[staffSize + staffIndex + voiceIndex];
16411
+ var dest = staffAbsolute[tabVoiceIndex];
16259
16412
  var tabPos = null;
16260
16413
  var defNote = null;
16261
16414
  if (source.children[0].abcelem.el_type != 'clef') {
@@ -16460,12 +16613,23 @@ function buildTabName(self, dest) {
16460
16613
  var controller = self.renderer.controller;
16461
16614
  var textSize = controller.getTextSize;
16462
16615
  var tabName = stringSemantics.tabInfos(self.plugin);
16463
- var size = textSize.calc(tabName, 'tablabelfont', 'text instrumentname');
16464
- dest.tabNameInfos = {
16465
- textSize: size,
16466
- name: tabName
16467
- };
16468
- return size.height;
16616
+ var suppress = stringSemantics.suppress(self.plugin);
16617
+ var doDraw = true;
16618
+ if (suppress) {
16619
+ doDraw = false;
16620
+ }
16621
+ if (doDraw) {
16622
+ var size = textSize.calc(tabName, 'tablabelfont', 'text instrumentname');
16623
+ dest.tabNameInfos = {
16624
+ textSize: {
16625
+ height: size.height,
16626
+ width: size.width
16627
+ },
16628
+ name: tabName
16629
+ };
16630
+ return size.height;
16631
+ }
16632
+ return 0;
16469
16633
  }
16470
16634
 
16471
16635
  /**
@@ -16637,13 +16801,16 @@ TabRenderer.prototype.doLayout = function () {
16637
16801
  if (ii > 0) tabVoice.duplicate = true;
16638
16802
  var nameHeight = buildTabName(this, tabVoice) / spacing.STEP;
16639
16803
  nameHeight = Math.max(nameHeight, 1); // If there is no label for the tab line, then there needs to be a little padding
16640
- staffGroup.staffs[this.staffIndex].top += nameHeight;
16641
- staffGroup.height += nameHeight * spacing.STEP;
16804
+ // This was pushing down the top staff by the tab label height
16805
+ //staffGroup.staffs[this.staffIndex].top += nameHeight;
16806
+ staffGroup.staffs[this.staffIndex].top += 1;
16807
+ staffGroup.height += nameHeight;
16642
16808
  tabVoice.staff = staffGroupInfos;
16809
+ var tabVoiceIndex = voices.length;
16643
16810
  voices.splice(voices.length, 0, tabVoice);
16644
16811
  var keySig = checkVoiceKeySig(voices, ii + this.staffIndex);
16645
16812
  this.tabStaff.voices[ii] = [];
16646
- this.absolutes.build(this.plugin, voices, this.tabStaff.voices[ii], ii, this.staffIndex, keySig);
16813
+ this.absolutes.build(this.plugin, voices, this.tabStaff.voices[ii], ii, this.staffIndex, keySig, tabVoiceIndex);
16647
16814
  }
16648
16815
  linkStaffAndTabs(staffGroup.staffs); // crossreference tabs and staff
16649
16816
  };
@@ -16783,6 +16950,7 @@ var AbstractEngraver = function AbstractEngraver(getTextSize, tuneNumber, option
16783
16950
  this.percmap = options.percmap;
16784
16951
  this.initialClef = options.initialClef;
16785
16952
  this.jazzchords = !!options.jazzchords;
16953
+ this.accentAbove = !!options.accentAbove;
16786
16954
  this.germanAlphabet = !!options.germanAlphabet;
16787
16955
  this.reset();
16788
16956
  };
@@ -17487,6 +17655,9 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
17487
17655
  if (noteHead && noteHead.c === 'noteheads.slash.quarter') {
17488
17656
  if (dir === 'down') p2 -= 1;else p1 += 1;
17489
17657
  }
17658
+ if (noteHead && noteHead.c === 'noteheads.triangle.quarter') {
17659
+ if (dir === 'down') p2 -= 0.7;else p1 -= 1.2;
17660
+ }
17490
17661
  abselem.addRight(new RelativeElement(null, dx, 0, p1, {
17491
17662
  "type": "stem",
17492
17663
  "pitch2": p2,
@@ -17576,7 +17747,7 @@ AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaf
17576
17747
  roomtaken += this.addGraceNotes(elem, voice, abselem, notehead, this.stemHeight * this.voiceScale, this.isBagpipes, roomtaken);
17577
17748
  }
17578
17749
  if (elem.decoration) {
17579
- this.decoration.createDecoration(voice, elem.decoration, abselem.top, notehead ? notehead.w : 0, abselem, roomtaken, dir, abselem.bottom, elem.positioning, this.hasVocals);
17750
+ this.decoration.createDecoration(voice, elem.decoration, abselem.top, notehead ? notehead.w : 0, abselem, roomtaken, dir, abselem.bottom, elem.positioning, this.hasVocals, this.accentAbove);
17580
17751
  }
17581
17752
  if (elem.barNumber) {
17582
17753
  abselem.addFixed(new RelativeElement(elem.barNumber, -10, 0, 0, {
@@ -17748,7 +17919,7 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
17748
17919
  abselem.addRight(anchor);
17749
17920
  }
17750
17921
  if (elem.decoration) {
17751
- this.decoration.createDecoration(voice, elem.decoration, 12, thick ? 3 : 1, abselem, 0, "down", 2, elem.positioning, this.hasVocals);
17922
+ this.decoration.createDecoration(voice, elem.decoration, 12, thick ? 3 : 1, abselem, 0, "down", 2, elem.positioning, this.hasVocals, this.accentAbove);
17752
17923
  }
17753
17924
  if (thick) {
17754
17925
  dx += 4; //3 hardcoded;
@@ -17818,100 +17989,121 @@ var addChord = function addChord(getTextSize, abselem, elem, roomTaken, roomTake
17818
17989
  for (var i = 0; i < elem.chord.length; i++) {
17819
17990
  var pos = elem.chord[i].position;
17820
17991
  var rel_position = elem.chord[i].rel_position;
17821
- var chords = elem.chord[i].name.split("\n");
17822
- for (var j = chords.length - 1; j >= 0; j--) {
17823
- // parse these in opposite order because we place them from bottom to top.
17824
- var chord = chords[j];
17825
- var x = 0;
17826
- var y;
17827
- var font;
17828
- var klass;
17829
- if (pos === "left" || pos === "right" || pos === "below" || pos === "above" || !!rel_position) {
17830
- font = 'annotationfont';
17831
- klass = "annotation";
17832
- } else {
17833
- font = 'gchordfont';
17834
- klass = "chord";
17835
- chord = translateChord(chord, jazzchords, germanAlphabet);
17836
- }
17837
- var attr = getTextSize.attr(font, klass);
17838
- var dim = getTextSize.calc(chord, font, klass);
17839
- var chordWidth = dim.width;
17840
- var chordHeight = dim.height / spacing.STEP;
17841
- switch (pos) {
17842
- case "left":
17843
- roomTaken += chordWidth + 7;
17844
- x = -roomTaken; // TODO-PER: This is just a guess from trial and error
17845
- y = elem.averagepitch;
17846
- abselem.addExtra(new RelativeElement(chord, x, chordWidth + 4, y, {
17847
- type: "text",
17848
- height: chordHeight,
17849
- dim: attr,
17850
- position: "left"
17851
- }));
17852
- break;
17853
- case "right":
17854
- roomTakenRight += 4;
17855
- x = roomTakenRight; // TODO-PER: This is just a guess from trial and error
17856
- y = elem.averagepitch;
17857
- abselem.addRight(new RelativeElement(chord, x, chordWidth + 4, y, {
17858
- type: "text",
17859
- height: chordHeight,
17860
- dim: attr,
17861
- position: "right"
17862
- }));
17863
- break;
17864
- case "below":
17865
- // setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
17866
- abselem.addRight(new RelativeElement(chord, 0, 0, undefined, {
17992
+ var isAnnotation = pos === "left" || pos === "right" || pos === "below" || pos === "above" || !!rel_position;
17993
+ var font;
17994
+ var klass;
17995
+ if (isAnnotation) {
17996
+ font = 'annotationfont';
17997
+ klass = "abcjs-annotation";
17998
+ } else {
17999
+ font = 'gchordfont';
18000
+ klass = "abcjs-chord";
18001
+ }
18002
+ var attr = getTextSize.attr(font, klass);
18003
+ var name = elem.chord[i].name;
18004
+ var ret;
18005
+ //console.log("chord",name)
18006
+ if (typeof name === "string") {
18007
+ ret = chordString(name, pos, rel_position, isAnnotation, font, klass, attr, getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet);
18008
+ roomTaken = ret.roomTaken;
18009
+ roomTakenRight = ret.roomTakenRight;
18010
+ } else {
18011
+ for (var j = 0; j < name.length; j++) {
18012
+ ret = chordString(name[j].text, pos, rel_position, isAnnotation, font, klass, attr, getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet);
18013
+ roomTaken = ret.roomTaken;
18014
+ roomTakenRight = ret.roomTakenRight;
18015
+ }
18016
+ }
18017
+ }
18018
+ return {
18019
+ roomTaken: roomTaken,
18020
+ roomTakenRight: roomTakenRight
18021
+ };
18022
+ };
18023
+ function chordString(chordString, pos, rel_position, isAnnotation, font, klass, attr, getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet) {
18024
+ var chords = chordString.split("\n");
18025
+ for (var j = chords.length - 1; j >= 0; j--) {
18026
+ // parse these in opposite order because we place them from bottom to top.
18027
+ var chord = chords[j];
18028
+ var x = 0;
18029
+ var y;
18030
+ if (!isAnnotation) chord = translateChord(chord, jazzchords, germanAlphabet);
18031
+ var dim = getTextSize.calc(chord, font, klass);
18032
+ var chordWidth = dim.width;
18033
+ var chordHeight = dim.height / spacing.STEP;
18034
+ switch (pos) {
18035
+ case "left":
18036
+ roomTaken += chordWidth + 7;
18037
+ x = -roomTaken; // TODO-PER: This is just a guess from trial and error
18038
+ y = elem.averagepitch;
18039
+ abselem.addExtra(new RelativeElement(chord, x, chordWidth + 4, y, {
18040
+ type: "text",
18041
+ height: chordHeight,
18042
+ dim: attr,
18043
+ position: "left"
18044
+ }));
18045
+ break;
18046
+ case "right":
18047
+ roomTakenRight += 4;
18048
+ x = roomTakenRight; // TODO-PER: This is just a guess from trial and error
18049
+ y = elem.averagepitch;
18050
+ abselem.addRight(new RelativeElement(chord, x, chordWidth + 4, y, {
18051
+ type: "text",
18052
+ height: chordHeight,
18053
+ dim: attr,
18054
+ position: "right"
18055
+ }));
18056
+ break;
18057
+ case "below":
18058
+ // setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
18059
+ abselem.addRight(new RelativeElement(chord, 0, 0, undefined, {
18060
+ type: "text",
18061
+ position: "below",
18062
+ height: chordHeight,
18063
+ dim: attr,
18064
+ realWidth: chordWidth
18065
+ }));
18066
+ break;
18067
+ case "above":
18068
+ // setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
18069
+ abselem.addRight(new RelativeElement(chord, 0, 0, undefined, {
18070
+ type: "text",
18071
+ position: "above",
18072
+ height: chordHeight,
18073
+ dim: attr,
18074
+ realWidth: chordWidth
18075
+ }));
18076
+ break;
18077
+ default:
18078
+ if (rel_position) {
18079
+ var relPositionY = rel_position.y + 3 * spacing.STEP; // TODO-PER: this is a fudge factor to make it line up with abcm2ps
18080
+ abselem.addRight(new RelativeElement(chord, x + rel_position.x, 0, elem.minpitch + relPositionY / spacing.STEP, {
18081
+ position: "relative",
17867
18082
  type: "text",
17868
- position: "below",
17869
18083
  height: chordHeight,
17870
- dim: attr,
17871
- realWidth: chordWidth
18084
+ dim: attr
17872
18085
  }));
17873
- break;
17874
- case "above":
18086
+ } else {
17875
18087
  // setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
17876
- abselem.addRight(new RelativeElement(chord, 0, 0, undefined, {
17877
- type: "text",
17878
- position: "above",
17879
- height: chordHeight,
17880
- dim: attr,
17881
- realWidth: chordWidth
17882
- }));
17883
- break;
17884
- default:
17885
- if (rel_position) {
17886
- var relPositionY = rel_position.y + 3 * spacing.STEP; // TODO-PER: this is a fudge factor to make it line up with abcm2ps
17887
- abselem.addRight(new RelativeElement(chord, x + rel_position.x, 0, elem.minpitch + relPositionY / spacing.STEP, {
17888
- position: "relative",
17889
- type: "text",
18088
+ var pos2 = 'above';
18089
+ if (elem.positioning && elem.positioning.chordPosition) pos2 = elem.positioning.chordPosition;
18090
+ if (pos2 !== 'hidden') {
18091
+ abselem.addCentered(new RelativeElement(chord, noteheadWidth / 2, chordWidth, undefined, {
18092
+ type: "chord",
18093
+ position: pos2,
17890
18094
  height: chordHeight,
17891
- dim: attr
18095
+ dim: attr,
18096
+ realWidth: chordWidth
17892
18097
  }));
17893
- } else {
17894
- // setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
17895
- var pos2 = 'above';
17896
- if (elem.positioning && elem.positioning.chordPosition) pos2 = elem.positioning.chordPosition;
17897
- if (pos2 !== 'hidden') {
17898
- abselem.addCentered(new RelativeElement(chord, noteheadWidth / 2, chordWidth, undefined, {
17899
- type: "chord",
17900
- position: pos2,
17901
- height: chordHeight,
17902
- dim: attr,
17903
- realWidth: chordWidth
17904
- }));
17905
- }
17906
18098
  }
17907
- }
18099
+ }
17908
18100
  }
17909
18101
  }
17910
18102
  return {
17911
18103
  roomTaken: roomTaken,
17912
18104
  roomTakenRight: roomTakenRight
17913
18105
  };
17914
- };
18106
+ }
17915
18107
  module.exports = addChord;
17916
18108
 
17917
18109
  /***/ }),
@@ -17940,10 +18132,11 @@ function addTextIf(rows, params, getTextSize) {
17940
18132
  font: params.font,
17941
18133
  anchor: params.anchor,
17942
18134
  startChar: params.info.startChar,
17943
- endChar: params.info.endChar
18135
+ endChar: params.info.endChar,
18136
+ 'dominant-baseline': params['dominant-baseline']
17944
18137
  };
17945
18138
  if (params.absElemType) attr.absElemType = params.absElemType;
17946
- if (!params.inGroup) attr.klass = params.klass;
18139
+ if (!params.inGroup && params.klass) attr.klass = params.klass;
17947
18140
  if (params.name) attr.name = params.name;
17948
18141
  rows.push(attr);
17949
18142
  // If there are blank lines they won't be counted by getTextSize, so just get the height of one line and multiply
@@ -18399,10 +18592,10 @@ var Decoration = function Decoration() {
18399
18592
  this.minTop = 12; // TODO-PER: this is assuming a 5-line staff. Pass that info in.
18400
18593
  this.minBottom = 0;
18401
18594
  };
18402
- var closeDecoration = function closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch) {
18595
+ var closeDecoration = function closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, accentAbove) {
18403
18596
  var yPos;
18404
18597
  for (var i = 0; i < decoration.length; i++) {
18405
- if (decoration[i] === "staccato" || decoration[i] === "tenuto" || decoration[i] === "accent") {
18598
+ if (decoration[i] === "staccato" || decoration[i] === "tenuto" || decoration[i] === "accent" && !accentAbove) {
18406
18599
  var symbol = "scripts." + decoration[i];
18407
18600
  if (decoration[i] === "accent") symbol = "scripts.sforzato";
18408
18601
  if (yPos === undefined) yPos = dir === "down" ? pitch + 2 : minPitch - 2;else yPos = dir === "down" ? yPos + 2 : yPos - 2;
@@ -18511,7 +18704,7 @@ var compoundDecoration = function compoundDecoration(decoration, pitch, width, a
18511
18704
  }
18512
18705
  }
18513
18706
  };
18514
- var stackedDecoration = function stackedDecoration(decoration, width, abselem, yPos, positioning, minTop, minBottom) {
18707
+ var stackedDecoration = function stackedDecoration(decoration, width, abselem, yPos, positioning, minTop, minBottom, accentAbove) {
18515
18708
  function incrementPlacement(placement, height) {
18516
18709
  if (placement === 'above') yPos.above += height;else yPos.below -= height;
18517
18710
  }
@@ -18650,6 +18843,12 @@ var stackedDecoration = function stackedDecoration(decoration, width, abselem, y
18650
18843
  case "mark":
18651
18844
  abselem.klass = "mark";
18652
18845
  break;
18846
+ case "accent":
18847
+ if (accentAbove) {
18848
+ symbolDecoration("scripts.sforzato", positioning);
18849
+ hasOne = true;
18850
+ }
18851
+ break;
18653
18852
  }
18654
18853
  }
18655
18854
  return hasOne;
@@ -18699,10 +18898,12 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
18699
18898
  };
18700
18899
  this.startCrescendoX = undefined;
18701
18900
  break;
18901
+ case '~(':
18702
18902
  case "glissando(":
18703
18903
  this.startGlissandoX = abselem;
18704
18904
  glissando = undefined;
18705
18905
  break;
18906
+ case '~)':
18706
18907
  case "glissando)":
18707
18908
  glissando = {
18708
18909
  start: this.startGlissandoX,
@@ -18722,7 +18923,7 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
18722
18923
  voice.addOther(new GlissandoElem(glissando.start, glissando.stop));
18723
18924
  }
18724
18925
  };
18725
- Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals) {
18926
+ Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals, accentAbove) {
18726
18927
  if (!positioning) positioning = {
18727
18928
  ornamentPosition: 'above',
18728
18929
  volumePosition: hasVocals ? 'above' : 'below',
@@ -18734,14 +18935,14 @@ Decoration.prototype.createDecoration = function (voice, decoration, pitch, widt
18734
18935
  compoundDecoration(decoration, pitch, width, abselem, dir);
18735
18936
 
18736
18937
  // treat staccato, accent, and tenuto first (may need to shift other markers)
18737
- var yPos = closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch);
18938
+ var yPos = closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, accentAbove);
18738
18939
  // yPos is an object containing 'above' and 'below'. That is the placement of the next symbol on either side.
18739
18940
 
18740
18941
  yPos.above = Math.max(yPos.above, this.minTop);
18741
- var hasOne = stackedDecoration(decoration, width, abselem, yPos, positioning.ornamentPosition, this.minTop, this.minBottom);
18742
- if (hasOne) {
18743
- // abselem.top = Math.max(yPos.above + 3, abselem.top); // TODO-PER: Not sure why we need this fudge factor.
18744
- }
18942
+ var hasOne = stackedDecoration(decoration, width, abselem, yPos, positioning.ornamentPosition, this.minTop, this.minBottom, accentAbove);
18943
+ //if (hasOne) {
18944
+ // abselem.top = Math.max(yPos.above + 3, abselem.top); // TODO-PER: Not sure why we need this fudge factor.
18945
+ //}
18745
18946
  leftDecoration(decoration, abselem, roomtaken);
18746
18947
  };
18747
18948
  module.exports = Decoration;
@@ -19101,94 +19302,109 @@ module.exports = BeamElem;
19101
19302
  /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
19102
19303
 
19103
19304
  var addTextIf = __webpack_require__(/*! ../add-text-if */ "./src/write/creation/add-text-if.js");
19104
- function BottomText(metaText, width, isPrint, paddingLeft, spacing, getTextSize) {
19305
+ var richText = __webpack_require__(/*! ./rich-text */ "./src/write/creation/elements/rich-text.js");
19306
+ function BottomText(metaText, width, isPrint, paddingLeft, spacing, shouldAddClasses, getTextSize) {
19105
19307
  this.rows = [];
19106
- if (metaText.unalignedWords && metaText.unalignedWords.length > 0) this.unalignedWords(metaText.unalignedWords, paddingLeft, spacing, getTextSize);
19107
- this.extraText(metaText, paddingLeft, spacing, getTextSize);
19308
+ if (metaText.unalignedWords && metaText.unalignedWords.length > 0) this.unalignedWords(metaText.unalignedWords, paddingLeft, spacing, shouldAddClasses, getTextSize);
19309
+ this.extraText(metaText, paddingLeft, spacing, shouldAddClasses, getTextSize);
19108
19310
  if (metaText.footer && isPrint) this.footer(metaText.footer, width, paddingLeft, getTextSize);
19109
19311
  }
19110
- BottomText.prototype.unalignedWords = function (unalignedWords, paddingLeft, spacing, getTextSize) {
19111
- var klass = 'meta-bottom unaligned-words';
19312
+ BottomText.prototype.unalignedWords = function (unalignedWords, marginLeft, spacing, shouldAddClasses, getTextSize) {
19313
+ var klass = shouldAddClasses ? 'abcjs-unaligned-words' : '';
19112
19314
  var defFont = 'wordsfont';
19113
- this.rows.push({
19114
- startGroup: "unalignedWords",
19115
- klass: 'abcjs-meta-bottom abcjs-unaligned-words',
19116
- name: "words"
19117
- });
19118
19315
  var space = getTextSize.calc("i", defFont, klass);
19119
19316
  this.rows.push({
19120
19317
  move: spacing.words
19121
19318
  });
19122
- for (var j = 0; j < unalignedWords.length; j++) {
19123
- if (unalignedWords[j] === '') this.rows.push({
19124
- move: space.height
19125
- });else if (typeof unalignedWords[j] === 'string') {
19126
- addTextIf(this.rows, {
19127
- marginLeft: paddingLeft,
19128
- text: unalignedWords[j],
19319
+ addMultiLine(this.rows, '', unalignedWords, marginLeft, defFont, "unalignedWords", "unalignedWords", klass, "unalignedWords", spacing, shouldAddClasses, getTextSize);
19320
+ this.rows.push({
19321
+ move: space.height
19322
+ });
19323
+ };
19324
+ function addSingleLine(rows, preface, text, marginLeft, klass, shouldAddClasses, getTextSize) {
19325
+ if (text) {
19326
+ if (preface) {
19327
+ if (typeof text === 'string') text = preface + text;else text = [{
19328
+ text: preface
19329
+ }].concat(text);
19330
+ }
19331
+ klass = shouldAddClasses ? 'abcjs-extra-text ' + klass : '';
19332
+ richText(rows, text, 'historyfont', klass, "description", marginLeft, {
19333
+ absElemType: "extraText",
19334
+ anchor: 'start'
19335
+ }, getTextSize);
19336
+ }
19337
+ }
19338
+ function addMultiLine(rows, preface, content, marginLeft, defFont, absElemType, groupName, klass, name, spacing, shouldAddClasses, getTextSize) {
19339
+ if (content) {
19340
+ klass = shouldAddClasses ? 'abcjs-extra-text ' + klass : '';
19341
+ var size = getTextSize.calc("A", defFont, klass);
19342
+ if (typeof content === 'string') {
19343
+ if (preface) content = preface + "\n" + content;
19344
+ addTextIf(rows, {
19345
+ marginLeft: marginLeft,
19346
+ text: content,
19129
19347
  font: defFont,
19130
- klass: klass,
19131
- inGroup: true,
19132
- name: "words"
19348
+ absElemType: "extraText",
19349
+ name: name,
19350
+ 'dominant-baseline': 'middle',
19351
+ klass: klass
19133
19352
  }, getTextSize);
19353
+ //rows.push({move: size.height*3/4})
19134
19354
  } else {
19135
- var largestY = 0;
19136
- var offsetX = 0;
19137
- for (var k = 0; k < unalignedWords[j].length; k++) {
19138
- var thisWord = unalignedWords[j][k];
19139
- var font = thisWord.font ? thisWord.font : defFont;
19140
- this.rows.push({
19141
- left: paddingLeft + offsetX,
19142
- text: thisWord.text,
19143
- font: font,
19355
+ rows.push({
19356
+ startGroup: groupName,
19357
+ klass: klass,
19358
+ name: name
19359
+ });
19360
+ rows.push({
19361
+ move: spacing.info
19362
+ });
19363
+ if (preface) {
19364
+ addTextIf(rows, {
19365
+ marginLeft: marginLeft,
19366
+ text: preface,
19367
+ font: defFont,
19368
+ absElemType: "extraText",
19369
+ name: name,
19370
+ 'dominant-baseline': 'middle'
19371
+ }, getTextSize);
19372
+ rows.push({
19373
+ move: size.height * 3 / 4
19374
+ });
19375
+ }
19376
+ for (var j = 0; j < content.length; j++) {
19377
+ richText(rows, content[j], defFont, '', name, marginLeft, {
19144
19378
  anchor: 'start'
19379
+ }, getTextSize);
19380
+ // TODO-PER: Hack! the string and rich lines should have used up the same amount of space without this.
19381
+ if (j < content.length - 1 && typeof content[j] === 'string' && typeof content[j + 1] !== 'string') rows.push({
19382
+ move: size.height * 3 / 4
19145
19383
  });
19146
- var size = getTextSize.calc(thisWord.text, defFont, klass);
19147
- largestY = Math.max(largestY, size.height);
19148
- offsetX += size.width;
19149
- // If the phrase ends in a space, then that is not counted in the width, so we need to add that in ourselves.
19150
- if (thisWord.text[thisWord.text.length - 1] === ' ') {
19151
- offsetX += space.width;
19152
- }
19153
19384
  }
19154
- this.rows.push({
19155
- move: largestY
19385
+ rows.push({
19386
+ endGroup: groupName,
19387
+ absElemType: absElemType,
19388
+ startChar: -1,
19389
+ endChar: -1,
19390
+ name: name
19391
+ });
19392
+ rows.push({
19393
+ move: size.height
19156
19394
  });
19157
19395
  }
19158
19396
  }
19159
- this.rows.push({
19160
- move: space.height * 2
19161
- });
19162
- this.rows.push({
19163
- endGroup: "unalignedWords",
19164
- absElemType: "unalignedWords",
19165
- startChar: -1,
19166
- endChar: -1,
19167
- name: "unalignedWords"
19168
- });
19169
- };
19170
- BottomText.prototype.extraText = function (metaText, marginLeft, spacing, getTextSize) {
19171
- var extraText = "";
19172
- if (metaText.book) extraText += "Book: " + metaText.book + "\n";
19173
- if (metaText.source) extraText += "Source: " + metaText.source + "\n";
19174
- if (metaText.discography) extraText += "Discography: " + metaText.discography + "\n";
19175
- if (metaText.notes) extraText += "Notes: " + metaText.notes + "\n";
19176
- if (metaText.transcription) extraText += "Transcription: " + metaText.transcription + "\n";
19177
- if (metaText.history) extraText += "History: " + metaText.history + "\n";
19178
- if (metaText['abc-copyright']) extraText += "Copyright: " + metaText['abc-copyright'] + "\n";
19179
- if (metaText['abc-creator']) extraText += "Creator: " + metaText['abc-creator'] + "\n";
19180
- if (metaText['abc-edited-by']) extraText += "Edited By: " + metaText['abc-edited-by'] + "\n";
19181
- if (extraText.length > 0) {
19182
- addTextIf(this.rows, {
19183
- marginLeft: marginLeft,
19184
- text: extraText,
19185
- font: 'historyfont',
19186
- klass: 'meta-bottom extra-text',
19187
- marginTop: spacing.info,
19188
- absElemType: "extraText",
19189
- name: "description"
19190
- }, getTextSize);
19191
- }
19397
+ }
19398
+ BottomText.prototype.extraText = function (metaText, marginLeft, spacing, shouldAddClasses, getTextSize) {
19399
+ addSingleLine(this.rows, "Book: ", metaText.book, marginLeft, 'abcjs-book', shouldAddClasses, getTextSize);
19400
+ addSingleLine(this.rows, "Source: ", metaText.source, marginLeft, 'abcjs-source', shouldAddClasses, getTextSize);
19401
+ addSingleLine(this.rows, "Discography: ", metaText.discography, marginLeft, 'abcjs-discography', shouldAddClasses, getTextSize);
19402
+ addMultiLine(this.rows, 'Notes:', metaText.notes, marginLeft, 'historyfont', "extraText", "notes", 'abcjs-notes', "description", spacing, shouldAddClasses, getTextSize);
19403
+ addSingleLine(this.rows, "Transcription: ", metaText.transcription, marginLeft, 'abcjs-transcription', shouldAddClasses, getTextSize);
19404
+ addMultiLine(this.rows, "History:", metaText.history, marginLeft, 'historyfont', "extraText", "history", 'abcjs-history', "description", spacing, shouldAddClasses, getTextSize);
19405
+ addSingleLine(this.rows, "Copyright: ", metaText['abc-copyright'], marginLeft, 'abcjs-copyright', shouldAddClasses, getTextSize);
19406
+ addSingleLine(this.rows, "Creator: ", metaText['abc-creator'], marginLeft, 'abcjs-creator', shouldAddClasses, getTextSize);
19407
+ addSingleLine(this.rows, "Edited By: ", metaText['abc-edited-by'], marginLeft, 'abcjs-edited-by', shouldAddClasses, getTextSize);
19192
19408
  };
19193
19409
  BottomText.prototype.footer = function (footer, width, paddingLeft, getTextSize) {
19194
19410
  var klass = 'header meta-bottom';
@@ -19530,6 +19746,76 @@ module.exports = RelativeElement;
19530
19746
 
19531
19747
  /***/ }),
19532
19748
 
19749
+ /***/ "./src/write/creation/elements/rich-text.js":
19750
+ /*!**************************************************!*\
19751
+ !*** ./src/write/creation/elements/rich-text.js ***!
19752
+ \**************************************************/
19753
+ /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
19754
+
19755
+ var addTextIf = __webpack_require__(/*! ../add-text-if */ "./src/write/creation/add-text-if.js");
19756
+ function richText(rows, str, defFont, klass, name, paddingLeft, attr, getTextSize) {
19757
+ var space = getTextSize.calc("i", defFont, klass);
19758
+ if (str === '') {
19759
+ rows.push({
19760
+ move: space.height
19761
+ });
19762
+ } else {
19763
+ if (typeof str === 'string') {
19764
+ addTextIf(rows, {
19765
+ marginLeft: paddingLeft,
19766
+ text: str,
19767
+ font: defFont,
19768
+ klass: klass,
19769
+ marginTop: attr.marginTop,
19770
+ anchor: attr.anchor,
19771
+ absElemType: attr.absElemType,
19772
+ info: attr.info,
19773
+ name: name
19774
+ }, getTextSize);
19775
+ return;
19776
+ }
19777
+ if (attr.marginTop) rows.push({
19778
+ move: attr.marginTop
19779
+ });
19780
+ var largestY = 0;
19781
+ var gap = 0;
19782
+ var row = {
19783
+ left: paddingLeft,
19784
+ anchor: attr.anchor,
19785
+ phrases: []
19786
+ };
19787
+ if (klass) row.klass = klass;
19788
+ rows.push(row);
19789
+ for (var k = 0; k < str.length; k++) {
19790
+ var thisWord = str[k];
19791
+ var font = thisWord.font ? thisWord.font : getTextSize.attr(defFont, klass).font;
19792
+ var phrase = {
19793
+ content: thisWord.text
19794
+ };
19795
+ if (font) phrase.attrs = {
19796
+ "font-family": getTextSize.getFamily(font.face),
19797
+ "font-size": font.size,
19798
+ "font-weight": font.weight,
19799
+ "font-style": font.style,
19800
+ "font-decoration": font.decoration
19801
+ };
19802
+ //if (thisWord.text) {
19803
+ row.phrases.push(phrase);
19804
+ var size = getTextSize.calc(thisWord.text, font, klass);
19805
+ largestY = Math.max(largestY, size.height);
19806
+ if (thisWord.text[thisWord.text.length - 1] === ' ') {
19807
+ gap = space.width;
19808
+ }
19809
+ }
19810
+ rows.push({
19811
+ move: largestY
19812
+ });
19813
+ }
19814
+ }
19815
+ module.exports = richText;
19816
+
19817
+ /***/ }),
19818
+
19533
19819
  /***/ "./src/write/creation/elements/separator.js":
19534
19820
  /*!**************************************************!*\
19535
19821
  !*** ./src/write/creation/elements/separator.js ***!
@@ -19954,7 +20240,8 @@ module.exports = TieElem;
19954
20240
  /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
19955
20241
 
19956
20242
  var addTextIf = __webpack_require__(/*! ../add-text-if */ "./src/write/creation/add-text-if.js");
19957
- function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, paddingLeft, spacing, getTextSize) {
20243
+ var richText = __webpack_require__(/*! ./rich-text */ "./src/write/creation/elements/rich-text.js");
20244
+ function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, paddingLeft, spacing, shouldAddClasses, getTextSize) {
19958
20245
  this.rows = [];
19959
20246
  if (metaText.header && isPrint) {
19960
20247
  // Note: whether there is a header or not doesn't change any other positioning, so this doesn't change the Y-coordinate.
@@ -19999,31 +20286,23 @@ function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, padd
19999
20286
  var tAnchor = formatting.titleleft ? 'start' : 'middle';
20000
20287
  var tLeft = formatting.titleleft ? paddingLeft : paddingLeft + width / 2;
20001
20288
  if (metaText.title) {
20002
- addTextIf(this.rows, {
20003
- marginLeft: tLeft,
20004
- text: metaText.title,
20005
- font: 'titlefont',
20006
- klass: 'title meta-top',
20289
+ var klass = shouldAddClasses ? 'abcjs-title' : '';
20290
+ richText(this.rows, metaText.title, "titlefont", klass, 'title', tLeft, {
20007
20291
  marginTop: spacing.title,
20008
20292
  anchor: tAnchor,
20009
20293
  absElemType: "title",
20010
- info: metaTextInfo.title,
20011
- name: "title"
20294
+ info: metaTextInfo.title
20012
20295
  }, getTextSize);
20013
20296
  }
20014
20297
  if (lines.length) {
20015
20298
  var index = 0;
20016
20299
  while (index < lines.length && lines[index].subtitle) {
20017
- addTextIf(this.rows, {
20018
- marginLeft: tLeft,
20019
- text: lines[index].subtitle.text,
20020
- font: 'subtitlefont',
20021
- klass: 'text meta-top subtitle',
20300
+ var klass = shouldAddClasses ? 'abcjs-text abcjs-subtitle' : '';
20301
+ richText(this.rows, lines[index].subtitle.text, "subtitlefont", klass, 'subtitle', tLeft, {
20022
20302
  marginTop: spacing.subtitle,
20023
20303
  anchor: tAnchor,
20024
20304
  absElemType: "subtitle",
20025
- info: lines[index].subtitle,
20026
- name: "subtitle"
20305
+ info: lines[index].subtitle
20027
20306
  }, getTextSize);
20028
20307
  index++;
20029
20308
  }
@@ -20034,54 +20313,68 @@ function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, padd
20034
20313
  });
20035
20314
  if (metaText.rhythm && metaText.rhythm.length > 0) {
20036
20315
  var noMove = !!(metaText.composer || metaText.origin);
20316
+ var klass = shouldAddClasses ? 'abcjs-rhythm' : '';
20037
20317
  addTextIf(this.rows, {
20038
20318
  marginLeft: paddingLeft,
20039
20319
  text: metaText.rhythm,
20040
20320
  font: 'infofont',
20041
- klass: 'meta-top rhythm',
20321
+ klass: klass,
20042
20322
  absElemType: "rhythm",
20043
20323
  noMove: noMove,
20044
20324
  info: metaTextInfo.rhythm,
20045
20325
  name: "rhythm"
20046
20326
  }, getTextSize);
20047
20327
  }
20048
- var composerLine = "";
20049
- if (metaText.composer) composerLine += metaText.composer;
20050
- if (metaText.origin) composerLine += ' (' + metaText.origin + ')';
20051
- if (composerLine.length > 0) {
20052
- addTextIf(this.rows, {
20053
- marginLeft: paddingLeft + width,
20054
- text: composerLine,
20055
- font: 'composerfont',
20056
- klass: 'meta-top composer',
20328
+ var hasSimpleComposerLine = true;
20329
+ if (metaText.composer && typeof metaText.composer !== 'string') hasSimpleComposerLine = false;
20330
+ if (metaText.origin && typeof metaText.origin !== 'string') hasSimpleComposerLine = false;
20331
+ var composerLine = metaText.composer ? metaText.composer : '';
20332
+ if (metaText.origin) {
20333
+ if (typeof composerLine === 'string' && typeof metaText.origin === 'string') composerLine += ' (' + metaText.origin + ')';else if (typeof composerLine === 'string' && typeof metaText.origin !== 'string') {
20334
+ composerLine = [{
20335
+ text: composerLine
20336
+ }];
20337
+ composerLine.push({
20338
+ text: " ("
20339
+ });
20340
+ composerLine = composerLine.concat(metaText.origin);
20341
+ composerLine.push({
20342
+ text: ")"
20343
+ });
20344
+ } else {
20345
+ composerLine.push({
20346
+ text: " ("
20347
+ });
20348
+ composerLine = composerLine.concat(metaText.origin);
20349
+ composerLine.push({
20350
+ text: ")"
20351
+ });
20352
+ }
20353
+ }
20354
+ if (composerLine) {
20355
+ var klass = shouldAddClasses ? 'abcjs-composer' : '';
20356
+ richText(this.rows, composerLine, 'composerfont', klass, "composer", paddingLeft + width, {
20057
20357
  anchor: "end",
20058
20358
  absElemType: "composer",
20059
20359
  info: metaTextInfo.composer,
20060
- name: "composer"
20360
+ ingroup: true
20061
20361
  }, getTextSize);
20062
20362
  }
20063
20363
  }
20064
20364
  if (metaText.author && metaText.author.length > 0) {
20065
- addTextIf(this.rows, {
20066
- marginLeft: paddingLeft + width,
20067
- text: metaText.author,
20068
- font: 'composerfont',
20069
- klass: 'meta-top author',
20365
+ var klass = shouldAddClasses ? 'abcjs-author' : '';
20366
+ richText(this.rows, metaText.author, 'composerfont', klass, "author", paddingLeft + width, {
20070
20367
  anchor: "end",
20071
20368
  absElemType: "author",
20072
- info: metaTextInfo.author,
20073
- name: "author"
20369
+ info: metaTextInfo.author
20074
20370
  }, getTextSize);
20075
20371
  }
20076
20372
  if (metaText.partOrder && metaText.partOrder.length > 0) {
20077
- addTextIf(this.rows, {
20078
- marginLeft: paddingLeft,
20079
- text: metaText.partOrder,
20080
- font: 'partsfont',
20081
- klass: 'meta-top part-order',
20373
+ var klass = shouldAddClasses ? 'abcjs-part-order' : '';
20374
+ richText(this.rows, metaText.partOrder, 'partsfont', klass, "part-order", paddingLeft, {
20082
20375
  absElemType: "partOrder",
20083
20376
  info: metaTextInfo.partOrder,
20084
- name: "part-order"
20377
+ anchor: 'start'
20085
20378
  }, getTextSize);
20086
20379
  }
20087
20380
  }
@@ -20627,7 +20920,7 @@ var glyphs = {
20627
20920
  h: 7.515
20628
20921
  },
20629
20922
  ',': {
20630
- d: [['M', 1.32, -3.36], ['c', 0.57, -0.15, 1.17, 0.03, 1.59, 0.45], ['c', 0.45, 0.45, 0.60, 0.96, 0.51, 1.89], ['c', -0.09, 1.23, -0.42, 2.46, -0.99, 3.93], ['c', -0.30, 0.72, -0.72, 1.62, -0.78, 1.68], ['c', -0.18, 0.21, -0.51, 0.18, -0.66, -0.06], ['c', -0.03, -0.06, -0.06, -0.15, -0.06, -0.18], ['c', 0.00, -0.06, 0.12, -0.33, 0.24, -0.63], ['c', 0.84, -1.80, 1.02, -2.61, 0.69, -3.24], ['c', -0.12, -0.24, -0.27, -0.36, -0.75, -0.60], ['c', -0.36, -0.15, -0.42, -0.21, -0.60, -0.39], ['c', -0.69, -0.69, -0.69, -1.71, 0.00, -2.40], ['c', 0.21, -0.21, 0.51, -0.39, 0.81, -0.45], ['z']],
20923
+ d: [['M', 1.85, -3.36], ['c', 0.57, -0.15, 1.17, 0.03, 1.59, 0.45], ['c', 0.45, 0.45, 0.60, 0.96, 0.51, 1.89], ['c', -0.09, 1.23, -0.42, 2.46, -0.99, 3.93], ['c', -0.30, 0.72, -0.72, 1.62, -0.78, 1.68], ['c', -0.18, 0.21, -0.51, 0.18, -0.66, -0.06], ['c', -0.03, -0.06, -0.06, -0.15, -0.06, -0.18], ['c', 0.00, -0.06, 0.12, -0.33, 0.24, -0.63], ['c', 0.84, -1.80, 1.02, -2.61, 0.69, -3.24], ['c', -0.12, -0.24, -0.27, -0.36, -0.75, -0.60], ['c', -0.36, -0.15, -0.42, -0.21, -0.60, -0.39], ['c', -0.69, -0.69, -0.69, -1.71, 0.00, -2.40], ['c', 0.21, -0.21, 0.51, -0.39, 0.81, -0.45], ['z']],
20631
20924
  w: 3.452,
20632
20925
  h: 8.143
20633
20926
  },
@@ -20695,7 +20988,7 @@ glyphs['noteheads.harmonic.quarter'] = {
20695
20988
  h: 8.165
20696
20989
  };
20697
20990
  glyphs['noteheads.triangle.quarter'] = {
20698
- d: [['M', 0, 0], ['l', 9, 0], ['l', -4.5, -9], ['z']],
20991
+ d: [['M', 0, 4], ['l', 9, 0], ['l', -4.5, -9], ['z']],
20699
20992
  w: 9,
20700
20993
  h: 9
20701
20994
  };
@@ -20896,7 +21189,10 @@ function drawAbsolute(renderer, params, bartop, selectables, staffPos) {
20896
21189
  drawTempo(renderer, child);
20897
21190
  break;
20898
21191
  default:
20899
- drawRelativeElement(renderer, child, bartop);
21192
+ var el = drawRelativeElement(renderer, child, bartop);
21193
+ if (child.type === "symbol" && child.c && child.c.indexOf('notehead') >= 0) {
21194
+ el.setAttribute('class', 'abcjs-notehead');
21195
+ }
20900
21196
  }
20901
21197
  }
20902
21198
  var klass = params.type;
@@ -21199,7 +21495,9 @@ var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers
21199
21495
  var Selectables = __webpack_require__(/*! ./selectables */ "./src/write/draw/selectables.js");
21200
21496
  function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, selectTypes, tuneNumber, lineOffset) {
21201
21497
  var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber);
21202
- renderer.paper.openGroup();
21498
+ var groupClasses = {};
21499
+ if (classes.shouldAddClasses) groupClasses.klass = "abcjs-meta-top";
21500
+ renderer.paper.openGroup(groupClasses);
21203
21501
  renderer.moveY(renderer.padding.top);
21204
21502
  nonMusic(renderer, abcTune.topText, selectables);
21205
21503
  renderer.paper.closeGroup();
@@ -21209,7 +21507,8 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
21209
21507
  classes.incrLine();
21210
21508
  var abcLine = abcTune.lines[line];
21211
21509
  if (abcLine.staff) {
21212
- renderer.paper.openGroup();
21510
+ if (classes.shouldAddClasses) groupClasses.klass = "abcjs-staff l" + classes.lineNumber;
21511
+ renderer.paper.openGroup(groupClasses);
21213
21512
  if (abcLine.vskip) {
21214
21513
  renderer.moveY(abcLine.vskip);
21215
21514
  }
@@ -21219,14 +21518,16 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
21219
21518
  staffgroups.push(staffgroup);
21220
21519
  renderer.paper.closeGroup();
21221
21520
  } else if (abcLine.nonMusic) {
21222
- renderer.paper.openGroup();
21521
+ if (classes.shouldAddClasses) groupClasses.klass = "abcjs-non-music";
21522
+ renderer.paper.openGroup(groupClasses);
21223
21523
  nonMusic(renderer, abcLine.nonMusic, selectables);
21224
21524
  renderer.paper.closeGroup();
21225
21525
  }
21226
21526
  }
21227
21527
  classes.reset();
21228
21528
  if (abcTune.bottomText && abcTune.bottomText.rows && abcTune.bottomText.rows.length > 0) {
21229
- renderer.paper.openGroup();
21529
+ if (classes.shouldAddClasses) groupClasses.klass = "abcjs-meta-bottom";
21530
+ renderer.paper.openGroup(groupClasses);
21230
21531
  renderer.moveY(24); // TODO-PER: Empirically discovered. What variable should this be?
21231
21532
  nonMusic(renderer, abcTune.bottomText, selectables);
21232
21533
  renderer.paper.closeGroup();
@@ -21510,12 +21811,14 @@ function nonMusic(renderer, obj, selectables) {
21510
21811
  renderer.absolutemoveY(row.absmove);
21511
21812
  } else if (row.move) {
21512
21813
  renderer.moveY(row.move);
21513
- } else if (row.text) {
21814
+ } else if (row.text || row.phrases) {
21514
21815
  var x = row.left ? row.left : 0;
21515
21816
  var el = renderText(renderer, {
21516
21817
  x: x,
21517
21818
  y: renderer.y,
21518
21819
  text: row.text,
21820
+ phrases: row.phrases,
21821
+ 'dominant-baseline': row['dominant-baseline'],
21519
21822
  type: row.font,
21520
21823
  klass: row.klass,
21521
21824
  name: row.name,
@@ -21797,7 +22100,7 @@ function drawRelativeElement(renderer, params, bartop) {
21797
22100
  case "tabNumber":
21798
22101
  var hAnchor = "middle";
21799
22102
  var tabFont = "tabnumberfont";
21800
- var tabClass = 'tab-number';
22103
+ var tabClass = 'abcjs-tab-number';
21801
22104
  if (params.isGrace) {
21802
22105
  tabFont = "tabgracefont";
21803
22106
  y += 2.5;
@@ -22039,7 +22342,7 @@ module.exports = drawSeparator;
22039
22342
  /***/ (function(module) {
22040
22343
 
22041
22344
  function setPaperSize(renderer, maxwidth, scale, responsive) {
22042
- var w = (maxwidth + renderer.padding.right) * scale;
22345
+ var w = (maxwidth + renderer.padding.left + renderer.padding.right) * scale;
22043
22346
  var h = (renderer.y + renderer.padding.bottom) * scale;
22044
22347
  if (renderer.isPrint) h = Math.max(h, 1056); // 11in x 72pt/in x 1.33px/pt
22045
22348
  // TODO-PER: We are letting the page get as long as it needs now, but eventually that should go to a second page.
@@ -22504,7 +22807,6 @@ function drawTempo(renderer, params) {
22504
22807
  klass: 'abcjs-tempo',
22505
22808
  anchor: "start",
22506
22809
  noClass: true,
22507
- "dominant-baseline": "ideographic",
22508
22810
  name: "pre"
22509
22811
  }, true);
22510
22812
  size = renderer.controller.getTextSize.calc(params.tempo.preString, 'tempofont', 'tempo', text);
@@ -22564,6 +22866,13 @@ module.exports = drawTempo;
22564
22866
  var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
22565
22867
  function renderText(renderer, params, alreadyInGroup) {
22566
22868
  var y = params.y;
22869
+
22870
+ // TODO-PER: Probably need to merge the regular text and rich text better. At the least, rich text loses the font box.
22871
+ if (params.phrases) {
22872
+ //richTextLine = function (phrases, x, y, klass, anchor, target)
22873
+ var elem = renderer.paper.richTextLine(params.phrases, params.x, params.y, params.klass, params.anchor);
22874
+ return elem;
22875
+ }
22567
22876
  if (params.lane) {
22568
22877
  var laneMargin = params.dim.font.size * 0.25;
22569
22878
  y += (params.dim.font.size + laneMargin) * params.lane;
@@ -22574,6 +22883,7 @@ function renderText(renderer, params, alreadyInGroup) {
22574
22883
  hash.attr["class"] = params.klass;
22575
22884
  } else hash = renderer.controller.getFontAndAttr.calc(params.type, params.klass);
22576
22885
  if (params.anchor) hash.attr["text-anchor"] = params.anchor;
22886
+ if (params['dominant-baseline']) hash.attr["dominant-baseline"] = params['dominant-baseline'];
22577
22887
  hash.attr.x = params.x;
22578
22888
  hash.attr.y = y;
22579
22889
  if (!params.centerVertically) hash.attr.y += hash.font.size;
@@ -22652,10 +22962,15 @@ function drawTie(renderer, params, linestartx, lineendx, selectables) {
22652
22962
  if (params.hint) klass = "abcjs-hint";
22653
22963
  var fudgeY = params.fixedY ? 1.5 : 0; // TODO-PER: This just compensates for drawArc, which contains too much knowledge of ties and slurs.
22654
22964
  var el = drawArc(renderer, params.startX, params.endX, params.startY + fudgeY, params.endY + fudgeY, params.above, klass, params.isTie, params.dotted);
22965
+ var startChar = -1;
22966
+ // 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.
22967
+ if (params.anchor1 && !params.isTie) startChar = params.anchor1.parent.abcelem.startChar - 1;
22968
+ var endChar = -1;
22969
+ if (params.anchor2 && !params.isTie) endChar = params.anchor2.parent.abcelem.endChar + 1;
22655
22970
  selectables.wrapSvgEl({
22656
22971
  el_type: "slur",
22657
- startChar: -1,
22658
- endChar: -1
22972
+ startChar: startChar,
22973
+ endChar: endChar
22659
22974
  }, el);
22660
22975
  return [el];
22661
22976
  }
@@ -22965,6 +23280,7 @@ var EngraverController = function EngraverController(paper, params) {
22965
23280
  this.responsive = params.responsive;
22966
23281
  this.space = 3 * spacing.SPACE;
22967
23282
  this.initialClef = params.initialClef;
23283
+ this.expandToWidest = !!params.expandToWidest;
22968
23284
  this.scale = params.scale ? parseFloat(params.scale) : 0;
22969
23285
  this.classes = new Classes({
22970
23286
  shouldAddClasses: params.add_classes
@@ -22986,6 +23302,7 @@ var EngraverController = function EngraverController(paper, params) {
22986
23302
  this.renderer.setPaddingOverride(params);
22987
23303
  if (params.showDebug) this.renderer.showDebug = params.showDebug;
22988
23304
  if (params.jazzchords) this.jazzchords = params.jazzchords;
23305
+ if (params.accentAbove) this.accentAbove = params.accentAbove;
22989
23306
  if (params.germanAlphabet) this.germanAlphabet = params.germanAlphabet;
22990
23307
  if (params.lineThickness) this.lineThickness = params.lineThickness;
22991
23308
  this.renderer.controller = this; // TODO-GD needed for highlighting
@@ -23037,6 +23354,7 @@ EngraverController.prototype.getMeasureWidths = function (abcTune) {
23037
23354
  this.reset();
23038
23355
  this.getFontAndAttr = new GetFontAndAttr(abcTune.formatting, this.classes);
23039
23356
  this.getTextSize = new GetTextSize(this.getFontAndAttr, this.renderer.paper);
23357
+ var origJazzChords = this.jazzchords;
23040
23358
  this.setupTune(abcTune, 0);
23041
23359
  this.constructTuneElements(abcTune);
23042
23360
  // layout() sets the x-coordinate of the abcTune element here:
@@ -23080,11 +23398,13 @@ EngraverController.prototype.getMeasureWidths = function (abcTune) {
23080
23398
  //section.height += calcHeight(abcLine.staffGroup) * spacing.STEP;
23081
23399
  } else needNewSection = true;
23082
23400
  }
23401
+ this.jazzchords = origJazzChords;
23083
23402
  return ret;
23084
23403
  };
23085
23404
  EngraverController.prototype.setupTune = function (abcTune, tuneNumber) {
23086
23405
  this.classes.reset();
23087
23406
  if (abcTune.formatting.jazzchords !== undefined) this.jazzchords = abcTune.formatting.jazzchords;
23407
+ if (abcTune.formatting.accentAbove !== undefined) this.accentAbove = abcTune.formatting.accentAbove;
23088
23408
  this.renderer.newTune(abcTune);
23089
23409
  this.engraver = new AbstractEngraver(this.getTextSize, tuneNumber, {
23090
23410
  bagpipes: abcTune.formatting.bagpipes,
@@ -23094,6 +23414,7 @@ EngraverController.prototype.setupTune = function (abcTune, tuneNumber) {
23094
23414
  percmap: abcTune.formatting.percmap,
23095
23415
  initialClef: this.initialClef,
23096
23416
  jazzchords: this.jazzchords,
23417
+ accentAbove: this.accentAbove,
23097
23418
  germanAlphabet: this.germanAlphabet
23098
23419
  });
23099
23420
  this.engraver.setStemHeight(this.renderer.spacing.stemHeight);
@@ -23112,7 +23433,7 @@ EngraverController.prototype.setupTune = function (abcTune, tuneNumber) {
23112
23433
  return scale;
23113
23434
  };
23114
23435
  EngraverController.prototype.constructTuneElements = function (abcTune) {
23115
- abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.getTextSize);
23436
+ abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.classes.shouldAddClasses, this.getTextSize);
23116
23437
 
23117
23438
  // Generate the raw staff line data
23118
23439
  var i;
@@ -23139,16 +23460,50 @@ EngraverController.prototype.constructTuneElements = function (abcTune) {
23139
23460
  abcLine.nonMusic = new Separator(abcLine.separator.spaceAbove, abcLine.separator.lineLength, abcLine.separator.spaceBelow);
23140
23461
  }
23141
23462
  }
23142
- abcTune.bottomText = new BottomText(abcTune.metaText, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.getTextSize);
23463
+ abcTune.bottomText = new BottomText(abcTune.metaText, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.classes.shouldAddClasses, this.getTextSize);
23143
23464
  };
23144
23465
  EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOffset) {
23466
+ var origJazzChords = this.jazzchords;
23145
23467
  var scale = this.setupTune(abcTune, tuneNumber);
23146
23468
 
23147
23469
  // Create all of the element objects that will appear on the page.
23148
23470
  this.constructTuneElements(abcTune);
23149
23471
 
23472
+ //Set the top text now that we know the width
23473
+
23150
23474
  // Do all the positioning, both horizontally and vertically
23151
- var maxWidth = layout(this.renderer, abcTune, this.width, this.space);
23475
+ var maxWidth = layout(this.renderer, abcTune, this.width, this.space, this.expandToWidest);
23476
+
23477
+ //Set the top text now that we know the width
23478
+ if (this.expandToWidest && maxWidth > this.width + 1) {
23479
+ abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, maxWidth, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.classes.shouldAddClasses, this.getTextSize);
23480
+ if (abcTune.lines && abcTune.lines.length > 0) {
23481
+ var nlines = abcTune.lines.length;
23482
+ for (var i = 0; i < nlines; ++i) {
23483
+ var entry = abcTune.lines[i];
23484
+ if (entry.nonMusic) {
23485
+ if (entry.nonMusic.rows && entry.nonMusic.rows.length > 0) {
23486
+ var nRows = entry.nonMusic.rows.length;
23487
+ for (var j = 0; j < nRows; ++j) {
23488
+ var thisRow = entry.nonMusic.rows[j];
23489
+ // Recenter the element if it's a subtitle or centered text
23490
+ if (thisRow.left) {
23491
+ if (entry.subtitle) {
23492
+ thisRow.left = maxWidth / 2 + this.renderer.padding.left;
23493
+ } else {
23494
+ if (entry.text && entry.text.length > 0) {
23495
+ if (entry.text[0].center) {
23496
+ thisRow.left = maxWidth / 2 + this.renderer.padding.left;
23497
+ }
23498
+ }
23499
+ }
23500
+ }
23501
+ }
23502
+ }
23503
+ }
23504
+ }
23505
+ }
23506
+ }
23152
23507
 
23153
23508
  // Deal with tablature for staff
23154
23509
  if (abcTune.tablatures) {
@@ -23166,6 +23521,7 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
23166
23521
  this.svgs = [this.renderer.paper.svg];
23167
23522
  }
23168
23523
  setupSelection(this, this.svgs);
23524
+ this.jazzchords = origJazzChords;
23169
23525
  };
23170
23526
  function splitSvgIntoLines(renderer, output, title, responsive) {
23171
23527
  // Each line is a top level <g> in the svg. To split it into separate
@@ -23313,7 +23669,7 @@ Classes.prototype.generate = function (c) {
23313
23669
  if (!this.shouldAddClasses) return "";
23314
23670
  var ret = [];
23315
23671
  if (c && c.length > 0) ret.push(c);
23316
- if (c === "tab-number")
23672
+ if (c === "abcjs-tab-number")
23317
23673
  // TODO-PER-HACK! straighten out the tablature
23318
23674
  return ret.join(' ');
23319
23675
  if (c === "text instrument-name") return "abcjs-text abcjs-instrument-name";
@@ -23354,6 +23710,12 @@ GetFontAndAttr.prototype.updateFonts = function (fontOverrides) {
23354
23710
  if (fontOverrides.annotationfont) this.formatting.annotationfont = fontOverrides.annotationfont;
23355
23711
  if (fontOverrides.vocalfont) this.formatting.vocalfont = fontOverrides.vocalfont;
23356
23712
  };
23713
+ GetFontAndAttr.prototype.getFamily = function (type) {
23714
+ if (type[0] === '"' && type[type.length - 1] === '"') {
23715
+ return type.substring(1, type.length - 1);
23716
+ }
23717
+ return type;
23718
+ };
23357
23719
  GetFontAndAttr.prototype.calc = function (type, klass) {
23358
23720
  var font;
23359
23721
  if (typeof type === 'string') {
@@ -23386,7 +23748,7 @@ GetFontAndAttr.prototype.calc = function (type, klass) {
23386
23748
  var attr = {
23387
23749
  "font-size": font.size,
23388
23750
  'font-style': font.style,
23389
- "font-family": font.face,
23751
+ "font-family": this.getFamily(font.face),
23390
23752
  'font-weight': font.weight,
23391
23753
  'text-decoration': font.decoration,
23392
23754
  'class': this.classes.generate(klass)
@@ -23416,6 +23778,12 @@ GetTextSize.prototype.updateFonts = function (fontOverrides) {
23416
23778
  GetTextSize.prototype.attr = function (type, klass) {
23417
23779
  return this.getFontAndAttr.calc(type, klass);
23418
23780
  };
23781
+ GetTextSize.prototype.getFamily = function (type) {
23782
+ if (type[0] === '"' && type[type.length - 1] === '"') {
23783
+ return type.substring(1, type.length - 1);
23784
+ }
23785
+ return type;
23786
+ };
23419
23787
  GetTextSize.prototype.calc = function (text, type, klass, el) {
23420
23788
  var hash;
23421
23789
  // This can be passed in either a string or a font. If it is a string it names one of the standard fonts.
@@ -23431,7 +23799,7 @@ GetTextSize.prototype.calc = function (text, type, klass, el) {
23431
23799
  attr: {
23432
23800
  "font-size": type.size,
23433
23801
  "font-style": type.style,
23434
- "font-family": type.face,
23802
+ "font-family": this.getFamily(type.face),
23435
23803
  "font-weight": type.weight,
23436
23804
  "text-decoration": type.decoration,
23437
23805
  "class": this.getFontAndAttr.classes.generate(klass)
@@ -23651,6 +24019,7 @@ function keyboardSelection(ev) {
23651
24019
  if (handled) ev.preventDefault();
23652
24020
  }
23653
24021
  function findElementInHistory(selectables, el) {
24022
+ if (!el) return -1;
23654
24023
  for (var i = 0; i < selectables.length; i++) {
23655
24024
  if (el.dataset.index === selectables[i].svgEl.dataset.index) return i;
23656
24025
  }
@@ -23709,7 +24078,9 @@ function getBestMatchCoordinates(dim, ev, scale) {
23709
24078
  }
23710
24079
  function getTarget(target) {
23711
24080
  // This searches up the dom for the first item containing the attribute "selectable", or stopping at the SVG.
24081
+ if (!target) return null;
23712
24082
  if (target.tagName === "svg") return target;
24083
+ if (!target.getAttribute) return null;
23713
24084
  var found = target.getAttribute("selectable");
23714
24085
  while (!found) {
23715
24086
  if (!target.parentElement) found = true;else {
@@ -24248,7 +24619,7 @@ var layoutVoice = __webpack_require__(/*! ./voice */ "./src/write/layout/voice.j
24248
24619
  var setUpperAndLowerElements = __webpack_require__(/*! ./set-upper-and-lower-elements */ "./src/write/layout/set-upper-and-lower-elements.js");
24249
24620
  var layoutStaffGroup = __webpack_require__(/*! ./staff-group */ "./src/write/layout/staff-group.js");
24250
24621
  var getLeftEdgeOfStaff = __webpack_require__(/*! ./get-left-edge-of-staff */ "./src/write/layout/get-left-edge-of-staff.js");
24251
- var layout = function layout(renderer, abctune, width, space) {
24622
+ var layout = function layout(renderer, abctune, width, space, expandToWidest) {
24252
24623
  var i;
24253
24624
  var abcLine;
24254
24625
  // Adjust the x-coordinates to their absolute positions
@@ -24256,8 +24627,14 @@ var layout = function layout(renderer, abctune, width, space) {
24256
24627
  for (i = 0; i < abctune.lines.length; i++) {
24257
24628
  abcLine = abctune.lines[i];
24258
24629
  if (abcLine.staff) {
24259
- setXSpacing(renderer, width, space, abcLine.staffGroup, abctune.formatting, i === abctune.lines.length - 1, false);
24260
- if (abcLine.staffGroup.w > maxWidth) maxWidth = abcLine.staffGroup.w;
24630
+ // console.log("=== line", i)
24631
+ var thisWidth = setXSpacing(renderer, maxWidth, space, abcLine.staffGroup, abctune.formatting, i === abctune.lines.length - 1, false);
24632
+ // console.log(thisWidth, maxWidth)
24633
+ if (Math.round(thisWidth) > Math.round(maxWidth)) {
24634
+ // to take care of floating point weirdness
24635
+ maxWidth = thisWidth;
24636
+ if (expandToWidest) i = -1; // do the calculations over with the new width
24637
+ }
24261
24638
  }
24262
24639
  }
24263
24640
 
@@ -24288,13 +24665,38 @@ var setXSpacing = function setXSpacing(renderer, width, space, staffGroup, forma
24288
24665
  var newspace = space;
24289
24666
  for (var it = 0; it < 8; it++) {
24290
24667
  // 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.)
24668
+ // console.log("iteration", it)
24669
+ // dumpGroup("before", staffGroup)
24291
24670
  var ret = layoutStaffGroup(newspace, renderer, debug, staffGroup, leftEdge);
24671
+ // dumpGroup("after",staffGroup)
24292
24672
  newspace = calcHorizontalSpacing(isLastLine, formatting.stretchlast, width + renderer.padding.left, staffGroup.w, newspace, ret.spacingUnits, ret.minSpace, renderer.padding.left + renderer.padding.right);
24293
24673
  if (debug) console.log("setXSpace", it, staffGroup.w, newspace, staffGroup.minspace);
24294
24674
  if (newspace === null) break;
24295
24675
  }
24296
24676
  centerWholeRests(staffGroup.voices);
24297
- };
24677
+ return staffGroup.w - leftEdge;
24678
+ };
24679
+
24680
+ // function dumpGroup(label, staffGroup) {
24681
+ // var output = {
24682
+ // line: staffGroup.line,
24683
+ // w: staffGroup.w,
24684
+ // voice: {
24685
+ // i: staffGroup.voices[0].i,
24686
+ // minx: staffGroup.voices[0].minx,
24687
+ // nextx: staffGroup.voices[0].nextx,
24688
+ // spacingduration: staffGroup.voices[0].spacingduration,
24689
+ // w: staffGroup.voices[0].w,
24690
+ // children: [],
24691
+ // }
24692
+ // }
24693
+ // for (var i = 0; i < staffGroup.voices[0].children.length; i++) {
24694
+ // var child = staffGroup.voices[0].children[i]
24695
+ // output.voice.children.push({ fixedW: child.fixed.w, w: child.w, x: child.x, type: child.type })
24696
+ // }
24697
+ // console.log(label,output)
24698
+ // }
24699
+
24298
24700
  function calcHorizontalSpacing(isLastLine, stretchLast, targetWidth, lineWidth, spacing, spacingUnits, minSpace, padding) {
24299
24701
  if (isLastLine) {
24300
24702
  if (stretchLast === undefined) {
@@ -25395,6 +25797,28 @@ Svg.prototype.text = function (text, attr, target) {
25395
25797
  if (target) target.appendChild(el);else this.append(el);
25396
25798
  return el;
25397
25799
  };
25800
+ Svg.prototype.richTextLine = function (phrases, x, y, klass, anchor, target) {
25801
+ var el = document.createElementNS(svgNS, 'text');
25802
+ el.setAttribute("stroke", "none");
25803
+ el.setAttribute("class", klass);
25804
+ el.setAttribute("x", x);
25805
+ el.setAttribute("y", y);
25806
+ el.setAttribute("text-anchor", anchor);
25807
+ el.setAttribute("dominant-baseline", "middle");
25808
+ for (var i = 0; i < phrases.length; i++) {
25809
+ var phrase = phrases[i];
25810
+ var tspan = document.createElementNS(svgNS, 'tspan');
25811
+ var attrs = Object.keys(phrase.attrs);
25812
+ for (var j = 0; j < attrs.length; j++) {
25813
+ var value = phrase.attrs[attrs[j]];
25814
+ if (value !== '') tspan.setAttribute(attrs[j], value);
25815
+ }
25816
+ tspan.textContent = phrase.content;
25817
+ el.appendChild(tspan);
25818
+ }
25819
+ if (target) target.appendChild(el);else this.append(el);
25820
+ return el;
25821
+ };
25398
25822
  Svg.prototype.guessWidth = function (text, attr) {
25399
25823
  var svg = this.createDummySvg();
25400
25824
  var el = this.text(text, attr, svg);
@@ -25546,7 +25970,7 @@ module.exports = Svg;
25546
25970
  \********************/
25547
25971
  /***/ (function(module) {
25548
25972
 
25549
- var version = '6.2.2';
25973
+ var version = '6.3.0';
25550
25974
  module.exports = version;
25551
25975
 
25552
25976
  /***/ })