abcjs 6.0.3 → 6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,6 +48,8 @@ var tuneBook = __webpack_require__(/*! ./src/api/abc_tunebook */ "./src/api/abc_
48
48
 
49
49
  var sequence = __webpack_require__(/*! ./src/synth/abc_midi_sequencer */ "./src/synth/abc_midi_sequencer.js");
50
50
 
51
+ var strTranspose = __webpack_require__(/*! ./src/str/output */ "./src/str/output.js");
52
+
51
53
  var abcjs = {};
52
54
  abcjs.signature = "abcjs-basic v" + version;
53
55
  Object.keys(animation).forEach(function (key) {
@@ -62,6 +64,7 @@ abcjs.TimingCallbacks = __webpack_require__(/*! ./src/api/abc_timing_callbacks *
62
64
  var glyphs = __webpack_require__(/*! ./src/write/abc_glyphs */ "./src/write/abc_glyphs.js");
63
65
 
64
66
  abcjs.setGlyph = glyphs.setSymbol;
67
+ abcjs.strTranspose = strTranspose;
65
68
 
66
69
  var CreateSynth = __webpack_require__(/*! ./src/synth/create-synth */ "./src/synth/create-synth.js");
67
70
 
@@ -1098,101 +1101,6 @@ function renderOne(div, tune, params, tuneNumber, lineOffset) {
1098
1101
  var parent = div.parentNode;
1099
1102
  parent.style.width = div.style.width;
1100
1103
  }
1101
- }
1102
-
1103
- function renderEachLineSeparately(div, tune, params, tuneNumber) {
1104
- function initializeTuneLine(tune) {
1105
- var obj = new Tune();
1106
- obj.formatting = tune.formatting;
1107
- obj.media = tune.media;
1108
- obj.version = tune.version;
1109
- return obj;
1110
- } // Before rendering, chop up the returned tune into an array where each element is a line.
1111
- // The first element of the array gets the title and other items that go on top, the last element
1112
- // of the array gets the extra text that goes on bottom. Each element gets any non-music info that comes before it.
1113
-
1114
-
1115
- var tunes = [];
1116
- var tuneLine;
1117
-
1118
- for (var i = 0; i < tune.lines.length; i++) {
1119
- var line = tune.lines[i];
1120
- if (!tuneLine) tuneLine = initializeTuneLine(tune);
1121
-
1122
- if (i === 0) {
1123
- // These items go on top of the music
1124
- tuneLine.copyTopInfo(tune);
1125
- } // push the lines until we get to a music line
1126
-
1127
-
1128
- tuneLine.lines.push(line);
1129
-
1130
- if (line.staff) {
1131
- tunes.push(tuneLine);
1132
- tuneLine = undefined;
1133
- }
1134
- } // Add any extra stuff to the last line.
1135
-
1136
-
1137
- if (tuneLine) {
1138
- var lastLine = tunes[tunes.length - 1];
1139
-
1140
- for (var j = 0; j < tuneLine.lines.length; j++) {
1141
- lastLine.lines.push(tuneLine.lines[j]);
1142
- }
1143
- } // These items go below the music
1144
-
1145
-
1146
- tuneLine = tunes[tunes.length - 1];
1147
- tuneLine.copyBottomInfo(tune); // Now create sub-divs and render each line. Need to copy the params to change the padding for the interior slices.
1148
-
1149
- var ep = {};
1150
-
1151
- for (var key in params) {
1152
- if (params.hasOwnProperty(key)) {
1153
- ep[key] = params[key];
1154
- }
1155
- }
1156
-
1157
- var origPaddingTop = ep.paddingtop;
1158
- var origPaddingBottom = ep.paddingbottom;
1159
- var currentScrollY = div.parentNode.scrollTop; // If there is scrolling it will be lost during the redraw so remember it.
1160
-
1161
- var currentScrollX = div.parentNode.scrollLeft;
1162
- div.innerHTML = "";
1163
- var lineCount = 0;
1164
-
1165
- for (var k = 0; k < tunes.length; k++) {
1166
- var lineEl = document.createElement("div");
1167
- div.appendChild(lineEl);
1168
-
1169
- if (k === 0) {
1170
- ep.paddingtop = origPaddingTop;
1171
- ep.paddingbottom = 0;
1172
- } else if (k === tunes.length - 1) {
1173
- ep.paddingtop = 10;
1174
- ep.paddingbottom = origPaddingBottom;
1175
- } else {
1176
- ep.paddingtop = 10;
1177
- ep.paddingbottom = 0;
1178
- }
1179
-
1180
- if (k < tunes.length - 1) {
1181
- // If it is not the last line, force stretchlast. If it is, stretchlast might have been set by the input parameters.
1182
- tunes[k].formatting = parseCommon.clone(tunes[k].formatting);
1183
- tunes[k].formatting.stretchlast = true;
1184
- }
1185
-
1186
- renderOne(lineEl, tunes[k], ep, tuneNumber, lineCount);
1187
- lineCount += tunes[k].lines.length;
1188
- if (k === 0) tune.engraver = tunes[k].engraver;else {
1189
- if (!tune.engraver.staffgroups) tune.engraver.staffgroups = tunes[k].engraver.staffgroups;else if (tunes[k].engraver.staffgroups.length > 0) tune.engraver.staffgroups.push(tunes[k].engraver.staffgroups[0]);
1190
- }
1191
- }
1192
-
1193
- if (currentScrollX || currentScrollY) {
1194
- div.parentNode.scrollTo(currentScrollX, currentScrollY);
1195
- }
1196
1104
  } // A quick way to render a tune from javascript when interactivity is not required.
1197
1105
  // This is used when a javascript routine has some abc text that it wants to render
1198
1106
  // in a div or collection of divs. One tune or many can be rendered.
@@ -1263,8 +1171,9 @@ var renderAbc = function renderAbc(output, abc, parserParams, engraverParams, re
1263
1171
  if (!removeDiv && params.wrap && params.staffwidth) {
1264
1172
  tune = doLineWrapping(div, tune, tuneNumber, abcString, params);
1265
1173
  return tune;
1266
- } else if (removeDiv || !params.oneSvgPerLine || tune.lines.length < 2) renderOne(div, tune, params, tuneNumber, 0);else renderEachLineSeparately(div, tune, params, tuneNumber);
1174
+ }
1267
1175
 
1176
+ renderOne(div, tune, params, tuneNumber, 0);
1268
1177
  if (removeDiv) div.parentNode.removeChild(div);
1269
1178
  return null;
1270
1179
  }
@@ -1285,7 +1194,7 @@ function doLineWrapping(div, tune, tuneNumber, abcString, params) {
1285
1194
  if (warnings) tune.warnings = warnings;
1286
1195
  }
1287
1196
 
1288
- if (!params.oneSvgPerLine || tune.lines.length < 2) renderOne(div, tune, ret.revisedParams, tuneNumber, 0);else renderEachLineSeparately(div, tune, ret.revisedParams, tuneNumber);
1197
+ renderOne(div, tune, ret.revisedParams, tuneNumber, 0);
1289
1198
  tune.explanation = ret.explanation;
1290
1199
  return tune;
1291
1200
  }
@@ -1294,6 +1203,262 @@ module.exports = renderAbc;
1294
1203
 
1295
1204
  /***/ }),
1296
1205
 
