abcjs 6.1.0 → 6.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abcjs",
3
- "version": "6.1.0",
3
+ "version": "6.1.3",
4
4
  "description": "Renderer for abc music notation",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -59,96 +59,6 @@ function renderOne(div, tune, params, tuneNumber, lineOffset) {
59
59
  }
60
60
  }
61
61
 
62
- function renderEachLineSeparately(div, tune, params, tuneNumber) {
63
- function initializeTuneLine(tune) {
64
- var obj = new Tune();
65
- obj.formatting = tune.formatting;
66
- obj.media = tune.media;
67
- obj.version = tune.version;
68
- return obj;
69
- }
70
-
71
- // Before rendering, chop up the returned tune into an array where each element is a line.
72
- // The first element of the array gets the title and other items that go on top, the last element
73
- // of the array gets the extra text that goes on bottom. Each element gets any non-music info that comes before it.
74
- var tunes = [];
75
- var tuneLine;
76
- for (var i = 0; i < tune.lines.length; i++) {
77
- var line = tune.lines[i];
78
- if (!tuneLine)
79
- tuneLine = initializeTuneLine(tune);
80
-
81
- if (i === 0) {
82
- // These items go on top of the music
83
- tuneLine.copyTopInfo(tune);
84
- }
85
-
86
- // push the lines until we get to a music line
87
- tuneLine.lines.push(line);
88
- if (line.staff) {
89
- tunes.push(tuneLine);
90
- tuneLine = undefined;
91
- }
92
- }
93
- // Add any extra stuff to the last line.
94
- if (tuneLine) {
95
- var lastLine = tunes[tunes.length-1];
96
- for (var j = 0; j < tuneLine.lines.length; j++)
97
- lastLine.lines.push(tuneLine.lines[j]);
98
- }
99
-
100
- // These items go below the music
101
- tuneLine = tunes[tunes.length-1];
102
- tuneLine.copyBottomInfo(tune);
103
-
104
- // Now create sub-divs and render each line. Need to copy the params to change the padding for the interior slices.
105
- var ep = {};
106
- for (var key in params) {
107
- if (params.hasOwnProperty(key)) {
108
- ep[key] = params[key];
109
- }
110
- }
111
- var origPaddingTop = ep.paddingtop;
112
- var origPaddingBottom = ep.paddingbottom;
113
- var currentScrollY = div.parentNode.scrollTop; // If there is scrolling it will be lost during the redraw so remember it.
114
- var currentScrollX = div.parentNode.scrollLeft;
115
- div.innerHTML = "";
116
- var lineCount = 0;
117
- for (var k = 0; k < tunes.length; k++) {
118
- var lineEl = document.createElement("div");
119
- div.appendChild(lineEl);
120
-
121
- if (k === 0) {
122
- ep.paddingtop = origPaddingTop;
123
- ep.paddingbottom = 0;
124
- } else if (k === tunes.length-1) {
125
- ep.paddingtop = 10;
126
- ep.paddingbottom = origPaddingBottom;
127
- } else {
128
- ep.paddingtop = 10;
129
- ep.paddingbottom = 0;
130
- }
131
- if (k < tunes.length-1) {
132
- // If it is not the last line, force stretchlast. If it is, stretchlast might have been set by the input parameters.
133
- tunes[k].formatting = parseCommon.clone(tunes[k].formatting);
134
- tunes[k].formatting.stretchlast = true;
135
- }
136
- renderOne(lineEl, tunes[k], ep, tuneNumber, lineCount);
137
- lineCount += tunes[k].lines.length;
138
- if (k === 0)
139
- tune.engraver = tunes[k].engraver;
140
- else {
141
- if (!tune.engraver.staffgroups)
142
- tune.engraver.staffgroups = tunes[k].engraver.staffgroups;
143
- else if (tunes[k].engraver.staffgroups.length > 0)
144
- tune.engraver.staffgroups.push(tunes[k].engraver.staffgroups[0]);
145
- }
146
- }
147
- if (currentScrollX || currentScrollY) {
148
- div.parentNode.scrollTo(currentScrollX, currentScrollY);
149
- }
150
- }
151
-
152
62
  // A quick way to render a tune from javascript when interactivity is not required.
