Browse Source

Many enhancements:
1. Fixes #8 Rename MusicSheetAPI (class renamed to 'OSMD')
2. Uses a temporary fallback display of titles at the top of sheet music
3. Implements a basic Cursor object to browse the sheet

Andrea Condoluci 9 years ago
parent
commit
cd8baf40fa

+ 1 - 0
external/vexflow/vexflow.d.ts

@@ -41,6 +41,7 @@ declare namespace Vex {
       public getNoteHeadBeginX(): number;
       public getNoteHeadEndX(): number;
       public addAccidental(index: number, accidental: Accidental): StaveNote;
+      public setStyle(style: any): void;
     }
 
     export class Stave {

+ 2 - 1
src/Common/FileIO/Mxl.ts

@@ -1,6 +1,7 @@
 import { IXmlElement } from "./Xml";
 import { Promise } from "es6-promise";
 import JSZip = require("jszip");
+//import { JSZip } from "jzsip";
 
 // Usage for extractSheetMusicFromMxl:
 // extractSheetFromMxl(" *** binary content *** ").then(
@@ -15,7 +16,7 @@ import JSZip = require("jszip");
 export function extractSheetFromMxl(data: string): Promise<any> {
   "use strict";
   // _zip_ must be of type 'any' for now, since typings for JSZip are not up-to-date
-  let zip: any = new JSZip();
+  let zip: JSZip.JSZip = new JSZip();
   // asynchronously load zip file and process it - with Promises
   return zip.loadAsync(data).then(
     (_: any) => {

+ 0 - 138
src/MusicSheetAPI.ts

@@ -1,138 +0,0 @@
-import {IXmlElement} from "./Common/FileIO/Xml";
-import {VexFlowMusicSheetCalculator} from "./MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator";
-import {MusicSheetReader} from "./MusicalScore/ScoreIO/MusicSheetReader";
-import {GraphicalMusicSheet} from "./MusicalScore/Graphical/GraphicalMusicSheet";
-import {MusicSheetCalculator} from "./MusicalScore/Graphical/MusicSheetCalculator";
-import {VexFlowMusicSheetDrawer} from "./MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer";
-import {MusicSheet} from "./MusicalScore/MusicSheet";
-import {Fraction} from "./Common/DataObjects/fraction";
-import {OutlineAndFillStyleEnum} from "./MusicalScore/Graphical/DrawingEnums";
-
-export class MusicSheetAPI {
-    constructor(container: HTMLElement) {
-        this.container = container;
-        this.titles = document.createElement("div");
-        this.canvas = document.createElement("canvas");
-        this.container.appendChild(this.titles);
-        this.container.appendChild(this.canvas);
-        this.drawer = new VexFlowMusicSheetDrawer(this.titles, this.canvas);
-    }
-
-    private container: HTMLElement;
-    private titles: HTMLElement;
-    private canvas: HTMLCanvasElement;
-    private sheet: MusicSheet;
-    private drawer: VexFlowMusicSheetDrawer;
-    private graphic: GraphicalMusicSheet;
-    private zoom: number = 1.0;
-    private unit: number = 10.0;
-
-    private fraction: Fraction = new Fraction(0, 4);
-
-    /**
-     * Load a MusicXML file
-     * @param doc is the root node of a MusicXML document
-     */
-    public load(content: string|Document): void {
-        this.reset();
-        let elem: Element;
-        let path: string = "Unknown path";
-        if (typeof content === "string") {
-            if ((<string>content).substr(0, 4) === "http") {
-                path = <string>content;
-                content = this.loadURL(path);
-            }
-            //if (<string>content.substr() === "")
-        }
-        if ("nodeName" in <any>content) {
-            elem = (<Document>content).getElementsByTagName("score-partwise")[0];
-            if (elem === undefined) {
-                throw new Error("Invalid partwise MusicXML document");
-            }
-        }
-        let score: IXmlElement = new IXmlElement(elem);
-        let calc: MusicSheetCalculator = new VexFlowMusicSheetCalculator();
-        let reader: MusicSheetReader = new MusicSheetReader();
-        this.sheet = reader.createMusicSheet(score, path);
-        this.graphic = new GraphicalMusicSheet(this.sheet, calc);
-    }
-
-    /**
-     * Set the zoom
-     * @param factor is the zooming factor
-     */
-    public scale(factor: number): void {
-        this.zoom = factor;
-    }
-
-    /**
-     * Render the music sheet in the container
-     */
-    public render(): void {
-        this.resetTitle();
-        if (!this.graphic) {
-            throw new Error("OSMD: Before rendering a music sheet, please load a MusicXML file");
-        }
-        let width: number = this.container.offsetWidth;
-        if (isNaN(width)) {
-            throw new Error("OSMD: Before rendering a music sheet, please set the width of the container");
-        }
-        // Set page width
-        this.sheet.pageWidth = width / this.zoom / this.unit;
-        // Calculate again
-        this.graphic.reCalculate();
-        // Update Sheet Page
-        let height: number = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * this.unit * this.zoom;
-        this.drawer.resize(width, height);
-        // Fix the label problem
-        // this.drawer.translate(0, 100);
-        this.drawer.scale(this.zoom);
-        // Finally, draw
-        this.drawer.drawSheet(this.graphic);
-    }
-
-    public next(): void {
-        //calculateCursorLineAtTimestamp
-        //let iterator: MusicPartManagerIterator = this.sheet.MusicPartManager.getIterator();
-        //while (!iterator.EndReached && iterator.CurrentVoiceEntries !== undefined) {
-        //    for (let idx: number = 0, len: number = iterator.CurrentVoiceEntries.length; idx < len; ++idx) {
-        //        let voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[idx];
-        //        for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
-        //            let note: Note = voiceEntry.Notes[idx2];
-        //            note.state = NoteState.Normal;
-        //        }
-        //    }
-        //    iterator.moveToNext();
-        //}
-        this.graphic.Cursors.length = 0;
-        this.graphic.Cursors.push(this.graphic.calculateCursorLineAtTimestamp(this.fraction, OutlineAndFillStyleEnum.PlaybackCursor));
-        this.fraction.Add(new Fraction(1, 8));
-        this.render();
-    }
-
-    private loadURL(url: string): Document {
-        return undefined;
-    }
-
-    //private loadMXL(content: string): Document {
-    //    return undefined;
-    //}
-
-    private resetTitle(): void {
-        // Empty this.titles
-        while (this.titles.firstChild) {
-            this.titles.removeChild(this.titles.firstChild);
-        }
-    }
-
-    /**
-     * Initialize this object to default values
-     */
-    private reset(): void {
-        this.sheet = undefined;
-        this.graphic = undefined;
-        this.zoom = 1.0;
-        this.unit = 10.0;
-        this.resetTitle();
-    }
-}

+ 9 - 10
src/MusicalScore/Graphical/GraphicalMusicSheet.ts

@@ -306,15 +306,14 @@ export class GraphicalMusicSheet {
             this.verticalGraphicalStaffEntryContainers.push(verticalGraphicalStaffEntryContainer);
             return verticalGraphicalStaffEntryContainer;
         }
-        let i: number;
-        for (; i >= 0; i--) {
-            if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp < timestamp) {
+        for (let i: number = this.verticalGraphicalStaffEntryContainers.length - 1; i >= 0; i--) {
+            if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp.lt(timestamp)) {
                 let verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
                     new VerticalGraphicalStaffEntryContainer(this.numberOfStaves, timestamp);
                 this.verticalGraphicalStaffEntryContainers.splice(i + 1, 0, verticalGraphicalStaffEntryContainer);
                 return verticalGraphicalStaffEntryContainer;
             }
-            if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp === timestamp) {
+            if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp.Equals(timestamp)) {
                 return this.verticalGraphicalStaffEntryContainers[i];
             }
         }
@@ -340,18 +339,18 @@ export class GraphicalMusicSheet {
         let foundIndex: number;
         let leftTS: Fraction = undefined;
         let rightTS: Fraction = undefined;
-        if (musicTimestamp <= containers[containers.length - 1].AbsoluteTimestamp) {
+        if (musicTimestamp.lte(containers[containers.length - 1].AbsoluteTimestamp)) {
             while (rightIndex - leftIndex > 1) {
-                let middleIndex: number = (rightIndex + leftIndex) / 2;
-                if (containers[leftIndex].AbsoluteTimestamp === musicTimestamp) {
+                let middleIndex: number = Math.floor((rightIndex + leftIndex) / 2);
+                if (containers[leftIndex].AbsoluteTimestamp.Equals(musicTimestamp)) {
                     rightIndex = leftIndex;
                     break;
-                } else if (containers[rightIndex].AbsoluteTimestamp === musicTimestamp) {
+                } else if (containers[rightIndex].AbsoluteTimestamp.Equals(musicTimestamp)) {
                     leftIndex = rightIndex;
                     break;
-                } else if (containers[middleIndex].AbsoluteTimestamp === musicTimestamp) {
+                } else if (containers[middleIndex].AbsoluteTimestamp.Equals(musicTimestamp)) {
                     return this.verticalGraphicalStaffEntryContainers.indexOf(containers[middleIndex]);
-                } else if (containers[middleIndex].AbsoluteTimestamp > musicTimestamp) {
+                } else if (musicTimestamp.lt(containers[middleIndex].AbsoluteTimestamp)) {
                     rightIndex = middleIndex;
                 } else {
                     leftIndex = middleIndex;

+ 1 - 1
src/MusicalScore/Graphical/GraphicalStaffEntry.ts

@@ -65,7 +65,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
     }
 
     public getAbsoluteTimestamp(): Fraction {
-        let result: Fraction = Fraction.createFromFraction(this.parentMeasure.parentSourceMeasure.AbsoluteTimestamp);
+        let result: Fraction = this.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.clone();
         if (this.relInMeasureTimestamp !== undefined) {
             result.Add(this.relInMeasureTimestamp);
         }

+ 5 - 5
src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.ts

@@ -9,7 +9,7 @@ export class VerticalGraphicalStaffEntryContainer {
         }
     }
 
-    public relativeInMeasureTimestamp: Fraction;
+    //public relativeInMeasureTimestamp: Fraction;
     private index: number;
     private absoluteTimestamp: Fraction;
     private staffEntries: GraphicalStaffEntry[] = [];
@@ -25,10 +25,10 @@ export class VerticalGraphicalStaffEntryContainer {
     public get AbsoluteTimestamp(): Fraction {
         return this.absoluteTimestamp;
     }
-
-    public set AbsoluteTimestamp(value: Fraction) {
-        this.absoluteTimestamp = value;
-    }
+    //
+    //public set AbsoluteTimestamp(value: Fraction) {
+    //    this.absoluteTimestamp = value;
+    //}
 
     public get StaffEntries(): GraphicalStaffEntry[] {
         return this.staffEntries;

+ 1 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -241,6 +241,6 @@ export class VexFlowConverter {
 
     public static style(styleId: OutlineAndFillStyleEnum): string {
         // TODO
-        return "black";
+        return "purple";
     }
 }

+ 15 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts

@@ -12,4 +12,19 @@ export class VexFlowStaffEntry extends GraphicalStaffEntry {
     public graphicalNotes: { [voiceID: number]: GraphicalNote[]; } = {};
     // The corresponding VexFlow.StaveNotes
     public vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = {};
+
+
+    public getXinpx(): number {
+        let x: number = 0;
+        let n: number = 0;
+        let vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = this.vfNotes;
+        for (let voiceId in vfNotes) {
+            if (vfNotes.hasOwnProperty(voiceId)) {
+                x += (vfNotes[voiceId].getNoteHeadBeginX() + vfNotes[voiceId].getNoteHeadEndX()) / 2;
+                n += 1;
+            }
+        }
+        console.log(x, n);
+        return x / n;
+    }
 }

+ 6 - 15
src/MusicalScore/ScoreIO/MusicSheetReader.ts

@@ -152,22 +152,15 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
 
             }
             if (couldReadMeasure) {
-                //Logging.debug("couldReadMeasure: 1");
                 this.musicSheet.addMeasure(this.currentMeasure);
-                //Logging.debug("couldReadMeasure: 2");
                 this.checkIfRhythmInstructionsAreSetAndEqual(instrumentReaders);
-                //Logging.debug("couldReadMeasure: 3");
                 this.checkSourceMeasureForundefinedEntries();
-                //Logging.debug("couldReadMeasure: 4");
                 this.setSourceMeasureDuration(instrumentReaders, sourceMeasureCounter);
-                //Logging.debug("couldReadMeasure: 5");
                 MusicSheetReader.doCalculationsAfterDurationHasBeenSet(instrumentReaders);
-                //Logging.debug("couldReadMeasure: 6");
                 this.currentMeasure.AbsoluteTimestamp = this.currentFraction.clone();
                 this.musicSheet.SheetErrors.finalizeMeasure(this.currentMeasure.MeasureNumber);
                 this.currentFraction.Add(this.currentMeasure.Duration);
                 this.previousMeasure = this.currentMeasure;
-                //Logging.debug("couldReadMeasure: 7");
             }
         }
 
@@ -321,26 +314,24 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     private setSourceMeasureDuration(instrumentReaders: InstrumentReader[], sourceMeasureCounter: number): void {
         let activeRhythm: Fraction = new Fraction(0, 1);
         let instrumentsMaxTieNoteFractions: Fraction[] = [];
-        for (let idx: number = 0, len: number = instrumentReaders.length; idx < len; ++idx) {
-            let instrumentReader: InstrumentReader = instrumentReaders[idx];
+        for (let instrumentReader of instrumentReaders) {
             instrumentsMaxTieNoteFractions.push(instrumentReader.MaxTieNoteFraction);
             let activeRythmMeasure: Fraction = instrumentReader.ActiveRhythm.Rhythm;
-            if (activeRhythm < activeRythmMeasure) {
+            if (activeRhythm.lt(activeRythmMeasure)) {
                 activeRhythm = new Fraction(activeRythmMeasure.Numerator, activeRythmMeasure.Denominator, false);
             }
         }
         let instrumentsDurations: Fraction[] = this.currentMeasure.calculateInstrumentsDuration(this.musicSheet, instrumentsMaxTieNoteFractions);
         let maxInstrumentDuration: Fraction = new Fraction(0, 1);
-        for (let idx: number = 0, len: number = instrumentsDurations.length; idx < len; ++idx) {
-            let instrumentsDuration: Fraction = instrumentsDurations[idx];
-            if (maxInstrumentDuration < instrumentsDuration) {
+        for (let instrumentsDuration of instrumentsDurations) {
+            if (maxInstrumentDuration.lt(instrumentsDuration)) {
                 maxInstrumentDuration = instrumentsDuration;
             }
         }
         if (Fraction.Equal(maxInstrumentDuration, activeRhythm)) {
             this.checkFractionsForEquivalence(maxInstrumentDuration, activeRhythm);
         } else {
-            if (maxInstrumentDuration < activeRhythm) {
+            if (maxInstrumentDuration.lt(activeRhythm)) {
                 maxInstrumentDuration = this.currentMeasure.reverseCheck(this.musicSheet, maxInstrumentDuration);
                 this.checkFractionsForEquivalence(maxInstrumentDuration, activeRhythm);
             }
@@ -355,7 +346,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             let instrumentsDuration: Fraction = instrumentsDurations[i];
             if (
                 (this.currentMeasure.ImplicitMeasure && instrumentsDuration !== maxInstrumentDuration) ||
-                instrumentsDuration !== activeRhythm && // FIXME
+                !Fraction.Equal(instrumentsDuration, activeRhythm) &&
                 !this.allInstrumentsHaveSameDuration(instrumentsDurations, maxInstrumentDuration)
             ) {
                 let firstStaffIndexOfInstrument: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.musicSheet.Instruments[i]);

+ 5 - 3
src/MusicalScore/VoiceData/SourceMeasure.ts

@@ -402,11 +402,13 @@ export class SourceMeasure {
     }
 
     private getLastSourceStaffEntryForInstrument(instrumentIndex: number): SourceStaffEntry {
+        let entry: SourceStaffEntry;
         for (let i: number = this.verticalSourceStaffEntryContainers.length - 1; i >= 0; i--) {
-            if (this.verticalSourceStaffEntryContainers[i][instrumentIndex] !== undefined) {
-                return this.verticalSourceStaffEntryContainers[i][instrumentIndex];
+            entry = this.verticalSourceStaffEntryContainers[i].StaffEntries[instrumentIndex];
+            if (entry) {
+                break;
             }
         }
-        return undefined;
+        return entry;
     }
 }

+ 145 - 0
src/OSMD/Cursor.ts

@@ -0,0 +1,145 @@
+import {MusicPartManagerIterator} from "../MusicalScore/MusicParts/MusicPartManagerIterator";
+import {MusicPartManager} from "../MusicalScore/MusicParts/MusicPartManager";
+import {VoiceEntry} from "../MusicalScore/VoiceData/VoiceEntry";
+import {VexFlowStaffEntry} from "../MusicalScore/Graphical/VexFlow/VexFlowStaffEntry";
+import {MusicSystem} from "../MusicalScore/Graphical/MusicSystem";
+import {OSMD} from "./OSMD";
+import {GraphicalMusicSheet} from "../MusicalScore/Graphical/GraphicalMusicSheet";
+
+
+export class Cursor {
+    constructor(container: HTMLElement, osmd: OSMD) {
+        this.container = container;
+        this.osmd = osmd;
+        let curs: HTMLElement = document.createElement("img");
+        curs.style.position = "absolute";
+        curs.style.zIndex = "-1";
+        this.cursorElement = <HTMLImageElement>curs;
+        container.appendChild(curs);
+    }
+
+    private container: HTMLElement;
+    private osmd: OSMD;
+    private iterator: MusicPartManagerIterator;
+    private graphic: GraphicalMusicSheet;
+    private hidden: boolean = true;
+    private cursorElement: HTMLImageElement;
+
+    public init(manager: MusicPartManager, graphic: GraphicalMusicSheet): void {
+        this.iterator = manager.getIterator();
+        this.graphic = graphic;
+        this.hidden = true;
+        this.hide();
+    }
+
+    public show(): void {
+        this.hidden = false;
+        this.update();
+        // Forcing the sheet to re-render is not necessary anymore
+        //this.osmd.render();
+    }
+
+    public update(): void {
+        // Should NEVER call this.osmd.render()
+        if (this.hidden) {
+            return;
+        }
+        this.graphic.Cursors.length = 0;
+        let iterator: MusicPartManagerIterator = this.iterator;
+        if  (iterator.EndReached || iterator.CurrentVoiceEntries === undefined) {
+            return;
+        }
+        let x: number = 0, y: number = 0, height: number = 0;
+        for (let idx: number = 0, len: number = iterator.CurrentVoiceEntries.length; idx < len; ++idx) {
+            let voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[idx];
+            let measureIndex: number = voiceEntry.ParentSourceStaffEntry.VerticalContainerParent.ParentMeasure.MeasureNumber;
+            let staffIndex: number = voiceEntry.ParentSourceStaffEntry.ParentStaff.idInMusicSheet;
+            let gse: VexFlowStaffEntry =
+                <VexFlowStaffEntry>this.graphic.findGraphicalStaffEntryFromMeasureList(staffIndex, measureIndex, voiceEntry.ParentSourceStaffEntry);
+            if (idx === 0) {
+                x = gse.getXinpx();
+                let musicSystem: MusicSystem = gse.parentMeasure.parentMusicSystem;
+                y = musicSystem.PositionAndShape.AbsolutePosition.y;
+                height = musicSystem.PositionAndShape.BorderBottom;
+            }
+            // The following code is not necessary (for now); it highlights the current notes.
+            //let vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = gse.vfNotes;
+            //for (let voiceId in vfNotes) {
+            //    if (vfNotes.hasOwnProperty(voiceId)) {
+            //        vfNotes[voiceId].setStyle({
+            //            fillStyle: "red",
+            //            strokeStyle: "red",
+            //        });
+            //    }
+            //}
+        }
+        // Update the graphical cursor
+        // The following is the legacy cursor rendered on the canvas:
+        // // let cursor: GraphicalLine = new GraphicalLine(new PointF2D(x, y), new PointF2D(x, y + height), 3, OutlineAndFillStyleEnum.PlaybackCursor);
+        // This the current HTML Cursor:
+        let cursorElement: HTMLImageElement = this.cursorElement;
+        cursorElement.height = (height * 10.0 * this.osmd.zoom);
+        let newWidth: number = 3 * 10.0 * this.osmd.zoom;
+        if (newWidth !== cursorElement.width) {
+            cursorElement.width = newWidth;
+            this.updateStyle(newWidth);
+        }
+        cursorElement.style.top = (y * 10.0 * this.osmd.zoom) + "px";
+        cursorElement.style.left = (x * this.osmd.zoom - (1.5 * 10.0 * this.osmd.zoom)) + "px";
+
+        // Show cursors
+        // // Old cursor: this.graphic.Cursors.push(cursor);
+        this.cursorElement.style.display = "";
+    }
+
+    /**
+     * Hide the cursor
+     */
+    public hide(): void {
+        // Hide the actual cursor element
+        this.cursorElement.style.display = "none";
+        //this.graphic.Cursors.length = 0;
+        // Forcing the sheet to re-render is not necessary anymore
+        //if (!this.hidden) {
+        //    this.osmd.render();
+        //}
+        this.hidden = true;
+    }
+
+    /**
+     * Go to next entry
+     */
+    public next(): void {
+        this.iterator.moveToNext();
+        if (!this.hidden) {
+            this.show();
+        }
+    }
+
+    /**
+     * Go to previous entry. Not implemented.
+     */
+    public prev(): void {
+        // TODO
+        // Previous does not seem to be implemented in the MusicPartManager iterator...
+    }
+
+    private updateStyle(width: number, color: string = "blue"): void {
+        // Create a dummy canvas to generate the gradient for the cursor
+        // FIXME This approach needs to be improved
+        let c: HTMLCanvasElement = document.createElement("canvas");
+        c.width = this.cursorElement.width;
+        c.height = 1;
+        let ctx: CanvasRenderingContext2D = c.getContext("2d");
+        ctx.globalAlpha = 0.5;
+        // Generate the gradient
+        let gradient: CanvasGradient = ctx.createLinearGradient(0, 0, this.cursorElement.width, 0);
+        gradient.addColorStop(0, "white"); // it was: "transparent"
+        gradient.addColorStop(0.5, color);
+        gradient.addColorStop(1, "white"); // it was: "transparent"
+        ctx.fillStyle = gradient;
+        ctx.fillRect(0, 0, width, 1);
+        // Set the actual image
+        this.cursorElement.src = c.toDataURL("image/png");
+    }
+}

+ 142 - 0
src/OSMD/OSMD.ts

@@ -0,0 +1,142 @@
+import {IXmlElement} from "./../Common/FileIO/Xml";
+import {VexFlowMusicSheetCalculator} from "./../MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator";
+import {MusicSheetReader} from "./../MusicalScore/ScoreIO/MusicSheetReader";
+import {GraphicalMusicSheet} from "./../MusicalScore/Graphical/GraphicalMusicSheet";
+import {MusicSheetCalculator} from "./../MusicalScore/Graphical/MusicSheetCalculator";
+import {VexFlowMusicSheetDrawer} from "./../MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer";
+import {MusicSheet} from "./../MusicalScore/MusicSheet";
+import {Cursor} from "./Cursor";
+
+export class OSMD {
+    /**
+     * 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
+     */
+    constructor(container: string|HTMLElement) {
+        // Store container element
+        if (typeof container === "string") {
+            this.container = document.getElementById(<string>container);
+        } else if ("appendChild" in <any>container) {
+            this.container = <HTMLElement>container;
+        }
+        // Create the elements inside the container
+        this.heading = document.createElement("div");
+        this.canvas = document.createElement("canvas");
+        this.canvas.style.zIndex = "0";
+        let inner: HTMLElement = document.createElement("div");
+        inner.style.position = "relative";
+        this.container.appendChild(this.heading);
+        inner.appendChild(this.canvas);
+        this.container.appendChild(inner);
+        // Create the drawer
+        this.drawer = new VexFlowMusicSheetDrawer(this.heading, this.canvas);
+        // Create the cursor
+        this.cursor = new Cursor(inner, this);
+    }
+
+    public cursor: Cursor;
+    public zoom: number = 1.0;
+
+    private container: HTMLElement;
+    private heading: HTMLElement;
+    private canvas: HTMLCanvasElement;
+    private sheet: MusicSheet;
+    private drawer: VexFlowMusicSheetDrawer;
+    private graphic: GraphicalMusicSheet;
+    private unit: number = 10.0;
+
+    /**
+     * Load a MusicXML file
+     * @param content is either the url of a file, or the root node of a MusicXML document, or the string content of a .xml/.mxl file
+     */
+    public load(content: string|Document): void {
+        this.reset();
+        let path: string = "Unknown path";
+        if (typeof content === "string") {
+            let str: string = <string>content;
+            if (str.substr(0, 4) === "http") {
+                path = str;
+                str = this.loadURL(path);
+            }
+            if (str.substr(0, 4) === "\x04\x03\x4b\x50") {
+                // This is a zip file, open the mx
+                // TODO
+                throw new Error("Not implemented: loading of mxl files!");
+            }
+            if (str.substr(0, 5) === "<?xml") {
+                // Parse the string representing an xml file
+                let parser: DOMParser = new DOMParser();
+                content = parser.parseFromString(str, "text/xml");
+            }
+        }
+
+        if (!content || !("nodeName" in <any>content)) {
+            throw new Error("Could not ");
+        }
+        let elem: Element = (<Document>content).getElementsByTagName("score-partwise")[0];
+        if (elem === undefined) {
+            throw new Error("Invalid partwise MusicXML document");
+        }
+        let score: IXmlElement = new IXmlElement(elem);
+        let calc: MusicSheetCalculator = new VexFlowMusicSheetCalculator();
+        let reader: MusicSheetReader = new MusicSheetReader();
+        this.sheet = reader.createMusicSheet(score, path);
+        this.graphic = new GraphicalMusicSheet(this.sheet, calc);
+        this.cursor.init(this.sheet.MusicPartManager, this.graphic);
+    }
+
+    /**
+     * Render the music sheet in the container
+     */
+    public render(): void {
+        this.resetHeadings();
+        if (!this.graphic) {
+            throw new Error("OSMD: Before rendering a music sheet, please load a MusicXML file");
+        }
+        let width: number = this.container.offsetWidth;
+        if (isNaN(width)) {
+            throw new Error("OSMD: Before rendering a music sheet, please set the width of the container");
+        }
+        // Set page width
+        this.sheet.pageWidth = width / this.zoom / this.unit;
+        // Calculate again
+        this.graphic.reCalculate();
+        // Update Sheet Page
+        let height: number = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * this.unit * this.zoom;
+        this.drawer.resize(width, height);
+        // Fix the label problem
+        // this.drawer.translate(0, 100);
+        this.drawer.scale(this.zoom);
+        // Finally, draw
+        this.drawer.drawSheet(this.graphic);
+        // Update the cursor position
+        this.cursor.update();
+    }
+
+
+    private loadURL(url: string): string {
+        throw new Error("Loading from URL not implemented.");
+    }
+
+    //private loadMXL(content: string): Document {
+    //    return undefined;
+    //}
+
+    private resetHeadings(): void {
+        // Empty this.headings
+        while (this.heading.firstChild) {
+            this.heading.removeChild(this.heading.firstChild);
+        }
+    }
+
+    /**
+     * Initialize this object to default values
+     */
+    private reset(): void {
+        this.sheet = undefined;
+        this.graphic = undefined;
+        this.zoom = 1.0;
+        this.unit = 10.0;
+        this.resetHeadings();
+    }
+}