|
@@ -13,14 +13,26 @@ import {MXLHelper} from "../Common/FileIO/Mxl";
|
|
import {Promise} from "es6-promise";
|
|
import {Promise} from "es6-promise";
|
|
import {AJAX} from "./AJAX";
|
|
import {AJAX} from "./AJAX";
|
|
import * as log from "loglevel";
|
|
import * as log from "loglevel";
|
|
|
|
+import {DrawingParametersEnum, DrawingParameters} from "../MusicalScore/Graphical/DrawingParameters";
|
|
|
|
+import {IOSMDOptions, OSMDOptions} from "./OSMDOptions";
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * The main class and control point of OpenSheetMusicDisplay.<br>
|
|
|
|
+ * It can display MusicXML sheet music files in an HTML element container.<br>
|
|
|
|
+ * After the constructor, use load() and render() to load and render a MusicXML file.
|
|
|
|
+ */
|
|
export class OpenSheetMusicDisplay {
|
|
export class OpenSheetMusicDisplay {
|
|
/**
|
|
/**
|
|
- * The easy way of displaying a MusicXML sheet music file
|
|
|
|
- * @param container is either the ID, or the actual "div" element which will host the music sheet
|
|
|
|
- * @autoResize automatically resize the sheet to full page width on window resize
|
|
|
|
|
|
+ * Creates and attaches an OpenSheetMusicDisplay object to an HTML element container.<br>
|
|
|
|
+ * After the constructor, use load() and render() to load and render a MusicXML file.
|
|
|
|
+ * @param container The container element OSMD will be rendered into.<br>
|
|
|
|
+ * Either a string specifying the ID of an HTML container element,<br>
|
|
|
|
+ * or a reference to the HTML element itself (e.g. div)
|
|
|
|
+ * @param options An object for rendering options like the backend (svg/canvas) or autoResize.<br>
|
|
|
|
+ * For defaults see the OSMDOptionsStandard method in the [[OSMDOptions]] class.
|
|
*/
|
|
*/
|
|
- constructor(container: string|HTMLElement, autoResize: boolean = false, backend: string = "svg") {
|
|
|
|
|
|
+ constructor(container: string|HTMLElement,
|
|
|
|
+ options: IOSMDOptions = OSMDOptions.OSMDOptionsStandard()) {
|
|
// Store container element
|
|
// Store container element
|
|
if (typeof container === "string") {
|
|
if (typeof container === "string") {
|
|
// ID passed
|
|
// ID passed
|
|
@@ -33,22 +45,23 @@ export class OpenSheetMusicDisplay {
|
|
throw new Error("Please pass a valid div container to OpenSheetMusicDisplay");
|
|
throw new Error("Please pass a valid div container to OpenSheetMusicDisplay");
|
|
}
|
|
}
|
|
|
|
|
|
- if (backend === "svg") {
|
|
|
|
|
|
+ if (options.backend === undefined || options.backend.toLowerCase() === "svg") {
|
|
this.backend = new SvgVexFlowBackend();
|
|
this.backend = new SvgVexFlowBackend();
|
|
} else {
|
|
} else {
|
|
this.backend = new CanvasVexFlowBackend();
|
|
this.backend = new CanvasVexFlowBackend();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ this.setDrawingParameters(options);
|
|
|
|
+
|
|
this.backend.initialize(this.container);
|
|
this.backend.initialize(this.container);
|
|
this.canvas = this.backend.getCanvas();
|
|
this.canvas = this.backend.getCanvas();
|
|
- const inner: HTMLElement = this.backend.getInnerElement();
|
|
|
|
|
|
+ this.innerElement = this.backend.getInnerElement();
|
|
|
|
+ this.enableOrDisableCursor(this.drawingParameters.drawCursors);
|
|
|
|
|
|
// Create the drawer
|
|
// Create the drawer
|
|
- this.drawer = new VexFlowMusicSheetDrawer(this.canvas, this.backend, false);
|
|
|
|
- // Create the cursor
|
|
|
|
- this.cursor = new Cursor(inner, this);
|
|
|
|
|
|
+ this.drawer = new VexFlowMusicSheetDrawer(this.canvas, this.backend, this.drawingParameters);
|
|
|
|
|
|
- if (autoResize) {
|
|
|
|
|
|
+ if (options.autoResize) {
|
|
this.autoResize();
|
|
this.autoResize();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -59,9 +72,11 @@ export class OpenSheetMusicDisplay {
|
|
private container: HTMLElement;
|
|
private container: HTMLElement;
|
|
private canvas: HTMLElement;
|
|
private canvas: HTMLElement;
|
|
private backend: VexFlowBackend;
|
|
private backend: VexFlowBackend;
|
|
|
|
+ private innerElement: HTMLElement;
|
|
private sheet: MusicSheet;
|
|
private sheet: MusicSheet;
|
|
private drawer: VexFlowMusicSheetDrawer;
|
|
private drawer: VexFlowMusicSheetDrawer;
|
|
private graphic: GraphicalMusicSheet;
|
|
private graphic: GraphicalMusicSheet;
|
|
|
|
+ private drawingParameters: DrawingParameters;
|
|
|
|
|
|
/**
|
|
/**
|
|
* Load a MusicXML file
|
|
* Load a MusicXML file
|
|
@@ -124,7 +139,9 @@ export class OpenSheetMusicDisplay {
|
|
const reader: MusicSheetReader = new MusicSheetReader();
|
|
const reader: MusicSheetReader = new MusicSheetReader();
|
|
this.sheet = reader.createMusicSheet(score, "Unknown path");
|
|
this.sheet = reader.createMusicSheet(score, "Unknown path");
|
|
this.graphic = new GraphicalMusicSheet(this.sheet, calc);
|
|
this.graphic = new GraphicalMusicSheet(this.sheet, calc);
|
|
- this.cursor.init(this.sheet.MusicPartManager, this.graphic);
|
|
|
|
|
|
+ if (this.drawingParameters.drawCursors) {
|
|
|
|
+ this.cursor.init(this.sheet.MusicPartManager, this.graphic);
|
|
|
|
+ }
|
|
log.info(`Loaded sheet ${this.sheet.TitleString} successfully.`);
|
|
log.info(`Loaded sheet ${this.sheet.TitleString} successfully.`);
|
|
return Promise.resolve({});
|
|
return Promise.resolve({});
|
|
}
|
|
}
|
|
@@ -147,15 +164,9 @@ export class OpenSheetMusicDisplay {
|
|
this.sheet.pageWidth = width / this.zoom / 10.0;
|
|
this.sheet.pageWidth = width / this.zoom / 10.0;
|
|
// Calculate again
|
|
// Calculate again
|
|
this.graphic.reCalculate();
|
|
this.graphic.reCalculate();
|
|
- this.graphic.Cursors.length = 0;
|
|
|
|
- /*this.graphic.Cursors.push(this.graphic.calculateCursorLineAtTimestamp(new Fraction(0, 4), OutlineAndFillStyleEnum.PlaybackCursor));
|
|
|
|
- this.graphic.Cursors.push(this.graphic.calculateCursorLineAtTimestamp(new Fraction(1, 4), OutlineAndFillStyleEnum.PlaybackCursor));
|
|
|
|
- this.graphic.Cursors.push(this.graphic.calculateCursorLineAtTimestamp(new Fraction(2, 4), OutlineAndFillStyleEnum.PlaybackCursor));
|
|
|
|
- this.graphic.Cursors.push(this.graphic.calculateCursorLineAtTimestamp(new Fraction(3, 4), OutlineAndFillStyleEnum.PlaybackCursor));
|
|
|
|
- this.graphic.Cursors.push(this.graphic.calculateCursorLineAtTimestamp(new Fraction(4, 4), OutlineAndFillStyleEnum.PlaybackCursor));
|
|
|
|
- this.graphic.Cursors.push(this.graphic.calculateCursorLineAtTimestamp(new Fraction(5, 4), OutlineAndFillStyleEnum.PlaybackCursor));
|
|
|
|
- this.graphic.Cursors.push(this.graphic.calculateCursorLineAtTimestamp(new Fraction(6, 4), OutlineAndFillStyleEnum.PlaybackCursor));
|
|
|
|
- this.graphic.Cursors.push(this.graphic.calculateCursorLineAtTimestamp(new Fraction(7, 4), OutlineAndFillStyleEnum.PlaybackCursor));*/
|
|
|
|
|
|
+ if (this.drawingParameters.drawCursors) {
|
|
|
|
+ this.graphic.Cursors.length = 0;
|
|
|
|
+ }
|
|
// Update Sheet Page
|
|
// Update Sheet Page
|
|
const height: number = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * 10.0 * this.zoom;
|
|
const height: number = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * 10.0 * this.zoom;
|
|
this.drawer.clear();
|
|
this.drawer.clear();
|
|
@@ -163,8 +174,10 @@ export class OpenSheetMusicDisplay {
|
|
this.drawer.scale(this.zoom);
|
|
this.drawer.scale(this.zoom);
|
|
// Finally, draw
|
|
// Finally, draw
|
|
this.drawer.drawSheet(this.graphic);
|
|
this.drawer.drawSheet(this.graphic);
|
|
- // Update the cursor position
|
|
|
|
- this.cursor.update();
|
|
|
|
|
|
+ if (this.drawingParameters.drawCursors) {
|
|
|
|
+ // Update the cursor position
|
|
|
|
+ this.cursor.update();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -201,7 +214,9 @@ export class OpenSheetMusicDisplay {
|
|
* FIXME: Probably unnecessary
|
|
* FIXME: Probably unnecessary
|
|
*/
|
|
*/
|
|
private reset(): void {
|
|
private reset(): void {
|
|
- this.cursor.hide();
|
|
|
|
|
|
+ if (this.drawingParameters.drawCursors) {
|
|
|
|
+ this.cursor.hide();
|
|
|
|
+ }
|
|
this.sheet = undefined;
|
|
this.sheet = undefined;
|
|
this.graphic = undefined;
|
|
this.graphic = undefined;
|
|
this.zoom = 1.0;
|
|
this.zoom = 1.0;
|
|
@@ -213,6 +228,7 @@ export class OpenSheetMusicDisplay {
|
|
* Attach the appropriate handler to the window.onResize event
|
|
* Attach the appropriate handler to the window.onResize event
|
|
*/
|
|
*/
|
|
private autoResize(): void {
|
|
private autoResize(): void {
|
|
|
|
+
|
|
const self: OpenSheetMusicDisplay = this;
|
|
const self: OpenSheetMusicDisplay = this;
|
|
this.handleResize(
|
|
this.handleResize(
|
|
() => {
|
|
() => {
|
|
@@ -229,7 +245,9 @@ export class OpenSheetMusicDisplay {
|
|
// document.documentElement.offsetWidth
|
|
// document.documentElement.offsetWidth
|
|
//);
|
|
//);
|
|
//self.container.style.width = width + "px";
|
|
//self.container.style.width = width + "px";
|
|
- self.render();
|
|
|
|
|
|
+ if (this.graphic !== undefined) {
|
|
|
|
+ self.render();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
);
|
|
);
|
|
}
|
|
}
|
|
@@ -240,6 +258,9 @@ export class OpenSheetMusicDisplay {
|
|
* @param endCallback is the function called when resizing (kind-of) ends
|
|
* @param endCallback is the function called when resizing (kind-of) ends
|
|
*/
|
|
*/
|
|
private handleResize(startCallback: () => void, endCallback: () => void): void {
|
|
private handleResize(startCallback: () => void, endCallback: () => void): void {
|
|
|
|
+ if (this.graphic === undefined) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
let rtime: number;
|
|
let rtime: number;
|
|
let timeout: number = undefined;
|
|
let timeout: number = undefined;
|
|
const delta: number = 200;
|
|
const delta: number = 200;
|
|
@@ -274,7 +295,59 @@ export class OpenSheetMusicDisplay {
|
|
window.setTimeout(endCallback, 1);
|
|
window.setTimeout(endCallback, 1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /** Enable or disable (hide) the cursor.
|
|
|
|
+ * @param enable whether to enable (true) or disable (false) the cursor
|
|
|
|
+ */
|
|
|
|
+ public enableOrDisableCursor(enable: boolean): void {
|
|
|
|
+ this.drawingParameters.drawCursors = enable;
|
|
|
|
+ if (enable) {
|
|
|
|
+ if (!this.cursor) {
|
|
|
|
+ this.cursor = new Cursor(this.innerElement, this);
|
|
|
|
+ if (this.sheet && this.graphic) { // else init is called in load()
|
|
|
|
+ this.cursor.init(this.sheet.MusicPartManager, this.graphic);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else { // disable cursor
|
|
|
|
+ if (!this.cursor) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ this.cursor.hide();
|
|
|
|
+ // this.cursor = undefined;
|
|
|
|
+ // TODO cursor should be disabled, not just hidden. otherwise user can just call osmd.cursor.hide().
|
|
|
|
+ // however, this could cause null calls (cursor.next() etc), maybe that needs some solution.
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
//#region GETTER / SETTER
|
|
//#region GETTER / SETTER
|
|
|
|
+ private setDrawingParameters(options: IOSMDOptions): void {
|
|
|
|
+ this.drawingParameters = new DrawingParameters();
|
|
|
|
+ if (options.drawingParameters) {
|
|
|
|
+ this.drawingParameters.DrawingParametersEnum = DrawingParametersEnum[options.drawingParameters];
|
|
|
|
+ }
|
|
|
|
+ // individual drawing parameters options
|
|
|
|
+ if (options.disableCursor) {
|
|
|
|
+ this.drawingParameters.drawCursors = false;
|
|
|
|
+ }
|
|
|
|
+ if (options.drawHiddenNotes) {
|
|
|
|
+ this.drawingParameters.drawHiddenNotes = true;
|
|
|
|
+ }
|
|
|
|
+ if (options.drawTitle !== undefined) {
|
|
|
|
+ this.drawingParameters.drawTitle = options.drawTitle;
|
|
|
|
+ }
|
|
|
|
+ if (options.drawPartName !== undefined) {
|
|
|
|
+ this.drawingParameters.drawPartName = options.drawPartName;
|
|
|
|
+ }
|
|
|
|
+ if (options.drawCredits !== undefined) {
|
|
|
|
+ this.drawingParameters.drawCredits = options.drawCredits;
|
|
|
|
+ }
|
|
|
|
+ if (options.defaultColorNoteHead) {
|
|
|
|
+ this.drawingParameters.defaultColorNoteHead = options.defaultColorNoteHead;
|
|
|
|
+ }
|
|
|
|
+ if (options.defaultColorStem) {
|
|
|
|
+ this.drawingParameters.defaultColorStem = options.defaultColorStem;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
public set DrawSkyLine(value: boolean) {
|
|
public set DrawSkyLine(value: boolean) {
|
|
if (this.drawer) {
|
|
if (this.drawer) {
|
|
this.drawer.skyLineVisible = value;
|
|
this.drawer.skyLineVisible = value;
|