153
63
  // This is used when a javascript routine has some abc text that it wants to render
154
64
  // in a div or collection of divs. One tune or many can be rendered.
@@ -214,10 +124,7 @@ var renderAbc = function(output, abc, parserParams, engraverParams, renderParams
214
124
  tune = doLineWrapping(div, tune, tuneNumber, abcString, params);
215
125
  return tune;
216
126
  }
217
- else if (removeDiv || !params.oneSvgPerLine || tune.lines.length < 2)
218
- renderOne(div, tune, params, tuneNumber, 0);
219
- else
220
- renderEachLineSeparately(div, tune, params, tuneNumber);
127
+ renderOne(div, tune, params, tuneNumber, 0);
221
128
  if (removeDiv)
222
129
  div.parentNode.removeChild(div);
223
130
  return null;
@@ -239,10 +146,7 @@ function doLineWrapping(div, tune, tuneNumber, abcString, params) {
239
146
  if (warnings)
240
147
  tune.warnings = warnings;
241
148
  }
242
- if (!params.oneSvgPerLine || tune.lines.length < 2)
243
- renderOne(div, tune, ret.revisedParams, tuneNumber, 0);
244
- else
245
- renderEachLineSeparately(div, tune, ret.revisedParams, tuneNumber);
149
+ renderOne(div, tune, ret.revisedParams, tuneNumber, 0);
246
150
  tune.explanation = ret.explanation;
247
151
  return tune;
248
152
  }
