abcjs 6.5.1 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/RELEASE.md +44 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +1198 -150
- 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/edit/abc_editarea.js +53 -50
- package/src/edit/abc_editor.js +176 -157
- package/src/midi/abc_midi_create.js +2 -2
- package/src/parse/abc_parse.js +20 -0
- package/src/parse/chord-grid.js +364 -0
- package/src/parse/tune-builder.js +4 -4
- package/src/str/output.js +97 -48
- package/src/synth/abc_midi_sequencer.js +20 -30
- package/src/synth/create-synth.js +1 -1
- package/src/synth/repeats.js +200 -0
- package/src/write/creation/abstract-engraver.js +4 -1
- package/src/write/draw/chord-grid.js +252 -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 +36 -3
- package/version.js +1 -1
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ var TimingCallbacks = function(target, params) {
|
|
|
12
12
|
self.lineEndCallback = params.lineEndCallback; // This is called when the end of a line is approaching.
|
|
13
13
|
self.lineEndAnticipation = params.lineEndAnticipation ? parseInt(params.lineEndAnticipation, 10) : 0; // How many milliseconds before the end should the call happen.
|
|
14
14
|
self.beatSubdivisions = params.beatSubdivisions ? parseInt(params.beatSubdivisions, 10) : 1; // how many callbacks per beat is desired.
|
|
15
|
+
if (!self.beatSubdivisions) self.beatSubdivisions = 1
|
|
15
16
|
self.joggerTimer = null;
|
|
16
17
|
|
|
17
18
|
self.replaceTarget = function(newTarget) {
|
|
@@ -38,7 +39,73 @@ var TimingCallbacks = function(target, params) {
|
|
|
38
39
|
// noteTimings contains an array of events sorted by time. Events that happen at the same time are in the same element of the array.
|
|
39
40
|
self.millisecondsPerBeat = 1000 / (self.qpm / 60) / self.beatSubdivisions;
|
|
40
41
|
self.lastMoment = self.noteTimings[self.noteTimings.length-1].milliseconds;
|
|
41
|
-
|
|
42
|
+
|
|
43
|
+
// For irregular time sigs that specify the beat divisions (that is, something like `M: 2+3/8`)
|
|
44
|
+
// Then the beat is not a regular pulse. To keep most of this logic easy, the beat will be specified
|
|
45
|
+
// as half as long, but the callback will happen according to the pattern. That is,
|
|
46
|
+
// for bpm=60 at the above time signature, internally the beat callback will happen at 120 bpm, but
|
|
47
|
+
// the callback function will be called at 2 sub beats, then 3 sub beats, etc.
|
|
48
|
+
// The beat number will be an integer for all of them and count up by one each time.
|
|
49
|
+
var meter = newTarget.getMeter()
|
|
50
|
+
var irregularMeter = ''
|
|
51
|
+
if (meter && meter.type === "specified" && meter.value && meter.value.length > 0 && meter.value[0].num.indexOf('+') > 0)
|
|
52
|
+
irregularMeter = meter.value[0].num
|
|
53
|
+
// for subdivisions = 1, then this should contain only whole numbers and the callbacks are irregular
|
|
54
|
+
// for subdivisions = 2, then this should be 1/8 notes. The beats should be something like: 0, 0.5, 1, 1.33, 1.66 2 (For M:2+3/8)
|
|
55
|
+
// etc for more subdivisions - they are just multiplied
|
|
56
|
+
self.beatStarts = []
|
|
57
|
+
if (irregularMeter) {
|
|
58
|
+
var measureLength = self.noteTimings[self.noteTimings.length-1].millisecondsPerMeasure
|
|
59
|
+
var numMeasures = self.lastMoment / measureLength
|
|
60
|
+
var parts = irregularMeter.split("+")
|
|
61
|
+
for (var i = 0; i < parts.length; i++)
|
|
62
|
+
parts[i] = parseInt(parts[i],10) / 2 // since we count a beat as a quarter note, but these numbers refer to 1/8 notes, we convert the beat length
|
|
63
|
+
var currentTs = 0
|
|
64
|
+
var beatNumber = 0
|
|
65
|
+
// For the input: parts = [ 1, 1.5 ] and beatSubdivisions = 2
|
|
66
|
+
// beatNumbers = 0 0.5 1 1.33 1.67 2 2.5 3 3.33 3.67 ...
|
|
67
|
+
// when part=1 then there is 1 extra sub beat and it is 0.5
|
|
68
|
+
// when part=1.5 then there are 2 extra sub beats at 0.33 and 0.67
|
|
69
|
+
//
|
|
70
|
+
// beatSubdivision | numFor1 | numFor1.5
|
|
71
|
+
// 2 | 2 | 3
|
|
72
|
+
// 3 | 3 | 4.5
|
|
73
|
+
// 4 | 4 | 6
|
|
74
|
+
for (var measureNumber = 0; measureNumber < numMeasures; measureNumber++) {
|
|
75
|
+
var measureStartTs = measureNumber * measureLength
|
|
76
|
+
var subBeatCounter = 0
|
|
77
|
+
for (var kk = 0; kk < parts.length; kk++) {
|
|
78
|
+
var beatLength = parts[kk] // This is either 1 or 1.5 (how many quarter notes in this beat)
|
|
79
|
+
if (self.beatSubdivisions === 1) {
|
|
80
|
+
if (self.beatSubdivisions === 1)
|
|
81
|
+
if (currentTs < self.lastMoment) {
|
|
82
|
+
self.beatStarts.push({b: beatNumber, ts: currentTs})
|
|
83
|
+
}
|
|
84
|
+
currentTs += beatLength * self.millisecondsPerBeat
|
|
85
|
+
} else {
|
|
86
|
+
var numDivisions = beatLength * self.beatSubdivisions
|
|
87
|
+
for (var k = 0; k < Math.floor(numDivisions); k++) {
|
|
88
|
+
var subBeat = k / numDivisions
|
|
89
|
+
var ts = Math.round(measureStartTs + subBeatCounter * self.millisecondsPerBeat)
|
|
90
|
+
if (ts < self.lastMoment) {
|
|
91
|
+
self.beatStarts.push({b: beatNumber + subBeat, ts: ts})
|
|
92
|
+
}
|
|
93
|
+
subBeatCounter++
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
beatNumber++
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
self.beatStarts.push({b: numMeasures * parts.length, ts: self.lastMoment})
|
|
100
|
+
self.totalBeats = self.beatStarts.length
|
|
101
|
+
} else {
|
|
102
|
+
self.totalBeats = Math.round(self.lastMoment / self.millisecondsPerBeat);
|
|
103
|
+
// Add one so the last beat is the last moment
|
|
104
|
+
for (var j = 0; j < self.totalBeats+1; j++) {
|
|
105
|
+
self.beatStarts.push({b: j/self.beatSubdivisions, ts: Math.round(j * self.millisecondsPerBeat)})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//console.log({lastMoment: self.lastMoment, beatStarts: self.beatStarts})
|
|
42
109
|
};
|
|
43
110
|
|
|
44
111
|
self.replaceTarget(target);
|
|
@@ -49,11 +116,11 @@ var TimingCallbacks = function(target, params) {
|
|
|
49
116
|
if (self.lastTimestamp === timestamp)
|
|
50
117
|
return; // If there are multiple seeks or other calls, then we can easily get multiple callbacks for the same instant.
|
|
51
118
|
self.lastTimestamp = timestamp;
|
|
52
|
-
if (!self.startTime) {
|
|
53
|
-
self.startTime = timestamp;
|
|
54
|
-
}
|
|
55
119
|
|
|
56
120
|
if (!self.isPaused && self.isRunning) {
|
|
121
|
+
if (!self.startTime) {
|
|
122
|
+
self.startTime = timestamp;
|
|
123
|
+
}
|
|
57
124
|
self.currentTime = timestamp - self.startTime;
|
|
58
125
|
self.currentTime += 16; // Add a little slop because this function isn't called exactly.
|
|
59
126
|
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
|
|
@@ -73,8 +140,9 @@ var TimingCallbacks = function(target, params) {
|
|
|
73
140
|
}
|
|
74
141
|
if (self.currentTime < self.lastMoment) {
|
|
75
142
|
requestAnimationFrame(self.doTiming);
|
|
76
|
-
if (self.currentBeat
|
|
143
|
+
if (self.currentBeat < self.beatStarts.length && self.beatStarts[self.currentBeat].ts <= self.currentTime) {
|
|
77
144
|
var ret = self.doBeatCallback(timestamp);
|
|
145
|
+
self.currentBeat++
|
|
78
146
|
if (ret !== null)
|
|
79
147
|
self.currentTime = ret;
|
|
80
148
|
}
|
|
@@ -82,6 +150,7 @@ var TimingCallbacks = function(target, params) {
|
|
|
82
150
|
// Because of timing issues (for instance, if the browser tab isn't active), the beat callbacks might not have happened when they are supposed to. To keep the client programs from having to deal with that, this will keep calling the loop until all of them have been sent.
|
|
83
151
|
if (self.beatCallback) {
|
|
84
152
|
var ret2 = self.doBeatCallback(timestamp);
|
|
153
|
+
self.currentBeat++
|
|
85
154
|
if (ret2 !== null)
|
|
86
155
|
self.currentTime = ret2;
|
|
87
156
|
requestAnimationFrame(self.doTiming);
|
|
@@ -178,15 +247,15 @@ var TimingCallbacks = function(target, params) {
|
|
|
178
247
|
|
|
179
248
|
var thisStartTime = self.startTime; // the beat callback can call seek and change the position from beneath us.
|
|
180
249
|
self.beatCallback(
|
|
181
|
-
self.
|
|
250
|
+
self.beatStarts[self.currentBeat].b,
|
|
182
251
|
self.totalBeats / self.beatSubdivisions,
|
|
183
252
|
self.lastMoment,
|
|
184
253
|
position,
|
|
185
254
|
debugInfo);
|
|
186
255
|
if (thisStartTime !== self.startTime) {
|
|
187
256
|
return timestamp - self.startTime;
|
|
188
|
-
} else
|
|
189
|
-
|
|
257
|
+
} // else
|
|
258
|
+
// self.currentBeat++;
|
|
190
259
|
}
|
|
191
260
|
return null;
|
|
192
261
|
};
|
|
@@ -285,7 +354,6 @@ var TimingCallbacks = function(target, params) {
|
|
|
285
354
|
var now = performance.now();
|
|
286
355
|
self.startTime = now - self.currentTime;
|
|
287
356
|
|
|
288
|
-
var oldEvent = self.currentEvent;
|
|
289
357
|
self.currentEvent = 0;
|
|
290
358
|
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
|
|
291
359
|
self.currentEvent++;
|
|
@@ -298,10 +366,18 @@ var TimingCallbacks = function(target, params) {
|
|
|
298
366
|
}
|
|
299
367
|
}
|
|
300
368
|
|
|
369
|
+
//console.log({jump:self.currentTime})
|
|
301
370
|
var oldBeat = self.currentBeat;
|
|
302
|
-
self.currentBeat =
|
|
303
|
-
|
|
304
|
-
|
|
371
|
+
for (self.currentBeat = 0; self.currentBeat < self.beatStarts.length; self.currentBeat++) {
|
|
372
|
+
if (self.beatStarts[self.currentBeat].ts > self.currentTime)
|
|
373
|
+
break
|
|
374
|
+
}
|
|
375
|
+
self.currentBeat--
|
|
376
|
+
if (self.beatCallback && oldBeat !== self.currentBeat) {
|
|
377
|
+
// If the movement caused the beat to change, then immediately report it to the client.
|
|
378
|
+
self.doBeatCallback(self.startTime + self.currentTime);
|
|
379
|
+
self.currentBeat++
|
|
380
|
+
}
|
|
305
381
|
|
|
306
382
|
if (self.eventCallback && self.currentEvent >= 0 && self.noteTimings[self.currentEvent].type === 'event')
|
|
307
383
|
self.eventCallback(self.noteTimings[self.currentEvent]);
|
|
@@ -328,4 +404,3 @@ function getLineEndTimings(timings, anticipation) {
|
|
|
328
404
|
}
|
|
329
405
|
|
|
330
406
|
module.exports = TimingCallbacks;
|
|
331
|
-
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
// All these keys have the same number of accidentals
|
|
2
2
|
var keys = {
|
|
3
|
-
'C': { modes: ['CMaj', 'Amin', 'Am', 'GMix', 'DDor', 'EPhr', 'FLyd', 'BLoc'], stepsFromC: 0 },
|
|
4
|
-
'Db': { modes: ['DbMaj', 'Bbmin', 'Bbm', 'AbMix', 'EbDor', 'FPhr', 'GbLyd', 'CLoc'], stepsFromC: 1 },
|
|
5
|
-
'D': { modes: ['DMaj', 'Bmin', 'Bm', 'AMix', 'EDor', 'F#Phr', 'GLyd', 'C#Loc'], stepsFromC: 2 },
|
|
6
|
-
'Eb': { modes: ['EbMaj', 'Cmin', 'Cm', 'BbMix', 'FDor', 'GPhr', 'AbLyd', 'DLoc'], stepsFromC: 3 },
|
|
7
|
-
'E': { modes: ['EMaj', 'C#min', 'C#m', 'BMix', 'F#Dor', 'G#Phr', 'ALyd', 'D#Loc'], stepsFromC: 4 },
|
|
8
|
-
'F': { modes: ['FMaj', 'Dmin', 'Dm', 'CMix', 'GDor', 'APhr', 'BbLyd', 'ELoc'], stepsFromC: 5 },
|
|
9
|
-
'Gb': { modes: ['GbMaj', 'Ebmin', 'Ebm', 'DbMix', 'AbDor', 'BbPhr', 'CbLyd', 'FLoc'], stepsFromC: 6 },
|
|
10
|
-
'G': { modes: ['GMaj', 'Emin', 'Em', 'DMix', 'ADor', 'BPhr', 'CLyd', 'F#Loc'], stepsFromC: 7 },
|
|
11
|
-
'Ab': { modes: ['AbMaj', 'Fmin', 'Fm', 'EbMix', 'BbDor', 'CPhr', 'DbLyd', 'GLoc'], stepsFromC: 8 },
|
|
12
|
-
'A': { modes: ['AMaj', 'F#min', 'F#m', 'EMix', 'BDor', 'C#Phr', 'DLyd', 'G#Loc'], stepsFromC: 9 },
|
|
13
|
-
'Bb': { modes: ['BbMaj', 'Gmin', 'Gm', 'FMix', 'CDor', 'DPhr', 'EbLyd', 'ALoc'], stepsFromC: 10 },
|
|
14
|
-
'B': { modes: ['BMaj', 'G#min', 'G#m', 'F#Mix', 'C#Dor', 'D#Phr', 'ELyd', 'A#Loc'], stepsFromC: 11 },
|
|
3
|
+
'C': { modes: ['CMaj', 'CIon', 'Amin', 'AAeo', 'Am', 'GMix', 'DDor', 'EPhr', 'FLyd', 'BLoc'], stepsFromC: 0 },
|
|
4
|
+
'Db': { modes: ['DbMaj', 'DbIon', 'Bbmin', 'BbAeo', 'Bbm', 'AbMix', 'EbDor', 'FPhr', 'GbLyd', 'CLoc'], stepsFromC: 1 },
|
|
5
|
+
'D': { modes: ['DMaj', 'DIon', 'Bmin', 'BAeo', 'Bm', 'AMix', 'EDor', 'F#Phr', 'GLyd', 'C#Loc'], stepsFromC: 2 },
|
|
6
|
+
'Eb': { modes: ['EbMaj', 'EbIon', 'Cmin', 'CAeo', 'Cm', 'BbMix', 'FDor', 'GPhr', 'AbLyd', 'DLoc'], stepsFromC: 3 },
|
|
7
|
+
'E': { modes: ['EMaj', 'EIon', 'C#min', 'C#Aeo', 'C#m', 'BMix', 'F#Dor', 'G#Phr', 'ALyd', 'D#Loc'], stepsFromC: 4 },
|
|
8
|
+
'F': { modes: ['FMaj', 'FIon', 'Dmin', 'DAeo', 'Dm', 'CMix', 'GDor', 'APhr', 'BbLyd', 'ELoc'], stepsFromC: 5 },
|
|
9
|
+
'Gb': { modes: ['GbMaj', 'GbIon', 'Ebmin', 'EbAeo', 'Ebm', 'DbMix', 'AbDor', 'BbPhr', 'CbLyd', 'FLoc'], stepsFromC: 6 },
|
|
10
|
+
'G': { modes: ['GMaj', 'GIon', 'Emin', 'EAeo', 'Em', 'DMix', 'ADor', 'BPhr', 'CLyd', 'F#Loc'], stepsFromC: 7 },
|
|
11
|
+
'Ab': { modes: ['AbMaj', 'AbIon', 'Fmin', 'FAeo', 'Fm', 'EbMix', 'BbDor', 'CPhr', 'DbLyd', 'GLoc'], stepsFromC: 8 },
|
|
12
|
+
'A': { modes: ['AMaj', 'AIon', 'F#min', 'F#Aeo', 'F#m', 'EMix', 'BDor', 'C#Phr', 'DLyd', 'G#Loc'], stepsFromC: 9 },
|
|
13
|
+
'Bb': { modes: ['BbMaj', 'BbIon', 'Gmin', 'GAeo', 'Gm', 'FMix', 'CDor', 'DPhr', 'EbLyd', 'ALoc'], stepsFromC: 10 },
|
|
14
|
+
'B': { modes: ['BMaj', 'BIon', 'G#min', 'G#Aeo', 'G#m', 'F#Mix', 'C#Dor', 'D#Phr', 'ELyd', 'A#Loc'], stepsFromC: 11 },
|
|
15
15
|
// Enharmonic keys
|
|
16
|
-
'C#': { modes: ['C#Maj', 'A#min', 'A#m', 'G#Mix', 'D#Dor', 'E#Phr', 'F#Lyd', 'B#Loc'], stepsFromC: 1 },
|
|
17
|
-
'F#': { modes: ['F#Maj', 'D#min', 'D#m', 'C#Mix', 'G#Dor', 'A#Phr', 'BLyd', 'E#Loc'], stepsFromC: 6 },
|
|
18
|
-
'Cb': { modes: ['CbMaj', 'Abmin', 'Abm', 'GbMix', 'DbDor', 'EbPhr', 'FbLyd', 'BbLoc'], stepsFromC: 11 },
|
|
16
|
+
'C#': { modes: ['C#Maj', 'C#Ion', 'A#min', 'A#Aeo', 'A#m', 'G#Mix', 'D#Dor', 'E#Phr', 'F#Lyd', 'B#Loc'], stepsFromC: 1 },
|
|
17
|
+
'F#': { modes: ['F#Maj', 'F#Ion', 'D#min', 'D#Aeo', 'D#m', 'C#Mix', 'G#Dor', 'A#Phr', 'BLyd', 'E#Loc'], stepsFromC: 6 },
|
|
18
|
+
'Cb': { modes: ['CbMaj', 'CbIon', 'Abmin', 'AbAeo', 'Abm', 'GbMix', 'DbDor', 'EbPhr', 'FbLyd', 'BbLoc'], stepsFromC: 11 },
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
var modeNames = ['maj', 'ion', 'min', 'aeo', 'm', 'mix', 'dor', 'phr', 'lyd', 'loc']
|
|
22
|
+
function isLegalMode(mode) {
|
|
23
|
+
return modeNames.indexOf(mode.toLowerCase()) >= 0
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
var keyReverse = null
|
|
@@ -42,7 +47,7 @@ function relativeMajor(key) {
|
|
|
42
47
|
createKeyReverse()
|
|
43
48
|
}
|
|
44
49
|
// get the key portion itself - there might be other stuff, like extra sharps and flats, or the mode written out.
|
|
45
|
-
var mode = key.toLowerCase().match(/([a-g][b#]?)(maj|min|mix|dor|phr|lyd|loc|m)?/)
|
|
50
|
+
var mode = key.toLowerCase().match(/([a-g][b#]?)(maj|ion|min|aeo|mix|dor|phr|lyd|loc|m)?/);
|
|
46
51
|
if (!mode || !mode[2])
|
|
47
52
|
return key;
|
|
48
53
|
mode = mode[1] + mode[2]
|
|
@@ -60,10 +65,10 @@ function relativeMode(majorKey, mode) {
|
|
|
60
65
|
return majorKey;
|
|
61
66
|
if (mode === '')
|
|
62
67
|
return majorKey;
|
|
63
|
-
var match = mode.toLowerCase().match(/^(maj|min|mix|dor|phr|lyd|loc|m)/)
|
|
68
|
+
var match = mode.toLowerCase().match(/^(maj|ion|min|aeo|mix|dor|phr|lyd|loc|m)/);
|
|
64
69
|
if (!match)
|
|
65
70
|
return majorKey
|
|
66
|
-
var regMode = match[1]
|
|
71
|
+
var regMode = match[1]
|
|
67
72
|
for (var i = 0; i < group.modes.length; i++) {
|
|
68
73
|
var thisMode = group.modes[i]
|
|
69
74
|
var ind = thisMode.toLowerCase().indexOf(regMode)
|
|
@@ -89,4 +94,4 @@ function transposeKey(key, steps) {
|
|
|
89
94
|
return key;
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
module.exports = {relativeMajor: relativeMajor, relativeMode: relativeMode, transposeKey: transposeKey};
|
|
97
|
+
module.exports = {relativeMajor: relativeMajor, relativeMode: relativeMode, transposeKey: transposeKey, isLegalMode:isLegalMode};
|
package/src/data/abc_tune.js
CHANGED
|
@@ -90,12 +90,17 @@ var Tune = function() {
|
|
|
90
90
|
this.getBeatLength = function() {
|
|
91
91
|
// This returns a fraction: for instance 1/4 for a quarter
|
|
92
92
|
// There are two types of meters: compound and regular. Compound meter has 3 beats counted as one.
|
|
93
|
+
|
|
94
|
+
// Irregular meters have the beat as an 1/8 note but the tempo as a 1/4.
|
|
95
|
+
// That keeps it a similar tempo to 4/4 but that may or may not be generally intuitive, so that might need to change.
|
|
93
96
|
var meter = this.getMeterFraction();
|
|
94
97
|
var multiplier = 1;
|
|
95
98
|
if (meter.num === 6 || meter.num === 9 || meter.num === 12)
|
|
96
99
|
multiplier = 3;
|
|
97
100
|
else if (meter.num === 3 && meter.den === 8)
|
|
98
101
|
multiplier = 3;
|
|
102
|
+
else if (meter.den === 8 && (meter.num === 5 || meter.num === 7))
|
|
103
|
+
multiplier = 2
|
|
99
104
|
|
|
100
105
|
return multiplier / meter.den;
|
|
101
106
|
};
|
|
@@ -194,7 +199,13 @@ var Tune = function() {
|
|
|
194
199
|
var den = 4;
|
|
195
200
|
if (meter) {
|
|
196
201
|
if (meter.type === 'specified') {
|
|
197
|
-
|
|
202
|
+
if (meter.value && meter.value.length > 0 && meter.value[0].num.indexOf('+') > 0) {
|
|
203
|
+
var parts = meter.value[0].num.split('+')
|
|
204
|
+
num = 0;
|
|
205
|
+
for (var i = 0; i < parts.length; i++)
|
|
206
|
+
num += parseInt(parts[i],10)
|
|
207
|
+
} else
|
|
208
|
+
num = parseInt(meter.value[0].num, 10);
|
|
198
209
|
den = parseInt(meter.value[0].den,10);
|
|
199
210
|
} else if (meter.type === 'cut_time') {
|
|
200
211
|
num = 2;
|
package/src/data/deline-tune.js
CHANGED
|
@@ -11,7 +11,7 @@ function delineTune(inputLines, options) {
|
|
|
11
11
|
var currentTripletFont = [];
|
|
12
12
|
var currentAnnotationFont = [];
|
|
13
13
|
for (var i = 0; i < inputLines.length; i++) {
|
|
14
|
-
var inputLine = inputLines[i];
|
|
14
|
+
var inputLine = cloneLine(inputLines[i]);
|
|
15
15
|
if (inputLine.staff) {
|
|
16
16
|
if (inMusicLine && !inputLine.vskip) {
|
|
17
17
|
var outputLine = outputLines[outputLines.length-1];
|
package/src/edit/abc_editarea.js
CHANGED
|
@@ -43,73 +43,76 @@ try {
|
|
|
43
43
|
// if we aren't in a browser, this code will crash, but it is not needed then either.
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
var EditArea = function(textareaid) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
var EditArea = function (textareaid) {
|
|
47
|
+
this.isEditArea = true
|
|
48
|
+
if (typeof textareaid === "string") {
|
|
49
|
+
this.textarea = document.getElementById(textareaid);
|
|
50
|
+
if (!this.textarea)
|
|
51
|
+
this.textarea = document.querySelector(textareaid)
|
|
52
|
+
} else
|
|
53
|
+
this.textarea = textareaid;
|
|
54
|
+
this.initialText = this.textarea.value;
|
|
55
|
+
this.isDragging = false;
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
EditArea.prototype.addSelectionListener = function(listener) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
EditArea.prototype.addSelectionListener = function (listener) {
|
|
59
|
+
this.textarea.onmousemove = function (ev) {
|
|
60
|
+
if (this.isDragging)
|
|
61
|
+
listener.fireSelectionChanged();
|
|
62
|
+
};
|
|
60
63
|
};
|
|
61
64
|
|
|
62
|
-
EditArea.prototype.addChangeListener = function(listener) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
EditArea.prototype.addChangeListener = function (listener) {
|
|
66
|
+
this.changelistener = listener;
|
|
67
|
+
this.textarea.onkeyup = function () {
|
|
68
|
+
listener.fireChanged();
|
|
69
|
+
};
|
|
70
|
+
this.textarea.onmousedown = function () {
|
|
71
|
+
this.isDragging = true;
|
|
72
|
+
listener.fireSelectionChanged();
|
|
73
|
+
};
|
|
74
|
+
this.textarea.onmouseup = function () {
|
|
75
|
+
this.isDragging = false;
|
|
76
|
+
listener.fireChanged();
|
|
77
|
+
};
|
|
78
|
+
this.textarea.onchange = function () {
|
|
79
|
+
listener.fireChanged();
|
|
80
|
+
};
|
|
78
81
|
};
|
|
79
82
|
|
|
80
83
|
//TODO won't work under IE?
|
|
81
|
-
EditArea.prototype.getSelection = function() {
|
|
82
|
-
|
|
84
|
+
EditArea.prototype.getSelection = function () {
|
|
85
|
+
return {start: this.textarea.selectionStart, end: this.textarea.selectionEnd};
|
|
83
86
|
};
|
|
84
87
|
|
|
85
|
-
EditArea.prototype.setSelection = function(start, end) {
|
|
86
|
-
if(this.textarea.setSelectionRange)
|
|
87
|
-
|
|
88
|
-
else if(this.textarea.createTextRange) {
|
|
88
|
+
EditArea.prototype.setSelection = function (start, end) {
|
|
89
|
+
if (this.textarea.setSelectionRange)
|
|
90
|
+
this.textarea.setSelectionRange(start, end);
|
|
91
|
+
else if (this.textarea.createTextRange) {
|
|
89
92
|
// For IE8
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
var e = this.textarea.createTextRange();
|
|
94
|
+
e.collapse(true);
|
|
95
|
+
e.moveEnd('character', end);
|
|
96
|
+
e.moveStart('character', start);
|
|
97
|
+
e.select();
|
|
95
98
|
}
|
|
96
|
-
|
|
99
|
+
this.textarea.focus();
|
|
97
100
|
};
|
|
98
101
|
|
|
99
|
-
EditArea.prototype.getString = function() {
|
|
100
|
-
|
|
102
|
+
EditArea.prototype.getString = function () {
|
|
103
|
+
return this.textarea.value;
|
|
101
104
|
};
|
|
102
105
|
|
|
103
|
-
EditArea.prototype.setString = function(str) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
EditArea.prototype.setString = function (str) {
|
|
107
|
+
this.textarea.value = str;
|
|
108
|
+
this.initialText = this.getString();
|
|
109
|
+
if (this.changelistener) {
|
|
110
|
+
this.changelistener.fireChanged();
|
|
111
|
+
}
|
|
109
112
|
};
|
|
110
113
|
|
|
111
|
-
EditArea.prototype.getElem = function() {
|
|
112
|
-
|
|
114
|
+
EditArea.prototype.getElem = function () {
|
|
115
|
+
return this.textarea;
|
|
113
116
|
};
|
|
114
117
|
|
|
115
118
|
module.exports = EditArea;
|