1206
+ /***/ "./src/const/key-accidentals.js":
1207
+ /*!**************************************!*\
1208
+ !*** ./src/const/key-accidentals.js ***!
1209
+ \**************************************/
1210
+ /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
1211
+
1212
+ var _require = __webpack_require__(/*! ./relative-major */ "./src/const/relative-major.js"),
1213
+ relativeMajor = _require.relativeMajor;
1214
+
1215
+ var key1sharp = {
1216
+ acc: 'sharp',
1217
+ note: 'f'
1218
+ };
1219
+ var key2sharp = {
1220
+ acc: 'sharp',
1221
+ note: 'c'
1222
+ };
1223
+ var key3sharp = {
1224
+ acc: 'sharp',
1225
+ note: 'g'
1226
+ };
1227
+ var key4sharp = {
1228
+ acc: 'sharp',
1229
+ note: 'd'
1230
+ };
1231
+ var key5sharp = {
1232
+ acc: 'sharp',
1233
+ note: 'A'
1234
+ };
1235
+ var key6sharp = {
1236
+ acc: 'sharp',
1237
+ note: 'e'
1238
+ };
1239
+ var key7sharp = {
1240
+ acc: 'sharp',
1241
+ note: 'B'
1242
+ };
1243
+ var key1flat = {
1244
+ acc: 'flat',
1245
+ note: 'B'
1246
+ };
1247
+ var key2flat = {
1248
+ acc: 'flat',
1249
+ note: 'e'
1250
+ };
1251
+ var key3flat = {
1252
+ acc: 'flat',
1253
+ note: 'A'
1254
+ };
1255
+ var key4flat = {
1256
+ acc: 'flat',
1257
+ note: 'd'
1258
+ };
1259
+ var key5flat = {
1260
+ acc: 'flat',
1261
+ note: 'G'
1262
+ };
1263
+ var key6flat = {
1264
+ acc: 'flat',
1265
+ note: 'c'
1266
+ };
1267
+ var key7flat = {
1268
+ acc: 'flat',
1269
+ note: 'F'
1270
+ };
1271
+ var keys = {
1272
+ 'C#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
1273
+ 'F#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
1274
+ 'B': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
1275
+ 'E': [key1sharp, key2sharp, key3sharp, key4sharp],
1276
+ 'A': [key1sharp, key2sharp, key3sharp],
1277
+ 'D': [key1sharp, key2sharp],
1278
+ 'G': [key1sharp],
1279
+ 'C': [],
1280
+ 'F': [key1flat],
1281
+ 'Bb': [key1flat, key2flat],
1282
+ 'Eb': [key1flat, key2flat, key3flat],
1283
+ 'Cm': [key1flat, key2flat, key3flat],
1284
+ 'Ab': [key1flat, key2flat, key3flat, key4flat],
1285
+ 'Db': [key1flat, key2flat, key3flat, key4flat, key5flat],
1286
+ 'Gb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
1287
+ 'Cb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
1288
+ // The following are not in the 2.0 spec, but seem normal enough.
1289
+ // TODO-PER: These SOUND the same as what's written, but they aren't right
1290
+ 'A#': [key1flat, key2flat],
1291
+ 'B#': [],
1292
+ 'D#': [key1flat, key2flat, key3flat],
1293
+ 'E#': [key1flat],
1294
+ 'G#': [key1flat, key2flat, key3flat, key4flat],
1295
+ 'none': []
1296
+ };
1297
+
1298
+ function keyAccidentals(key) {
1299
+ var newKey = keys[relativeMajor(key)];
1300
+ if (!newKey) // If we don't recognize the key then there is no change
1301
+ return null;
1302
+ return JSON.parse(JSON.stringify(newKey));
1303
+ }
1304
+
1305
+ ;
1306
+ module.exports = keyAccidentals;
1307
+
1308
+ /***/ }),
1309
+
1310
+ /***/ "./src/const/relative-major.js":
1311
+ /*!*************************************!*\
1312
+ !*** ./src/const/relative-major.js ***!
1313
+ \*************************************/
1314
+ /***/ (function(module) {
1315
+
1316
+ // All these keys have the same number of accidentals
1317
+ var keys = {
1318
+ 'C': {
1319
+ modes: ['CMaj', 'Amin', 'Am', 'GMix', 'DDor', 'EPhr', 'FLyd', 'BLoc'],
1320
+ stepsFromC: 0
1321
+ },
1322
+ 'Db': {
1323
+ modes: ['DbMaj', 'Bbmin', 'Bbm', 'AbMix', 'EbDor', 'FPhr', 'GbLyd', 'CLoc'],
1324
+ stepsFromC: 1
1325
+ },
1326
+ 'D': {
1327
+ modes: ['DMaj', 'Bmin', 'Bm', 'AMix', 'EDor', 'F#Phr', 'GLyd', 'C#Loc'],
1328
+ stepsFromC: 2
1329
+ },
1330
+ 'Eb': {
1331
+ modes: ['EbMaj', 'Cmin', 'Cm', 'BbMix', 'FDor', 'GPhr', 'AbLyd', 'DLoc'],
1332
+ stepsFromC: 3
1333
+ },
1334
+ 'E': {
1335
+ modes: ['EMaj', 'C#min', 'C#m', 'BMix', 'F#Dor', 'G#Phr', 'ALyd', 'D#Loc'],
1336
+ stepsFromC: 4
1337
+ },
1338
+ 'F': {
1339
+ modes: ['FMaj', 'Dmin', 'Dm', 'CMix', 'GDor', 'APhr', 'BbLyd', 'ELoc'],
1340
+ stepsFromC: 5
1341
+ },
1342
+ 'Gb': {
1343
+ modes: ['GbMaj', 'Ebmin', 'Ebm', 'DbMix', 'AbDor', 'BbPhr', 'CbLyd', 'FLoc'],
1344
+ stepsFromC: 6
1345
+ },
1346
+ 'G': {
1347
+ modes: ['GMaj', 'Emin', 'Em', 'DMix', 'ADor', 'BPhr', 'CLyd', 'F#Loc'],
1348
+ stepsFromC: 7
1349
+ },
1350
+ 'Ab': {
1351
+ modes: ['AbMaj', 'Fmin', 'Fm', 'EbMix', 'BbDor', 'CPhr', 'DbLyd', 'GLoc'],
1352
+ stepsFromC: 8
1353
+ },
1354
+ 'A': {
1355
+ modes: ['AMaj', 'F#min', 'F#m', 'EMix', 'BDor', 'C#Phr', 'DLyd', 'G#Loc'],
1356
+ stepsFromC: 9
1357
+ },
1358
+ 'Bb': {
1359
+ modes: ['BbMaj', 'Gmin', 'Gm', 'FMix', 'CDor', 'DPhr', 'EbLyd', 'ALoc'],
1360
+ stepsFromC: 10
1361
+ },
1362
+ 'B': {
1363
+ modes: ['BMaj', 'G#min', 'G#m', 'F#Mix', 'C#Dor', 'D#Phr', 'ELyd', 'A#Loc'],
1364
+ stepsFromC: 11
1365
+ },
1366
+ // Enharmonic keys
1367
+ 'C#': {
1368
+ modes: ['C#Maj', 'A#min', 'A#m', 'G#Mix', 'D#Dor', 'E#Phr', 'F#Lyd', 'B#Loc'],
1369
+ stepsFromC: 1
1370
+ },
1371
+ 'F#': {
1372
+ modes: ['F#Maj', 'D#min', 'D#m', 'C#Mix', 'G#Dor', 'A#Phr', 'BLyd', 'E#Loc'],
1373
+ stepsFromC: 6
1374
+ },
1375
+ 'Cb': {
1376
+ modes: ['CbMaj', 'Abmin', 'Abm', 'GbMix', 'DbDor', 'EbPhr', 'FbLyd', 'BbLoc'],
1377
+ stepsFromC: 11
1378
+ }
1379
+ };
1380
+ var keyReverse = null;
1381
+
1382
+ function createKeyReverse() {
1383
+ keyReverse = {};
1384
+ var allKeys = Object.keys(keys);
1385
+
1386
+ for (var i = 0; i < allKeys.length; i++) {
1387
+ var keyObj = keys[allKeys[i]];
1388
+ keyReverse[allKeys[i].toLowerCase()] = allKeys[i];
1389
+
1390
+ for (var j = 0; j < keyObj.modes.length; j++) {
1391
+ var mode = keyObj.modes[j].toLowerCase();
1392
+ keyReverse[mode] = allKeys[i];
1393
+ }
1394
+ }
1395
+ }
1396
+
1397
+ function relativeMajor(key) {
1398
+ // Translate a key to its relative major. If it doesn't exist, do the best we can
1399
+ // by just returning the original key.
1400
+ // There are alternate spellings of these - so the search needs to be case insensitive.
1401
+ // To make this efficient, the first time this is called the "keys" object is reversed so this search is fast in the future
1402
+ if (!keyReverse) {
1403
+ createKeyReverse();
1404
+ } // get the key portion itself - there might be other stuff, like extra sharps and flats, or the mode written out.
1405
+
1406
+
1407
+ var mode = key.toLowerCase().match(/([a-g][b#]?)(maj|min|mix|dor|phr|lyd|loc|m)?/);
1408
+ if (!mode || !mode[2]) return key;
1409
+ mode = mode[1] + mode[2];
1410
+ var maj = keyReverse[mode];
1411
+ if (maj) return maj;
1412
+ return key;
1413
+ }
1414
+
1415
+ function relativeMode(majorKey, mode) {
1416
+ // The reverse of the relativeMajor. Translate it back to the original mode.
1417
+ // If it isn't a recognized mode or it is already major, then just return the major key.
1418
+ var group = keys[majorKey];
1419
+ if (!group) return majorKey;
1420
+ if (mode === '') return majorKey;
1421
+ var match = mode.toLowerCase().match(/^(maj|min|mix|dor|phr|lyd|loc|m)/);
1422
+ if (!match) return majorKey;
1423
+ var regMode = match[1];
1424
+
1425
+ for (var i = 0; i < group.modes.length; i++) {
1426
+ var thisMode = group.modes[i];
1427
+ var ind = thisMode.toLowerCase().indexOf(regMode);
1428
+ if (ind !== -1 && ind === thisMode.length - regMode.length) return thisMode.substring(0, thisMode.length - regMode.length);
1429
+ }
1430
+
1431
+ return majorKey;
1432
+ }
1433
+
1434
+ function transposeKey(key, steps) {
1435
+ // This takes a major key and adds the desired steps.
1436
+ // It assigns each key a number that is the number of steps from C so that there can just be arithmetic.
1437
+ var match = keys[key];
1438
+ if (!match) return key;
1439
+
1440
+ while (steps < 0) {
1441
+ steps += 12;
1442
+ }
1443
+
1444
+ var fromC = (match.stepsFromC + steps) % 12;
1445
+
1446
+ for (var i = 0; i < Object.keys(keys).length; i++) {
1447
+ var k = Object.keys(keys)[i];
1448
+ if (keys[k].stepsFromC === fromC) return k;
1449
+ }
1450
+
1451
+ return key;
1452
+ }
1453
+
1454
+ module.exports = {
1455
+ relativeMajor: relativeMajor,
1456
+ relativeMode: relativeMode,
1457
+ transposeKey: transposeKey
1458
+ };
1459
+
1460
+ /***/ }),
1461
+
1297
1462
  /***/ "./src/data/abc_tune.js":
1298
1463
  /*!******************************!*\
1299
1464
  !*** ./src/data/abc_tune.js ***!
@@ -3232,6 +3397,7 @@ var Parse = function Parse() {
3232
3397
 
3233
3398
  var addWord = function addWord(i) {
3234
3399
  var word = parseCommon.strip(words.substring(last_divider, i));
3400
+ word = word.replace(/\\([-_*|~])/g, '$1');
3235
3401
  last_divider = i + 1;
3236
3402
 
3237
3403
  if (word.length > 0) {
@@ -3249,15 +3415,17 @@ var Parse = function Parse() {
3249
3415
  return false;
3250
3416
  };
3251
3417
 
3418
+ var escNext = false;
3419
+
3252
3420
  for (var i = 0; i < words.length; i++) {
3253
- switch (words.charAt(i)) {
3421
+ switch (words[i]) {
3254
3422
  case ' ':
3255
3423
  case '\x12':
3256
3424
  addWord(i);
3257
3425
  break;
3258
3426
 
3259
3427
  case '-':
3260
- if (!addWord(i) && word_list.length > 0) {
3428
+ if (!escNext && !addWord(i) && word_list.length > 0) {
3261
3429
  parseCommon.last(word_list).divider = '-';
3262
3430
  word_list.push({
3263
3431
  skip: true,
@@ -3268,33 +3436,47 @@ var Parse = function Parse() {
3268
3436
  break;
3269
3437
 
3270
3438
  case '_':
3271
- addWord(i);
3272
- word_list.push({
3273
- skip: true,
3274
- to: 'slur'
3275
- });
3439
+ if (!escNext) {
3440
+ addWord(i);
3441
+ word_list.push({
3442
+ skip: true,
3443
+ to: 'slur'
3444
+ });
3445
+ }
3446
+
3276
3447
  break;
3277
3448
 
3278
3449
  case '*':
3279
- addWord(i);
3280
- word_list.push({
3281
- skip: true,
3282
- to: 'next'
3283
- });
3450
+ if (!escNext) {
3451
+ addWord(i);
3452
+ word_list.push({
3453
+ skip: true,
3454
+ to: 'next'
3455
+ });
3456
+ }
3457
+
3284
3458
  break;
3285
3459
 
3286
3460
  case '|':
3287
- addWord(i);
3288
- word_list.push({
3289
- skip: true,
3290
- to: 'bar'
3291
- });
3461
+ if (!escNext) {
3462
+ addWord(i);
3463
+ word_list.push({
3464
+ skip: true,
3465
+ to: 'bar'
3466
+ });
3467
+ }
3468
+
3292
3469
  break;
3293
3470
 
3294
3471
  case '~':
3295
- replace = true;
3472
+ if (!escNext) {
3473
+ replace = true;
3474
+ }
3475
+
3296
3476
  break;
3297
3477
  }
3478
+
3479
+ escNext = words[i] === '\\';
3298
3480
  }
3299
3481
 
3300
3482
  var inSlur = false;
@@ -3678,15 +3860,16 @@ var bookParser = function bookParser(book) {
3678
3860
  "use strict";
3679
3861
 
3680
3862
  var directives = "";
3863
+ var initialWhiteSpace = book.match(/(\s*)/);
3681
3864
  book = parseCommon.strip(book);
3682
3865
  var tuneStrings = book.split("\nX:"); // Put back the X: that we lost when splitting the tunes.
3683
3866
 
3684
3867
  for (var i = 1; i < tuneStrings.length; i++) {
3685
3868
  tuneStrings[i] = "X:" + tuneStrings[i];
3686
- } // Keep track of the character position each tune starts with.
3869
+ } // Keep track of the character position each tune starts with. If the string starts with white space, count that, too.
3687
3870
 
3688
3871
 
3689
- var pos = 0;
3872
+ var pos = initialWhiteSpace ? initialWhiteSpace[0].length : 0;
3690
3873
  var tunes = [];
3691
3874
  parseCommon.each(tuneStrings, function (tune) {
3692
3875
  tunes.push({
@@ -5979,179 +6162,7 @@ var parseKeyVoice = {};
5979
6162
  };
5980
6163
 
5981
6164
  parseKeyVoice.standardKey = function (keyName, root, acc, localTranspose) {
5982
- var key1sharp = {
5983
- acc: 'sharp',
5984
- note: 'f'
5985
- };
5986
- var key2sharp = {
5987
- acc: 'sharp',
5988
- note: 'c'
5989
- };
5990
- var key3sharp = {
5991
- acc: 'sharp',
5992
- note: 'g'
5993
- };
5994
- var key4sharp = {
5995
- acc: 'sharp',
5996
- note: 'd'
5997
- };
5998
- var key5sharp = {
5999
- acc: 'sharp',
6000
- note: 'A'
6001
- };
6002
- var key6sharp = {
6003
- acc: 'sharp',
6004
- note: 'e'
6005
- };
6006
- var key7sharp = {
6007
- acc: 'sharp',
6008
- note: 'B'
6009
- };
6010
- var key1flat = {
6011
- acc: 'flat',
6012
- note: 'B'
6013
- };
6014
- var key2flat = {
6015
- acc: 'flat',
6016
- note: 'e'
6017
- };
6018
- var key3flat = {
6019
- acc: 'flat',
6020
- note: 'A'
6021
- };
6022
- var key4flat = {
6023
- acc: 'flat',
6024
- note: 'd'
6025
- };
6026
- var key5flat = {
6027
- acc: 'flat',
6028
- note: 'G'
6029
- };
6030
- var key6flat = {
6031
- acc: 'flat',
6032
- note: 'c'
6033
- };
6034
- var key7flat = {
6035
- acc: 'flat',
6036
- note: 'F'
6037
- };
6038
- var keys = {
6039
- 'C#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
6040
- 'A#m': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
6041
- 'G#Mix': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
6042
- 'D#Dor': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
6043
- 'E#Phr': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
6044
- 'F#Lyd': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
6045
- 'B#Loc': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
6046
- 'F#': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
6047
- 'D#m': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
6048
- 'C#Mix': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
6049
- 'G#Dor': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
6050
- 'A#Phr': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
6051
- 'BLyd': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
6052
- 'E#Loc': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp],
6053
- 'B': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
6054
- 'G#m': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
6055
- 'F#Mix': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
6056
- 'C#Dor': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
6057
- 'D#Phr': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
6058
- 'ELyd': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
6059
- 'A#Loc': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp],
6060
- 'E': [key1sharp, key2sharp, key3sharp, key4sharp],
6061
- 'C#m': [key1sharp, key2sharp, key3sharp, key4sharp],
6062
- 'BMix': [key1sharp, key2sharp, key3sharp, key4sharp],
6063
- 'F#Dor': [key1sharp, key2sharp, key3sharp, key4sharp],
6064
- 'G#Phr': [key1sharp, key2sharp, key3sharp, key4sharp],
6065
- 'ALyd': [key1sharp, key2sharp, key3sharp, key4sharp],
6066
- 'D#Loc': [key1sharp, key2sharp, key3sharp, key4sharp],
6067
- 'A': [key1sharp, key2sharp, key3sharp],
6068
- 'F#m': [key1sharp, key2sharp, key3sharp],
6069
- 'EMix': [key1sharp, key2sharp, key3sharp],
6070
- 'BDor': [key1sharp, key2sharp, key3sharp],
6071
- 'C#Phr': [key1sharp, key2sharp, key3sharp],
6072
- 'DLyd': [key1sharp, key2sharp, key3sharp],
6073
- 'G#Loc': [key1sharp, key2sharp, key3sharp],
6074
- 'D': [key1sharp, key2sharp],
6075
- 'Bm': [key1sharp, key2sharp],
6076
- 'AMix': [key1sharp, key2sharp],
6077
- 'EDor': [key1sharp, key2sharp],
6078
- 'F#Phr': [key1sharp, key2sharp],
6079
- 'GLyd': [key1sharp, key2sharp],
6080
- 'C#Loc': [key1sharp, key2sharp],
6081
- 'G': [key1sharp],
6082
- 'Em': [key1sharp],
6083
- 'DMix': [key1sharp],
6084
- 'ADor': [key1sharp],
6085
- 'BPhr': [key1sharp],
6086
- 'CLyd': [key1sharp],
6087
- 'F#Loc': [key1sharp],
6088
- 'C': [],
6089
- 'Am': [],
6090
- 'GMix': [],
6091
- 'DDor': [],
6092
- 'EPhr': [],
6093
- 'FLyd': [],
6094
- 'BLoc': [],
6095
- 'F': [key1flat],
6096
- 'Dm': [key1flat],
6097
- 'CMix': [key1flat],
6098
- 'GDor': [key1flat],
6099
- 'APhr': [key1flat],
6100
- 'BbLyd': [key1flat],
6101
- 'ELoc': [key1flat],
6102
- 'Bb': [key1flat, key2flat],
6103
- 'Gm': [key1flat, key2flat],
6104
- 'FMix': [key1flat, key2flat],
6105
- 'CDor': [key1flat, key2flat],
6106
- 'DPhr': [key1flat, key2flat],
6107
- 'EbLyd': [key1flat, key2flat],
6108
- 'ALoc': [key1flat, key2flat],
6109
- 'Eb': [key1flat, key2flat, key3flat],
6110
- 'Cm': [key1flat, key2flat, key3flat],
6111
- 'BbMix': [key1flat, key2flat, key3flat],
6112
- 'FDor': [key1flat, key2flat, key3flat],
6113
- 'GPhr': [key1flat, key2flat, key3flat],
6114
- 'AbLyd': [key1flat, key2flat, key3flat],
6115
- 'DLoc': [key1flat, key2flat, key3flat],
6116
- 'Ab': [key1flat, key2flat, key3flat, key4flat],
6117
- 'Fm': [key1flat, key2flat, key3flat, key4flat],
6118
- 'EbMix': [key1flat, key2flat, key3flat, key4flat],
6119
- 'BbDor': [key1flat, key2flat, key3flat, key4flat],
6120
- 'CPhr': [key1flat, key2flat, key3flat, key4flat],
6121
- 'DbLyd': [key1flat, key2flat, key3flat, key4flat],
6122
- 'GLoc': [key1flat, key2flat, key3flat, key4flat],
6123
- 'Db': [key1flat, key2flat, key3flat, key4flat, key5flat],
6124
- 'Bbm': [key1flat, key2flat, key3flat, key4flat, key5flat],
6125
- 'AbMix': [key1flat, key2flat, key3flat, key4flat, key5flat],
6126
- 'EbDor': [key1flat, key2flat, key3flat, key4flat, key5flat],
6127
- 'FPhr': [key1flat, key2flat, key3flat, key4flat, key5flat],
6128
- 'GbLyd': [key1flat, key2flat, key3flat, key4flat, key5flat],
6129
- 'CLoc': [key1flat, key2flat, key3flat, key4flat, key5flat],
6130
- 'Gb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
6131
- 'Ebm': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
6132
- 'DbMix': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
6133
- 'AbDor': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
6134
- 'BbPhr': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
6135
- 'CbLyd': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
6136
- 'FLoc': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat],
6137
- 'Cb': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
6138
- 'Abm': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
6139
- 'GbMix': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
6140
- 'DbDor': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
6141
- 'EbPhr': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
6142
- 'FbLyd': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
6143
- 'BbLoc': [key1flat, key2flat, key3flat, key4flat, key5flat, key6flat, key7flat],
6144
- // The following are not in the 2.0 spec, but seem normal enough.
6145
- // TODO-PER: These SOUND the same as what's written, but they aren't right
6146
- 'A#': [key1flat, key2flat],
6147
- 'B#': [],
6148
- 'D#': [key1flat, key2flat, key3flat],
6149
- 'E#': [key1flat],
6150
- 'G#': [key1flat, key2flat, key3flat, key4flat],
6151
- 'Gbm': [key1sharp, key2sharp, key3sharp, key4sharp, key5sharp, key6sharp, key7sharp],
6152
- 'none': []
6153
- };
6154
- return transpose.keySignature(multilineVars, keys, keyName, root, acc, localTranspose);
6165
+ return transpose.keySignature(multilineVars, keyName, root, acc, localTranspose);
6155
6166
  };
6156
6167
 
6157
6168
  var clefLines = {
@@ -7546,7 +7557,7 @@ MusicParser.prototype.parseMusic = function (line) {
7546
7557
  }
7547
7558
 
7548
7559
  multilineVars.addFormattingOptions(el, tune.formatting, 'bar');
7549
- tuneBuilder.appendElement('bar', startOfLine + i, startOfLine + i + ret[0], bar);
7560
+ tuneBuilder.appendElement('bar', startOfLine + startI, startOfLine + i + ret[0], bar);
7550
7561
  multilineVars.measureNotEmpty = false;
7551
7562
  el = {};
7552
7563
  }
@@ -7985,7 +7996,7 @@ function durationOfMeasure(multilineVars) {
7985
7996
 
7986
7997
  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"];
7987
7998
  var volumeDecorations = ["p", "pp", "f", "ff", "mf", "mp", "ppp", "pppp", "fff", "ffff", "sfz"];
7988
- var dynamicDecorations = ["crescendo(", "crescendo)", "diminuendo(", "diminuendo)"];
7999
+ var dynamicDecorations = ["crescendo(", "crescendo)", "diminuendo(", "diminuendo)", "glissando(", "glissando)"];
7989
8000
  var accentPseudonyms = [["<", "accent"], [">", "accent"], ["tr", "trill"], ["plus", "+"], ["emphasis", "accent"], ["^", "umarcato"], ["marcato", "umarcato"]];
7990
8001
  var accentDynamicPseudonyms = [["<(", "crescendo("], ["<)", "crescendo)"], [">(", "diminuendo("], [">)", "diminuendo)"]];
7991
8002
 
@@ -10135,6 +10146,10 @@ module.exports = Tokenizer;
10135
10146
  // abc_transpose.js: Handles the automatic transposition of key signatures, chord symbols, and notes.
10136
10147
  var allNotes = __webpack_require__(/*! ./all-notes */ "./src/parse/all-notes.js");
10137
10148
 
10149
+ var transposeChordName = __webpack_require__(/*! ../parse/transpose-chord */ "./src/parse/transpose-chord.js");
10150
+
10151
+ var keyAccidentals = __webpack_require__(/*! ../const/key-accidentals */ "./src/const/key-accidentals.js");
10152
+
10138
10153
  var transpose = {};
10139
10154
  var keyIndex = {
10140
10155
  'C': 0,
@@ -10158,16 +10173,16 @@ var keyIndex = {
10158
10173
  var newKey = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B'];
10159
10174
  var newKeyMinor = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'Bb', 'B'];
10160
10175
 
10161
- transpose.keySignature = function (multilineVars, keys, keyName, root, acc, localTranspose) {
10162
- if (multilineVars.clef.type === "perc") return {
10163
- accidentals: keys[keyName],
10176
+ transpose.keySignature = function (multilineVars, keyName, root, acc, localTranspose) {
10177
+ if (multilineVars.clef.type === "perc" || multilineVars.clef.type === "none") return {
10178
+ accidentals: keyAccidentals(keyName),
10164
10179
  root: root,
10165
10180
  acc: acc
10166
10181
  };
10167
10182
  if (!localTranspose) localTranspose = 0;
10168
10183
  multilineVars.localTransposeVerticalMovement = 0;
10169
10184
  multilineVars.localTransposePreferFlats = false;
10170
- var k = keys[keyName];
10185
+ var k = keyAccidentals(keyName);
10171
10186
  if (!k) return multilineVars.key; // If the key isn't in the list, it is non-standard. We won't attempt to transpose it.
10172
10187
 
10173
10188
  multilineVars.localTranspose = (multilineVars.globalTranspose ? multilineVars.globalTranspose : 0) + localTranspose;
@@ -10203,7 +10218,7 @@ transpose.keySignature = function (multilineVars, keys, keyName, root, acc, loca
10203
10218
  if (index > 11) index = index % 12;
10204
10219
  var newKeyName = keyName[0] === 'm' ? newKeyMinor[index] : newKey[index];
10205
10220
  var transposedKey = newKeyName + keyName;
10206
- var newKeySig = keys[transposedKey];
10221
+ var newKeySig = keyAccidentals(transposedKey);
10207
10222
  if (newKeySig.length > 0 && newKeySig[0].acc === 'flat') multilineVars.localTransposePreferFlats = true;
10208
10223
  var distance = transposedKey.charCodeAt(0) - baseKey.charCodeAt(0);
10209
10224
 
@@ -10232,76 +10247,8 @@ transpose.keySignature = function (multilineVars, keys, keyName, root, acc, loca
10232
10247
  };
10233
10248
  };
10234
10249
 
10235
- var sharpChords = ['C', 'C♯', 'D', "D♯", 'E', 'F', "F♯", 'G', 'G♯', 'A', 'A♯', 'B'];
10236
- var flatChords = ['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B'];
10237
- var sharpChordsFree = ['C', 'C#', 'D', "D#", 'E', 'F', "F#", 'G', 'G#', 'A', 'A#', 'B'];
10238
- var flatChordsFree = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
10239
-
10240
10250
  transpose.chordName = function (multilineVars, chord) {
10241
- if (multilineVars.localTranspose && multilineVars.localTranspose % 12 !== 0) {
10242
- // The chords are the same if it is an exact octave change.
10243
- var transposeFactor = multilineVars.localTranspose;
10244
-
10245
- while (transposeFactor < 0) {
10246
- transposeFactor += 12;
10247
- }
10248
-
10249
- if (transposeFactor > 11) transposeFactor = transposeFactor % 12;
10250
-
10251
- if (multilineVars.freegchord) {
10252
- chord = chord.replace(/Cb/g, "`~11`");
10253
- chord = chord.replace(/Db/g, "`~1`");
10254
- chord = chord.replace(/Eb/g, "`~3`");
10255
- chord = chord.replace(/Fb/g, "`~4`");
10256
- chord = chord.replace(/Gb/g, "`~6`");
10257
- chord = chord.replace(/Ab/g, "`~8`");
10258
- chord = chord.replace(/Bb/g, "`~10`");
10259
- chord = chord.replace(/C#/g, "`~1`");
10260
- chord = chord.replace(/D#/g, "`~3`");
10261
- chord = chord.replace(/E#/g, "`~5`");
10262
- chord = chord.replace(/F#/g, "`~6`");
10263
- chord = chord.replace(/G#/g, "`~8`");
10264
- chord = chord.replace(/A#/g, "`~10`");
10265
- chord = chord.replace(/B#/g, "`~0`");
10266
- } else {
10267
- chord = chord.replace(/C♭/g, "`~11`");
10268
- chord = chord.replace(/D♭/g, "`~1`");
10269
- chord = chord.replace(/E♭/g, "`~3`");
10270
- chord = chord.replace(/F♭/g, "`~4`");
10271
- chord = chord.replace(/G♭/g, "`~6`");
10272
- chord = chord.replace(/A♭/g, "`~8`");
10273
- chord = chord.replace(/B♭/g, "`~10`");
10274
- chord = chord.replace(/C♯/g, "`~1`");
10275
- chord = chord.replace(/D♯/g, "`~3`");
10276
- chord = chord.replace(/E♯/g, "`~5`");
10277
- chord = chord.replace(/F♯/g, "`~6`");
10278
- chord = chord.replace(/G♯/g, "`~8`");
10279
- chord = chord.replace(/A♯/g, "`~10`");
10280
- chord = chord.replace(/B♯/g, "`~0`");
10281
- }
10282
-
10283
- chord = chord.replace(/C/g, "`~0`");
10284
- chord = chord.replace(/D/g, "`~2`");
10285
- chord = chord.replace(/E/g, "`~4`");
10286
- chord = chord.replace(/F/g, "`~5`");
10287
- chord = chord.replace(/G/g, "`~7`");
10288
- chord = chord.replace(/A/g, "`~9`");
10289
- chord = chord.replace(/B/g, "`~11`");
10290
- var arr = chord.split("`");
10291
-
10292
- for (var i = 0; i < arr.length; i++) {
10293
- if (arr[i][0] === '~') {
10294
- var chordNum = parseInt(arr[i].substr(1), 10);
10295
- chordNum += transposeFactor;
10296
- if (chordNum > 11) chordNum -= 12;
10297
- if (multilineVars.freegchord) arr[i] = multilineVars.localTransposePreferFlats ? flatChordsFree[chordNum] : sharpChordsFree[chordNum];else arr[i] = multilineVars.localTransposePreferFlats ? flatChords[chordNum] : sharpChords[chordNum];
10298
- }
10299
- }
10300
-
10301
- chord = arr.join("");
10302
- }
10303
-
10304
- return chord;
10251
+ return transposeChordName(chord, multilineVars.localTranspose, multilineVars.localTransposePreferFlats, multilineVars.freegchord);
10305
10252
  };
10306
10253
 
10307
10254
  var pitchToLetter = ['c', 'd', 'e', 'f', 'g', 'a', 'b'];
@@ -10418,6 +10365,83 @@ module.exports = allNotes;
10418
10365
 
10419
10366
  /***/ }),
10420
10367
 
10368
+ /***/ "./src/parse/transpose-chord.js":
10369
+ /*!**************************************!*\
10370
+ !*** ./src/parse/transpose-chord.js ***!
10371
+ \**************************************/
10372
+ /***/ (function(module) {
10373
+
10374
+ var sharpChords = ['C', 'C♯', 'D', "D♯", 'E', 'F', "F♯", 'G', 'G♯', 'A', 'A♯', 'B'];
10375
+ var flatChords = ['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B'];
10376
+ var sharpChordsFree = ['C', 'C#', 'D', "D#", 'E', 'F', "F#", 'G', 'G#', 'A', 'A#', 'B'];
10377
+ var flatChordsFree = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
10378
+
10379
+ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
10380
+ if (!steps || steps % 12 === 0) // The chords are the same if it is an exact octave change.
10381
+ return chord; // There are two things in the chord that might need to be transposed:
10382
+ // The chord will start with a letter from A-G, and might have one accidental after it.
10383
+ // That accidental might be an actual sharp or flat char, or it might be a pound sign or lower case "b".
10384
+ // Then there is a bunch of stuff that isn't transposed and should just be copied. That is stuff like "7" and more complicated chords.
10385
+ // But there is one other exception: right after a slash there will be a bass note and possibly an accidental. That should also be transposed.
10386
+
10387
+ while (steps < 0) {
10388
+ steps += 12;
10389
+ }
10390
+
10391
+ if (steps > 11) steps = steps % 12; // (chord name w/accidental) (a bunch of stuff) (/) (bass note) (anything else)
10392
+
10393
+ var match = chord.match(/^([A-G][b#♭♯]?)([^\/]+)?\/?([A-G][b#♭♯]?)?(.+)?/);
10394
+ if (!match) return chord; // We don't recognize the format of the chord, so skip it.
10395
+
10396
+ var name = match[1];
10397
+ var extra1 = match[2];
10398
+ var bass = match[3];
10399
+ var extra2 = match[4];
10400
+ var index = sharpChords.indexOf(name);
10401
+ if (index < 0) index = flatChords.indexOf(name);
10402
+ if (index < 0) index = sharpChordsFree.indexOf(name);
10403
+ if (index < 0) index = flatChordsFree.indexOf(name);
10404
+ if (index < 0) return chord; // This should never happen, but if we can't find the chord just bail.
10405
+
10406
+ index += steps;
10407
+ index = index % 12;
10408
+
10409
+ if (preferFlats) {
10410
+ if (freeGCchord) chord = flatChordsFree[index];else chord = flatChords[index];
10411
+ } else {
10412
+ if (freeGCchord) chord = sharpChordsFree[index];else chord = sharpChords[index];
10413
+ }
10414
+
10415
+ if (extra1) chord += extra1;
10416
+
10417
+ if (bass) {
10418
+ var index = sharpChords.indexOf(bass);
10419
+ if (index < 0) index = flatChords.indexOf(bass);
10420
+ if (index < 0) index = sharpChordsFree.indexOf(bass);
10421
+ if (index < 0) index = flatChordsFree.indexOf(bass);
10422
+ chord += '/';
10423
+
10424
+ if (index >= 0) {
10425
+ index += steps;
10426
+ index = index % 12;
10427
+
10428
+ if (preferFlats) {
10429
+ if (freeGCchord) chord += flatChordsFree[index];else chord += flatChords[index];
10430
+ } else {
10431
+ if (freeGCchord) chord += sharpChordsFree[index];else chord += sharpChords[index];
10432
+ }
10433
+ } else chord += bass; // Don't know what to do so do nothing
10434
+
10435
+ }
10436
+
10437
+ if (extra2) chord += extra2;
10438
+ return chord;
10439
+ }
10440
+
10441
+ module.exports = transposeChordName;
10442
+
10443
+ /***/ }),
10444
+
10421
10445
  /***/ "./src/parse/tune-builder.js":
10422
10446
  /*!***********************************!*\
10423
10447
  !*** ./src/parse/tune-builder.js ***!
@@ -11890,107 +11914,676 @@ function getRevisedTuneParams(lineBreaks, staffWidth, params) {
11890
11914
  }
11891
11915
  }
11892
11916
 
11893
- return {
11894
- revisedParams: revisedParams
11895
- };
11896
- }
11917
+ return {
11918
+ revisedParams: revisedParams
11919
+ };
11920
+ }
11921
+
11922
+ function calcLineWraps(tune, widths, params) {
11923
+ // For calculating how much can go on the line, it depends on the width of the line. It is a convenience to just divide it here
11924
+ // by the minimum spacing instead of multiplying the min spacing later.
11925
+ // The scaling works differently: this is done by changing the scaling of the outer SVG, so the scaling needs to be compensated
11926
+ // for here, because the actual width will be different from the calculated numbers.
11927
+ // If the desired width is less than the margin, just punt and return the original tune
11928
+ //console.log(widths)
11929
+ if (widths.length === 0 || params.staffwidth < widths[0].left) {
11930
+ return {
11931
+ reParse: false,
11932
+ explanation: "Staff width is narrower than the margin",
11933
+ revisedParams: params
11934
+ };
11935
+ }
11936
+
11937
+ var scale = params.scale ? Math.max(params.scale, 0.1) : 1;
11938
+ var minSpacing = params.wrap.minSpacing ? Math.max(parseFloat(params.wrap.minSpacing), 1) : 1;
11939
+ var minSpacingLimit = params.wrap.minSpacingLimit ? Math.max(parseFloat(params.wrap.minSpacingLimit), 1) : minSpacing - 0.1;
11940
+ var maxSpacing = params.wrap.maxSpacing ? Math.max(parseFloat(params.wrap.maxSpacing), 1) : undefined;
11941
+ if (params.wrap.lastLineLimit && !maxSpacing) maxSpacing = Math.max(parseFloat(params.wrap.lastLineLimit), 1); // var targetHeight = params.wrap.targetHeight ? Math.max(parseInt(params.wrap.targetHeight, 10), 100) : undefined;
11942
+
11943
+ var preferredMeasuresPerLine = params.wrap.preferredMeasuresPerLine ? Math.max(parseInt(params.wrap.preferredMeasuresPerLine, 10), 0) : undefined;
11944
+ var accumulatedLineBreaks = [];
11945
+ var explanations = [];
11946
+
11947
+ for (var s = 0; s < widths.length; s++) {
11948
+ var section = widths[s];
11949
+ var usableWidth = params.staffwidth - section.left;
11950
+ var lineBreakPoint = usableWidth / minSpacing / scale;
11951
+ var minLineSize = usableWidth / maxSpacing / scale;
11952
+ var allowableVariance = usableWidth / minSpacingLimit / scale;
11953
+ var explanation = {
11954
+ widths: section,
11955
+ lineBreakPoint: lineBreakPoint,
11956
+ minLineSize: minLineSize,
11957
+ attempts: [],
11958
+ staffWidth: params.staffwidth,
11959
+ minWidth: Math.round(allowableVariance)
11960
+ }; // If there is a preferred number of measures per line, test that first. If none of the lines is too long, then we're finished.
11961
+
11962
+ var lineBreaks = null;
11963
+
11964
+ if (preferredMeasuresPerLine) {
11965
+ var f = fixedMeasureLineBreaks(section.measureWidths, lineBreakPoint, preferredMeasuresPerLine);
11966
+ explanation.attempts.push({
11967
+ type: "Fixed Measures Per Line",
11968
+ preferredMeasuresPerLine: preferredMeasuresPerLine,
11969
+ lineBreaks: f.lineBreaks,
11970
+ failed: f.failed,
11971
+ totals: f.totals
11972
+ });
11973
+ if (!f.failed) lineBreaks = f.lineBreaks;
11974
+ } // If we don't have lineBreaks yet, use the free form method of line breaks.
11975
+ // This will be called either if Preferred Measures is not used, or if the music is just weird - like a single measure is way too crowded.
11976
+
11977
+
11978
+ if (!lineBreaks) {
11979
+ var ff = freeFormLineBreaks(section.measureWidths, lineBreakPoint);
11980
+ explanation.attempts.push({
11981
+ type: "Free Form",
11982
+ lineBreaks: ff.lineBreaks,
11983
+ totals: ff.totals
11984
+ });
11985
+ lineBreaks = ff.lineBreaks; // We now have an acceptable number of lines, but the measures may not be optimally distributed. See if there is a better distribution.
11986
+
11987
+ if (lineBreaks.length > 0 && section.measureWidths.length < 25) {
11988
+ // Only do this if everything doesn't fit on one line.
11989
+ // This is an intensive operation and it is optional so just do it for shorter music.
11990
+ ff = optimizeLineWidths(section, lineBreakPoint, lineBreaks, explanation);
11991
+ explanation.attempts.push({
11992
+ type: "Optimize",
11993
+ failed: ff.failed,
11994
+ reason: ff.reason,
11995
+ lineBreaks: ff.lineBreaks,
11996
+ totals: ff.totals
11997
+ });
11998
+ if (!ff.failed) lineBreaks = ff.lineBreaks;
11999
+ }
12000
+ }
12001
+
12002
+ accumulatedLineBreaks.push(lineBreaks);
12003
+ explanations.push(explanation);
12004
+ } // If the vertical space exceeds targetHeight, remove a line and try again. If that is too crowded, then don't use it.
12005
+
12006
+
12007
+ var staffWidth = params.staffwidth;
12008
+ var ret = getRevisedTuneParams(accumulatedLineBreaks, staffWidth, params);
12009
+ ret.explanation = explanations;
12010
+ ret.reParse = true;
12011
+ return ret;
12012
+ }
12013
+
12014
+ module.exports = {
12015
+ wrapLines: wrapLines,
12016
+ calcLineWraps: calcLineWraps
12017
+ };
12018
+
12019
+ /***/ }),
12020
+
12021
+ /***/ "./src/str/output.js":
12022
+ /*!***************************!*\
12023
+ !*** ./src/str/output.js ***!
12024
+ \***************************/
12025
+ /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
12026
+
12027
+ var keyAccidentals = __webpack_require__(/*! ../const/key-accidentals */ "./src/const/key-accidentals.js");
12028
+
12029
+ var _require = __webpack_require__(/*! ../const/relative-major */ "./src/const/relative-major.js"),
12030
+ relativeMajor = _require.relativeMajor,
12031
+ transposeKey = _require.transposeKey,
12032
+ relativeMode = _require.relativeMode;
12033
+
12034
+ var transposeChordName = __webpack_require__(/*! ../parse/transpose-chord */ "./src/parse/transpose-chord.js");
12035
+
12036
+ var strTranspose;
12037
+
12038
+ (function () {
12039
+ "use strict";
12040
+
12041
+ strTranspose = function strTranspose(abc, abcTune, steps) {
12042
+ if (abcTune === "TEST") // Backdoor way to get entry points for unit tests
12043
+ return {
12044
+ keyAccidentals: keyAccidentals,
12045
+ relativeMajor: relativeMajor,
12046
+ transposeKey: transposeKey,
12047
+ relativeMode: relativeMode,
12048
+ transposeChordName: transposeChordName
12049
+ };
12050
+ steps = parseInt(steps, 10);
12051
+ var changes = [];
12052
+ var i;
12053
+
12054
+ for (i = 0; i < abcTune.length; i++) {
12055
+ changes = changes.concat(transposeOneTune(abc, abcTune[i], steps));
12056
+ } // Reverse sort so that we are replacing strings from the end to the beginning so that the indexes aren't invalidated as we go.
12057
+ // (Because voices can be written in different ways we can't count on the notes being encountered in the order they appear in the string.)
12058
+
12059
+
12060
+ changes = changes.sort(function (a, b) {
12061
+ return b.start - a.start;
12062
+ });
12063
+ var output = abc.split('');
12064
+
12065
+ for (i = 0; i < changes.length; i++) {
12066
+ var ch = changes[i];
12067
+ output.splice(ch.start, ch.end - ch.start, ch.note);
12068
+ }
12069
+
12070
+ return output.join('');
12071
+ };
12072
+
12073
+ function transposeOneTune(abc, abcTune, steps) {
12074
+ var changes = []; // Don't transpose bagpipe music - that is a special case and is always a particular key
12075
+
12076
+ var key = abcTune.getKeySignature();
12077
+ if (key.root === 'Hp' || key.root === "HP") return changes;
12078
+ changes = changes.concat(changeAllKeySigs(abc, steps));
12079
+
12080
+ for (var i = 0; i < abcTune.lines.length; i++) {
12081
+ var staves = abcTune.lines[i].staff;
12082
+
12083
+ if (staves) {
12084
+ for (var j = 0; j < staves.length; j++) {
12085
+ var staff = staves[j];
12086
+ if (staff.clef.type !== "perc") changes = changes.concat(transposeVoices(abc, staff.voices, staff.key, steps));
12087
+ }
12088
+ }
12089
+ }
12090
+
12091
+ return changes;
12092
+ }
12093
+
12094
+ function changeAllKeySigs(abc, steps) {
12095
+ var changes = [];
12096
+ var arr = abc.split("K:"); // now each line except the first one will start with whatever is right after "K:"
12097
+
12098
+ var count = arr[0].length;
12099
+
12100
+ for (var i = 1; i < arr.length; i++) {
12101
+ var segment = arr[i];
12102
+ var match = segment.match(/^( *)([A-G])([#b]?)(\w*)/);
12103
+
12104
+ if (match) {
12105
+ var start = count + 2 + match[1].length; // move past the 'K:' and optional white space
12106
+
12107
+ var key = match[2] + match[3] + match[4]; // key name, accidental, and mode
12108
+
12109
+ var destinationKey = newKey({
12110
+ root: match[2],
12111
+ acc: match[3],
12112
+ mode: match[4]
12113
+ }, steps);
12114
+ var dest = destinationKey.root + destinationKey.acc + destinationKey.mode;
12115
+ changes.push({
12116
+ start: start,
12117
+ end: start + key.length,
12118
+ note: dest
12119
+ });
12120
+ }
12121
+
12122
+ count += segment.length + 2;
12123
+ }
12124
+
12125
+ return changes;
12126
+ }
12127
+
12128
+ function transposeVoices(abc, voices, key, steps) {
12129
+ var changes = [];
12130
+ var destinationKey = newKey(key, steps);
12131
+
12132
+ for (var i = 0; i < voices.length; i++) {
12133
+ changes = changes.concat(transposeVoice(abc, voices[i], key.root, createKeyAccidentals(key), destinationKey, steps));
12134
+ }
12135
+
12136
+ return changes;
12137
+ }
12138
+
12139
+ function createKeyAccidentals(key) {
12140
+ var ret = {};
12141
+
12142
+ for (var i = 0; i < key.accidentals.length; i++) {
12143
+ var acc = key.accidentals[i];
12144
+ if (acc.acc === 'flat') ret[acc.note.toUpperCase()] = '_';else if (acc.acc === 'sharp') ret[acc.note.toUpperCase()] = '^';
12145
+ }
12146
+
12147
+ return ret;
12148
+ }
12149
+
12150
+ function setLetterDistance(destinationKey, keyRoot, steps) {
12151
+ var letterDistance = letters.indexOf(destinationKey.root) - letters.indexOf(keyRoot);
12152
+ if (keyRoot === "none") letterDistance = letters.indexOf(destinationKey.root);
12153
+
12154
+ if (letterDistance === 0) {
12155
+ // This could either be a half step (like Eb => E) or almost an octave (like E => Eb)
12156
+ if (steps > 2) // If it is a large leap, then we are going up an octave
12157
+ letterDistance += 7;else if (steps === -12) // If it is a large leap, then we are going down an octave
12158
+ letterDistance -= 7;
12159
+ } else if (steps > 0 && letterDistance < 0) letterDistance += 7;else if (steps < 0 && letterDistance > 0) letterDistance -= 7;
12160
+
12161
+ if (steps > 12) letterDistance += 7;else if (steps < -12) letterDistance -= 7;
12162
+ return letterDistance;
12163
+ }
12164
+
12165
+ function transposeVoice(abc, voice, keyRoot, keyAccidentals, destinationKey, steps) {
12166
+ var changes = [];
12167
+ var letterDistance = setLetterDistance(destinationKey, keyRoot, steps);
12168
+ var measureAccidentals = {};
12169
+ var transposedMeasureAccidentals = {};
12170
+
12171
+ for (var i = 0; i < voice.length; i++) {
12172
+ var el = voice[i];
12173
+
12174
+ if (el.chord) {
12175
+ for (var c = 0; c < el.chord.length; c++) {
12176
+ var ch = el.chord[c];
12177
+
12178
+ if (ch.position === 'default') {
12179
+ var prefersFlats = destinationKey.accidentals.length && destinationKey.accidentals[0].acc === 'flat';
12180
+ var newChord = transposeChordName(ch.name, steps, prefersFlats, true);
12181
+ newChord = newChord.replace(/♭/g, "b").replace(/♯/g, "#");
12182
+ if (newChord !== ch.name) // If we didn't recognize the chord the input is returned unchanged and there is nothing to replace
12183
+ changes.push(replaceChord(abc, el.startChar, el.endChar, newChord));
12184
+ }
12185
+ }
12186
+ }
12187
+
12188
+ if (el.el_type === 'note' && el.pitches) {
12189
+ for (var j = 0; j < el.pitches.length; j++) {
12190
+ var note = parseNote(el.pitches[j].name, keyRoot, keyAccidentals, measureAccidentals);
12191
+ if (note.acc) measureAccidentals[note.name.toUpperCase()] = note.acc;
12192
+ var newPitch = transposePitch(note, destinationKey, letterDistance, transposedMeasureAccidentals);
12193
+ if (newPitch.acc) transposedMeasureAccidentals[newPitch.upper] = newPitch.acc;
12194
+ changes.push(replaceNote(abc, el.startChar, el.endChar, newPitch.acc + newPitch.name, j));
12195
+ }
12196
+
12197
+ if (el.gracenotes) {
12198
+ for (var g = 0; g < el.gracenotes.length; g++) {
12199
+ var grace = parseNote(el.gracenotes[g].name, keyRoot, keyAccidentals, measureAccidentals);
12200
+ if (grace.acc) measureAccidentals[grace.name.toUpperCase()] = grace.acc;
12201
+ var newGrace = transposePitch(grace, destinationKey, letterDistance, measureAccidentals);
12202
+ if (newGrace.acc) transposedMeasureAccidentals[newGrace.upper] = newGrace.acc;
12203
+ changes.push(replaceGrace(abc, el.startChar, el.endChar, newGrace.acc + newGrace.name, g));
12204
+ }
12205
+ }
12206
+ } else if (el.el_type === "bar") {
12207
+ measureAccidentals = {};
12208
+ transposedMeasureAccidentals = {};
12209
+ } else if (el.el_type === "keySignature") {
12210
+ keyRoot = el.root;
12211
+ keyAccidentals = createKeyAccidentals(el);
12212
+ destinationKey = newKey(el, steps);
12213
+ letterDistance = setLetterDistance(destinationKey, keyRoot, steps);
12214
+ }
12215
+ }
12216
+
12217
+ return changes;
12218
+ }
12219
+
12220
+ var letters = "CDEFGAB";
12221
+ var octaves = [",,,,", ",,,", ",,", ",", "", "'", "''", "'''", "''''"];
12222
+
12223
+ function newKey(key, steps) {
12224
+ if (key.root === "none") {
12225
+ return {
12226
+ root: transposeKey("C", steps),
12227
+ mode: "",
12228
+ acc: "",
12229
+ accidentals: []
12230
+ };
12231
+ }
12232
+
12233
+ var major = relativeMajor(key.root + key.acc + key.mode);
12234
+ var newMajor = transposeKey(major, steps);
12235
+ var newMode = relativeMode(newMajor, key.mode);
12236
+ var acc = keyAccidentals(newMajor);
12237
+ return {
12238
+ root: newMode[0],
12239
+ mode: key.mode,
12240
+ acc: newMode.length > 1 ? newMode[1] : '',
12241
+ accidentals: acc
12242
+ };
12243
+ }
12244
+
12245
+ function transposePitch(note, key, letterDistance, measureAccidentals) {
12246
+ // Depending on what the current note and new note are, the octave might have changed
12247
+ // The letterDistance is how far the change is to see if we passed "C" when transposing.
12248
+ var pitch = note.pitch;
12249
+ var origDistFromC = letters.indexOf(note.name);
12250
+ var root = letters.indexOf(key.root);
12251
+ var index = (root + pitch) % 7; // if the note crosses "c" then the octave changes, so that is true of "B" when going up one step, "A" and "B" when going up two steps, etc., and reverse when going down.
12252
+
12253
+ var newDistFromC = origDistFromC + letterDistance;
12254
+ var oct = note.oct;
12255
+
12256
+ while (newDistFromC > 6) {
12257
+ oct++;
12258
+ newDistFromC -= 7;
12259
+ }
12260
+
12261
+ while (newDistFromC < 0) {
12262
+ oct--;
12263
+ newDistFromC += 7;
12264
+ }
12265
+
12266
+ var name = letters[index];
12267
+ var acc = '';
12268
+ var adj = note.adj; // the amount of adjustment depends on the key - if there is a sharp in the key sig, then -1 is a natural, if there isn't, then -1 is a flat.
12269
+
12270
+ var keyAcc = '=';
12271
+
12272
+ for (var i = 0; i < key.accidentals.length; i++) {
12273
+ if (key.accidentals[i].note.toLowerCase() === name.toLowerCase()) {
12274
+ adj = adj + (key.accidentals[i].acc === 'flat' ? -1 : 1);
12275
+ keyAcc = key.accidentals[i].acc === 'flat' ? '_' : '^';
12276
+ break;
12277
+ }
12278
+ }
12279
+
12280
+ switch (adj) {
12281
+ case -2:
12282
+ acc = "__";
12283
+ break;
12284
+
12285
+ case -1:
12286
+ acc = "_";
12287
+ break;
12288
+
12289
+ case 0:
12290
+ acc = "=";
12291
+ break;
12292
+
12293
+ case 1:
12294
+ acc = "^";
12295
+ break;
12296
+
12297
+ case 2:
12298
+ acc = "^^";
12299
+ break;
12300
+
12301
+ case -3:
12302
+ // This requires a triple flat, so bump down the pitch and try again
12303
+ var newNote = {};
12304
+ newNote.pitch = note.pitch - 1;
12305
+ newNote.oct = note.oct;
12306
+ newNote.name = letters[letters.indexOf(note.name) - 1];
12307
+
12308
+ if (!newNote.name) {
12309
+ newNote.name = "B";
12310
+ newNote.oct--;
12311
+ }
12312
+
12313
+ if (newNote.name === "B" || newNote.name === "E") newNote.adj = note.adj + 1;else newNote.adj = note.adj + 2;
12314
+ return transposePitch(newNote, key, letterDistance + 1, measureAccidentals);
12315
+
12316
+ case 3:
12317
+ // This requires a triple sharp, so bump up the pitch and try again
12318
+ var newNote = {};
12319
+ newNote.pitch = note.pitch + 1;
12320
+ newNote.oct = note.oct;
12321
+ newNote.name = letters[letters.indexOf(note.name) + 1];
12322
+
12323
+ if (!newNote.name) {
12324
+ newNote.name = "C";
12325
+ newNote.oct++;
12326
+ }
12327
+
12328
+ if (newNote.name === "C" || newNote.name === "F") newNote.adj = note.adj - 1;else newNote.adj = note.adj - 2;
12329
+ return transposePitch(newNote, key, letterDistance + 1, measureAccidentals);
12330
+ }
12331
+
12332
+ if ((measureAccidentals[name] === acc || !measureAccidentals[name] && acc === keyAcc) && !note.courtesy) acc = "";
12333
+
12334
+ switch (oct) {
12335
+ case 0:
12336
+ name = name + ",,,";
12337
+ break;
12338
+
12339
+ case 1:
12340
+ name = name + ",,";
12341
+ break;
12342
+
12343
+ case 2:
12344
+ name = name + ",";
12345
+ break;
12346
+ // case 3: it is already correct
12347
+
12348
+ case 4:
12349
+ name = name.toLowerCase();
12350
+ break;
12351
+
12352
+ case 5:
12353
+ name = name.toLowerCase() + "'";
12354
+ break;
12355
+
12356
+ case 6:
12357
+ name = name.toLowerCase() + "''";
12358
+ break;
12359
+
12360
+ case 7:
12361
+ name = name.toLowerCase() + "'''";
12362
+ break;
12363
+
12364
+ case 8:
12365
+ name = name.toLowerCase() + "''''";
12366
+ break;
12367
+ }
12368
+
12369
+ if (oct > 4) name = name.toLowerCase();
12370
+ return {
12371
+ acc: acc,
12372
+ name: name,
12373
+ upper: name.toUpperCase()
12374
+ };
12375
+ }
12376
+
12377
+ var regPitch = /([_^=]*)([A-Ga-g])([,']*)/;
12378
+ var regNote = /([_^=]*[A-Ga-g][,']*)(\d*\/*\d*)([\>\<\-\)\.\s\\]*)/;
12379
+ var regOptionalNote = /([_^=]*[A-Ga-g][,']*)?(\d*\/*\d*)?([\>\<\-\)]*)?/;
12380
+ var regSpace = /(\s*)$/; // This the relationship of the note to the tonic and an octave. So what is returned is a distance in steps from the tonic and the amount of adjustment from
12381
+ // a normal scale. That is - in the key of D an F# is two steps from the tonic and no adjustment. A G# is three steps from the tonic and one half-step higher.
12382
+ // I don't think there is any adjustment needed for minor keys since the adjustment is based on the key signature and the accidentals.
12383
+
12384
+ function parseNote(note, keyRoot, keyAccidentals, measureAccidentals) {
12385
+ var root = keyRoot === "none" ? 0 : letters.indexOf(keyRoot);
12386
+ var reg = note.match(regPitch); // reg[1] : "__", "_", "", "=", "^", or "^^"
12387
+ // reg[2] : A-G a-g
12388
+ // reg[3] : commas or apostrophes
12389
+
12390
+ var name = reg[2].toUpperCase();
12391
+ var pos = letters.indexOf(name) - root;
12392
+ if (pos < 0) pos += 7;
12393
+ var oct = octaves.indexOf(reg[3]);
12394
+ if (name === reg[2]) // See if it is a capital letter and subtract an octave if so.
12395
+ oct--;
12396
+ var currentAcc = measureAccidentals[name] || keyAccidentals[name] || "="; // use the key accidentals if they exist, but override with the measure accidentals, and if neither of them exist, use a natural.
12397
+
12398
+ return {
12399
+ acc: reg[1],
12400
+ name: name,
12401
+ pitch: pos,
12402
+ oct: oct,
12403
+ adj: calcAdjustment(reg[1], keyAccidentals[name], measureAccidentals[name]),
12404
+ courtesy: reg[1] === currentAcc
12405
+ };
12406
+ }
12407
+
12408
+ function replaceNote(abc, start, end, newPitch, index) {
12409
+ // There may be more than just the note between the start and end - there could be spaces, there could be a chord symbol, there could be a decoration.
12410
+ // This could also be a part of a chord. If so, then the particular note needs to be teased out.
12411
+ var note = abc.substring(start, end);
12412
+ var match = note.match(new RegExp(regNote.source + regSpace.source), '');
12413
+
12414
+ if (match) {
12415
+ // This will match a single note
12416
+ var noteLen = match[1].length;
12417
+ var trailingLen = match[2].length + match[3].length + match[4].length;
12418
+ var leadingLen = end - start - noteLen - trailingLen;
12419
+ start += leadingLen;
12420
+ end -= trailingLen;
12421
+ } else {
12422
+ // I don't know how to capture more than one note, so I'm separating them. There is a limit of the number of notes in a chord depending on the repeats I have here, but it is unlikely to happen in real music.
12423
+ var regPreBracket = /([^\[]*)/;
12424
+ var regOpenBracket = /\[/;
12425
+ var regCloseBracket = /\-?](\d*\/*\d*)?([\>\<\-\)]*)/;
12426
+ match = note.match(new RegExp(regPreBracket.source + regOpenBracket.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regCloseBracket.source + regSpace.source));
12427
+
12428
+ if (match) {
12429
+ // This will match a chord
12430
+ // Get the number of chars used by the previous notes in this chord
12431
+ var count = 1 + match[1].length; // one character for the open bracket
12432
+
12433
+ for (var i = 0; i < index; i++) {
12434
+ // index is the iteration through the chord. This function gets called for each one.
12435
+ if (match[i * 3 + 2]) count += match[i * 3 + 2].length;
12436
+ if (match[i * 3 + 3]) count += match[i * 3 + 3].length;
12437
+ if (match[i * 3 + 4]) count += match[i * 3 + 4].length;
12438
+ }
12439
+
12440
+ start += count;
12441
+ var endLen = match[index * 3 + 2] ? match[index * 3 + 2].length : 0; // endLen += match[index * 3 + 3] ? match[index * 3 + 3].length : 0
12442
+ // endLen += match[index * 3 + 4] ? match[index * 3 + 4].length : 0
12443
+
12444
+ end = start + endLen;
12445
+ }
12446
+ }
12447
+
12448
+ return {
12449
+ start: start,
12450
+ end: end,
12451
+ note: newPitch
12452
+ };
12453
+ }
12454
+
12455
+ function replaceGrace(abc, start, end, newGrace, index) {
12456
+ var note = abc.substring(start, end); // I don't know how to capture more than one note, so I'm separating them. There is a limit of the number of notes in a chord depending on the repeats I have here, but it is unlikely to happen in real music.
12457
+
12458
+ var regOpenBrace = /\{/;
12459
+ var regCloseBrace = /\}/;
12460
+ var regPreBrace = /([^\{]*)/;
12461
+ var regPreNote = /(\/*)/;
12462
+ var match = note.match(new RegExp(regPreBrace.source + regOpenBrace.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regPreNote.source + regOptionalNote.source + regCloseBrace.source));
12463
+
12464
+ if (match) {
12465
+ // This will match all notes inside a grace symbol
12466
+ // Get the number of chars used by the previous graces
12467
+ var count = 1 + match[1].length; // one character for the open brace, and whatever comes before the brace
12468
+
12469
+ for (var i = 0; i < index; i++) {
12470
+ // index is the iteration through the chord. This function gets called for each one.
12471
+ if (match[i * 3 + 2]) count += match[i * 3 + 2].length;
12472
+ if (match[i * 3 + 3]) count += match[i * 3 + 3].length;
12473
+ if (match[i * 3 + 4]) count += match[i * 3 + 4].length;
12474
+ if (match[i * 3 + 5]) count += match[i * 3 + 5].length;
12475
+ }
12476
+
12477
+ if (match[index * 3 + 2]) count += match[i * 3 + 2].length;
12478
+ start += count;
12479
+ var endLen = match[index * 3 + 3] ? match[index * 3 + 3].length : 0;
12480
+ endLen += match[index * 3 + 4] ? match[index * 3 + 4].length : 0;
12481
+ endLen += match[index * 3 + 5] ? match[index * 3 + 5].length : 0;
12482
+ end = start + endLen;
12483
+ }
12484
+
12485
+ return {
12486
+ start: start,
12487
+ end: end,
12488
+ note: newGrace
12489
+ };
12490
+ }
12491
+
12492
+ function replaceChord(abc, start, end, newChord) {
12493
+ // Isolate the chord and just replace that
12494
+ var match = abc.substring(start, end).match(/([^"]+)?(".+")+/);
12495
+ if (match[1]) start += match[1].length;
12496
+ end = start + match[2].length; // leave the quote in, so skip one more
11897
12497
 
11898
- function calcLineWraps(tune, widths, params) {
11899
- // For calculating how much can go on the line, it depends on the width of the line. It is a convenience to just divide it here
11900
- // by the minimum spacing instead of multiplying the min spacing later.
11901
- // The scaling works differently: this is done by changing the scaling of the outer SVG, so the scaling needs to be compensated
11902
- // for here, because the actual width will be different from the calculated numbers.
11903
- // If the desired width is less than the margin, just punt and return the original tune
11904
- //console.log(widths)
11905
- if (widths.length === 0 || params.staffwidth < widths[0].left) {
11906
12498
  return {
11907
- reParse: false,
11908
- explanation: "Staff width is narrower than the margin",
11909
- revisedParams: params
12499
+ start: start + 1,
12500
+ end: end - 1,
12501
+ note: newChord
11910
12502
  };
11911
12503
  }
11912
12504
 
11913
- var scale = params.scale ? Math.max(params.scale, 0.1) : 1;
11914
- var minSpacing = params.wrap.minSpacing ? Math.max(parseFloat(params.wrap.minSpacing), 1) : 1;
11915
- var minSpacingLimit = params.wrap.minSpacingLimit ? Math.max(parseFloat(params.wrap.minSpacingLimit), 1) : minSpacing - 0.1;
11916
- var maxSpacing = params.wrap.maxSpacing ? Math.max(parseFloat(params.wrap.maxSpacing), 1) : undefined;
11917
- if (params.wrap.lastLineLimit && !maxSpacing) maxSpacing = Math.max(parseFloat(params.wrap.lastLineLimit), 1); // var targetHeight = params.wrap.targetHeight ? Math.max(parseInt(params.wrap.targetHeight, 10), 100) : undefined;
12505
+ function calcAdjustment(thisAccidental, keyAccidental, measureAccidental) {
12506
+ if (!thisAccidental && measureAccidental) {
12507
+ // There was no accidental on this note, but there was earlier in the measure, so we'll use that
12508
+ thisAccidental = measureAccidental;
12509
+ }
11918
12510
 
11919
- var preferredMeasuresPerLine = params.wrap.preferredMeasuresPerLine ? Math.max(parseInt(params.wrap.preferredMeasuresPerLine, 10), 0) : undefined;
11920
- var accumulatedLineBreaks = [];
11921
- var explanations = [];
12511
+ if (!thisAccidental) return 0; // there is no deviation from the key.
11922
12512
 
11923
- for (var s = 0; s < widths.length; s++) {
11924
- var section = widths[s];
11925
- var usableWidth = params.staffwidth - section.left;
11926
- var lineBreakPoint = usableWidth / minSpacing / scale;
11927
- var minLineSize = usableWidth / maxSpacing / scale;
11928
- var allowableVariance = usableWidth / minSpacingLimit / scale;
11929
- var explanation = {
11930
- widths: section,
11931
- lineBreakPoint: lineBreakPoint,
11932
- minLineSize: minLineSize,
11933
- attempts: [],
11934
- staffWidth: params.staffwidth,
11935
- minWidth: Math.round(allowableVariance)
11936
- }; // If there is a preferred number of measures per line, test that first. If none of the lines is too long, then we're finished.
12513
+ switch (keyAccidental) {
12514
+ case undefined:
12515
+ switch (thisAccidental) {
12516
+ case '__':
12517
+ return -2;
11937
12518
 
11938
- var lineBreaks = null;
12519
+ case '_':
12520
+ return -1;
11939
12521
 
11940
- if (preferredMeasuresPerLine) {
11941
- var f = fixedMeasureLineBreaks(section.measureWidths, lineBreakPoint, preferredMeasuresPerLine);
11942
- explanation.attempts.push({
11943
- type: "Fixed Measures Per Line",
11944
- preferredMeasuresPerLine: preferredMeasuresPerLine,
11945
- lineBreaks: f.lineBreaks,
11946
- failed: f.failed,
11947
- totals: f.totals
11948
- });
11949
- if (!f.failed) lineBreaks = f.lineBreaks;
11950
- } // If we don't have lineBreaks yet, use the free form method of line breaks.
11951
- // This will be called either if Preferred Measures is not used, or if the music is just weird - like a single measure is way too crowded.
12522
+ case '=':
12523
+ return 0;
11952
12524
 
12525
+ case '^':
12526
+ return 1;
11953
12527
 
11954
- if (!lineBreaks) {
11955
- var ff = freeFormLineBreaks(section.measureWidths, lineBreakPoint);
11956
- explanation.attempts.push({
11957
- type: "Free Form",
11958
- lineBreaks: ff.lineBreaks,
11959
- totals: ff.totals
11960
- });
11961
- lineBreaks = ff.lineBreaks; // We now have an acceptable number of lines, but the measures may not be optimally distributed. See if there is a better distribution.
12528
+ case '^^':
12529
+ return 2;
11962
12530
 
11963
- if (lineBreaks.length > 0 && section.measureWidths.length < 25) {
11964
- // Only do this if everything doesn't fit on one line.
11965
- // This is an intensive operation and it is optional so just do it for shorter music.
11966
- ff = optimizeLineWidths(section, lineBreakPoint, lineBreaks, explanation);
11967
- explanation.attempts.push({
11968
- type: "Optimize",
11969
- failed: ff.failed,
11970
- reason: ff.reason,
11971
- lineBreaks: ff.lineBreaks,
11972
- totals: ff.totals
11973
- });
11974
- if (!ff.failed) lineBreaks = ff.lineBreaks;
11975
- }
11976
- }
12531
+ default:
12532
+ return 0;
12533
+ // this should never happen
12534
+ }
11977
12535
 
11978
- accumulatedLineBreaks.push(lineBreaks);
11979
- explanations.push(explanation);
11980
- } // If the vertical space exceeds targetHeight, remove a line and try again. If that is too crowded, then don't use it.
12536
+ case '_':
12537
+ switch (thisAccidental) {
12538
+ case '__':
12539
+ return -1;
11981
12540
 
12541
+ case '_':
12542
+ return 0;
11982
12543
 
11983
- var staffWidth = params.staffwidth;
11984
- var ret = getRevisedTuneParams(accumulatedLineBreaks, staffWidth, params);
11985
- ret.explanation = explanations;
11986
- ret.reParse = true;
11987
- return ret;
11988
- }
12544
+ case '=':
12545
+ return 1;
11989
12546
 
11990
- module.exports = {
11991
- wrapLines: wrapLines,
11992
- calcLineWraps: calcLineWraps
11993
- };
12547
+ case '^':
12548
+ return 2;
12549
+
12550
+ case '^^':
12551
+ return 3;
12552
+
12553
+ default:
12554
+ return 0;
12555
+ // this should never happen
12556
+ }
12557
+
12558
+ case '^':
12559
+ switch (thisAccidental) {
12560
+ case '__':
12561
+ return -3;
12562
+
12563
+ case '_':
12564
+ return -2;
12565
+
12566
+ case '=':
12567
+ return -1;
12568
+
12569
+ case '^':
12570
+ return 0;
12571
+
12572
+ case '^^':
12573
+ return 1;
12574
+
12575
+ default:
12576
+ return 0;
12577
+ // this should never happen
12578
+ }
12579
+
12580
+ }
12581
+
12582
+ return 0; // this should never happen
12583
+ }
12584
+ })();
12585
+
12586
+ module.exports = strTranspose;
11994
12587
 
11995
12588
  /***/ }),
11996
12589
 
@@ -12771,7 +13364,9 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
12771
13364
  volume: velocity,
12772
13365
  start: timeToRealTime(elem.time),
12773
13366
  duration: durationRounded(note.duration),
12774
- instrument: currentInstrument
13367
+ instrument: currentInstrument,
13368
+ startChar: elem.elem.startChar,
13369
+ endChar: elem.elem.endChar
12775
13370
  };
12776
13371
  p = adjustForMicroTone(p);
12777
13372
 
@@ -14844,6 +15439,8 @@ var createNoteMap = function createNoteMap(sequence) {
14844
15439
  end: Math.round((ev.start + len - gap) * 1000000) / 1000000,
14845
15440
  volume: ev.volume
14846
15441
  };
15442
+ if (ev.startChar) obj.startChar = ev.startChar;
15443
+ if (ev.endChar) obj.endChar = ev.endChar;
14847
15444
  if (ev.style) obj.style = ev.style;
14848
15445
  if (ev.cents) obj.cents = ev.cents;
14849
15446
  map[i].push(obj);
@@ -19354,6 +19951,15 @@ AbstractEngraver.prototype.createBeam = function (isSingleLineStaff, voice, elem
19354
19951
  if (hint) beamelem.setHint();
19355
19952
 
19356
19953
  for (var i = 0; i < elems.length; i++) {
19954
+ // Do a first pass to figure out the stem direction before creating the notes, so that staccatos and other decorations can be placed correctly.
19955
+ beamelem.runningDirection(elems[i]);
19956
+ }
19957
+
19958
+ beamelem.setStemDirection();
19959
+ var tempStemDir = this.stemdir;
19960
+ this.stemdir = beamelem.stemsUp ? 'up' : 'down';
19961
+
19962
+ for (i = 0; i < elems.length; i++) {
19357
19963
  var elem = elems[i];
19358
19964
  var abselem = this.createNote(elem, true, isSingleLineStaff, voice);
19359
19965
  abselemset.push(abselem);
@@ -19368,6 +19974,7 @@ AbstractEngraver.prototype.createBeam = function (isSingleLineStaff, voice, elem
19368
19974
 
19369
19975
  beamelem.calcDir();
19370
19976
  voice.addBeam(beamelem);
19977
+ this.stemdir = tempStemDir;
19371
19978
  return abselemset;
19372
19979
  };
19373
19980
 
@@ -20113,6 +20720,11 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
20113
20720
 
20114
20721
 
20115
20722
  abselem.extraw -= 5;
20723
+
20724
+ if (elem.chord !== undefined) {
20725
+ var ret3 = addChord(this.getTextSize, abselem, elem, 0, 0, 0, false);
20726
+ }
20727
+
20116
20728
  return abselem;
20117
20729
  };
20118
20730
 
@@ -20171,6 +20783,15 @@ BeamElem.prototype.setHint = function () {
20171
20783
  this.hint = true;
20172
20784
  };
20173
20785
 
20786
+ BeamElem.prototype.runningDirection = function (abcelem) {
20787
+ var pitch = abcelem.averagepitch;
20788
+ if (pitch === undefined) return; // don't include elements like spacers in beams
20789
+
20790
+ this.total = Math.round(this.total + pitch);
20791
+ if (!this.count) this.count = 0;
20792
+ this.count++;
20793
+ };
20794
+
20174
20795
  BeamElem.prototype.add = function (abselem) {
20175
20796
  var pitch = abselem.abcelem.averagepitch;
20176
20797
  if (pitch === undefined) return; // don't include elements like spacers in beams
@@ -20193,6 +20814,24 @@ BeamElem.prototype.addBeam = function (beam) {
20193
20814
  this.beams.push(beam);
20194
20815
  };
20195
20816
 
20817
+ BeamElem.prototype.setStemDirection = function () {
20818
+ // Have to figure this out before the notes are placed because placing the notes also places the decorations.
20819
+ this.average = calcAverage(this.total, this.count);
20820
+
20821
+ if (this.forceup) {
20822
+ this.stemsUp = true;
20823
+ } else if (this.forcedown) {
20824
+ this.stemsUp = false;
20825
+ } else {
20826
+ var middleLine = 6; // hardcoded 6 is B
20827
+
20828
+ this.stemsUp = this.average < middleLine; // true is up, false is down;
20829
+ }
20830
+
20831
+ delete this.count;
20832
+ this.total = 0;
20833
+ };
20834
+
20196
20835
  BeamElem.prototype.calcDir = function () {
20197
20836
  this.average = calcAverage(this.total, this.elems.length);
20198
20837
 
@@ -20761,6 +21400,8 @@ var DynamicDecoration = __webpack_require__(/*! ./abc_dynamic_decoration */ "./s
20761
21400
 
20762
21401
  var CrescendoElem = __webpack_require__(/*! ./abc_crescendo_element */ "./src/write/abc_crescendo_element.js");
20763
21402
 
21403
+ var GlissandoElem = __webpack_require__(/*! ./abc_glissando_element */ "./src/write/abc_glissando_element.js");
21404
+
20764
21405
  var glyphs = __webpack_require__(/*! ./abc_glyphs */ "./src/write/abc_glyphs.js");
20765
21406
 
20766
21407
  var RelativeElement = __webpack_require__(/*! ./abc_relative_element */ "./src/write/abc_relative_element.js");
@@ -21077,6 +21718,7 @@ function leftDecoration(decoration, abselem, roomtaken) {
21077
21718
  Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, positioning) {
21078
21719
  var diminuendo;
21079
21720
  var crescendo;
21721
+ var glissando;
21080
21722
 
21081
21723
  for (var i = 0; i < decoration.length; i++) {
21082
21724
  switch (decoration[i]) {
@@ -21105,6 +21747,19 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
21105
21747
  };
21106
21748
  this.startCrescendoX = undefined;
21107
21749
  break;
21750
+
21751
+ case "glissando(":
21752
+ this.startGlissandoX = abselem;
21753
+ glissando = undefined;
21754
+ break;
21755
+
21756
+ case "glissando)":
21757
+ glissando = {
21758
+ start: this.startGlissandoX,
21759
+ stop: abselem
21760
+ };
21761
+ this.startGlissandoX = undefined;
21762
+ break;
21108
21763
  }
21109
21764
  }
21110
21765
 
@@ -21115,6 +21770,10 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
21115
21770
  if (crescendo) {
21116
21771
  voice.addOther(new CrescendoElem(crescendo.start, crescendo.stop, "<", positioning));
21117
21772
  }
21773
+
21774
+ if (glissando) {
21775
+ voice.addOther(new GlissandoElem(glissando.start, glissando.stop));
21776
+ }
21118
21777
  };
21119
21778
 
21120
21779
  Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals) {
@@ -21239,6 +21898,7 @@ var tablatures = __webpack_require__(/*! ../api/abc_tablatures */ "./src/api/abc
21239
21898
 
21240
21899
  var EngraverController = function EngraverController(paper, params) {
21241
21900
  params = params || {};
21901
+ this.oneSvgPerLine = params.oneSvgPerLine;
21242
21902
  this.selectionColor = params.selectionColor;
21243
21903
  this.dragColor = params.dragColor ? params.dragColor : params.selectionColor;
21244
21904
  this.dragging = !!params.dragging;
@@ -21455,8 +22115,78 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
21455
22115
  var ret = draw(this.renderer, this.classes, abcTune, this.width, maxWidth, this.responsive, scale, this.selectTypes, tuneNumber, lineOffset);
21456
22116
  this.staffgroups = ret.staffgroups;
21457
22117
  this.selectables = ret.selectables;
21458
- setupSelection(this);
21459
- };
22118
+
22119
+ if (this.oneSvgPerLine) {
22120
+ var div = this.renderer.paper.svg.parentNode;
22121
+ this.svgs = splitSvgIntoLines(div, abcTune.metaText.title);
22122
+ } else {
22123
+ this.svgs = [this.renderer.paper.svg];
22124
+ }
22125
+
22126
+ setupSelection(this, this.svgs);
22127
+ };
22128
+
22129
+ function splitSvgIntoLines(output, title) {
22130
+ // Each line is a top level <g> in the svg. To split it into separate
22131
+ // svgs iterate through each of those and put them in a new svg. Since
22132
+ // they are placed absolutely, the viewBox needs to be manipulated to
22133
+ // get the correct vertical positioning.
22134
+ // We copy all the attributes from the original svg except for the aria-label
22135
+ // since we want that to include a count. And the height is now a fraction of the original svg.
22136
+ if (!title) title = "Untitled";
22137
+ var source = output.querySelector("svg");
22138
+ var style = source.querySelector("style");
22139
+ var width = source.getAttribute("width");
22140
+ var sections = output.querySelectorAll("svg > g"); // each section is a line, or the top matter or the bottom matter, or text that has been inserted.
22141
+
22142
+ var nextTop = 0; // There are often gaps between the elements for spacing, so the actual top and height needs to be inferred.
22143
+
22144
+ var wrappers = []; // Create all the elements and place them at once because we use the current svg to get data. It would disappear after placing the first line.
22145
+
22146
+ var svgs = [];
22147
+
22148
+ for (var i = 0; i < sections.length; i++) {
22149
+ var section = sections[i];
22150
+ var box = section.getBBox();
22151
+ var gapBetweenLines = box.y - nextTop; // take the margin into account
22152
+
22153
+ var height = box.height + gapBetweenLines;
22154
+ var wrapper = document.createElement("div");
22155
+ wrapper.setAttribute("style", "overflow: hidden;height:" + height + "px;");
22156
+ var svg = duplicateSvg(source);
22157
+ var fullTitle = "Sheet Music for \"" + title + "\" section " + (i + 1);
22158
+ svg.setAttribute("aria-label", fullTitle);
22159
+ svg.setAttribute("height", height);
22160
+ svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + height);
22161
+ svg.appendChild(style.cloneNode(true));
22162
+ var titleEl = document.createElement("title");
22163
+ titleEl.innerText = fullTitle;
22164
+ svg.appendChild(titleEl);
22165
+ svg.appendChild(section);
22166
+ wrapper.appendChild(svg);
22167
+ svgs.push(svg);
22168
+ output.appendChild(wrapper); //wrappers.push(wrapper)
22169
+
22170
+ nextTop = box.y + box.height;
22171
+ } // for (i = 0; i < wrappers.length; i++)
22172
+ // output.appendChild(wrappers[i])
22173
+
22174
+
22175
+ output.removeChild(source);
22176
+ return svgs;
22177
+ }
22178
+
22179
+ function duplicateSvg(source) {
22180
+ var svgNS = "http://www.w3.org/2000/svg";
22181
+ var svg = document.createElementNS(svgNS, "svg");
22182
+
22183
+ for (var i = 0; i < source.attributes.length; i++) {
22184
+ var attr = source.attributes[i];
22185
+ if (attr.name !== "height" && attr.name != "aria-label") svg.setAttribute(attr.name, attr.value);
22186
+ }
22187
+
22188
+ return svg;
22189
+ }
21460
22190
 
21461
22191
  EngraverController.prototype.getDim = function (historyEl) {
21462
22192
  // Get the dimensions on demand because the getBBox call is expensive.
@@ -21481,6 +22211,23 @@ module.exports = EngraverController;
21481
22211
 
21482
22212
  /***/ }),
21483
22213
 
22214
+ /***/ "./src/write/abc_glissando_element.js":
22215
+ /*!********************************************!*\
22216
+ !*** ./src/write/abc_glissando_element.js ***!
22217
+ \********************************************/
22218
+ /***/ (function(module) {
22219
+
22220
+ var GlissandoElem = function GlissandoElem(anchor1, anchor2) {
22221
+ this.type = "GlissandoElem";
22222
+ this.anchor1 = anchor1; // must have a .x and a .parent property or be null (means starts at the "beginning" of the line - after keysig)
22223
+
22224
+ this.anchor2 = anchor2; // must have a .x property or be null (means ends at the end of the line)
22225
+ };
22226
+
22227
+ module.exports = GlissandoElem;
22228
+
22229
+ /***/ }),
22230
+
21484
22231
  /***/ "./src/write/abc_glyphs.js":
21485
22232
  /*!*********************************!*\
21486
22233
  !*** ./src/write/abc_glyphs.js ***!
@@ -23806,8 +24553,10 @@ var Selectables = __webpack_require__(/*! ./selectables */ "./src/write/draw/sel
23806
24553
 
23807
24554
  function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, selectTypes, tuneNumber, lineOffset) {
23808
24555
  var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber);
24556
+ renderer.paper.openGroup();
23809
24557
  renderer.moveY(renderer.padding.top);
23810
24558
  nonMusic(renderer, abcTune.topText, selectables);
24559
+ renderer.paper.closeGroup();
23811
24560
  renderer.moveY(renderer.spacing.music);
23812
24561
  var staffgroups = [];
23813
24562
 
@@ -23816,6 +24565,8 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
23816
24565
  var abcLine = abcTune.lines[line];
23817
24566
 
23818
24567
  if (abcLine.staff) {
24568
+ renderer.paper.openGroup();
24569
+
23819
24570
  if (abcLine.vskip) {
23820
24571
  renderer.moveY(abcLine.vskip);
23821
24572
  }
@@ -23825,17 +24576,22 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
23825
24576
  staffgroup.line = lineOffset + line; // If there are non-music lines then the staffgroup array won't line up with the line array, so this keeps track.
23826
24577
 
23827
24578
  staffgroups.push(staffgroup);
24579
+ renderer.paper.closeGroup();
23828
24580
  } else if (abcLine.nonMusic) {
24581
+ renderer.paper.openGroup();
23829
24582
  nonMusic(renderer, abcLine.nonMusic, selectables);
24583
+ renderer.paper.closeGroup();
23830
24584
  }
23831
24585
  }
23832
24586
 
23833
24587
  classes.reset();
23834
24588
 
23835
24589
  if (abcTune.bottomText && abcTune.bottomText.rows && abcTune.bottomText.rows.length > 0) {
24590
+ renderer.paper.openGroup();
23836
24591
  renderer.moveY(24); // TODO-PER: Empirically discovered. What variable should this be?
23837
24592
 
23838
24593
  nonMusic(renderer, abcTune.bottomText, selectables);
24594
+ renderer.paper.closeGroup();
23839
24595
  }
23840
24596
 
23841
24597
  setPaperSize(renderer, maxWidth, scale, responsive);
@@ -23965,6 +24721,107 @@ module.exports = drawEnding;
23965
24721
 
23966
24722
  /***/ }),
23967
24723
 
24724
+ /***/ "./src/write/draw/glissando.js":
24725
+ /*!*************************************!*\
24726
+ !*** ./src/write/draw/glissando.js ***!
24727
+ \*************************************/
24728
+ /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
24729
+
24730
+ var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
24731
+
24732
+ var printPath = __webpack_require__(/*! ./print-path */ "./src/write/draw/print-path.js");
24733
+
24734
+ var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
24735
+
24736
+ function drawGlissando(renderer, params, selectables) {
24737
+ if (!params.anchor1 || !params.anchor2 || !params.anchor1.heads || !params.anchor2.heads || params.anchor1.heads.length === 0 || params.anchor2.heads.length === 0) window.console.error("Glissando Element not set.");
24738
+ var margin = 4;
24739
+ var leftY = renderer.calcY(params.anchor1.heads[0].pitch);
24740
+ var rightY = renderer.calcY(params.anchor2.heads[0].pitch);
24741
+ var leftX = params.anchor1.x + params.anchor1.w / 2;
24742
+ var rightX = params.anchor2.x + params.anchor2.w / 2;
24743
+ var len = lineLength(leftX, leftY, rightX, rightY);
24744
+ var marginLeft = params.anchor1.w / 2 + margin;
24745
+ var marginRight = params.anchor2.w / 2 + margin;
24746
+ var s = slope(leftX, leftY, rightX, rightY);
24747
+ var leftYAdj = getY(leftY, s, marginLeft);
24748
+ var rightYAdj = getY(rightY, s, -marginRight);
24749
+ var num = numSquigglies(len - marginLeft - marginRight);
24750
+ var el = drawSquiggly(renderer, leftX + marginLeft, leftYAdj, num, s);
24751
+ selectables.wrapSvgEl({
24752
+ el_type: "glissando",
24753
+ startChar: -1,
24754
+ endChar: -1
24755
+ }, el);
24756
+ return [el];
24757
+ }
24758
+
24759
+ function lineLength(leftX, leftY, rightX, rightY) {
24760
+ // The length from notehead center to notehead center.
24761
+ var w = rightX - leftX;
24762
+ var h = rightY - leftY;
24763
+ return Math.sqrt(w * w + h * h);
24764
+ }
24765
+
24766
+ function slope(leftX, leftY, rightX, rightY) {
24767
+ return (rightY - leftY) / (rightX - leftX);
24768
+ }
24769
+
24770
+ function getY(y, slope, xOfs) {
24771
+ return roundNumber(y + xOfs * slope);
24772
+ }
24773
+
24774
+ function numSquigglies(length) {
24775
+ var endLen = 5; // The width of the end - that is, the non repeating part
24776
+
24777
+ return Math.max(2, Math.floor((length - endLen * 2) / 6));
24778
+ }
24779
+
24780
+ var leftStart = [[3.5, -4.8]];
24781
+ var right = [[1.5, -1], [.3, -.3], [-3.5, 3.8]];
24782
+ var leftEnd = [[-1.5, 2]];
24783
+ var top = [[3, 4], [3, -4]];
24784
+ var bottom = [[-3, 4], [-3, -4]];
24785
+
24786
+ function segment(arr, slope) {
24787
+ var ret = "";
24788
+
24789
+ for (var i = 0; i < arr.length; i++) {
24790
+ ret += 'l' + arr[i][0] + ' ' + getY(arr[i][1], slope, arr[i][0]);
24791
+ }
24792
+
24793
+ return ret;
24794
+ }
24795
+
24796
+ var drawSquiggly = function drawSquiggly(renderer, x, y, num, slope) {
24797
+ var p = sprintf("M %f %f", x, y);
24798
+ p += segment(leftStart, slope);
24799
+ var i;
24800
+
24801
+ for (i = 0; i < num; i++) {
24802
+ p += segment(top, slope);
24803
+ }
24804
+
24805
+ p += segment(right, slope);
24806
+
24807
+ for (i = 0; i < num; i++) {
24808
+ p += segment(bottom, slope);
24809
+ }
24810
+
24811
+ p += segment(leftEnd, slope) + 'z';
24812
+ return printPath(renderer, {
24813
+ path: p,
24814
+ highlight: "stroke",
24815
+ stroke: renderer.foregroundColor,
24816
+ 'class': renderer.controller.classes.generate('decoration'),
24817
+ "data-name": "glissando"
24818
+ });
24819
+ };
24820
+
24821
+ module.exports = drawGlissando;
24822
+
24823
+ /***/ }),
24824
+
23968
24825
  /***/ "./src/write/draw/group-elements.js":
23969
24826
  /*!******************************************!*\
23970
24827
  !*** ./src/write/draw/group-elements.js ***!
@@ -24801,8 +25658,8 @@ function drawStaffGroup(renderer, params, selectables, lineNumber) {
24801
25658
  // renderer.y will be offset at the beginning of each staff by the amount required to make the relative pitch work.
24802
25659
  // If there are multiple staves, then renderer.y will be incremented for each new staff.
24803
25660
  var colorIndex; // An invisible marker is useful to be able to find where each system starts.
25661
+ //addInvisibleMarker(renderer, "abcjs-top-of-system");
24804
25662
 
24805
- addInvisibleMarker(renderer, "abcjs-top-of-system");
24806
25663
  var startY = renderer.y; // So that it can be restored after we're done.
24807
25664
  // Set the absolute Y position for each staff here, so the voice drawing below can just use if.
24808
25665
 
@@ -24994,20 +25851,11 @@ function printBrace(renderer, absoluteY, brace, index, selectables) {
24994
25851
  }
24995
25852
  }
24996
25853
  }
24997
- }
25854
+ } // function addInvisibleMarker(renderer, className) {
25855
+ // var y = Math.round(renderer.y);
25856
+ // renderer.paper.pathToBack({path:"M 0 " + y + " L 0 0", stroke:"none", fill:"none", "stroke-opacity": 0, "fill-opacity": 0, 'class': renderer.controller.classes.generate(className), 'data-vertical': y });
25857
+ // }
24998
25858
 
24999
- function addInvisibleMarker(renderer, className) {
25000
- var y = Math.round(renderer.y);
25001
- renderer.paper.pathToBack({
25002
- path: "M 0 " + y + " L 0 0",
25003
- stroke: "none",
25004
- fill: "none",
25005
- "stroke-opacity": 0,
25006
- "fill-opacity": 0,
25007
- 'class': renderer.controller.classes.generate(className),
25008
- 'data-vertical': y
25009
- });
25010
- }
25011
25859
 
25012
25860
  function boxAllElements(renderer, voices, which) {
25013
25861
  for (var i = 0; i < which.length; i++) {
@@ -25502,6 +26350,8 @@ module.exports = drawTriplet;
25502
26350
  \*********************************/
25503
26351
  /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
25504
26352
 
26353
+ var drawGlissando = __webpack_require__(/*! ./glissando */ "./src/write/draw/glissando.js");
26354
+
25505
26355
  var drawCrescendo = __webpack_require__(/*! ./crescendo */ "./src/write/draw/crescendo.js");
25506
26356
 
25507
26357
  var drawDynamics = __webpack_require__(/*! ./dynamics */ "./src/write/draw/dynamics.js");
@@ -25601,6 +26451,10 @@ function drawVoice(renderer, params, bartop, selectables, staffPos) {
25601
26451
  renderer.controller.classes.incrMeasure();
25602
26452
  } else {
25603
26453
  switch (child.type) {
26454
+ case "GlissandoElem":
26455
+ child.elemset = drawGlissando(renderer, child, selectables);
26456
+ break;
26457
+
25604
26458
  case "CrescendoElem":
25605
26459
  child.elemset = drawCrescendo(renderer, child, selectables);
25606
26460
  break;
@@ -27168,7 +28022,7 @@ module.exports = layoutVoice;
27168
28022
 
27169
28023
  var spacing = __webpack_require__(/*! ./abc_spacing */ "./src/write/abc_spacing.js");
27170
28024
 
27171
- function setupSelection(engraver) {
28025
+ function setupSelection(engraver, svgs) {
27172
28026
  engraver.rangeHighlight = rangeHighlight;
27173
28027
 
27174
28028
  if (engraver.dragging) {
@@ -27185,14 +28039,21 @@ function setupSelection(engraver) {
27185
28039
  }
27186
28040
  }
27187
28041
 
27188
- engraver.renderer.paper.svg.addEventListener('mousedown', mouseDown.bind(engraver));
27189
- engraver.renderer.paper.svg.addEventListener('mousemove', mouseMove.bind(engraver));
27190
- engraver.renderer.paper.svg.addEventListener('mouseup', mouseUp.bind(engraver));
28042
+ for (var i = 0; i < svgs.length; i++) {
28043
+ svgs[i].addEventListener('touchstart', mouseDown.bind(engraver));
28044
+ svgs[i].addEventListener('touchmove', mouseMove.bind(engraver));
28045
+ svgs[i].addEventListener('touchend', mouseUp.bind(engraver));
28046
+ svgs[i].addEventListener('mousedown', mouseDown.bind(engraver));
28047
+ svgs[i].addEventListener('mousemove', mouseMove.bind(engraver));
28048
+ svgs[i].addEventListener('mouseup', mouseUp.bind(engraver));
28049
+ }
27191
28050
  }
27192
28051
 
27193
- function getCoord(ev, svg) {
28052
+ function getCoord(ev) {
27194
28053
  var scaleX = 1;
27195
- var scaleY = 1; // when renderer.options.responsive === 'resize' the click coords are in relation to the HTML
28054
+ var scaleY = 1;
28055
+ var svg = ev.target.closest('svg');
28056
+ var yOffset = 0; // when renderer.options.responsive === 'resize' the click coords are in relation to the HTML
27196
28057
  // element, we need to convert to the SVG viewBox coords
27197
28058
 
27198
28059
  if (svg.viewBox.baseVal) {
@@ -27200,6 +28061,7 @@ function getCoord(ev, svg) {
27200
28061
  // Chrome makes these values null when no viewBox is given.
27201
28062
  if (svg.viewBox.baseVal.width !== 0) scaleX = svg.viewBox.baseVal.width / svg.clientWidth;
27202
28063
  if (svg.viewBox.baseVal.height !== 0) scaleY = svg.viewBox.baseVal.height / svg.clientHeight;
28064
+ yOffset = svg.viewBox.baseVal.y;
27203
28065
  }
27204
28066
 
27205
28067
  var svgClicked = ev.target.tagName === "svg";
@@ -27217,7 +28079,7 @@ function getCoord(ev, svg) {
27217
28079
  x = x * scaleX;
27218
28080
  y = y * scaleY; //console.log(x, y)
27219
28081
 
27220
- return [x, y];
28082
+ return [x, y + yOffset];
27221
28083
  }
27222
28084
 
27223
28085
  function elementFocused(ev) {
@@ -27298,7 +28160,7 @@ function keyboardSelection(ev) {
27298
28160
 
27299
28161
  function findElementInHistory(selectables, el) {
27300
28162
  for (var i = 0; i < selectables.length; i++) {
27301
- if (el === selectables[i].svgEl) return i;
28163
+ if (el.dataset.index === selectables[i].svgEl.dataset.index) return i;
27302
28164
  }
27303
28165
 
27304
28166
  return -1;
@@ -27391,7 +28253,7 @@ function getMousePosition(self, ev) {
27391
28253
  y = box[1]; //console.log("clicked on", clickedOn, x, y, self.selectables[clickedOn].svgEl.getBBox(), ev.target.getBBox());
27392
28254
  } else {
27393
28255
  // See if they clicked close to an element.
27394
- box = getCoord(ev, self.renderer.paper.svg);
28256
+ box = getCoord(ev);
27395
28257
  x = box[0];
27396
28258
  y = box[1];
27397
28259
  clickedOn = findElementByCoord(self, x, y); //console.log("clicked near", clickedOn, x, y, printEl(ev.target));
@@ -27404,11 +28266,28 @@ function getMousePosition(self, ev) {
27404
28266
  };
27405
28267
  }
27406
28268
 
28269
+ function attachMissingTouchEventAttributes(touchEv) {
28270
+ var rect = touchEv.target.getBoundingClientRect();
28271
+ var offsetX = touchEv.touches[0].pageX - rect.left;
28272
+ var offsetY = touchEv.touches[0].pageY - rect.top;
28273
+ touchEv.touches[0].offsetX = offsetX;
28274
+ touchEv.touches[0].offsetY = offsetY;
28275
+ touchEv.touches[0].layerX = touchEv.touches[0].pageX;
28276
+ touchEv.touches[0].layerY = touchEv.touches[0].pageY;
28277
+ }
28278
+
27407
28279
  function mouseDown(ev) {
27408
28280
  // "this" is the EngraverController because of the bind(this) when setting the event listener.
27409
- var positioning = getMousePosition(this, ev); // Only start dragging if the user clicked close enough to an element and clicked with the main mouse button.
28281
+ var _ev = ev;
28282
+
28283
+ if (ev.type === 'touchstart') {
28284
+ attachMissingTouchEventAttributes(ev);
28285
+ _ev = ev.touches[0];
28286
+ }
28287
+
28288
+ var positioning = getMousePosition(this, _ev); // Only start dragging if the user clicked close enough to an element and clicked with the main mouse button.
27410
28289
 
27411
- if (positioning.clickedOn >= 0 && ev.button === 0) {
28290
+ if (positioning.clickedOn >= 0 && (ev.type === 'touchstart' || ev.button === 0)) {
27412
28291
  this.dragTarget = this.selectables[positioning.clickedOn];
27413
28292
  this.dragIndex = positioning.clickedOn;
27414
28293
  this.dragMechanism = "mouse";
@@ -27425,9 +28304,17 @@ function mouseDown(ev) {
27425
28304
  }
27426
28305
 
27427
28306
  function mouseMove(ev) {
27428
- // "this" is the EngraverController because of the bind(this) when setting the event listener.
28307
+ var _ev = ev;
28308
+
28309
+ if (ev.type === 'touchmove') {
28310
+ attachMissingTouchEventAttributes(ev);
28311
+ _ev = ev.touches[0];
28312
+ }
28313
+
28314
+ this.lastTouchMove = ev; // "this" is the EngraverController because of the bind(this) when setting the event listener.
28315
+
27429
28316
  if (!this.dragTarget || !this.dragging || !this.dragTarget.isDraggable || this.dragMechanism !== 'mouse') return;
27430
- var positioning = getMousePosition(this, ev);
28317
+ var positioning = getMousePosition(this, _ev);
27431
28318
  var yDist = Math.round((positioning.y - this.dragMouseStart.y) / spacing.STEP);
27432
28319
 
27433
28320
  if (yDist !== this.dragYStep) {
@@ -27438,6 +28325,13 @@ function mouseMove(ev) {
27438
28325
 
27439
28326
  function mouseUp(ev) {
27440
28327
  // "this" is the EngraverController because of the bind(this) when setting the event listener.
28328
+ var _ev = ev;
28329
+
28330
+ if (ev.type === 'touchend') {
28331
+ attachMissingTouchEventAttributes(this.lastTouchMove);
28332
+ _ev = this.lastTouchMove.touches[0];
28333
+ }
28334
+
27441
28335
  if (!this.dragTarget) return;
27442
28336
  clearSelection.bind(this)();
27443
28337
 
@@ -27446,7 +28340,7 @@ function mouseUp(ev) {
27446
28340
  this.dragTarget.absEl.highlight(undefined, this.selectionColor);
27447
28341
  }
27448
28342
 
27449
- notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, ev);
28343
+ notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, _ev);
27450
28344
 
27451
28345
  if (this.dragTarget.svgEl && this.dragTarget.svgEl.focus) {
27452
28346
  this.dragTarget.svgEl.focus();
@@ -28175,7 +29069,7 @@ function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, padd
28175
29069
  font: 'infofont',
28176
29070
  klass: 'meta-top rhythm',
28177
29071
  absElemType: "rhythm",
28178
- noMove: true,
29072
+ noMove: noMove,
28179
29073
  info: metaTextInfo.rhythm,
28180
29074
  name: "rhythm"
28181
29075
  }, getTextSize);
@@ -28253,7 +29147,7 @@ module.exports = unhighlight;
28253
29147
  \********************/
28254
29148
  /***/ (function(module) {
28255
29149
 
28256
- var version = '6.0.3';
29150
+ var version = '6.1.1';
28257
29151
  module.exports = version;
28258
29152
 
28259
29153
  /***/ })