@@ -198,7 +198,7 @@ TabRenderer.prototype.doLayout = function () {
198
198
  var padd = 3;
199
199
  var prevIndex = this.staffIndex;
200
200
  var previousStaff = staffGroup.staffs[prevIndex];
201
- var tabTop = previousStaff.top + padd + lyricsHeight;
201
+ var tabTop = this.tabSize + padd - previousStaff.bottom - lyricsHeight;
202
202
  if (previousStaff.isTabStaff) {
203
203
  tabTop = previousStaff.top;
204
204
  }
@@ -32,6 +32,7 @@ var tablatures = require('../api/abc_tablatures');
32
32
  */
33
33
  var EngraverController = function(paper, params) {
34
34
  params = params || {};
35
+ this.oneSvgPerLine = params.oneSvgPerLine;
35
36
  this.selectionColor = params.selectionColor;
36
37
  this.dragColor = params.dragColor ? params.dragColor : params.selectionColor;
37
38
  this.dragging = !!params.dragging;
@@ -246,9 +247,79 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
246
247
  this.staffgroups = ret.staffgroups;
247
248
  this.selectables = ret.selectables;
248
249
 
249
- setupSelection(this);
250
+ if (this.oneSvgPerLine) {
251
+ var div = this.renderer.paper.svg.parentNode
252
+ this.svgs = splitSvgIntoLines(div, abcTune.metaText.title, this.responsive)
253
+ } else {
254
+ this.svgs = [this.renderer.paper.svg];
255
+ }
256
+ setupSelection(this, this.svgs);
250
257
  };
251
258
 
259
+ function splitSvgIntoLines(output, title, responsive) {
260
+ // Each line is a top level <g> in the svg. To split it into separate
261
+ // svgs iterate through each of those and put them in a new svg. Since
262
+ // they are placed absolutely, the viewBox needs to be manipulated to
263
+ // get the correct vertical positioning.
264
+ // We copy all the attributes from the original svg except for the aria-label
265
+ // since we want that to include a count. And the height is now a fraction of the original svg.
266
+ if (!title) title = "Untitled"
267
+ var source = output.querySelector("svg")
268
+ if (responsive === 'resize')
269
+ output.style.paddingBottom = ''
270
+ var style = source.querySelector("style")
271
+ var width = responsive === 'resize' ? source.viewBox.baseVal.width : source.getAttribute("width")
272
+ 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.
273
+ var nextTop = 0 // There are often gaps between the elements for spacing, so the actual top and height needs to be inferred.
274
+ 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.
275
+ var svgs = []
276
+ for (var i = 0; i < sections.length; i++) {
277
+ var section = sections[i]
278
+ var box = section.getBBox()
279
+ var gapBetweenLines = box.y - nextTop // take the margin into account
280
+ var height = box.height + gapBetweenLines;
281
+ var wrapper = document.createElement("div");
282
+ var divStyles = "overflow: hidden;"
283
+ if (responsive !== 'resize')
284
+ divStyles += "height:"+height+"px;"
285
+ wrapper.setAttribute("style", divStyles)
286
+ var svg = duplicateSvg(source)
287
+ var fullTitle = "Sheet Music for \"" + title + "\" section " + (i+1)
288
+ svg.setAttribute("aria-label", fullTitle)
289
+ if (responsive !== 'resize')
290
+ svg.setAttribute("height", height)
291
+ if (responsive === 'resize')
292
+ svg.style.position = ''
293
+ svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + height )
294
+ svg.appendChild(style.cloneNode(true))
295
+ var titleEl = document.createElement("title")
296
+ titleEl.innerText = fullTitle
297
+ svg.appendChild(titleEl)
298
+ svg.appendChild(section)
299
+
300
+ wrapper.appendChild(svg)
301
+ svgs.push(svg)
302
+ output.appendChild(wrapper)
303
+ //wrappers.push(wrapper)
304
+ nextTop = box.y+box.height
305
+ }
306
+ // for (i = 0; i < wrappers.length; i++)
307
+ // output.appendChild(wrappers[i])
308
+ output.removeChild(source)
309
+ return svgs;
310
+ }
311
+
312
+ function duplicateSvg(source) {
313
+ var svgNS = "http://www.w3.org/2000/svg";
314
+ var svg = document.createElementNS(svgNS, "svg");
315
+ for (var i = 0; i < source.attributes.length; i++) {
316
+ var attr = source.attributes[i];
317
+ if (attr.name !== "height" && attr.name != "aria-label")
318
+ svg.setAttribute(attr.name, attr.value)
319
+ }
320
+ return svg;
321
+ }
322
+
252
323
  EngraverController.prototype.getDim = function(historyEl) {
253
324
  // Get the dimensions on demand because the getBBox call is expensive.
254
325
  if (!historyEl.dim) {
@@ -6,14 +6,17 @@ var Selectables = require('./selectables');
6
6
 
7
7
  function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, selectTypes, tuneNumber, lineOffset) {
8
8
  var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber);
9
+ renderer.paper.openGroup()
9
10
  renderer.moveY(renderer.padding.top);
10
11
  nonMusic(renderer, abcTune.topText, selectables);
12
+ renderer.paper.closeGroup()
11
13
  renderer.moveY(renderer.spacing.music);
12
14
  var staffgroups = [];
13
15
  for (var line = 0; line < abcTune.lines.length; line++) {
14
16
  classes.incrLine();
15
17
  var abcLine = abcTune.lines[line];
16
18
  if (abcLine.staff) {
19
+ renderer.paper.openGroup()
17
20
  if (abcLine.vskip) {
18
21
  renderer.moveY(abcLine.vskip);
19
22
  }
@@ -22,15 +25,20 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
22
25
  var staffgroup = engraveStaffLine(renderer, abcLine.staffGroup, selectables,line);
23
26
  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.
24
27
  staffgroups.push(staffgroup);
28
+ renderer.paper.closeGroup()
25
29
  } else if (abcLine.nonMusic) {
30
+ renderer.paper.openGroup()
26
31
  nonMusic(renderer, abcLine.nonMusic, selectables);
32
+ renderer.paper.closeGroup()
27
33
  }
28
34
  }
29
35
 
30
36
  classes.reset();
31
37
  if (abcTune.bottomText && abcTune.bottomText.rows && abcTune.bottomText.rows.length > 0) {
38
+ renderer.paper.openGroup()
32
39
  renderer.moveY(24); // TODO-PER: Empirically discovered. What variable should this be?
33
40
  nonMusic(renderer, abcTune.bottomText, selectables);
41
+ renderer.paper.closeGroup()
34
42
  }
35
43
  setPaperSize(renderer, maxWidth, scale, responsive);
36
44
  return { staffgroups: staffgroups, selectables: selectables.getElements() };
@@ -15,7 +15,7 @@ function drawStaffGroup(renderer, params, selectables,lineNumber) {
15
15
  var colorIndex;
16
16
 
17
17
  // An invisible marker is useful to be able to find where each system starts.
18
- addInvisibleMarker(renderer, "abcjs-top-of-system");
18
+ //addInvisibleMarker(renderer, "abcjs-top-of-system");
19
19
 
20
20
  var startY = renderer.y; // So that it can be restored after we're done.
21
21
  // Set the absolute Y position for each staff here, so the voice drawing below can just use if.
@@ -177,10 +177,10 @@ function printBrace(renderer, absoluteY, brace, index, selectables) {
177
177
  }
178
178
  }
179
179
 
180
- function addInvisibleMarker(renderer, className) {
181
- var y = Math.round(renderer.y);
182
- 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 });
183
- }
180
+ // function addInvisibleMarker(renderer, className) {
181
+ // var y = Math.round(renderer.y);
182
+ // 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 });
183
+ // }
184
184
 
