vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature
1,408 lines (1,160 loc) • 530 kB
JavaScript
/**!
* VexFlow 3.0.9 built on 2020-04-21.
* Copyright (c) 2010 Mohit Muthanna Cheppudira <mohit@muthanna.com>
*
* http://www.vexflow.com http://github.com/0xfe/vexflow
*//**
* VexFlow Test Support Library
* Copyright Mohit Muthanna 2010 <mohit@muthanna.com>
*/
/* eslint-disable global-require, import/no-unresolved, import/no-extraneous-dependencies */
/* eslint max-classes-per-file: "off" */
// Mock out the QUnit stuff for generating svg images,
// since we don't really care about the assertions.
if (!window.QUnit) {
window.QUnit = {};
QUnit = window.QUnit;
QUnit.assertions = {
ok: () => true,
equal: () => true,
deepEqual: () => true,
expect: () => true,
throws: () => true,
notOk: () => true,
notEqual: () => true,
notDeepEqual: () => true,
strictEqual: () => true,
notStrictEqual: () => true,
};
QUnit.module = (name) => {
QUnit.current_module = name;
};
/* eslint-disable */
QUnit.test = (name, func) => {
QUnit.current_test = name;
process.stdout.write(" \u001B[0G" + QUnit.current_module + " :: " + name + "\u001B[0K");
func(QUnit.assertions);
};
test = QUnit.test;
ok = QUnit.assertions.ok;
equal = QUnit.assertions.equal;
deepEqual = QUnit.assertions.deepEqual;
expect = QUnit.assertions.expect;
throws = QUnit.assertions.throws;
notOk = QUnit.assertions.notOk;
notEqual = QUnit.assertions.notEqual;
notDeepEqual = QUnit.assertions.notDeepEqual;
strictEqual = QUnit.assertions.strictEqual;
notStrictEqual = QUnit.assertions.notStrictEqual;
}
if (typeof require === 'function') {
Vex = require('./vexflow-debug.js');
}
var VF = Vex.Flow;
VF.Test = (function () {
var Test = {
// Test Options.
RUN_CANVAS_TESTS: true,
RUN_SVG_TESTS: true,
RUN_RAPHAEL_TESTS: false,
RUN_NODE_TESTS: false,
// Where images are stored for NodeJS tests.
NODE_IMAGEDIR: 'images',
// Default font properties for tests.
Font: { size: 10 },
// Returns a unique ID for a test.
genID: function (prefix) {
return prefix + VF.Test.genID.ID++;
},
genTitle: function (type, assert, name) {
return assert.test.module.name + ' (' + type + '): ' + name;
},
// Run `func` inside a QUnit test for each of the enabled
// rendering backends.
runTests: function (name, func, params) {
if (VF.Test.RUN_CANVAS_TESTS) {
VF.Test.runCanvasTest(name, func, params);
}
if (VF.Test.RUN_SVG_TESTS) {
VF.Test.runSVGTest(name, func, params);
}
if (VF.Test.RUN_RAPHAEL_TESTS) {
VF.Test.runRaphaelTest(name, func, params);
}
if (VF.Test.RUN_NODE_TESTS) {
VF.Test.runNodeTest(name, func, params);
}
},
// Run `func` inside a QUnit test for each of the enabled
// rendering backends. These are for interactivity tests, and
// currently only work with the SVG backend.
runUITests: function (name, func, params) {
if (VF.Test.RUN_SVG_TESTS) {
VF.Test.runSVGTest(name, func, params);
}
},
createTestCanvas: function (testId, testName) {
var testContainer = $('<div></div>').addClass('testcanvas');
testContainer.append(
$('<div></div>')
.addClass('name')
.text(testName)
);
testContainer.append(
$('<canvas></canvas>')
.addClass('vex-tabdiv')
.attr('id', testId)
.addClass('name')
.text(name)
);
$(VF.Test.testRootSelector).append(testContainer);
},
createTestSVG: function (testId, testName) {
var testContainer = $('<div></div>').addClass('testcanvas');
testContainer.append(
$('<div></div>')
.addClass('name')
.text(testName)
);
testContainer.append(
$('<div></div>')
.addClass('vex-tabdiv')
.attr('id', testId)
);
$(VF.Test.testRootSelector).append(testContainer);
},
resizeCanvas: function (elementId, width, height) {
$('#' + elementId).width(width);
$('#' + elementId).attr('width', width);
$('#' + elementId).attr('height', height);
},
makeFactory: function (options, width, height) {
return new VF.Factory({
renderer: {
elementId: options.elementId,
backend: options.backend,
width: width || 450,
height: height || 140,
},
});
},
runCanvasTest: function (name, func, params) {
QUnit.test(name, function (assert) {
var elementId = VF.Test.genID('canvas_');
var title = VF.Test.genTitle('Canvas', assert, name);
VF.Test.createTestCanvas(elementId, title);
var testOptions = {
backend: VF.Renderer.Backends.CANVAS,
elementId: elementId,
params: params,
assert: assert,
};
func(testOptions, VF.Renderer.getCanvasContext);
});
},
runRaphaelTest: function (name, func, params) {
QUnit.test(name, function (assert) {
var elementId = VF.Test.genID('raphael_');
var title = VF.Test.genTitle('Raphael', assert, name);
VF.Test.createTestSVG(elementId, title);
var testOptions = {
elementId: elementId,
backend: VF.Renderer.Backends.RAPHAEL,
params: params,
assert: assert,
};
func(testOptions, VF.Renderer.getRaphaelContext);
});
},
runSVGTest: function (name, func, params) {
if (!VF.Test.RUN_SVG_TESTS) return;
const fontStacks = {
Bravura: [VF.Fonts.Bravura, VF.Fonts.Gonville, VF.Fonts.Custom],
Gonville: [VF.Fonts.Gonville, VF.Fonts.Bravura, VF.Fonts.Custom],
Petaluma: [VF.Fonts.Petaluma, VF.Fonts.Gonville, VF.Fonts.Custom],
}
const testFunc = (fontName) => (assert) => {
const defaultFontStack = VF.DEFAULT_FONT_STACK;
VF.DEFAULT_FONT_STACK = fontStacks[fontName];
var elementId = VF.Test.genID('svg_'+fontName);
var title = VF.Test.genTitle('SVG '+fontName, assert, name);
VF.Test.createTestSVG(elementId, title);
var testOptions = {
elementId: elementId,
backend: VF.Renderer.Backends.SVG,
params: params,
assert: assert,
};
func(testOptions, VF.Renderer.getSVGContext);
VF.DEFAULT_FONT_STACK = defaultFontStack;
}
QUnit.test(name, testFunc('Bravura'));
QUnit.test(name, testFunc('Gonville'));
QUnit.test(name, testFunc('Petaluma'));
},
runNodeTest: function (name, func, params) {
var fs = require('fs');
// Allows `name` to be used inside file names.
function sanitizeName(name) {
return name.replace(/[^a-zA-Z0-9]/g, '_');
}
QUnit.test(name, function (assert) {
var elementId = VF.Test.genID('nodecanvas_');
var canvas = document.createElement('canvas');
canvas.setAttribute('id', elementId);
document.body.appendChild(canvas);
var testOptions = {
elementId: elementId,
backend: VF.Renderer.Backends.CANVAS,
params: params,
assert: assert,
};
func(testOptions, VF.Renderer.getCanvasContext);
if (VF.Renderer.lastContext !== null) {
var moduleName = sanitizeName(QUnit.current_module);
var testName = sanitizeName(QUnit.current_test);
var fileName = `${VF.Test.NODE_IMAGEDIR}/${moduleName}.${testName}.png`;
var imageData = canvas.toDataURL().split(';base64,').pop();
var image = Buffer.from(imageData, 'base64');
fs.writeFileSync(fileName, image, { encoding: 'base64' });
}
});
},
plotNoteWidth: VF.Note.plotMetrics,
plotLegendForNoteWidth: function (ctx, x, y) {
ctx.save();
ctx.setFont('Arial', 8, '');
var spacing = 12;
var lastY = y;
function legend(color, text) {
ctx.beginPath();
ctx.setStrokeStyle(color);
ctx.setFillStyle(color);
ctx.setLineWidth(10);
ctx.moveTo(x, lastY - 4);
ctx.lineTo(x + 10, lastY - 4);
ctx.stroke();
ctx.setFillStyle('black');
ctx.fillText(text, x + 15, lastY);
lastY += spacing;
}
legend('green', 'Note + Flag');
legend('red', 'Modifiers');
legend('#999', 'Displaced Head');
legend('#DDD', 'Formatter Shift');
ctx.restore();
},
almostEqual: function (value, expectedValue, errorMargin) {
return equal(Math.abs(value - expectedValue) < errorMargin, true);
},
};
Test.genID.ID = 0;
Test.testRootSelector = '#vexflow_testoutput';
return Test;
}());
/**
* VexFlow - TickContext Mocks
* Copyright Mohit Muthanna 2010 <mohit@muthanna.com>
*/
/* Mock Tickable */
VF.Test.MockTickable = (function() {
function MockTickable() {
this.ignore_ticks = false;
}
MockTickable.prototype = {
init: function() {},
getX: function() { return this.tickContext.getX(); },
getIntrinsicTicks: function() { return this.ticks; },
getTicks: function() { return this.ticks; },
setTicks: function(t) { this.ticks = new VF.Fraction(t, 1); return this; },
getMetrics: function() {
return {
width: 0,
glyphWidth: 0,
notePx: this.width,
left_shift: 0,
modLeftPx: 0,
modRightPx: 0,
leftDisplacedHeadPx: 0,
rightDisplacedHeadPx: 0,
};
},
getWidth: function() { return this.width; },
setWidth: function(w) { this.width = w; return this; },
setVoice: function(v) { this.voice = v; return this; },
setStave: function(stave) { this.stave = stave; return this; },
setTickContext: function(tc) { this.tickContext = tc; return this; },
setIgnoreTicks: function(ignore_ticks) { this.ignore_ticks = ignore_ticks; return this; },
shouldIgnoreTicks: function() { return this.ignore_ticks; },
preFormat: function() {},
};
return MockTickable;
})();
/**
* VexFlow - Accidental Tests
* Copyright Mohit Muthanna 2010 <mohit@muthanna.com>
*/
Vex.Flow.Test.Accidental = (function() {
function hasAccidental(note) {
return note.modifiers.reduce(function(hasAcc, modifier) {
return hasAcc || modifier.getCategory() === 'accidentals';
}, false);
}
// newAccid factory
function makeNewAccid(factory) {
return function(accidType) {
return factory.Accidental({ type: accidType });
};
}
var Accidental = {
Start: function() {
QUnit.module('Accidental');
Vex.Flow.Test.runTests('Basic', Vex.Flow.Test.Accidental.basic);
Vex.Flow.Test.runTests('Stem Down', Vex.Flow.Test.Accidental.basicStemDown);
Vex.Flow.Test.runTests('Cautionary Accidental', Vex.Flow.Test.Accidental.cautionary);
Vex.Flow.Test.runTests('Accidental Arrangement Special Cases', Vex.Flow.Test.Accidental.specialCases);
Vex.Flow.Test.runTests('Multi Voice', Vex.Flow.Test.Accidental.multiVoice);
Vex.Flow.Test.runTests('Microtonal', Vex.Flow.Test.Accidental.microtonal);
Vex.Flow.Test.runTests('Microtonal (Iranian)', Vex.Flow.Test.Accidental.microtonal_iranian);
test('Automatic Accidentals - Simple Tests', Vex.Flow.Test.Accidental.autoAccidentalWorking);
Vex.Flow.Test.runTests('Automatic Accidentals', Vex.Flow.Test.Accidental.automaticAccidentals0);
Vex.Flow.Test.runTests('Automatic Accidentals - C major scale in Ab', Vex.Flow.Test.Accidental.automaticAccidentals1);
Vex.Flow.Test.runTests('Automatic Accidentals - No Accidentals Necsesary', Vex.Flow.Test.Accidental.automaticAccidentals2);
Vex.Flow.Test.runTests('Automatic Accidentals - Multi Voice Inline', Vex.Flow.Test.Accidental.automaticAccidentalsMultiVoiceInline);
Vex.Flow.Test.runTests('Automatic Accidentals - Multi Voice Offset', Vex.Flow.Test.Accidental.automaticAccidentalsMultiVoiceOffset);
Vex.Flow.Test.runTests('Factory API', Vex.Flow.Test.Accidental.factoryAPI);
},
basic: function(options) {
var vf = VF.Test.makeFactory(options, 700, 240);
var newAccid = makeNewAccid(vf);
vf.Stave({ x: 10, y: 10, width: 550 });
var notes = [
vf.StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: '1' })
.addAccidental(0, newAccid('b'))
.addAccidental(1, newAccid('#')),
vf.StaveNote({ keys: ['d/4', 'e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5'], duration: '2' })
.addAccidental(0, newAccid('##'))
.addAccidental(1, newAccid('n'))
.addAccidental(2, newAccid('bb'))
.addAccidental(3, newAccid('b'))
.addAccidental(4, newAccid('#'))
.addAccidental(5, newAccid('n'))
.addAccidental(6, newAccid('bb')),
vf.StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5', 'g/5'], duration: '16' })
.addAccidental(0, newAccid('n'))
.addAccidental(1, newAccid('#'))
.addAccidental(2, newAccid('#'))
.addAccidental(3, newAccid('b'))
.addAccidental(4, newAccid('bb'))
.addAccidental(5, newAccid('##'))
.addAccidental(6, newAccid('#')),
vf.StaveNote({ keys: ['a/3', 'c/4', 'e/4', 'b/4', 'd/5', 'g/5'], duration: '1' })
.addAccidental(0, newAccid('#'))
.addAccidental(1, newAccid('##').setAsCautionary())
.addAccidental(2, newAccid('#').setAsCautionary())
.addAccidental(3, newAccid('b'))
.addAccidental(4, newAccid('bb').setAsCautionary())
.addAccidental(5, newAccid('b').setAsCautionary()),
];
VF.Formatter.SimpleFormat(notes, 10, { paddingBetween: 45 });
notes.forEach(function(note, index) {
Vex.Flow.Test.plotNoteWidth(vf.getContext(), note, 140);
ok(note.getAccidentals().length > 0, 'Note ' + index + ' has accidentals');
note.getAccidentals().forEach(function(accid, index) {
ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width');
});
});
vf.draw();
Vex.Flow.Test.plotLegendForNoteWidth(vf.getContext(), 480, 140);
ok(true, 'Full Accidental');
},
cautionary: function(options) {
var vf = VF.Test.makeFactory(options, 800, 240);
var stave = vf.Stave({ x: 0, y: 10, width: 780 });
var score = vf.EasyScore();
var accids = Object
.keys(VF.accidentalCodes.accidentals)
.filter(function(accid) { return accid !== '{' && accid !== '}'; });
var notes = accids
.map(function(accid) {
return vf
.StaveNote({ keys: ['a/4'], duration: '4', stem_direction: VF.Stem.UP })
.addAccidental(0, vf.Accidental({ type: accid }));
});
var voice = score.voice(notes, { time: accids.length + '/4' });
voice
.getTickables()
.forEach(function(tickable) {
tickable.modifiers
.filter(function(modifier) {
return modifier.getAttribute('type') === 'Accidental';
})
.forEach(function(accid) {
accid.setAsCautionary();
});
});
vf.Formatter()
.joinVoices([voice])
.formatToStave([voice], stave);
vf.draw();
ok(true, 'Must successfully render cautionary accidentals');
},
specialCases: function(options) {
var vf = VF.Test.makeFactory(options, 700, 240);
var newAccid = makeNewAccid(vf);
vf.Stave({ x: 10, y: 10, width: 550 });
var notes = [
vf.StaveNote({ keys: ['f/4', 'd/5'], duration: '1' })
.addAccidental(0, newAccid('#'))
.addAccidental(1, newAccid('b')),
vf.StaveNote({ keys: ['c/4', 'g/4'], duration: '2' })
.addAccidental(0, newAccid('##'))
.addAccidental(1, newAccid('##')),
vf.StaveNote({ keys: ['b/3', 'd/4', 'f/4'], duration: '16' })
.addAccidental(0, newAccid('#'))
.addAccidental(1, newAccid('#'))
.addAccidental(2, newAccid('##')),
vf.StaveNote({ keys: ['g/4', 'a/4', 'c/5', 'e/5'], duration: '16' })
.addAccidental(0, newAccid('b'))
.addAccidental(1, newAccid('b'))
.addAccidental(3, newAccid('n')),
vf.StaveNote({ keys: ['e/4', 'g/4', 'b/4', 'c/5'], duration: '4' })
.addAccidental(0, newAccid('b').setAsCautionary())
.addAccidental(1, newAccid('b').setAsCautionary())
.addAccidental(2, newAccid('bb'))
.addAccidental(3, newAccid('b')),
vf.StaveNote({ keys: ['b/3', 'e/4', 'a/4', 'd/5', 'g/5'], duration: '8' })
.addAccidental(0, newAccid('bb'))
.addAccidental(1, newAccid('b').setAsCautionary())
.addAccidental(2, newAccid('n').setAsCautionary())
.addAccidental(3, newAccid('#'))
.addAccidental(4, newAccid('n').setAsCautionary()),
];
VF.Formatter.SimpleFormat(notes, 0, { paddingBetween: 20 });
notes.forEach(function(note, index) {
Vex.Flow.Test.plotNoteWidth(vf.getContext(), note, 140);
ok(note.getAccidentals().length > 0, 'Note ' + index + ' has accidentals');
note.getAccidentals().forEach(function(accid, index) {
ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width');
});
});
vf.draw();
Vex.Flow.Test.plotLegendForNoteWidth(vf.getContext(), 480, 140);
ok(true, 'Full Accidental');
},
basicStemDown: function(options) {
var vf = VF.Test.makeFactory(options, 700, 240);
var newAccid = makeNewAccid(vf);
vf.Stave({ x: 10, y: 10, width: 550 });
var notes = [
vf.StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w', stem_direction: -1 })
.addAccidental(0, newAccid('b'))
.addAccidental(1, newAccid('#')),
vf.StaveNote({ keys: ['d/4', 'e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5'], duration: '2', stem_direction: -1 })
.addAccidental(0, newAccid('##'))
.addAccidental(1, newAccid('n'))
.addAccidental(2, newAccid('bb'))
.addAccidental(3, newAccid('b'))
.addAccidental(4, newAccid('#'))
.addAccidental(5, newAccid('n'))
.addAccidental(6, newAccid('bb')),
vf.StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5', 'g/5'], duration: '16', stem_direction: -1 })
.addAccidental(0, newAccid('n'))
.addAccidental(1, newAccid('#'))
.addAccidental(2, newAccid('#'))
.addAccidental(3, newAccid('b'))
.addAccidental(4, newAccid('bb'))
.addAccidental(5, newAccid('##'))
.addAccidental(6, newAccid('#')),
];
VF.Formatter.SimpleFormat(notes, 0, { paddingBetween: 30 });
notes.forEach(function(note, index) {
Vex.Flow.Test.plotNoteWidth(vf.getContext(), note, 140);
ok(note.getAccidentals().length > 0, 'Note ' + index + ' has accidentals');
note.getAccidentals().forEach(function(accid, index) {
ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width');
});
});
vf.draw();
Vex.Flow.Test.plotLegendForNoteWidth(vf.getContext(), 480, 140);
ok(true, 'Full Accidental');
},
showNotes: function(note1, note2, stave, ctx, x) {
var modifierContext = new Vex.Flow.ModifierContext();
note1.addToModifierContext(modifierContext);
note2.addToModifierContext(modifierContext);
new VF.TickContext()
.addTickable(note1)
.addTickable(note2)
.preFormat()
.setX(x);
note1.setContext(ctx).draw();
note2.setContext(ctx).draw();
Vex.Flow.Test.plotNoteWidth(ctx, note1, 180);
Vex.Flow.Test.plotNoteWidth(ctx, note2, 15);
},
multiVoice: function(options) {
var vf = VF.Test.makeFactory(options, 460, 250);
var newAccid = makeNewAccid(vf);
var stave = vf.Stave({ x: 10, y: 45, width: 420 });
var ctx = vf.getContext();
stave.draw();
var note1 = vf.StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: '2', stem_direction: -1 })
.addAccidental(0, newAccid('b'))
.addAccidental(1, newAccid('n'))
.addAccidental(2, newAccid('#'))
.setStave(stave);
var note2 = vf.StaveNote({ keys: ['d/5', 'a/5', 'b/5'], duration: '2', stem_direction: 1 })
.addAccidental(0, newAccid('b'))
.addAccidental(1, newAccid('bb'))
.addAccidental(2, newAccid('##'))
.setStave(stave);
Vex.Flow.Test.Accidental.showNotes(note1, note2, stave, ctx, 60);
note1 = vf.StaveNote({ keys: ['c/4', 'e/4', 'c/5'], duration: '2', stem_direction: -1 })
.addAccidental(0, newAccid('b'))
.addAccidental(1, newAccid('n'))
.addAccidental(2, newAccid('#'))
.setStave(stave);
note2 = vf.StaveNote({ keys: ['d/5', 'a/5', 'b/5'], duration: '4', stem_direction: 1 })
.addAccidental(0, newAccid('b'))
.setStave(stave);
Vex.Flow.Test.Accidental.showNotes(note1, note2, stave, ctx, 150);
note1 = vf.StaveNote({ keys: ['d/4', 'c/5', 'd/5'], duration: '2', stem_direction: -1 })
.addAccidental(0, newAccid('b'))
.addAccidental(1, newAccid('n'))
.addAccidental(2, newAccid('#'))
.setStave(stave);
note2 = vf.StaveNote({ keys: ['d/5', 'a/5', 'b/5'], duration: '4', stem_direction: 1 })
.addAccidental(0, newAccid('b'))
.setStave(stave);
Vex.Flow.Test.Accidental.showNotes(note1, note2, stave, ctx, 250);
Vex.Flow.Test.plotLegendForNoteWidth(ctx, 350, 150);
ok(true, 'Full Accidental');
},
microtonal: function(options) {
var assert = options.assert;
var vf = VF.Test.makeFactory(options, 700, 240);
var newAccid = makeNewAccid(vf);
var ctx = vf.getContext();
vf.Stave({ x: 10, y: 10, width: 650 });
var notes = [
vf.StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: '1' })
.addAccidental(0, newAccid('db'))
.addAccidental(1, newAccid('d')),
vf.StaveNote({ keys: ['d/4', 'e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5'], duration: '2' })
.addAccidental(0, newAccid('bbs'))
.addAccidental(1, newAccid('++'))
.addAccidental(2, newAccid('+'))
.addAccidental(3, newAccid('d'))
.addAccidental(4, newAccid('db'))
.addAccidental(5, newAccid('+'))
.addAccidental(6, newAccid('##')),
vf.StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5', 'g/5'], duration: '16' })
.addAccidental(0, newAccid('++'))
.addAccidental(1, newAccid('bbs'))
.addAccidental(2, newAccid('+'))
.addAccidental(3, newAccid('b'))
.addAccidental(4, newAccid('db'))
.addAccidental(5, newAccid('##'))
.addAccidental(6, newAccid('#')),
vf.StaveNote({ keys: ['a/3', 'c/4', 'e/4', 'b/4', 'd/5', 'g/5'], duration: '1' })
.addAccidental(0, newAccid('#'))
.addAccidental(1, newAccid('db').setAsCautionary())
.addAccidental(2, newAccid('bbs').setAsCautionary())
.addAccidental(3, newAccid('b'))
.addAccidental(4, newAccid('++').setAsCautionary())
.addAccidental(5, newAccid('d').setAsCautionary()),
vf.StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'd/5', 'g/5'], duration: '16' })
.addAccidental(0, newAccid('++-'))
.addAccidental(1, newAccid('+-'))
.addAccidental(2, newAccid('bs'))
.addAccidental(3, newAccid('bss'))
.addAccidental(4, newAccid('afhf'))
.addAccidental(5, newAccid('ashs')),
];
VF.Formatter.SimpleFormat(notes, 0, { paddingBetween: 35 });
notes.forEach(function(note, index) {
Vex.Flow.Test.plotNoteWidth(vf.getContext(), note, 140);
assert.ok(note.getAccidentals().length > 0, 'Note ' + index + ' has accidentals');
note.getAccidentals().forEach(function(accid, index) {
assert.ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width');
});
});
vf.draw();
Vex.Flow.Test.plotLegendForNoteWidth(ctx, 580, 140);
ok(true, 'Microtonal Accidental');
},
microtonal_iranian: function(options) {
var assert = options.assert;
var vf = VF.Test.makeFactory(options, 700, 240);
var newAccid = makeNewAccid(vf);
var ctx = vf.getContext();
vf.Stave({ x: 10, y: 10, width: 650 });
var notes = [
vf.StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: '1' })
.addAccidental(0, newAccid('k'))
.addAccidental(1, newAccid('o')),
vf.StaveNote({ keys: ['d/4', 'e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5'], duration: '2' })
.addAccidental(0, newAccid('b'))
.addAccidental(1, newAccid('k'))
.addAccidental(2, newAccid('n'))
.addAccidental(3, newAccid('o'))
.addAccidental(4, newAccid('#'))
.addAccidental(5, newAccid('bb'))
.addAccidental(6, newAccid('##')),
vf.StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5', 'g/5'], duration: '16' })
.addAccidental(0, newAccid('o'))
.addAccidental(1, newAccid('k'))
.addAccidental(2, newAccid('n'))
.addAccidental(3, newAccid('b'))
.addAccidental(4, newAccid('bb'))
.addAccidental(5, newAccid('##'))
.addAccidental(6, newAccid('#')),
vf.StaveNote({ keys: ['a/3', 'c/4', 'e/4', 'b/4', 'd/5', 'g/5'], duration: '1' })
.addAccidental(0, newAccid('#'))
.addAccidental(1, newAccid('o').setAsCautionary())
.addAccidental(2, newAccid('n').setAsCautionary())
.addAccidental(3, newAccid('b'))
.addAccidental(4, newAccid('k').setAsCautionary()),
vf.StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4'], duration: '16' })
.addAccidental(0, newAccid('k'))
.addAccidental(1, newAccid('k'))
.addAccidental(2, newAccid('k'))
.addAccidental(3, newAccid('k')),
];
VF.Formatter.SimpleFormat(notes, 0, { paddingBetween: 35 });
notes.forEach(function(note, index) {
Vex.Flow.Test.plotNoteWidth(vf.getContext(), note, 140);
assert.ok(note.getAccidentals().length > 0, 'Note ' + index + ' has accidentals');
note.getAccidentals().forEach(function(accid, index) {
assert.ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width');
});
});
vf.draw();
Vex.Flow.Test.plotLegendForNoteWidth(ctx, 580, 140);
ok(true, 'Microtonal Accidental (Iranian)');
},
automaticAccidentals0: function(options) {
var vf = VF.Test.makeFactory(options, 700, 200);
var stave = vf.Stave();
const notes = [
{ keys: ['c/4', 'c/5'], duration: '4' },
{ keys: ['c#/4', 'c#/5'], duration: '4' },
{ keys: ['c#/4', 'c#/5'], duration: '4' },
{ keys: ['c##/4', 'c##/5'], duration: '4' },
{ keys: ['c##/4', 'c##/5'], duration: '4' },
{ keys: ['c/4', 'c/5'], duration: '4' },
{ keys: ['cn/4', 'cn/5'], duration: '4' },
{ keys: ['cbb/4', 'cbb/5'], duration: '4' },
{ keys: ['cbb/4', 'cbb/5'], duration: '4' },
{ keys: ['cb/4', 'cb/5'], duration: '4' },
{ keys: ['cb/4', 'cb/5'], duration: '4' },
{ keys: ['c/4', 'c/5'], duration: '4' },
].map(vf.StaveNote.bind(vf));
const gracenotes = [
{ keys: ['d#/4'], duration: '16', slash: true },
].map(vf.GraceNote.bind(vf));
notes[0].addModifier(0, vf.GraceNoteGroup({ notes: gracenotes }).beamNotes());
const voice = vf.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickable(new Vex.Flow.TimeSigNote('12/4').setStave(stave))
.addTickables(notes);
Vex.Flow.Accidental.applyAccidentals([voice], 'C');
new Vex.Flow.Formatter()
.joinVoices([voice])
.formatToStave([voice], stave);
vf.draw();
ok(true);
},
automaticAccidentals1: function(options) {
var vf = VF.Test.makeFactory(options, 700, 150);
var stave = vf.Stave().addKeySignature('Ab');
var notes = [
{ keys: ['c/4'], duration: '4' },
{ keys: ['d/4'], duration: '4' },
{ keys: ['e/4'], duration: '4' },
{ keys: ['f/4'], duration: '4' },
{ keys: ['g/4'], duration: '4' },
{ keys: ['a/4'], duration: '4' },
{ keys: ['b/4'], duration: '4' },
{ keys: ['c/5'], duration: '4' },
].map(vf.StaveNote.bind(vf));
var voice = vf.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickables(notes);
Vex.Flow.Accidental.applyAccidentals([voice], 'Ab');
new Vex.Flow.Formatter()
.joinVoices([voice])
.formatToStave([voice], stave);
vf.draw();
ok(true);
},
automaticAccidentals2: function(options) {
var vf = VF.Test.makeFactory(options, 700, 150);
var stave = vf.Stave().addKeySignature('A');
var notes = [
{ keys: ['a/4'], duration: '4' },
{ keys: ['b/4'], duration: '4' },
{ keys: ['c#/5'], duration: '4' },
{ keys: ['d/5'], duration: '4' },
{ keys: ['e/5'], duration: '4' },
{ keys: ['f#/5'], duration: '4' },
{ keys: ['g#/5'], duration: '4' },
{ keys: ['a/5'], duration: '4' },
].map(vf.StaveNote.bind(vf));
var voice = vf.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickables(notes);
Vex.Flow.Accidental.applyAccidentals([voice], 'A');
new Vex.Flow.Formatter()
.joinVoices([voice])
.formatToStave([voice], stave);
vf.draw();
ok(true);
},
automaticAccidentalsMultiVoiceInline: function(options) {
var vf = VF.Test.makeFactory(options, 700, 150);
var stave = vf.Stave().addKeySignature('Ab');
var notes0 = [
{ keys: ['c/4'], duration: '4', stem_direction: -1 },
{ keys: ['d/4'], duration: '4', stem_direction: -1 },
{ keys: ['e/4'], duration: '4', stem_direction: -1 },
{ keys: ['f/4'], duration: '4', stem_direction: -1 },
{ keys: ['g/4'], duration: '4', stem_direction: -1 },
{ keys: ['a/4'], duration: '4', stem_direction: -1 },
{ keys: ['b/4'], duration: '4', stem_direction: -1 },
{ keys: ['c/5'], duration: '4', stem_direction: -1 },
].map(vf.StaveNote.bind(vf));
var notes1 = [
{ keys: ['c/5'], duration: '4' },
{ keys: ['d/5'], duration: '4' },
{ keys: ['e/5'], duration: '4' },
{ keys: ['f/5'], duration: '4' },
{ keys: ['g/5'], duration: '4' },
{ keys: ['a/5'], duration: '4' },
{ keys: ['b/5'], duration: '4' },
{ keys: ['c/6'], duration: '4' },
].map(vf.StaveNote.bind(vf));
var voice0 = vf.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickables(notes0);
var voice1 = vf.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickables(notes1);
// Ab Major
Vex.Flow.Accidental.applyAccidentals([voice0, voice1], 'Ab');
equal(hasAccidental(notes0[0]), false);
equal(hasAccidental(notes0[1]), true);
equal(hasAccidental(notes0[2]), true);
equal(hasAccidental(notes0[3]), false);
equal(hasAccidental(notes0[4]), false);
equal(hasAccidental(notes0[5]), true);
equal(hasAccidental(notes0[6]), true);
equal(hasAccidental(notes0[7]), false);
equal(hasAccidental(notes1[0]), false);
equal(hasAccidental(notes1[1]), true);
equal(hasAccidental(notes1[2]), true);
equal(hasAccidental(notes1[3]), false);
equal(hasAccidental(notes1[4]), false);
equal(hasAccidental(notes1[5]), true);
equal(hasAccidental(notes1[6]), true);
equal(hasAccidental(notes1[7]), false);
new Vex.Flow.Formatter()
.joinVoices([voice0, voice1])
.formatToStave([voice0, voice1], stave);
vf.draw();
ok(true);
},
automaticAccidentalsMultiVoiceOffset: function(options) {
var vf = VF.Test.makeFactory(options, 700, 150);
var stave = vf.Stave().addKeySignature('Cb');
var notes0 = [
{ keys: ['c/4'], duration: '4', stem_direction: -1 },
{ keys: ['d/4'], duration: '4', stem_direction: -1 },
{ keys: ['e/4'], duration: '4', stem_direction: -1 },
{ keys: ['f/4'], duration: '4', stem_direction: -1 },
{ keys: ['g/4'], duration: '4', stem_direction: -1 },
{ keys: ['a/4'], duration: '4', stem_direction: -1 },
{ keys: ['b/4'], duration: '4', stem_direction: -1 },
{ keys: ['c/5'], duration: '4', stem_direction: -1 },
].map(vf.StaveNote.bind(vf));
var notes1 = [
{ keys: ['c/5'], duration: '8' },
{ keys: ['c/5'], duration: '4' },
{ keys: ['d/5'], duration: '4' },
{ keys: ['e/5'], duration: '4' },
{ keys: ['f/5'], duration: '4' },
{ keys: ['g/5'], duration: '4' },
{ keys: ['a/5'], duration: '4' },
{ keys: ['b/5'], duration: '4' },
{ keys: ['c/6'], duration: '4' },
].map(vf.StaveNote.bind(vf));
var voice0 = vf.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickables(notes0);
var voice1 = vf.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickables(notes1);
// Cb Major (All flats)
Vex.Flow.Accidental.applyAccidentals([voice0, voice1], 'Cb');
equal(hasAccidental(notes0[0]), true);
equal(hasAccidental(notes0[1]), true);
equal(hasAccidental(notes0[2]), true);
equal(hasAccidental(notes0[3]), true);
equal(hasAccidental(notes0[4]), true);
equal(hasAccidental(notes0[5]), true);
equal(hasAccidental(notes0[6]), true);
equal(hasAccidental(notes0[7]), false, 'Natural Remembered');
equal(hasAccidental(notes1[0]), true);
equal(hasAccidental(notes1[1]), false);
equal(hasAccidental(notes1[2]), false);
equal(hasAccidental(notes1[3]), false);
equal(hasAccidental(notes1[4]), false);
equal(hasAccidental(notes1[5]), false);
equal(hasAccidental(notes1[6]), false);
equal(hasAccidental(notes1[7]), false);
new Vex.Flow.Formatter()
.joinVoices([voice0, voice1])
.formatToStave([voice0, voice1], stave);
vf.draw();
ok(true);
},
autoAccidentalWorking: function() {
function makeNote(noteStruct) { return new VF.StaveNote(noteStruct); }
var notes = [
{ keys: ['bb/4'], duration: '4' },
{ keys: ['bb/4'], duration: '4' },
{ keys: ['g#/4'], duration: '4' },
{ keys: ['g/4'], duration: '4' },
{ keys: ['b/4'], duration: '4' },
{ keys: ['b/4'], duration: '4' },
{ keys: ['a#/4'], duration: '4' },
{ keys: ['g#/4'], duration: '4' },
].map(makeNote);
var voice = new VF.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickables(notes);
// F Major (Bb)
Vex.Flow.Accidental.applyAccidentals([voice], 'F');
equal(hasAccidental(notes[0]), false, 'No flat because of key signature');
equal(hasAccidental(notes[1]), false, 'No flat because of key signature');
equal(hasAccidental(notes[2]), true, 'Added a sharp');
equal(hasAccidental(notes[3]), true, 'Back to natural');
equal(hasAccidental(notes[4]), true, 'Back to natural');
equal(hasAccidental(notes[5]), false, 'Natural remembered');
equal(hasAccidental(notes[6]), true, 'Added sharp');
equal(hasAccidental(notes[7]), true, 'Added sharp');
notes = [
{ keys: ['e#/4'], duration: '4' },
{ keys: ['cb/4'], duration: '4' },
{ keys: ['fb/4'], duration: '4' },
{ keys: ['b#/4'], duration: '4' },
{ keys: ['b#/4'], duration: '4' },
{ keys: ['cb/5'], duration: '4' },
{ keys: ['fb/5'], duration: '4' },
{ keys: ['e#/4'], duration: '4' },
].map(makeNote);
voice = new VF.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickables(notes);
// A Major (F#,G#,C#)
Vex.Flow.Accidental.applyAccidentals([voice], 'A');
equal(hasAccidental(notes[0]), true, 'Added sharp');
equal(hasAccidental(notes[1]), true, 'Added flat');
equal(hasAccidental(notes[2]), true, 'Added flat');
equal(hasAccidental(notes[3]), true, 'Added sharp');
equal(hasAccidental(notes[4]), false, 'Sharp remembered');
equal(hasAccidental(notes[5]), false, 'Flat remembered');
equal(hasAccidental(notes[6]), false, 'Flat remembered');
equal(hasAccidental(notes[7]), false, 'sharp remembered');
notes = [
{ keys: ['c/4'], duration: '4' },
{ keys: ['cb/4'], duration: '4' },
{ keys: ['cb/4'], duration: '4' },
{ keys: ['c#/4'], duration: '4' },
{ keys: ['c#/4'], duration: '4' },
{ keys: ['cbb/4'], duration: '4' },
{ keys: ['cbb/4'], duration: '4' },
{ keys: ['c##/4'], duration: '4' },
{ keys: ['c##/4'], duration: '4' },
{ keys: ['c/4'], duration: '4' },
{ keys: ['c/4'], duration: '4' },
].map(makeNote);
voice = new VF.Voice()
.setMode(Vex.Flow.Voice.Mode.SOFT)
.addTickables(notes);
// C Major (no sharps/flats)
Vex.Flow.Accidental.applyAccidentals([voice], 'C');
equal(hasAccidental(notes[0]), false, 'No accidental');
equal(hasAccidental(notes[1]), true, 'Added flat');
equal(hasAccidental(notes[2]), false, 'Flat remembered');
equal(hasAccidental(notes[3]), true, 'Sharp added');
equal(hasAccidental(notes[4]), false, 'Sharp remembered');
equal(hasAccidental(notes[5]), true, 'Added doubled flat');
equal(hasAccidental(notes[6]), false, 'Double flat remembered');
equal(hasAccidental(notes[7]), true, 'Added double sharp');
equal(hasAccidental(notes[8]), false, 'Double sharp rememberd');
equal(hasAccidental(notes[9]), true, 'Added natural');
equal(hasAccidental(notes[10]), false, 'Natural remembered');
},
factoryAPI: function(options) {
var assert = options.assert;
var vf = VF.Test.makeFactory(options, 700, 240);
vf.Stave({ x: 10, y: 10, width: 550 });
function newAcc(type) { return vf.Accidental({ type: type }); }
var notes = [
vf.StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w' })
.addAccidental(0, newAcc('b'))
.addAccidental(1, newAcc('#')),
vf.StaveNote({ keys: ['d/4', 'e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5'], duration: 'h' })
.addAccidental(0, newAcc('##'))
.addAccidental(1, newAcc('n'))
.addAccidental(2, newAcc('bb'))
.addAccidental(3, newAcc('b'))
.addAccidental(4, newAcc('#'))
.addAccidental(5, newAcc('n'))
.addAccidental(6, newAcc('bb')),
vf.StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5', 'g/5'], duration: '16' })
.addAccidental(0, newAcc('n'))
.addAccidental(1, newAcc('#'))
.addAccidental(2, newAcc('#'))
.addAccidental(3, newAcc('b'))
.addAccidental(4, newAcc('bb'))
.addAccidental(5, newAcc('##'))
.addAccidental(6, newAcc('#')),
vf.StaveNote({ keys: ['a/3', 'c/4', 'e/4', 'b/4', 'd/5', 'g/5'], duration: 'w' })
.addAccidental(0, newAcc('#'))
.addAccidental(1, newAcc('##').setAsCautionary())
.addAccidental(2, newAcc('#').setAsCautionary())
.addAccidental(3, newAcc('b'))
.addAccidental(4, newAcc('bb').setAsCautionary())
.addAccidental(5, newAcc('b').setAsCautionary()),
];
VF.Formatter.SimpleFormat(notes);
notes.forEach(function(n, i) {
assert.ok(n.getAccidentals().length > 0, 'Note ' + i + ' has accidentals');
n.getAccidentals().forEach(function(accid, i) {
assert.ok(accid.getWidth() > 0, 'Accidental ' + i + ' has set width');
});
});
vf.draw();
assert.ok(true, 'Factory API');
},
};
return Accidental;
})();
/**
* VexFlow - Annotation Tests
* Copyright Mohit Muthanna 2010 <mohit@muthanna.com>
*/
VF.Test.Annotation = (function() {
var runTests = VF.Test.runTests;
var Annotation = {
Start: function() {
QUnit.module('Annotation');
runTests('Simple Annotation', Annotation.simple);
runTests('Standard Notation Annotation', Annotation.standard);
runTests('Harmonics', Annotation.harmonic);
runTests('Fingerpicking', Annotation.picking);
runTests('Bottom Annotation', Annotation.bottom);
runTests('Bottom Annotations with Beams', Annotation.bottomWithBeam);
runTests('Test Justification Annotation Stem Up', Annotation.justificationStemUp);
runTests('Test Justification Annotation Stem Down', Annotation.justificationStemDown);
runTests('TabNote Annotations', Annotation.tabNotes);
},
simple: function(options, contextBuilder) {
var ctx = contextBuilder(options.elementId, 500, 240);
ctx.scale(1.5, 1.5); ctx.fillStyle = '#221'; ctx.strokeStyle = '#221';
ctx.font = ' 10pt Arial';
var stave = new VF.TabStave(10, 10, 450)
.addTabGlyph().setContext(ctx).draw();
function newNote(tab_struct) { return new VF.TabNote(tab_struct); }
function newBend(text) { return new VF.Bend(text); }
function newAnnotation(text) { return new VF.Annotation(text); }
var notes = [
newNote({
positions: [{ str: 2, fret: 10 }, { str: 4, fret: 9 }], duration: 'h',
})
.addModifier(newAnnotation('T'), 0),
newNote({
positions: [{ str: 2, fret: 10 }], duration: 'h',
})
.addModifier(newAnnotation('T'), 0)
.addModifier(newBend('Full'), 0),
];
VF.Formatter.FormatAndDraw(ctx, stave, notes, 200);
ok(true, 'Simple Annotation');
},
standard: function(options, contextBuilder) {
var ctx = contextBuilder(options.elementId, 500, 240);
ctx.scale(1.5, 1.5); ctx.fillStyle = '#221'; ctx.strokeStyle = '#221';
var stave = new VF.Stave(10, 10, 450)
.addClef('treble').setContext(ctx).draw();
function newNote(note_struct) { return new VF.StaveNote(note_struct); }
function newAnnotation(text) {
return (new VF.Annotation(text)).setFont('Times',
VF.Test.Font.size, 'italic');
}
var notes = [
newNote({ keys: ['c/4', 'e/4'], duration: 'h' })
.addAnnotation(0, newAnnotation('quiet')),
newNote({ keys: ['c/4', 'e/4', 'c/5'], duration: 'h' })
.addAnnotation(2, newAnnotation('Allegro')),
];
VF.Formatter.FormatAndDraw(ctx, stave, notes, 200);
ok(true, 'Standard Notation Annotation');
},
harmonic: function(options, contextBuilder) {
var ctx = contextBuilder(options.elementId, 500, 240);
ctx.scale(1.5, 1.5); ctx.fillStyle = '#221'; ctx.strokeStyle = '#221';
ctx.font = ' 10pt Arial';
var stave = new VF.TabStave(10, 10, 450)
.addTabGlyph().setContext(ctx).draw();
function newNote(tab_struct) { return new VF.TabNote(tab_struct); }
function newAnnotation(text) { return new VF.Annotation(text); }
var notes = [
newNote({
positions: [{ str: 2, fret: 12 }, { str: 3, fret: 12 }], duration: 'h',
})
.addModifier(newAnnotation('Harm.'), 0),
newNote({
positions: [{ str: 2, fret: 9 }], duration: 'h',
})
.addModifier(newAnnotation('(8va)').setFont('Times',
VF.Test.Font.size, 'italic'), 0)
.addModifier(newAnnotation('A.H.'), 0),
];
VF.Formatter.FormatAndDraw(ctx, stave, notes, 200);
ok(true, 'Simple Annotation');
},
picking: function(options, contextBuilder) {
var ctx = contextBuilder(options.elementId, 500, 240);
ctx.scale(1.5, 1.5); ctx.setFillStyle('#221'); ctx.setStrokeStyle('#221');
ctx.setFont('Arial', VF.Test.Font.size, '');
var stave = new VF.TabStave(10, 10, 450)
.addTabGlyph().setContext(ctx).draw();
function newNote(tab_struct) { return new VF.TabNote(tab_struct); }
function newAnnotation(text) {
return new VF.Annotation(text)
.setFont('Times', VF.Test.Font.size, 'italic');
}
var notes = [
newNote({
positions: [
{ str: 1, fret: 0 },
{ str: 2, fret: 1 },
{ str: 3, fret: 2 },
{ str: 4, fret: 2 },
{ str: 5, fret: 0 },
], duration: 'h',
})
.addModifier(new VF.Vibrato().setVibratoWidth(40)),
newNote({
positions: [{ str: 6, fret: 9 }], duration: '8',
})
.addModifier(newAnnotation('p'), 0),
newNote({
positions: [{ str: 3, fret: 9 }], duration: '8',
})
.addModifier(newAnnotation('i'), 0),
newNote({
positions: [{ str: 2, fret: 9 }], duration: '8',
})
.addModifier(newAnnotation('m'), 0),
newNote({
positions: [{ str: 1, fret: 9 }], duration: '8',
})
.addModifier(newAnnotation('a'), 0),
];
VF.Formatter.FormatAndDraw(ctx, stave, notes, 200);
ok(true, 'Fingerpicking');
},
bottom: function(options, contextBuilder) {
var ctx = contextBuilder(options.elementId, 500, 240);
ctx.scale(1.5, 1.5); ctx.fillStyle = '#221'; ctx.strokeStyle = '#221';
var stave = new VF.Stave(10, 10, 300)
.addClef('treble').setContext(ctx).draw();
function newNote(note_struct) { return new VF.StaveNote(note_struct); }
function newAnnotation(text) {
return (
new VF.Annotation(text))
.setFont('Times', VF.Test.Font.size)
.setVerticalJustification(VF.Annotation.VerticalJustify.BOTTOM);
}
var notes = [
newNote({ keys: ['f/4'], duration: 'w' })
.addAnnotation(0, newAnnotation('F')),
newNote({ keys: ['a/4'], duration: 'w' })
.addAnnotation(0, newAnnotation('A')),
newNote({ keys: ['c/5'], duration: 'w' })
.addAnnotation(0, newAnnotation('C')),
newNote({ keys: ['e/5'], duration: 'w' })
.addAnnotation(0, newAnnotation('E')),
];
VF.Formatter.FormatAndDraw(ctx, stave, notes, 100);
ok(true, 'Bottom Annotation');
},
bottomWithBeam: function(options, contextBuilder) {
var ctx = contextBuilder(options.elementId, 500, 240);
ctx.scale(1.5, 1.5); ctx.fillStyle = '#221'; ctx.strokeStyle = '#221';
var stave = new VF.Stave(10, 10, 300)
.addClef('treble').setContext(ctx).draw();
// Create some notes
var notes = [
new VF.StaveNote({ keys: ['a/3'], duration: '8' })
.addModifier(0, new VF.Annotation('good')
.setVerticalJustification(VF.Annotation.VerticalJustify.BOTTOM)),
new VF.StaveNote({ keys: ['g/3'], duration: '8' })
.addModifier(0, new VF.Annotation('even')
.setVerticalJustification(VF.Annotation.VerticalJustify.BOTTOM)),
new VF.StaveNote({ keys: ['c/4'], duration: '8' })
.addModifier(0, new VF.Annotation('under')
.setVerticalJustification(VF.Annotation.VerticalJustify.BOTTOM)),
new VF.StaveNote({ keys: ['d/4'], duration: '8' })
.addModifier(0, new VF.Annotation('beam')
.setVerticalJustification(VF.Annotation.VerticalJustify.BOTTOM)),
];
var beam = new VF.Beam(notes.slice(1));
VF.Formatter.FormatAndDraw(ctx, stave, notes);
beam.setContext(ctx).draw();
ok(true, 'Bottom Annotation with Beams');
},
justificationStemUp: function(options, contextBuilder) {
var ctx = contextBuilder(options.elementId, 650, 950);
ctx.scale(1.5, 1.5); ctx.fillStyle = '#221'; ctx.strokeStyle = '#221';
function newNote(note_struct) { return new VF.StaveNote(note_struct); }
function newAnnotation(text, hJustifcation, vJustifcation) {
return (
new VF.Annotation(text))
.setFont('Arial', VF.Test.Font.size)
.setJustification(hJustifcation)
.setVerticalJustification(vJustifcation);
}
for (var v = 1; v <= 4; ++v) {
var stave = new VF.Stave(10, (v - 1) * 150 + 40, 400)
.addClef('treble').setContext(ctx).draw();
var notes = [];
notes.push(newNote({ keys: ['c/3'], duration: 'q' }).addAnnotation(0, newAnnotation('Text', 1, v)));
notes.push(newNote({ keys: ['c/4'], duration: 'q' }).addAnnotation(0, newAnnotation('Text', 2, v)));
notes.push(newNote({ keys: ['c/5'], duration: 'q' }).addAnnotation(0, newAnnotation('Text', 3, v)));
notes.push(newNote({ keys: ['c/6'], duration: 'q' }).addAnnotation(0, newAnnotation('Text', 4, v)));
VF.Formatter.FormatAndDraw(ctx, stave, notes, 100);
}
ok(true, 'Test Justification Annotation');
},
justificationStemDown: function(options, contextBuilder) {
var ctx = contextBuilder(options.elementId, 650, 1000);
ctx.scale(1.5, 1.5); ctx.fillStyle = '#221'; ctx.strokeStyle = '#221';
function newNote(note_struct) { return new VF.StaveNote(note_struct); }
function newAnnotation(text, hJustifcation, vJustifcation) {
return (
new VF.Annotation(text))
.setFont('Arial', VF.Test.Font.size)
.setJustification(hJustifcation)
.setVerticalJustification(vJustifcation);
}
for (var v = 1; v <= 4; ++v) {
var stave = new VF.Stave(10, (v - 1) * 150 + 40, 400)
.addClef('treble').setContext(ctx).draw();
var notes = [];
notes.push(newNote({ keys: ['c/3'], duration: 'q', stem_direction: -1