abcjs 6.5.2 → 6.6.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.
- package/README.md +8 -2
- package/RELEASE.md +46 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +1332 -189
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/package.json +1 -1
- package/src/api/abc_timing_callbacks.js +88 -13
- package/src/const/relative-major.js +24 -19
- package/src/data/abc_tune.js +12 -1
- package/src/data/deline-tune.js +1 -1
- package/src/midi/abc_midi_create.js +2 -2
- package/src/parse/abc_parse.js +20 -0
- package/src/parse/chord-grid.js +375 -0
- package/src/parse/tune-builder.js +87 -18
- package/src/str/output.js +98 -62
- package/src/synth/abc_midi_flattener.js +1 -1
- package/src/synth/abc_midi_sequencer.js +20 -30
- package/src/synth/create-synth.js +1 -1
- package/src/synth/repeats.js +236 -0
- package/src/write/creation/abstract-engraver.js +5 -1
- package/src/write/creation/decoration.js +30 -0
- package/src/write/draw/chord-grid.js +254 -0
- package/src/write/draw/draw.js +48 -35
- package/src/write/draw/text.js +1 -1
- package/src/write/engraver-controller.js +4 -2
- package/src/write/layout/beam.js +16 -1
- package/src/write/renderer.js +4 -0
- package/src/write/svg.js +8 -1
- package/types/index.d.ts +25 -0
- package/version.js +1 -1
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
const printSymbol = require("./print-symbol");
|
|
2
|
+
const printStem = require("./print-stem");
|
|
3
|
+
|
|
4
|
+
function drawChordGrid(renderer, parts, leftMargin, pageWidth, fonts) {
|
|
5
|
+
const chordFont = fonts.gchordfont
|
|
6
|
+
const partFont = fonts.partsfont
|
|
7
|
+
const annotationFont = fonts.annotationfont
|
|
8
|
+
const endingFont = fonts.repeatfont
|
|
9
|
+
const textFont = fonts.textfont
|
|
10
|
+
const subtitleFont = fonts.subtitlefont
|
|
11
|
+
|
|
12
|
+
const ROW_HEIGHT = 50
|
|
13
|
+
const ENDING_HEIGHT = 10
|
|
14
|
+
const ANNOTATION_HEIGHT = 14
|
|
15
|
+
const PART_MARGIN_TOP = 10
|
|
16
|
+
const PART_MARGIN_BOTTOM = 20
|
|
17
|
+
const TEXT_MARGIN = 16
|
|
18
|
+
|
|
19
|
+
renderer.paper.openGroup({klass: 'abcjs-chord-grid'})
|
|
20
|
+
parts.forEach(part => {
|
|
21
|
+
switch (part.type) {
|
|
22
|
+
case "text": {
|
|
23
|
+
text(renderer, part.text, leftMargin, renderer.y, 16, textFont, null, null, false )
|
|
24
|
+
renderer.moveY(TEXT_MARGIN)
|
|
25
|
+
}
|
|
26
|
+
break
|
|
27
|
+
case "subtitle": {
|
|
28
|
+
text(renderer, part.subtitle, leftMargin, renderer.y+PART_MARGIN_TOP, 20, subtitleFont, null, "abcjs-subtitle", false )
|
|
29
|
+
renderer.moveY(PART_MARGIN_BOTTOM)
|
|
30
|
+
}
|
|
31
|
+
break
|
|
32
|
+
case "part":
|
|
33
|
+
if (part.lines.length > 0) {
|
|
34
|
+
text(renderer, part.name, leftMargin, renderer.y+PART_MARGIN_TOP, 20, subtitleFont, part.name, "abcjs-part", false )
|
|
35
|
+
|
|
36
|
+
renderer.moveY(PART_MARGIN_BOTTOM)
|
|
37
|
+
const numCols = part.lines[0].length
|
|
38
|
+
const colWidth = pageWidth / numCols
|
|
39
|
+
part.lines.forEach((line, lineNum) => {
|
|
40
|
+
let hasEnding = false
|
|
41
|
+
let hasAnnotation = false
|
|
42
|
+
line.forEach(measure => {
|
|
43
|
+
if (measure.ending)
|
|
44
|
+
hasEnding = true
|
|
45
|
+
if (measure.annotations && measure.annotations.length > 0)
|
|
46
|
+
hasAnnotation = true
|
|
47
|
+
})
|
|
48
|
+
const extraTop = hasAnnotation ? ANNOTATION_HEIGHT : hasEnding ? ENDING_HEIGHT : 0
|
|
49
|
+
line.forEach((measure, barNum) => {
|
|
50
|
+
const RECT_WIDTH = 1
|
|
51
|
+
if (!measure.noBorder) {
|
|
52
|
+
renderer.paper.rect({x: leftMargin + barNum * colWidth, y: renderer.y, width: colWidth, height: extraTop + ROW_HEIGHT})
|
|
53
|
+
renderer.paper.rect({x: leftMargin + barNum * colWidth + RECT_WIDTH, y: renderer.y + RECT_WIDTH, width: colWidth - RECT_WIDTH * 2, height: extraTop + ROW_HEIGHT - RECT_WIDTH * 2})
|
|
54
|
+
let repeatLeft = 0
|
|
55
|
+
let repeatRight = 0
|
|
56
|
+
const top = renderer.y
|
|
57
|
+
const left = leftMargin + colWidth * barNum
|
|
58
|
+
if (measure.hasStartRepeat) {
|
|
59
|
+
drawRepeat(renderer, left, top, top+ROW_HEIGHT+extraTop, true, extraTop)
|
|
60
|
+
repeatLeft = 12
|
|
61
|
+
}
|
|
62
|
+
if (measure.hasEndRepeat) {
|
|
63
|
+
drawRepeat(renderer, left+colWidth, top, top+ROW_HEIGHT+extraTop, false, extraTop)
|
|
64
|
+
repeatRight = 12
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let endingWidth = 0
|
|
68
|
+
if (measure.ending) {
|
|
69
|
+
const endingEl = text(renderer, measure.ending, leftMargin + barNum * colWidth + 4, top + 10, 12, endingFont, null, null, false )
|
|
70
|
+
endingWidth = endingEl.getBBox().width + 4
|
|
71
|
+
}
|
|
72
|
+
drawMeasure(renderer, top, leftMargin+repeatLeft, colWidth, lineNum, barNum, measure.chord, chordFont, repeatLeft+repeatRight, ROW_HEIGHT, extraTop)
|
|
73
|
+
if (measure.annotations && measure.annotations.length > 0) {
|
|
74
|
+
drawAnnotations(renderer, top, leftMargin + barNum * colWidth +endingWidth, measure.annotations, annotationFont)
|
|
75
|
+
}
|
|
76
|
+
if (extraTop) {
|
|
77
|
+
renderer.paper.rectBeneath({x: leftMargin + barNum * colWidth, y: renderer.y, width: colWidth, height: extraTop, fill: '#e8e8e8', stroke: 'none'})
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
renderer.moveY(extraTop + ROW_HEIGHT)
|
|
82
|
+
})
|
|
83
|
+
renderer.moveY(PART_MARGIN_BOTTOM)
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
renderer.paper.closeGroup()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function drawPercent(renderer, x, y, offset) {
|
|
92
|
+
var lineX1 = x - 10
|
|
93
|
+
var lineX2 = x + 10
|
|
94
|
+
var lineY1 = y + 10
|
|
95
|
+
var lineY2 = y - 10
|
|
96
|
+
var leftDotX = x - 10
|
|
97
|
+
var leftDotY = -renderer.yToPitch(offset) + 2
|
|
98
|
+
var rightDotX = x + 6.5
|
|
99
|
+
var rightDotY = -renderer.yToPitch(offset) -2.3
|
|
100
|
+
|
|
101
|
+
renderer.paper.lineToBack({x1: lineX1, x2: lineX2, y1: lineY1, y2: lineY2, 'stroke-width': '3px', 'stroke-linecap':"round" })
|
|
102
|
+
|
|
103
|
+
printSymbol(renderer, leftDotX, leftDotY, "dots.dot", {
|
|
104
|
+
scalex: 1,
|
|
105
|
+
scaley: 1,
|
|
106
|
+
klass: "",
|
|
107
|
+
name: "dot"
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
printSymbol(renderer, rightDotX, rightDotY, "dots.dot", {
|
|
111
|
+
scalex: 1,
|
|
112
|
+
scaley: 1,
|
|
113
|
+
klass: "",
|
|
114
|
+
name: "dot"
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function drawRepeat(renderer, x, y1, y2, isStart, offset) {
|
|
120
|
+
const lineX = isStart ? x+2 : x-4
|
|
121
|
+
const circleX = isStart ? x+9 : x-11
|
|
122
|
+
|
|
123
|
+
renderer.paper.openGroup({klass:'abcjs-repeat'})
|
|
124
|
+
printStem(renderer, lineX, 3 + renderer.lineThickness, y1, y2, null, "bar")
|
|
125
|
+
|
|
126
|
+
printSymbol(renderer, circleX, -renderer.yToPitch(offset)-4, "dots.dot", {
|
|
127
|
+
scalex: 1,
|
|
128
|
+
scaley: 1,
|
|
129
|
+
klass: "",
|
|
130
|
+
name: "dot"
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
printSymbol(renderer, circleX, -renderer.yToPitch(offset)-8, "dots.dot", {
|
|
134
|
+
scalex: 1,
|
|
135
|
+
scaley: 1,
|
|
136
|
+
klass: "",
|
|
137
|
+
name: "dot"
|
|
138
|
+
});
|
|
139
|
+
renderer.paper.closeGroup()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const symbols = {
|
|
143
|
+
'segno': "scripts.segno",
|
|
144
|
+
'coda': "scripts.coda",
|
|
145
|
+
"fermata": "scripts.ufermata",
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function drawAnnotations(renderer, offset, left, annotations, annotationFont) {
|
|
149
|
+
left += 3
|
|
150
|
+
let el
|
|
151
|
+
for (let a = 0; a < annotations.length; a++) {
|
|
152
|
+
switch (annotations[a]) {
|
|
153
|
+
case 'segno':
|
|
154
|
+
case 'coda':
|
|
155
|
+
case "fermata": {
|
|
156
|
+
left += 12
|
|
157
|
+
el = printSymbol(renderer, left, -3, symbols[annotations[a]], {
|
|
158
|
+
scalex: 1,
|
|
159
|
+
scaley: 1,
|
|
160
|
+
//klass: renderer.controller.classes.generate(klass),
|
|
161
|
+
name: symbols[annotations[a]]
|
|
162
|
+
});
|
|
163
|
+
const box = el.getBBox()
|
|
164
|
+
left += box.width
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
default:
|
|
168
|
+
text(renderer, annotations[a], left, offset + 12, 12, annotationFont, null, null, false )
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function drawMeasure(renderer, offset, leftMargin, colWidth, lineNum, barNum, chords, chordFont, margin, height, extraTop) {
|
|
175
|
+
const left = leftMargin + colWidth * barNum
|
|
176
|
+
if (!chords[1] && !chords[2] && !chords[3])
|
|
177
|
+
drawSingleChord(renderer, left, offset+extraTop, colWidth-margin, height, chords[0], chordFont, extraTop)
|
|
178
|
+
else if (!chords[1] && !chords[3])
|
|
179
|
+
drawTwoChords(renderer, left, offset, colWidth-margin, height, chords[0], chords[2], chordFont, extraTop)
|
|
180
|
+
else
|
|
181
|
+
drawFourChords(renderer, left, offset, colWidth-margin, height, chords, chordFont, extraTop)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function renderChord(renderer, x, y, size, chord, font, maxWidth) {
|
|
185
|
+
const el = text(renderer, chord, x, y, size, font, null, "abcjs-chord", true)
|
|
186
|
+
let bb = el.getBBox()
|
|
187
|
+
let fontSize = size
|
|
188
|
+
while (bb.width > maxWidth && fontSize >= 14) {
|
|
189
|
+
fontSize -= 2
|
|
190
|
+
el.setAttribute('font-size', fontSize)
|
|
191
|
+
bb = el.getBBox()
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const MAX_ONE_CHORD = 34
|
|
196
|
+
const MAX_TWO_CHORDS = 26
|
|
197
|
+
const MAX_FOUR_CHORDS = 20
|
|
198
|
+
const TOP_MARGIN = -3
|
|
199
|
+
|
|
200
|
+
function drawSingleChord(renderer, left, top, width, height, chord, font, extraTop) {
|
|
201
|
+
if (chord === '%')
|
|
202
|
+
drawPercent(renderer, left+width/2, top+height/2, extraTop+height/2)
|
|
203
|
+
else
|
|
204
|
+
renderChord(renderer, left+width/2, top+height/2+TOP_MARGIN, MAX_ONE_CHORD, chord, font, width)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function drawTwoChords(renderer, left, top, width, height, chord1, chord2, font, extraTop) {
|
|
208
|
+
renderer.paper.lineToBack({x1: left, x2: left+width, y1: top+height+extraTop, y2: top+2 })
|
|
209
|
+
renderChord(renderer, left+width/4, top+height/4+5+extraTop+TOP_MARGIN, MAX_TWO_CHORDS, chord1, font, width/2)
|
|
210
|
+
renderChord(renderer, left+3*width/4, top+3*height/4+extraTop+TOP_MARGIN, MAX_TWO_CHORDS, chord2, font, width/2)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function drawFourChords(renderer, left, top, width, height, chords, font, extraTop) {
|
|
214
|
+
const MARGIN = 3
|
|
215
|
+
renderer.paper.lineToBack({x1: left+MARGIN, x2: left+width-MARGIN, y1: top+height/2+extraTop, y2: top+height/2+extraTop })
|
|
216
|
+
renderer.paper.lineToBack({x1: left+width/2, x2: left+width/2, y1: top+MARGIN+extraTop, y2: top+height-MARGIN+extraTop })
|
|
217
|
+
|
|
218
|
+
if (chords[0])
|
|
219
|
+
renderChord(renderer, left+width/4, top+height/4+2+extraTop+TOP_MARGIN, MAX_FOUR_CHORDS, shortenChord(chords[0]), font, width / 2)
|
|
220
|
+
if (chords[1])
|
|
221
|
+
renderChord(renderer, left+3*width/4, top+height/4+2+extraTop+TOP_MARGIN, MAX_FOUR_CHORDS, shortenChord(chords[1]), font, width / 2)
|
|
222
|
+
if (chords[2])
|
|
223
|
+
renderChord(renderer, left+width/4, top+3*height/4+extraTop+TOP_MARGIN, MAX_FOUR_CHORDS, shortenChord(chords[2]), font, width / 2)
|
|
224
|
+
if (chords[3])
|
|
225
|
+
renderChord(renderer, left+3*width/4, top+3*height/4+extraTop+TOP_MARGIN, MAX_FOUR_CHORDS, shortenChord(chords[3]), font, width / 2)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function shortenChord(chord) {
|
|
229
|
+
if (chord === "No Chord")
|
|
230
|
+
return "N.C."
|
|
231
|
+
return chord
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function text(renderer, str, x, y, size, font, dataName, klass, alignCenter) {
|
|
235
|
+
const attr = {
|
|
236
|
+
x: x,
|
|
237
|
+
y: y,
|
|
238
|
+
stroke:"none",
|
|
239
|
+
'font-size':size,
|
|
240
|
+
'font-style':font.style,
|
|
241
|
+
'font-family':font.face,
|
|
242
|
+
'font-weight':font.weight,
|
|
243
|
+
'text-decoration':font.decoration,
|
|
244
|
+
}
|
|
245
|
+
if (dataName)
|
|
246
|
+
attr['data-name'] = dataName
|
|
247
|
+
if (klass)
|
|
248
|
+
attr['class'] = klass
|
|
249
|
+
attr["text-anchor"] = alignCenter ? "middle" : "start"
|
|
250
|
+
|
|
251
|
+
return renderer.paper.text(str, attr, null, {"alignment-baseline": "middle"})
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = drawChordGrid
|
package/src/write/draw/draw.js
CHANGED
|
@@ -3,8 +3,9 @@ var setPaperSize = require('./set-paper-size');
|
|
|
3
3
|
var nonMusic = require('./non-music');
|
|
4
4
|
var spacing = require('../helpers/spacing');
|
|
5
5
|
var Selectables = require('./selectables');
|
|
6
|
+
var drawChordGrid = require('./chord-grid');
|
|
6
7
|
|
|
7
|
-
function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, selectTypes, tuneNumber, lineOffset) {
|
|
8
|
+
function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, selectTypes, tuneNumber, lineOffset, chordGrid) {
|
|
8
9
|
var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber);
|
|
9
10
|
var groupClasses = {}
|
|
10
11
|
if (classes.shouldAddClasses)
|
|
@@ -14,48 +15,60 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
|
|
|
14
15
|
nonMusic(renderer, abcTune.topText, selectables);
|
|
15
16
|
renderer.paper.closeGroup()
|
|
16
17
|
renderer.moveY(renderer.spacing.music);
|
|
18
|
+
|
|
19
|
+
let suppressMusic = false
|
|
20
|
+
if (chordGrid && abcTune.chordGrid) {
|
|
21
|
+
drawChordGrid(renderer, abcTune.chordGrid, renderer.padding.left, width, abcTune.formatting)
|
|
22
|
+
if (chordGrid === 'noMusic')
|
|
23
|
+
suppressMusic = true
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
var staffgroups = [];
|
|
18
27
|
var nStaves = 0;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
+
if (!suppressMusic) {
|
|
29
|
+
for (var line = 0; line < abcTune.lines.length; line++) {
|
|
30
|
+
classes.incrLine();
|
|
31
|
+
var abcLine = abcTune.lines[line];
|
|
32
|
+
if (abcLine.staff) {
|
|
33
|
+
// MAE 26 May 2025 - for incipits staff count limiting
|
|
34
|
+
nStaves++;
|
|
35
|
+
if (abcTune.formatting.maxStaves) {
|
|
36
|
+
if (nStaves > abcTune.formatting.maxStaves) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
28
39
|
}
|
|
40
|
+
if (classes.shouldAddClasses)
|
|
41
|
+
groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber
|
|
42
|
+
renderer.paper.openGroup(groupClasses)
|
|
43
|
+
if (abcLine.vskip) {
|
|
44
|
+
renderer.moveY(abcLine.vskip);
|
|
45
|
+
}
|
|
46
|
+
if (staffgroups.length >= 1)
|
|
47
|
+
addStaffPadding(renderer, renderer.spacing.staffSeparation, staffgroups[staffgroups.length - 1], abcLine.staffGroup);
|
|
48
|
+
var staffgroup = engraveStaffLine(renderer, abcLine.staffGroup, selectables, line);
|
|
49
|
+
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.
|
|
50
|
+
staffgroups.push(staffgroup);
|
|
51
|
+
renderer.paper.closeGroup()
|
|
52
|
+
} else if (abcLine.nonMusic) {
|
|
53
|
+
if (classes.shouldAddClasses)
|
|
54
|
+
groupClasses.klass = "abcjs-non-music"
|
|
55
|
+
renderer.paper.openGroup(groupClasses)
|
|
56
|
+
nonMusic(renderer, abcLine.nonMusic, selectables);
|
|
57
|
+
renderer.paper.closeGroup()
|
|
29
58
|
}
|
|
30
|
-
if (classes.shouldAddClasses)
|
|
31
|
-
groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber
|
|
32
|
-
renderer.paper.openGroup(groupClasses)
|
|
33
|
-
if (abcLine.vskip) {
|
|
34
|
-
renderer.moveY(abcLine.vskip);
|
|
35
|
-
}
|
|
36
|
-
if (staffgroups.length >= 1)
|
|
37
|
-
addStaffPadding(renderer, renderer.spacing.staffSeparation, staffgroups[staffgroups.length - 1], abcLine.staffGroup);
|
|
38
|
-
var staffgroup = engraveStaffLine(renderer, abcLine.staffGroup, selectables, line);
|
|
39
|
-
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.
|
|
40
|
-
staffgroups.push(staffgroup);
|
|
41
|
-
renderer.paper.closeGroup()
|
|
42
|
-
} else if (abcLine.nonMusic) {
|
|
43
|
-
if (classes.shouldAddClasses)
|
|
44
|
-
groupClasses.klass = "abcjs-non-music"
|
|
45
|
-
renderer.paper.openGroup(groupClasses)
|
|
46
|
-
nonMusic(renderer, abcLine.nonMusic, selectables);
|
|
47
|
-
renderer.paper.closeGroup()
|
|
48
59
|
}
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
classes.reset();
|
|
52
|
-
if (
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
if (!suppressMusic) {
|
|
64
|
+
if (abcTune.bottomText && abcTune.bottomText.rows && abcTune.bottomText.rows.length > 0) {
|
|
65
|
+
if (classes.shouldAddClasses)
|
|
66
|
+
groupClasses.klass = "abcjs-meta-bottom"
|
|
67
|
+
renderer.paper.openGroup(groupClasses)
|
|
68
|
+
renderer.moveY(24); // TODO-PER: Empirically discovered. What variable should this be?
|
|
69
|
+
nonMusic(renderer, abcTune.bottomText, selectables);
|
|
70
|
+
renderer.paper.closeGroup()
|
|
71
|
+
}
|
|
59
72
|
}
|
|
60
73
|
setPaperSize(renderer, maxWidth, scale, responsive);
|
|
61
74
|
return { staffgroups: staffgroups, selectables: selectables.getElements() };
|
package/src/write/draw/text.js
CHANGED
|
@@ -74,6 +74,8 @@ var EngraverController = function (paper, params) {
|
|
|
74
74
|
this.germanAlphabet = params.germanAlphabet;
|
|
75
75
|
if (params.lineThickness)
|
|
76
76
|
this.lineThickness = params.lineThickness;
|
|
77
|
+
if (params.chordGrid)
|
|
78
|
+
this.chordGrid = params.chordGrid;
|
|
77
79
|
this.renderer.controller = this; // TODO-GD needed for highlighting
|
|
78
80
|
this.renderer.foregroundColor = params.foregroundColor ? params.foregroundColor : "currentColor";
|
|
79
81
|
if (params.ariaLabel !== undefined)
|
|
@@ -251,7 +253,7 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
251
253
|
|
|
252
254
|
var origJazzChords = this.jazzchords
|
|
253
255
|
var scale = this.setupTune(abcTune, tuneNumber);
|
|
254
|
-
|
|
256
|
+
|
|
255
257
|
// Create all of the element objects that will appear on the page.
|
|
256
258
|
this.constructTuneElements(abcTune);
|
|
257
259
|
|
|
@@ -300,7 +302,7 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
300
302
|
}
|
|
301
303
|
|
|
302
304
|
// Do all the writing to the SVG
|
|
303
|
-
var ret = draw(this.renderer, this.classes, abcTune, this.width, maxWidth, this.responsive, scale, this.selectTypes, tuneNumber, lineOffset);
|
|
305
|
+
var ret = draw(this.renderer, this.classes, abcTune, this.width, maxWidth, this.responsive, scale, this.selectTypes, tuneNumber, lineOffset, this.chordGrid);
|
|
304
306
|
this.staffgroups = ret.staffgroups;
|
|
305
307
|
this.selectables = ret.selectables;
|
|
306
308
|
if (this.oneSvgPerLine) {
|
package/src/write/layout/beam.js
CHANGED
|
@@ -189,7 +189,22 @@ function createAdditionalBeams(elems, asc, beam, isGrace, dy) {
|
|
|
189
189
|
|
|
190
190
|
|
|
191
191
|
if (auxBeams[j].single) {
|
|
192
|
-
|
|
192
|
+
if(i === 0) {
|
|
193
|
+
// This is the first note in the group, always draw the beam to the right
|
|
194
|
+
auxBeamEndX = x + 5;
|
|
195
|
+
} else if (i === elems.length - 1) {
|
|
196
|
+
// This is the last note in the group, always draw the beam to the left
|
|
197
|
+
auxBeamEndX = x - 5;
|
|
198
|
+
} else {
|
|
199
|
+
// This is a middle note, check the note durations of the notes to the left and right
|
|
200
|
+
if(elems[i-1].duration === elems[i+1].duration) {
|
|
201
|
+
// The notes on either side are the same duration, alternate which side the beam goes to
|
|
202
|
+
auxBeamEndX = i%2 === 0 ? x + 5 : x - 5;
|
|
203
|
+
} else {
|
|
204
|
+
// The notes on either side are different durations, draw the beam to the shorter note
|
|
205
|
+
auxBeamEndX = elems[i-1].duration > elems[i+1].duration ? x + 5 : x - 5;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
193
208
|
auxBeamEndY = getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, auxBeamEndX) + sy * (j + 1);
|
|
194
209
|
}
|
|
195
210
|
var b = { startX: auxBeams[j].x, endX: auxBeamEndX, startY: auxBeams[j].y, endY: auxBeamEndY, dy: dy }
|
package/src/write/renderer.js
CHANGED
|
@@ -179,6 +179,10 @@ Renderer.prototype.calcY = function (ofs) {
|
|
|
179
179
|
return this.y - ofs * spacing.STEP;
|
|
180
180
|
};
|
|
181
181
|
|
|
182
|
+
Renderer.prototype.yToPitch = function (ofs) {
|
|
183
|
+
return ofs / spacing.STEP;
|
|
184
|
+
};
|
|
185
|
+
|
|
182
186
|
Renderer.prototype.moveY = function (em, numLines) {
|
|
183
187
|
if (numLines === undefined) numLines = 1;
|
|
184
188
|
this.y += em * numLines;
|
package/src/write/svg.js
CHANGED
|
@@ -168,7 +168,7 @@ Svg.prototype.rectBeneath = function (attr) {
|
|
|
168
168
|
this.svg.insertBefore(el, this.svg.firstChild);
|
|
169
169
|
};
|
|
170
170
|
|
|
171
|
-
Svg.prototype.text = function (text, attr, target) {
|
|
171
|
+
Svg.prototype.text = function (text, attr, target, spanAttr) {
|
|
172
172
|
var el = document.createElementNS(svgNS, 'text');
|
|
173
173
|
el.setAttribute("stroke", "none");
|
|
174
174
|
for (var key in attr) {
|
|
@@ -185,6 +185,13 @@ Svg.prototype.text = function (text, attr, target) {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
var line = document.createElementNS(svgNS, 'tspan');
|
|
188
|
+
if (spanAttr) {
|
|
189
|
+
for (var skey in spanAttr) {
|
|
190
|
+
if (spanAttr.hasOwnProperty(skey)) {
|
|
191
|
+
line.setAttribute(skey, spanAttr[skey]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
188
195
|
line.setAttribute("x", attr.x ? attr.x : 0);
|
|
189
196
|
if (i !== 0)
|
|
190
197
|
line.setAttribute("dy", "1.2em");
|
package/types/index.d.ts
CHANGED
|
@@ -266,6 +266,7 @@ declare module 'abcjs' {
|
|
|
266
266
|
add_classes?: boolean;
|
|
267
267
|
afterParsing?: AfterParsing;
|
|
268
268
|
ariaLabel?: string;
|
|
269
|
+
chordGrid?:'noMusic'|'withMusic';
|
|
269
270
|
clickListener?: ClickListener;
|
|
270
271
|
dragColor?: string;
|
|
271
272
|
dragging?: boolean;
|
|
@@ -872,6 +873,29 @@ declare module 'abcjs' {
|
|
|
872
873
|
}
|
|
873
874
|
}
|
|
874
875
|
|
|
876
|
+
interface ChordGridSubtitle {
|
|
877
|
+
type: "subtitle";
|
|
878
|
+
subtitle: string;
|
|
879
|
+
}
|
|
880
|
+
interface ChordGridText {
|
|
881
|
+
type: "text";
|
|
882
|
+
text: string;
|
|
883
|
+
}
|
|
884
|
+
interface ChordGridMeasure {
|
|
885
|
+
chord: [string,string,string,string];
|
|
886
|
+
hasStartRepeat?:boolean;
|
|
887
|
+
hasEndRepeat?:boolean;
|
|
888
|
+
noBorder?:boolean; // for when the line isn't complete, this is a placeholder
|
|
889
|
+
ending?:number; // This bar starts an ending
|
|
890
|
+
annotations?: Array<string>;
|
|
891
|
+
}
|
|
892
|
+
interface ChordGridPart {
|
|
893
|
+
type: "part";
|
|
894
|
+
name: string;
|
|
895
|
+
lines: Array<ChordGridMeasure>;
|
|
896
|
+
}
|
|
897
|
+
type ChordGrid = ChordGridSubtitle | ChordGridText | ChordGridPart;
|
|
898
|
+
|
|
875
899
|
export interface TuneObject {
|
|
876
900
|
formatting: Formatting;
|
|
877
901
|
engraver?: EngraverController;
|
|
@@ -881,6 +905,7 @@ declare module 'abcjs' {
|
|
|
881
905
|
metaTextInfo: MetaTextInfo;
|
|
882
906
|
version: string;
|
|
883
907
|
warnings?: Array<string>;
|
|
908
|
+
chordGrid?: Array<ChordGrid>;
|
|
884
909
|
|
|
885
910
|
getTotalTime: NumberFunction;
|
|
886
911
|
getTotalBeats: NumberFunction;
|
package/version.js
CHANGED