185
185
  function boxAllElements(renderer, voices, which) {
186
186
  for (var i = 0; i < which.length; i++) {
@@ -1,6 +1,6 @@
1
1
  var spacing = require('./abc_spacing');
2
2
 
3
- function setupSelection(engraver) {
3
+ function setupSelection(engraver, svgs) {
4
4
  engraver.rangeHighlight = rangeHighlight;
5
5
  if (engraver.dragging) {
6
6
  for (var h = 0; h < engraver.selectables.length; h++) {
@@ -14,14 +14,21 @@ function setupSelection(engraver) {
14
14
  }
15
15
  }
16
16
  }
17
- engraver.renderer.paper.svg.addEventListener('mousedown', mouseDown.bind(engraver));
18
- engraver.renderer.paper.svg.addEventListener('mousemove', mouseMove.bind(engraver));
19
- engraver.renderer.paper.svg.addEventListener('mouseup', mouseUp.bind(engraver));
17
+ for (var i = 0; i < svgs.length; i++) {
18
+ svgs[i].addEventListener('touchstart', mouseDown.bind(engraver));
19
+ svgs[i].addEventListener('touchmove', mouseMove.bind(engraver));
20
+ svgs[i].addEventListener('touchend', mouseUp.bind(engraver));
21
+ svgs[i].addEventListener('mousedown', mouseDown.bind(engraver));
22
+ svgs[i].addEventListener('mousemove', mouseMove.bind(engraver));
23
+ svgs[i].addEventListener('mouseup', mouseUp.bind(engraver));
24
+ }
20
25
  }
21
26
 
22
- function getCoord(ev, svg) {
27
+ function getCoord(ev) {
23
28
  var scaleX = 1;
24
29
  var scaleY = 1;
30
+ var svg = ev.target.closest('svg')
31
+ var yOffset = 0
25
32
 
26
33
  // when renderer.options.responsive === 'resize' the click coords are in relation to the HTML
27
34
  // element, we need to convert to the SVG viewBox coords
@@ -31,6 +38,7 @@ function getCoord(ev, svg) {
31
38
  scaleX = svg.viewBox.baseVal.width / svg.clientWidth
32
39
  if (svg.viewBox.baseVal.height !== 0)
33
40
  scaleY = svg.viewBox.baseVal.height / svg.clientHeight
41
+ yOffset = svg.viewBox.baseVal.y
34
42
  }
35
43
 
36
44
  var svgClicked = ev.target.tagName === "svg";
@@ -48,7 +56,7 @@ function getCoord(ev, svg) {
48
56
  y = y * scaleY;
49
57
  //console.log(x, y)
50
58
 
51
- return [x, y];
59
+ return [x, y+yOffset];
52
60
  }
53
61
 
54
62
  function elementFocused(ev) {
@@ -120,7 +128,7 @@ function keyboardSelection(ev) {
120
128
 
121
129
  function findElementInHistory(selectables, el) {
122
130
  for (var i = 0; i < selectables.length; i++) {
123
- if (el === selectables[i].svgEl)
131
+ if (el.dataset.index === selectables[i].svgEl.dataset.index)
124
132
  return i;
125
133
  }
126
134
  return -1;
@@ -216,7 +224,7 @@ function getMousePosition(self, ev) {
216
224
  //console.log("clicked on", clickedOn, x, y, self.selectables[clickedOn].svgEl.getBBox(), ev.target.getBBox());
217
225
  } else {
218
226
  // See if they clicked close to an element.
219
- box = getCoord(ev, self.renderer.paper.svg);
227
+ box = getCoord(ev);
220
228
  x = box[0];
221
229
  y = box[1];
222
230
  clickedOn = findElementByCoord(self, x, y);
@@ -225,13 +233,30 @@ function getMousePosition(self, ev) {
225
233
  return { x: x, y: y, clickedOn: clickedOn };
226
234
  }
227
235
 
236
+ function attachMissingTouchEventAttributes(touchEv) {
237
+ var rect = touchEv.target.getBoundingClientRect();
238
+ var offsetX = touchEv.touches[0].pageX - rect.left;
239
+ var offsetY = touchEv.touches[0].pageY - rect.top;
240
+
241
+ touchEv.touches[0].offsetX = offsetX;
242
+ touchEv.touches[0].offsetY = offsetY;
243
+
244
+ touchEv.touches[0].layerX = touchEv.touches[0].pageX;
245
+ touchEv.touches[0].layerY = touchEv.touches[0].pageY;
246
+ }
247
+
228
248
  function mouseDown(ev) {
229
249
  // "this" is the EngraverController because of the bind(this) when setting the event listener.
250
+ var _ev = ev;
251
+ if (ev.type === 'touchstart') {
252
+ attachMissingTouchEventAttributes(ev);
253
+ _ev = ev.touches[0];
254
+ }
230
255
 
231
- var positioning = getMousePosition(this, ev);
256
+ var positioning = getMousePosition(this, _ev);
232
257
 
233
258
  // Only start dragging if the user clicked close enough to an element and clicked with the main mouse button.
234
- if (positioning.clickedOn >= 0 && ev.button === 0) {
259
+ if (positioning.clickedOn >= 0 && (ev.type === 'touchstart' || ev.button === 0)) {
235
260
  this.dragTarget = this.selectables[positioning.clickedOn];
236
261
  this.dragIndex = positioning.clickedOn;
237
262
  this.dragMechanism = "mouse";
@@ -244,12 +269,18 @@ function mouseDown(ev) {
244
269
  }
245
270
 
246
271
  function mouseMove(ev) {
272
+ var _ev = ev;
273
+ if (ev.type === 'touchmove') {
274
+ attachMissingTouchEventAttributes(ev);
275
+ _ev = ev.touches[0];
276
+ }
277
+ this.lastTouchMove = ev;
247
278
  // "this" is the EngraverController because of the bind(this) when setting the event listener.
248
279
 
249
280
  if (!this.dragTarget || !this.dragging || !this.dragTarget.isDraggable || this.dragMechanism !== 'mouse')
250
281
  return;
251
282
 
252
- var positioning = getMousePosition(this, ev);
283
+ var positioning = getMousePosition(this, _ev);
253
284
 
254
285
  var yDist = Math.round((positioning.y - this.dragMouseStart.y)/spacing.STEP);
255
286
  if (yDist !== this.dragYStep) {
@@ -260,6 +291,11 @@ function mouseMove(ev) {
260
291
 
261
292
  function mouseUp(ev) {
262
293
  // "this" is the EngraverController because of the bind(this) when setting the event listener.
294
+ var _ev = ev;
295
+ if (ev.type === 'touchend') {
296
+ attachMissingTouchEventAttributes(this.lastTouchMove);
297
+ _ev = this.lastTouchMove.touches[0];
298
+ }
263
299
 
264
300
  if (!this.dragTarget)
265
301
  return;
@@ -270,7 +306,7 @@ function mouseUp(ev) {
270
306
  this.dragTarget.absEl.highlight(undefined, this.selectionColor);
271
307
  }
272
308
 
273
- notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, ev);
309
+ notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, _ev);
274
310
  if (this.dragTarget.svgEl && this.dragTarget.svgEl.focus) {
275
311
  this.dragTarget.svgEl.focus();
276
312
  this.dragTarget = null;
package/src/write/svg.js CHANGED
@@ -307,7 +307,7 @@ Svg.prototype.closeGroup = function() {
307
307
  var g = this.currentGroup.shift();
308
308
  if (g && g.children.length === 0) {
309
309
  // If nothing was added to the group it is because all the elements were invisible. We don't need the group, then.
310
- this.svg.removeChild(g);
310
+ g.parentElement.removeChild(g);
311
311
  return null;
312
312
  }
313
313
  return g;
package/types/index.d.ts CHANGED
@@ -171,6 +171,8 @@ declare module 'abcjs' {
171
171
 
172
172
  export type AbsoluteElement = any; // TODO
173
173
 
174
+ export type AbstractEngraver = any;
175
+
174
176
  export type NoteProperties = any; // TODO
175
177
 
176
178
  export type AudioTrackCommand = 'program' | 'text' | 'note';
@@ -504,6 +506,34 @@ declare module 'abcjs' {
504
506
  wordsfont: Font;
505
507
  }
506
508
 
509
+ export interface EngraverController {
510
+ classes: any;
511
+ dragColor: string;
512
+ dragIndex: number;
513
+ dragMouseStart: { x: number, y: number; };
514
+ dragTarget: null | any;
515
+ dragYStep: number;
516
+ dragging: boolean;
517
+ engraver: AbstractEngraver;
518
+ getFontAndAttr: any;
519
+ getTextSize: any;
520
+ listeners: [ClickListener];
521
+ rangeHighlight: any;
522
+ renderer: any;
523
+ responsive?: boolean;
524
+ scale: number;
525
+ initialClef?: any;
526
+ selectTypes: boolean | Array<DragTypes>;
527
+ selectables: Array<Selectable>;
528
+ selected: Array<any>;
529
+ selectionColor: string;
530
+ space: number;
531
+ staffgroups: [any];
532
+ staffwidthPrint: number;
533
+ staffwidthScreen: number;
534
+ width: number;
535
+ }
536
+
507
537
  export interface MetaText {
508
538
  "abc-copyright"?: string;
509
539
  "abc-creator"?: string;
@@ -712,6 +742,7 @@ declare module 'abcjs' {
712
742
 
713
743
  export interface TuneObject {
714
744
  formatting: Formatting;
745
+ engraver?: EngraverController;
715
746
  lines: Array<TuneLine>;
716
747
  media: Media;
717
748
  metaText: MetaText;
@@ -732,12 +763,23 @@ declare module 'abcjs' {
732
763
  millisecondsPerMeasure: NumberFunction;
733
764
  setTiming: (bpm?: number, measuresOfDelay? : number) => void;
734
765
  setUpAudio: (options: SynthOptions) => AudioTracks;
766
+ makeVoicesArray: () => Array<Selectable[]>
735
767
  lineBreaks?: Array<number>;
736
768
  visualTranspose?: number;
737
769
  }
738
770
 
739
771
  export type TuneObjectArray = [TuneObject]
740
772
 
773
+ export interface Selectable {
774
+ absEl: AbsoluteElement;
775
+ isDraggable: boolean;
776
+ staffPos: {
777
+ height: number;
778
+ top: number;
779
+ zero: number;
780
+ }
781
+ }
782
+
741
783
  export interface AbcElem {
742
784
  el_type: string //TODO enumerate these
743
785
  abselem: any;
@@ -1091,6 +1133,8 @@ declare module 'abcjs' {
1091
1133
 
1092
1134
  export function numberOfTunes(abc: string) : number;
1093
1135
  export function extractMeasures(abc: string) : Array<MeasureList>;
1136
+
1137
+ export function strTranspose(originalAbc: string, visualObj: TuneObject, steps: number): string;
1094
1138
 
1095
1139
  //
1096
1140
  // Glyph
package/version.js CHANGED
@@ -1,3 +1,3 @@
1
- var version = '6.1.0';
1
+ var version = '6.1.3';
2
2
 
3
3
  module.exports = version;