瀏覽代碼

Merge branch 'release/0.0.1-alpha.1'

Sebastian Haas 9 年之前
父節點
當前提交
37850c0fe9
共有 78 個文件被更改,包括 1329 次插入704 次删除
  1. 1 0
      dist/src/Common/FileIO/Mxl.d.ts
  2. 11 1
      dist/src/Common/FileIO/Mxl.js
  3. 2 3
      dist/src/Common/logging.d.ts
  4. 16 4
      dist/src/Common/logging.js
  5. 2 2
      dist/src/MusicalScore/Graphical/EngravingRules.js
  6. 1 0
      dist/src/MusicalScore/Graphical/GraphicalLabel.d.ts
  7. 3 0
      dist/src/MusicalScore/Graphical/GraphicalLabel.js
  8. 14 15
      dist/src/MusicalScore/Graphical/GraphicalMusicSheet.js
  9. 1 1
      dist/src/MusicalScore/Graphical/GraphicalStaffEntry.js
  10. 7 7
      dist/src/MusicalScore/Graphical/MusicSheetCalculator.js
  11. 0 1
      dist/src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.d.ts
  12. 4 3
      dist/src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.js
  13. 5 0
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowConverter.d.ts
  14. 37 0
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowConverter.js
  15. 1 3
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.d.ts
  16. 48 33
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.js
  17. 11 6
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.js
  18. 13 5
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.d.ts
  19. 49 21
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.js
  20. 5 0
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.d.ts
  21. 16 0
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.js
  22. 3 3
      dist/src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer.js
  23. 2 1
      dist/src/MusicalScore/MusicParts/MusicPartManagerIterator.js
  24. 6 3
      dist/src/MusicalScore/MusicSheet.js
  25. 2 1
      dist/src/MusicalScore/MusicSource/Repetition.js
  26. 11 11
      dist/src/MusicalScore/ScoreIO/InstrumentReader.js
  27. 15 24
      dist/src/MusicalScore/ScoreIO/MusicSheetReader.js
  28. 3 3
      dist/src/MusicalScore/ScoreIO/VoiceGenerator.js
  29. 2 1
      dist/src/MusicalScore/SubInstrument.js
  30. 2 2
      dist/src/MusicalScore/VoiceData/Expressions/instantaniousDynamicExpression.js
  31. 5 3
      dist/src/MusicalScore/VoiceData/SourceMeasure.js
  32. 28 0
      dist/src/OSMD/Cursor.d.ts
  33. 118 0
      dist/src/OSMD/Cursor.js
  34. 44 0
      dist/src/OSMD/OSMD.d.ts
  35. 167 0
      dist/src/OSMD/OSMD.js
  36. 9 0
      dist/src/OSMD/ResizeHandler.d.ts
  37. 43 0
      dist/src/OSMD/ResizeHandler.js
  38. 2 1
      dist/test/Common/DataObjects/fraction_Test.js
  39. 0 0
      dist/test/Common/OSMD/OSMD.d.ts
  40. 18 0
      dist/test/Common/OSMD/OSMD.js
  41. 2 3
      dist/test/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.js
  42. 0 1
      dist/test/MusicalScore/ScoreIO/MusicSheetReader.js
  43. 4 5
      external/vexflow/vexflow.d.ts
  44. 4 0
      karma.conf.js
  45. 3 3
      package.json
  46. 15 2
      src/Common/FileIO/Mxl.ts
  47. 16 0
      src/Common/Logging.ts
  48. 0 13
      src/Common/logging.ts
  49. 0 110
      src/MusicSheetAPI.ts
  50. 0 216
      src/MusicalScore/Calculation/MeasureSizeCalculator.ts
  51. 1 1
      src/MusicalScore/Graphical/EngravingRules.ts
  52. 11 12
      src/MusicalScore/Graphical/GraphicalMusicSheet.ts
  53. 1 1
      src/MusicalScore/Graphical/GraphicalStaffEntry.ts
  54. 7 7
      src/MusicalScore/Graphical/MusicSheetCalculator.ts
  55. 5 5
      src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.ts
  56. 1 1
      src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts
  57. 45 32
      src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts
  58. 8 3
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts
  59. 21 3
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts
  60. 17 0
      src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts
  61. 2 1
      src/MusicalScore/MusicParts/MusicPartManagerIterator.ts
  62. 6 3
      src/MusicalScore/MusicSheet.ts
  63. 2 1
      src/MusicalScore/MusicSource/Repetition.ts
  64. 1 1
      src/MusicalScore/ScoreIO/InstrumentReader.ts
  65. 7 19
      src/MusicalScore/ScoreIO/MusicSheetReader.ts
  66. 1 1
      src/MusicalScore/ScoreIO/VoiceGenerator.ts
  67. 2 1
      src/MusicalScore/SubInstrument.ts
  68. 1 1
      src/MusicalScore/VoiceData/Expressions/instantaniousDynamicExpression.ts
  69. 5 3
      src/MusicalScore/VoiceData/SourceMeasure.ts
  70. 149 0
      src/OSMD/Cursor.ts
  71. 193 0
      src/OSMD/OSMD.ts
  72. 44 0
      src/OSMD/ResizeHandler.ts
  73. 2 1
      test/Common/DataObjects/fraction_Test.ts
  74. 22 0
      test/Common/OSMD/OSMD.ts
  75. 0 90
      test/MusicalScore/Calculation/MeasureSizeCalculator.ts
  76. 2 3
      test/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts
  77. 0 1
      test/MusicalScore/ScoreIO/MusicSheetReader.ts
  78. 2 2
      typings.json

+ 1 - 0
dist/src/Common/FileIO/Mxl.d.ts

@@ -1,2 +1,3 @@
 import { Promise } from "es6-promise";
 export declare function extractSheetFromMxl(data: string): Promise<any>;
+export declare function openMxl(data: string): Promise<any>;

+ 11 - 1
dist/src/Common/FileIO/Mxl.js

@@ -14,7 +14,6 @@ var JSZip = require("jszip");
 // )
 function extractSheetFromMxl(data) {
     "use strict";
-    // _zip_ must be of type 'any' for now, since typings for JSZip are not up-to-date
     var zip = new JSZip();
     // asynchronously load zip file and process it - with Promises
     return zip.loadAsync(data).then(function (_) {
@@ -42,3 +41,14 @@ function extractSheetFromMxl(data) {
     });
 }
 exports.extractSheetFromMxl = extractSheetFromMxl;
+function openMxl(data) {
+    "use strict";
+    var zip = new JSZip();
+    // asynchronously load zip file and process it - with Promises
+    return zip.loadAsync(data).then(function (_) {
+        return zip.file("META-INF/container.xml").async("string");
+    }, function (err) {
+        throw err;
+    });
+}
+exports.openMxl = openMxl;

+ 2 - 3
dist/src/Common/logging.d.ts

@@ -1,7 +1,6 @@
-/**
- * Created by acondolu on 26/04/16.
- */
 export declare class Logging {
     static debug(...args: any[]): void;
     static log(...args: any[]): void;
+    static error(...args: any[]): void;
+    static warn(...args: any[]): void;
 }

+ 16 - 4
dist/src/Common/logging.js

@@ -1,6 +1,4 @@
-/**
- * Created by acondolu on 26/04/16.
- */
+/* tslint:disable:no-console */
 "use strict";
 var Logging = (function () {
     function Logging() {
@@ -10,7 +8,7 @@ var Logging = (function () {
         for (var _i = 0; _i < arguments.length; _i++) {
             args[_i - 0] = arguments[_i];
         }
-        console.log("[OSMD] DEBUG: ", args.join(" "));
+        console.debug("[OSMD] ", args.join(" "));
     };
     Logging.log = function () {
         var args = [];
@@ -19,6 +17,20 @@ var Logging = (function () {
         }
         console.log("[OSMD] ", args.join(" "));
     };
+    Logging.error = function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i - 0] = arguments[_i];
+        }
+        console.error("[OSMD] ", args.join(" "));
+    };
+    Logging.warn = function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i - 0] = arguments[_i];
+        }
+        console.warn("[OSMD] ", args.join(" "));
+    };
     return Logging;
 }());
 exports.Logging = Logging;

+ 2 - 2
dist/src/MusicalScore/Graphical/EngravingRules.js

@@ -1,7 +1,7 @@
 "use strict";
 var GraphicalMusicPage_1 = require("./GraphicalMusicPage");
 //import {MusicSymbol} from "./MusicSymbol";
-var logging_1 = require("../../Common/logging");
+var Logging_1 = require("../../Common/Logging");
 var EngravingRules = (function () {
     function EngravingRules() {
         this.noteDistances = [1.0, 1.0, 1.3, 1.6, 2.0, 2.5, 3.0, 4.0];
@@ -138,7 +138,7 @@ var EngravingRules = (function () {
             this.maxInstructionsConstValue = this.ClefLeftMargin + this.ClefRightMargin + this.KeyRightMargin + this.RhythmRightMargin + 11;
         }
         catch (ex) {
-            logging_1.Logging.log("EngravingRules()", ex);
+            Logging_1.Logging.log("EngravingRules()", ex);
         }
     }
     Object.defineProperty(EngravingRules, "Rules", {

+ 1 - 0
dist/src/MusicalScore/Graphical/GraphicalLabel.d.ts

@@ -6,5 +6,6 @@ export declare class GraphicalLabel extends Clickable {
     private label;
     constructor(label: Label, textHeight: number, alignment: TextAlignment, parent?: BoundingBox);
     Label: Label;
+    toString(): string;
     setLabelPositionAndShapeBorders(): void;
 }

+ 3 - 0
dist/src/MusicalScore/Graphical/GraphicalLabel.js

@@ -26,6 +26,9 @@ var GraphicalLabel = (function (_super) {
         enumerable: true,
         configurable: true
     });
+    GraphicalLabel.prototype.toString = function () {
+        return this.label.text;
+    };
     GraphicalLabel.prototype.setLabelPositionAndShapeBorders = function () {
         if (this.Label.text.trim() === "") {
             return;

+ 14 - 15
dist/src/MusicalScore/Graphical/GraphicalMusicSheet.js

@@ -6,7 +6,7 @@ var ClefInstruction_1 = require("../VoiceData/Instructions/ClefInstruction");
 var KeyInstruction_1 = require("../VoiceData/Instructions/KeyInstruction");
 var fraction_1 = require("../../Common/DataObjects/fraction");
 var BoundingBox_1 = require("./BoundingBox");
-var logging_1 = require("../../Common/logging");
+var Logging_1 = require("../../Common/Logging");
 var Dictionary_1 = require("typescript-collections/dist/lib/Dictionary");
 var collectionUtil_1 = require("../../Util/collectionUtil");
 var GraphicalMusicSheet = (function () {
@@ -292,19 +292,18 @@ var GraphicalMusicSheet = (function () {
     };
     GraphicalMusicSheet.prototype.getOrCreateVerticalContainer = function (timestamp) {
         if (this.verticalGraphicalStaffEntryContainers.length === 0 ||
-            timestamp > collectionUtil_1.CollectionUtil.getLastElement(this.verticalGraphicalStaffEntryContainers).AbsoluteTimestamp) {
+            timestamp.lt(collectionUtil_1.CollectionUtil.getLastElement(this.verticalGraphicalStaffEntryContainers).AbsoluteTimestamp)) {
             var verticalGraphicalStaffEntryContainer = new VerticalGraphicalStaffEntryContainer_1.VerticalGraphicalStaffEntryContainer(this.numberOfStaves, timestamp);
             this.verticalGraphicalStaffEntryContainers.push(verticalGraphicalStaffEntryContainer);
             return verticalGraphicalStaffEntryContainer;
         }
-        var i;
-        for (; i >= 0; i--) {
-            if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp < timestamp) {
+        for (var i = this.verticalGraphicalStaffEntryContainers.length - 1; i >= 0; i--) {
+            if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp.lt(timestamp)) {
                 var verticalGraphicalStaffEntryContainer = new VerticalGraphicalStaffEntryContainer_1.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];
             }
         }
@@ -325,21 +324,21 @@ var GraphicalMusicSheet = (function () {
         var foundIndex;
         var leftTS = undefined;
         var rightTS = undefined;
-        if (musicTimestamp <= containers[containers.length - 1].AbsoluteTimestamp) {
+        if (musicTimestamp.lte(containers[containers.length - 1].AbsoluteTimestamp)) {
             while (rightIndex - leftIndex > 1) {
-                var middleIndex = (rightIndex + leftIndex) / 2;
-                if (containers[leftIndex].AbsoluteTimestamp === musicTimestamp) {
+                var middleIndex = 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 {
@@ -547,7 +546,7 @@ var GraphicalMusicSheet = (function () {
             return this.GetClickableLabel(positionOnMusicSheet);
         }
         catch (ex) {
-            logging_1.Logging.log("GraphicalMusicSheet.tryGetClickableObject", "positionOnMusicSheet: " + positionOnMusicSheet, ex);
+            Logging_1.Logging.log("GraphicalMusicSheet.tryGetClickableObject", "positionOnMusicSheet: " + positionOnMusicSheet, ex);
         }
         return undefined;
     };
@@ -560,7 +559,7 @@ var GraphicalMusicSheet = (function () {
             return entry.getAbsoluteTimestamp();
         }
         catch (ex) {
-            logging_1.Logging.log("GraphicalMusicSheet.tryGetTimeStampFromPosition", "positionOnMusicSheet: " + positionOnMusicSheet, ex);
+            Logging_1.Logging.log("GraphicalMusicSheet.tryGetTimeStampFromPosition", "positionOnMusicSheet: " + positionOnMusicSheet, ex);
         }
         return undefined;
     };
@@ -584,7 +583,7 @@ var GraphicalMusicSheet = (function () {
             }
         }
         catch (ex) {
-            logging_1.Logging.log("GraphicalMusicSheet.getStaffEntry", ex);
+            Logging_1.Logging.log("GraphicalMusicSheet.getStaffEntry", ex);
         }
         return staffEntry;
     };

+ 1 - 1
dist/src/MusicalScore/Graphical/GraphicalStaffEntry.js

@@ -57,7 +57,7 @@ var GraphicalStaffEntry = (function (_super) {
         configurable: true
     });
     GraphicalStaffEntry.prototype.getAbsoluteTimestamp = function () {
-        var result = fraction_1.Fraction.createFromFraction(this.parentMeasure.parentSourceMeasure.AbsoluteTimestamp);
+        var result = this.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.clone();
         if (this.relInMeasureTimestamp !== undefined) {
             result.Add(this.relInMeasureTimestamp);
         }

+ 7 - 7
dist/src/MusicalScore/Graphical/MusicSheetCalculator.js

@@ -17,7 +17,7 @@ var BoundingBox_1 = require("./BoundingBox");
 var OctaveShiftParams_1 = require("./OctaveShiftParams");
 var AccidentalCalculator_1 = require("./AccidentalCalculator");
 var ClefInstruction_3 = require("../VoiceData/Instructions/ClefInstruction");
-var logging_1 = require("../../Common/logging");
+var Logging_1 = require("../../Common/Logging");
 var Dictionary_1 = require("typescript-collections/dist/lib/Dictionary");
 var collectionUtil_1 = require("../../Util/collectionUtil");
 var MusicSheetCalculator = (function () {
@@ -189,10 +189,10 @@ var MusicSheetCalculator = (function () {
         throw new Error("abstract, not implemented");
     };
     MusicSheetCalculator.prototype.clearRecreatedObjects = function () {
-        logging_1.Logging.log("clearRecreatedObjects not implemented");
+        Logging_1.Logging.debug("clearRecreatedObjects not implemented");
     };
     MusicSheetCalculator.prototype.handleStaffEntryLink = function (graphicalStaffEntry, staffEntryLinks) {
-        logging_1.Logging.log("handleStaffEntryLink not implemented");
+        Logging_1.Logging.debug("handleStaffEntryLink not implemented");
     };
     MusicSheetCalculator.prototype.calculateMusicSystems = function () {
         if (this.graphicalMusicSheet.MeasureList === undefined) {
@@ -314,16 +314,16 @@ var MusicSheetCalculator = (function () {
         }
     };
     MusicSheetCalculator.prototype.updateSkyBottomLine = function (staffLine) {
-        logging_1.Logging.log("updateSkyBottomLine not implemented");
+        Logging_1.Logging.debug("updateSkyBottomLine not implemented");
     };
     MusicSheetCalculator.prototype.calculateSkyBottomLine = function (staffLine) {
-        logging_1.Logging.log("calculateSkyBottomLine not implemented");
+        Logging_1.Logging.debug("calculateSkyBottomLine not implemented");
     };
     MusicSheetCalculator.prototype.calculateMarkedAreas = function () {
-        logging_1.Logging.log("calculateMarkedAreas not implemented");
+        Logging_1.Logging.debug("calculateMarkedAreas not implemented");
     };
     MusicSheetCalculator.prototype.calculateComments = function () {
-        logging_1.Logging.log("calculateComments not implemented");
+        Logging_1.Logging.debug("calculateComments not implemented");
     };
     MusicSheetCalculator.prototype.optimizeStaffLineDynamicExpressionsPositions = function () {
         return;

+ 0 - 1
dist/src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.d.ts

@@ -2,7 +2,6 @@ import { Fraction } from "../../Common/DataObjects/fraction";
 import { GraphicalStaffEntry } from "./GraphicalStaffEntry";
 export declare class VerticalGraphicalStaffEntryContainer {
     constructor(numberOfEntries: number, absoluteTimestamp: Fraction);
-    relativeInMeasureTimestamp: Fraction;
     private index;
     private absoluteTimestamp;
     private staffEntries;

+ 4 - 3
dist/src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.js

@@ -21,13 +21,14 @@ var VerticalGraphicalStaffEntryContainer = (function () {
         get: function () {
             return this.absoluteTimestamp;
         },
-        set: function (value) {
-            this.absoluteTimestamp = value;
-        },
         enumerable: true,
         configurable: true
     });
     Object.defineProperty(VerticalGraphicalStaffEntryContainer.prototype, "StaffEntries", {
+        //
+        //public set AbsoluteTimestamp(value: Fraction) {
+        //    this.absoluteTimestamp = value;
+        //}
         get: function () {
             return this.staffEntries;
         },

+ 5 - 0
dist/src/MusicalScore/Graphical/VexFlow/VexFlowConverter.d.ts

@@ -7,6 +7,9 @@ import { KeyInstruction } from "../../VoiceData/Instructions/KeyInstruction";
 import { AccidentalEnum } from "../../../Common/DataObjects/pitch";
 import { GraphicalNote } from "../GraphicalNote";
 import { SystemLinesEnum } from "../SystemLinesEnum";
+import { FontStyles } from "../../../Common/Enums/FontStyles";
+import { Fonts } from "../../../Common/Enums/Fonts";
+import { OutlineAndFillStyleEnum } from "../DrawingEnums";
 export declare class VexFlowConverter {
     private static majorMap;
     private static minorMap;
@@ -29,4 +32,6 @@ export declare class VexFlowConverter {
     static TimeSignature(rhythm: RhythmInstruction): Vex.Flow.TimeSignature;
     static keySignature(key: KeyInstruction): string;
     static line(lineType: SystemLinesEnum): any;
+    static font(fontSize: number, fontStyle?: FontStyles, font?: Fonts): string;
+    static style(styleId: OutlineAndFillStyleEnum): string;
 }

+ 37 - 0
dist/src/MusicalScore/Graphical/VexFlow/VexFlowConverter.js

@@ -6,6 +6,8 @@ var KeyInstruction_1 = require("../../VoiceData/Instructions/KeyInstruction");
 var pitch_1 = require("../../../Common/DataObjects/pitch");
 var pitch_2 = require("../../../Common/DataObjects/pitch");
 var SystemLinesEnum_1 = require("../SystemLinesEnum");
+var FontStyles_1 = require("../../../Common/Enums/FontStyles");
+var Fonts_1 = require("../../../Common/Enums/Fonts");
 var VexFlowConverter = (function () {
     function VexFlowConverter() {
     }
@@ -185,6 +187,41 @@ var VexFlowConverter = (function () {
             default:
         }
     };
+    VexFlowConverter.font = function (fontSize, fontStyle, font) {
+        if (fontStyle === void 0) { fontStyle = FontStyles_1.FontStyles.Regular; }
+        if (font === void 0) { font = Fonts_1.Fonts.TimesNewRoman; }
+        var style = "normal";
+        var weight = "normal";
+        var family = "'Times New Roman'";
+        switch (fontStyle) {
+            case FontStyles_1.FontStyles.Bold:
+                weight = "bold";
+                break;
+            case FontStyles_1.FontStyles.Italic:
+                style = "italic";
+                break;
+            case FontStyles_1.FontStyles.BoldItalic:
+                style = "italic";
+                weight = "bold";
+                break;
+            case FontStyles_1.FontStyles.Underlined:
+                // TODO
+                break;
+            default:
+                break;
+        }
+        switch (font) {
+            case Fonts_1.Fonts.Kokila:
+                // TODO Not Supported
+                break;
+            default:
+        }
+        return style + " " + weight + " " + Math.floor(fontSize) + "px " + family;
+    };
+    VexFlowConverter.style = function (styleId) {
+        // TODO
+        return "purple";
+    };
     VexFlowConverter.majorMap = {
         "0": "C", 1: "G", 2: "D", 3: "A", 4: "E", 5: "B", 6: "F#", 7: "C#",
         8: "G#", "-1": "F", "-8": "Fb", "-7": "Cb", "-6": "Gb", "-5": "Db", "-4": "Ab", "-3": "Eb", "-2": "Bb",

+ 1 - 3
dist/src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.d.ts

@@ -17,7 +17,6 @@ export declare class VexFlowMeasure extends StaffMeasure {
         [voiceID: number]: Vex.Flow.Voice;
     };
     formatVoices: (width: number) => void;
-    unit: number;
     private stave;
     private connectors;
     private beams;
@@ -95,6 +94,5 @@ export declare class VexFlowMeasure extends StaffMeasure {
      */
     lineTo(top: VexFlowMeasure, lineType: any): void;
     getVFStave(): Vex.Flow.Stave;
-    private increaseBeginInstructionWidth();
-    private increaseEndInstructionWidth();
+    private updateInstructionWidth();
 }

+ 48 - 33
dist/src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.js

@@ -7,6 +7,7 @@ var __extends = (this && this.__extends) || function (d, b) {
 var Vex = require("vexflow");
 var StaffMeasure_1 = require("../StaffMeasure");
 var VexFlowConverter_1 = require("./VexFlowConverter");
+var Logging_1 = require("../../../Common/Logging");
 var VexFlowMeasure = (function (_super) {
     __extends(VexFlowMeasure, _super);
     function VexFlowMeasure(staff, staffLine, sourceMeasure) {
@@ -17,14 +18,10 @@ var VexFlowMeasure = (function (_super) {
         this.octaveOffset = 3;
         // The VexFlow Voices in the measure
         this.vfVoices = {};
-        // The unit
-        this.unit = 10.0;
         // VexFlow StaveConnectors (vertical lines)
         this.connectors = [];
         // Intermediate object to construct beams
         this.beams = {};
-        // VexFlow Beams
-        this.vfbeams = {};
         this.minimumStaffEntriesWidth = -1;
         this.resetLayout();
     }
@@ -39,15 +36,20 @@ var VexFlowMeasure = (function (_super) {
     VexFlowMeasure.prototype.resetLayout = function () {
         // Take into account some space for the begin and end lines of the stave
         // Will be changed when repetitions will be implemented
-        this.beginInstructionsWidth = 20 / this.unit;
-        this.endInstructionsWidth = 20 / this.unit;
-        this.stave = new Vex.Flow.Stave(0, 0, 0);
+        //this.beginInstructionsWidth = 20 / 10.0;
+        //this.endInstructionsWidth = 20 / 10.0;
+        this.stave = new Vex.Flow.Stave(0, 0, 0, {
+            space_above_staff_ln: 0,
+            space_below_staff_ln: 0,
+        });
+        this.updateInstructionWidth();
     };
     VexFlowMeasure.prototype.clean = function () {
         //this.beams = {};
-        //this.vfbeams = {};
+        //this.vfbeams = undefined;
         this.connectors = [];
-        console.log("clean!");
+        // Clean up instructions
+        this.resetLayout();
     };
     /**
      * returns the x-width of a given measure line.
@@ -59,9 +61,9 @@ var VexFlowMeasure = (function (_super) {
         var vfline = VexFlowConverter_1.VexFlowConverter.line(line);
         switch (vfline) {
             case Vex.Flow.StaveConnector.type.SINGLE:
-                return 1.0 / this.unit;
+                return 1.0 / 10.0;
             case Vex.Flow.StaveConnector.type.DOUBLE:
-                return 3.0 / this.unit;
+                return 3.0 / 10.0;
             default:
                 return 0;
         }
@@ -75,7 +77,7 @@ var VexFlowMeasure = (function (_super) {
         this.octaveOffset = clef.OctaveOffset;
         var vfclef = VexFlowConverter_1.VexFlowConverter.Clef(clef);
         this.stave.addClef(vfclef, undefined, undefined, Vex.Flow.Modifier.Position.BEGIN);
-        this.increaseBeginInstructionWidth();
+        this.updateInstructionWidth();
     };
     /**
      * adds the given key to the begin of the measure.
@@ -96,7 +98,7 @@ var VexFlowMeasure = (function (_super) {
     VexFlowMeasure.prototype.addRhythmAtBegin = function (rhythm) {
         var timeSig = VexFlowConverter_1.VexFlowConverter.TimeSignature(rhythm);
         this.stave.addModifier(timeSig, Vex.Flow.Modifier.Position.BEGIN);
-        this.increaseBeginInstructionWidth();
+        this.updateInstructionWidth();
     };
     /**
      * adds the given clef to the end of the measure.
@@ -106,7 +108,7 @@ var VexFlowMeasure = (function (_super) {
     VexFlowMeasure.prototype.addClefAtEnd = function (clef) {
         var vfclef = VexFlowConverter_1.VexFlowConverter.Clef(clef);
         this.stave.setEndClef(vfclef, undefined, undefined);
-        this.increaseEndInstructionWidth();
+        this.updateInstructionWidth();
     };
     /**
      * Sets the overall x-width of the measure.
@@ -115,12 +117,14 @@ var VexFlowMeasure = (function (_super) {
     VexFlowMeasure.prototype.setWidth = function (width) {
         _super.prototype.setWidth.call(this, width);
         // Set the width of the Vex.Flow.Stave
-        this.stave.setWidth(width * this.unit);
+        this.stave.setWidth(width * 10.0);
+        // Force the width of the Begin Instructions
+        this.stave.setNoteStartX(this.beginInstructionsWidth * 10.0);
         // If this is the first stave in the vertical measure, call the format
         // method to set the width of all the voices
         if (this.formatVoices) {
             // The width of the voices does not include the instructions (StaveModifiers)
-            this.formatVoices((width - this.beginInstructionsWidth - this.endInstructionsWidth) * this.unit);
+            this.formatVoices((width - this.beginInstructionsWidth - this.endInstructionsWidth) * 10.0);
         }
     };
     /**
@@ -198,6 +202,10 @@ var VexFlowMeasure = (function (_super) {
      * Complete the creation of VexFlow Beams in this measure
      */
     VexFlowMeasure.prototype.finalizeBeams = function () {
+        // The following line resets the created Vex.Flow Beams and
+        // created them brand new. Is this needed? And more importantly,
+        // should the old beams be removed manually by the notes?
+        this.vfbeams = {};
         for (var voiceID in this.beams) {
             if (this.beams.hasOwnProperty(voiceID)) {
                 var vfbeams = this.vfbeams[voiceID];
@@ -215,7 +223,7 @@ var VexFlowMeasure = (function (_super) {
                         vfbeams.push(new Vex.Flow.Beam(notes, true));
                     }
                     else {
-                        console.log("Warning! Beam with no notes! Trying to ignore, but this is a serious problem.");
+                        Logging_1.Logging.log("Warning! Beam with no notes! Trying to ignore, but this is a serious problem.");
                     }
                 }
             }
@@ -228,8 +236,8 @@ var VexFlowMeasure = (function (_super) {
             if (gnotes.hasOwnProperty(voiceID)) {
                 if (!(voiceID in vfVoices)) {
                     vfVoices[voiceID] = new Vex.Flow.Voice({
-                        beat_value: 4,
-                        num_beats: 3,
+                        beat_value: this.parentSourceMeasure.Duration.Denominator,
+                        num_beats: this.parentSourceMeasure.Duration.Numerator,
                         resolution: Vex.Flow.RESOLUTION,
                     }).setMode(Vex.Flow.Voice.Mode.SOFT);
                 }
@@ -252,20 +260,27 @@ var VexFlowMeasure = (function (_super) {
     VexFlowMeasure.prototype.getVFStave = function () {
         return this.stave;
     };
-    VexFlowMeasure.prototype.increaseBeginInstructionWidth = function () {
-        var modifiers = this.stave.getModifiers();
-        var modifier = modifiers[modifiers.length - 1];
-        //let padding: number = modifier.getCategory() === "keysignatures" ? modifier.getPadding(2) : 0;
-        var padding = modifier.getPadding(20);
-        var width = modifier.getWidth();
-        this.beginInstructionsWidth += (padding + width) / this.unit;
-    };
-    VexFlowMeasure.prototype.increaseEndInstructionWidth = function () {
-        var modifiers = this.stave.getModifiers();
-        var modifier = modifiers[modifiers.length - 1];
-        var padding = 0;
-        var width = modifier.getWidth();
-        this.endInstructionsWidth += (padding + width) / this.unit;
+    //private increaseBeginInstructionWidth(): void {
+    //    let modifiers: StaveModifier[] = this.stave.getModifiers();
+    //    let modifier: StaveModifier = modifiers[modifiers.length - 1];
+    //    //let padding: number = modifier.getCategory() === "keysignatures" ? modifier.getPadding(2) : 0;
+    //    let padding: number = modifier.getPadding(20);
+    //    let width: number = modifier.getWidth();
+    //    this.beginInstructionsWidth += (padding + width) / 10.0;
+    //}
+    //
+    //private increaseEndInstructionWidth(): void {
+    //    let modifiers: StaveModifier[] = this.stave.getModifiers();
+    //    let modifier: StaveModifier = modifiers[modifiers.length - 1];
+    //    let padding: number = 0;
+    //    let width: number = modifier.getWidth();
+    //    this.endInstructionsWidth += (padding + width) / 10.0;
+    //
+    //}
+    VexFlowMeasure.prototype.updateInstructionWidth = function () {
+        this.stave.format();
+        this.beginInstructionsWidth = this.stave.getNoteStartX() / 10.0;
+        this.endInstructionsWidth = this.stave.getNoteEndX() / 10.0;
     };
     return VexFlowMeasure;
 }(StaffMeasure_1.StaffMeasure));

+ 11 - 6
dist/src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.js

@@ -8,6 +8,7 @@ var MusicSheetCalculator_1 = require("../MusicSheetCalculator");
 var VexFlowGraphicalSymbolFactory_1 = require("./VexFlowGraphicalSymbolFactory");
 var VexFlowTextMeasurer_1 = require("./VexFlowTextMeasurer");
 var Vex = require("vexflow");
+var Logging_1 = require("../../../Common/Logging");
 var VexFlowMusicSheetCalculator = (function (_super) {
     __extends(VexFlowMusicSheetCalculator, _super);
     function VexFlowMusicSheetCalculator() {
@@ -46,7 +47,9 @@ var VexFlowMusicSheetCalculator = (function (_super) {
         }
         // Format the voices
         var allVoices = [];
-        var formatter = new Vex.Flow.Formatter();
+        var formatter = new Vex.Flow.Formatter({
+            align_rests: true,
+        });
         for (var _a = 0, measures_2 = measures; _a < measures_2.length; _a++) {
             var measure = measures_2[_a];
             var mvoices = measure.vfVoices;
@@ -58,13 +61,15 @@ var VexFlowMusicSheetCalculator = (function (_super) {
                 }
             }
             if (voices.length === 0) {
-                console.warn("Found a measure with no voices... Continuing anyway.", mvoices);
+                Logging_1.Logging.warn("Found a measure with no voices... Continuing anyway.", mvoices);
                 continue;
             }
             formatter.joinVoices(voices);
         }
         var firstMeasure = measures[0];
-        var width = formatter.preCalculateMinTotalWidth(allVoices) / firstMeasure.unit;
+        // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
+        // FIXME: a more relaxed formatting of voices
+        var width = formatter.preCalculateMinTotalWidth(allVoices) / 10.0 + 5.0;
         for (var _b = 0, measures_3 = measures; _b < measures_3.length; _b++) {
             var measure = measures_3[_b];
             measure.minimumStaffEntriesWidth = width;
@@ -114,16 +119,16 @@ var VexFlowMusicSheetCalculator = (function (_super) {
                 for (var idx2 = 0, len2 = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
                     var musicSystem = graphicalMusicPage.MusicSystems[idx2];
                     // calculate y positions of stafflines within system
-                    var y = 0;
+                    var y = 10;
                     for (var _i = 0, _a = musicSystem.StaffLines; _i < _a.length; _i++) {
                         var line = _a[_i];
                         line.PositionAndShape.RelativePosition.y = y;
                         y += 10;
                     }
                     // set y positions of systems using the previous system and a fixed distance.
-                    musicSystem.PositionAndShape.BorderBottom = y + 10;
+                    musicSystem.PositionAndShape.BorderBottom = y + 0;
                     musicSystem.PositionAndShape.RelativePosition.y = globalY;
-                    globalY += y + 10;
+                    globalY += y + 0;
                 }
             }
         }

+ 13 - 5
dist/src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.d.ts

@@ -1,17 +1,25 @@
 import { MusicSheetDrawer } from "../MusicSheetDrawer";
 import { RectangleF2D } from "../../../Common/DataObjects/RectangleF2D";
 import { VexFlowMeasure } from "./VexFlowMeasure";
-import { ITextMeasurer } from "../../Interfaces/ITextMeasurer";
 import { PointF2D } from "../../../Common/DataObjects/PointF2D";
 import { GraphicalLabel } from "../GraphicalLabel";
-/**
- * Created by Matthias on 22.06.2016.
- */
 export declare class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     private renderer;
+    private vfctx;
     private ctx;
-    constructor(canvas: HTMLCanvasElement, textMeasurer: ITextMeasurer, isPreviewImageDrawer?: boolean);
+    private titles;
+    private zoom;
+    constructor(titles: HTMLElement, canvas: HTMLCanvasElement, isPreviewImageDrawer?: boolean);
+    /**
+     * Zoom the rendering areas
+     * @param k is the zoom factor
+     */
     scale(k: number): void;
+    /**
+     * Resize the rendering areas
+     * @param x
+     * @param y
+     */
     resize(x: number, y: number): void;
     translate(x: number, y: number): void;
     /**

+ 49 - 21
dist/src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.js

@@ -8,26 +8,40 @@ var Vex = require("vexflow");
 var MusicSheetDrawer_1 = require("../MusicSheetDrawer");
 var RectangleF2D_1 = require("../../../Common/DataObjects/RectangleF2D");
 var PointF2D_1 = require("../../../Common/DataObjects/PointF2D");
-/**
- * Created by Matthias on 22.06.2016.
- */
+var VexFlowConverter_1 = require("./VexFlowConverter");
+var VexFlowTextMeasurer_1 = require("./VexFlowTextMeasurer");
 var VexFlowMusicSheetDrawer = (function (_super) {
     __extends(VexFlowMusicSheetDrawer, _super);
-    function VexFlowMusicSheetDrawer(canvas, textMeasurer, isPreviewImageDrawer) {
+    function VexFlowMusicSheetDrawer(titles, canvas, isPreviewImageDrawer) {
         if (isPreviewImageDrawer === void 0) { isPreviewImageDrawer = false; }
-        _super.call(this, textMeasurer, isPreviewImageDrawer);
+        _super.call(this, new VexFlowTextMeasurer_1.VexFlowTextMeasurer(), isPreviewImageDrawer);
+        this.zoom = 1.0;
         this.renderer = new Vex.Flow.Renderer(canvas, Vex.Flow.Renderer.Backends.CANVAS);
-        this.ctx = this.renderer.getContext();
+        this.vfctx = this.renderer.getContext();
+        // The following is a hack to retrieve the actual canvas' drawing context
+        // Not supposed to work forever....
+        this.ctx = this.vfctx.vexFlowCanvasContext;
+        this.titles = titles;
     }
+    /**
+     * Zoom the rendering areas
+     * @param k is the zoom factor
+     */
     VexFlowMusicSheetDrawer.prototype.scale = function (k) {
-        this.ctx.scale(k, k);
+        this.zoom = k;
+        this.vfctx.scale(k, k);
     };
+    /**
+     * Resize the rendering areas
+     * @param x
+     * @param y
+     */
     VexFlowMusicSheetDrawer.prototype.resize = function (x, y) {
         this.renderer.resize(x, y);
     };
     VexFlowMusicSheetDrawer.prototype.translate = function (x, y) {
-        // FIXME
-        this.ctx.vexFlowCanvasContext.translate(x, y);
+        // Translation seems not supported by VexFlow
+        this.ctx.translate(x, y);
     };
     /**
      * Converts a distance from unit to pixel space.
@@ -35,12 +49,11 @@ var VexFlowMusicSheetDrawer = (function (_super) {
      * @returns {number} the distance in pixels
      */
     VexFlowMusicSheetDrawer.prototype.calculatePixelDistance = function (unitDistance) {
-        // ToDo: implement!
         return unitDistance * 10.0;
     };
     VexFlowMusicSheetDrawer.prototype.drawMeasure = function (measure) {
-        measure.setAbsoluteCoordinates(measure.PositionAndShape.AbsolutePosition.x * measure.unit, measure.PositionAndShape.AbsolutePosition.y * measure.unit);
-        return measure.draw(this.ctx);
+        measure.setAbsoluteCoordinates(measure.PositionAndShape.AbsolutePosition.x * 10.0, measure.PositionAndShape.AbsolutePosition.y * 10.0);
+        return measure.draw(this.vfctx);
     };
     /**
      * Renders a Label to the screen (e.g. Title, composer..)
@@ -52,11 +65,25 @@ var VexFlowMusicSheetDrawer = (function (_super) {
      * @param screenPosition the position of the lower left corner of the text in screen coordinates
      */
     VexFlowMusicSheetDrawer.prototype.renderLabel = function (graphicalLabel, layer, bitmapWidth, bitmapHeight, heightInPixel, screenPosition) {
-        // ToDo: implement!
-        var ctx = this.ctx.vexFlowCanvasContext;
-        ctx.font = Math.floor(graphicalLabel.Label.fontHeight * 10) + "px 'Times New Roman'";
-        console.log(graphicalLabel.Label.text, screenPosition.x, screenPosition.y);
-        ctx.fillText(graphicalLabel.Label.text, screenPosition.x, screenPosition.y);
+        if (screenPosition.y < 0) {
+            // Temportary solution for title labels
+            var div = document.createElement("div");
+            div.style.fontSize = (graphicalLabel.Label.fontHeight * this.zoom * 10.0) + "px";
+            //span.style.width = (bitmapWidth * this.zoom * 1.1) + "px";
+            //span.style.height = (bitmapHeight * this.zoom * 1.1) + "px";
+            //span.style.overflow = "hidden";
+            div.style.fontFamily = "Times New Roman";
+            //span.style.marginLeft = (screenPosition.x * this.zoom) + "px";
+            div.style.textAlign = "center";
+            div.appendChild(document.createTextNode(graphicalLabel.Label.text));
+            this.titles.appendChild(div);
+            return;
+        }
+        var ctx = this.vfctx.vexFlowCanvasContext;
+        var old = ctx.font;
+        ctx.font = VexFlowConverter_1.VexFlowConverter.font(graphicalLabel.Label.fontHeight * 10.0, graphicalLabel.Label.fontStyle, graphicalLabel.Label.font);
+        ctx.fillText(graphicalLabel.Label.text, screenPosition.x, screenPosition.y + heightInPixel);
+        ctx.font = old;
     };
     /**
      * Renders a rectangle with the given style to the screen.
@@ -66,7 +93,10 @@ var VexFlowMusicSheetDrawer = (function (_super) {
      * @param styleId the style id
      */
     VexFlowMusicSheetDrawer.prototype.renderRectangle = function (rectangle, layer, styleId) {
-        // ToDo: implement!
+        var old = this.ctx.fillStyle;
+        this.ctx.fillStyle = VexFlowConverter_1.VexFlowConverter.style(styleId);
+        this.ctx.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+        this.ctx.fillStyle = old;
     };
     /**
      * Converts a point from unit to pixel space.
@@ -74,7 +104,6 @@ var VexFlowMusicSheetDrawer = (function (_super) {
      * @returns {PointF2D}
      */
     VexFlowMusicSheetDrawer.prototype.applyScreenTransformation = function (point) {
-        // ToDo: implement!
         return new PointF2D_1.PointF2D(point.x * 10.0, point.y * 10.0);
     };
     /**
@@ -83,8 +112,7 @@ var VexFlowMusicSheetDrawer = (function (_super) {
      * @returns {RectangleF2D}
      */
     VexFlowMusicSheetDrawer.prototype.applyScreenTransformationForRect = function (rectangle) {
-        // FIXME Check if correct
-        return new RectangleF2D_1.RectangleF2D(rectangle.x * 10, rectangle.y * 10, rectangle.width * 10, rectangle.height * 10);
+        return new RectangleF2D_1.RectangleF2D(rectangle.x * 10.0, rectangle.y * 10.0, rectangle.width * 10.0, rectangle.height * 10.0);
     };
     return VexFlowMusicSheetDrawer;
 }(MusicSheetDrawer_1.MusicSheetDrawer));

+ 5 - 0
dist/src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.d.ts

@@ -10,4 +10,9 @@ export declare class VexFlowStaffEntry extends GraphicalStaffEntry {
     vfNotes: {
         [voiceID: number]: Vex.Flow.StaveNote;
     };
+    /**
+     *
+     * @returns {number} the x-position (in units) of this Staff Entry
+     */
+    getX(): number;
 }

+ 16 - 0
dist/src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.js

@@ -14,6 +14,22 @@ var VexFlowStaffEntry = (function (_super) {
         // The corresponding VexFlow.StaveNotes
         this.vfNotes = {};
     }
+    /**
+     *
+     * @returns {number} the x-position (in units) of this Staff Entry
+     */
+    VexFlowStaffEntry.prototype.getX = function () {
+        var x = 0;
+        var n = 0;
+        var vfNotes = this.vfNotes;
+        for (var voiceId in vfNotes) {
+            if (vfNotes.hasOwnProperty(voiceId)) {
+                x += (vfNotes[voiceId].getNoteHeadBeginX() + vfNotes[voiceId].getNoteHeadEndX()) / 2;
+                n += 1;
+            }
+        }
+        return x / n / 10.0;
+    };
     return VexFlowStaffEntry;
 }(GraphicalStaffEntry_1.GraphicalStaffEntry));
 exports.VexFlowStaffEntry = VexFlowStaffEntry;

+ 3 - 3
dist/src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer.js

@@ -1,4 +1,5 @@
 "use strict";
+var VexFlowConverter_1 = require("./VexFlowConverter");
 /**
  * Created by Matthias on 21.06.2016.
  */
@@ -6,11 +7,10 @@ var VexFlowTextMeasurer = (function () {
     function VexFlowTextMeasurer() {
         var canvas = document.createElement("canvas");
         this.context = canvas.getContext("2d");
-        this.context.font = "20px 'Times New Roman'";
     }
     VexFlowTextMeasurer.prototype.computeTextWidthToHeightRatio = function (text, font, style) {
-        var size = this.context.measureText(text);
-        return size.width / 20;
+        this.context.font = VexFlowConverter_1.VexFlowConverter.font(20, style, font);
+        return this.context.measureText(text).width / 20;
     };
     return VexFlowTextMeasurer;
 }());

+ 2 - 1
dist/src/MusicalScore/MusicParts/MusicPartManagerIterator.js

@@ -3,6 +3,7 @@ var fraction_1 = require("../../Common/DataObjects/fraction");
 var DynamicsContainer_1 = require("../VoiceData/HelperObjects/DynamicsContainer");
 var RhythmInstruction_1 = require("../VoiceData/Instructions/RhythmInstruction");
 var continuousDynamicExpression_1 = require("../VoiceData/Expressions/ContinuousExpressions/continuousDynamicExpression");
+var Logging_1 = require("../../Common/Logging");
 var MusicPartManagerIterator = (function () {
     function MusicPartManagerIterator(manager, startTimestamp, endTimestamp) {
         this.currentMeasureIndex = 0;
@@ -51,7 +52,7 @@ var MusicPartManagerIterator = (function () {
             this.currentTempoChangingExpression = this.activeTempoExpression;
         }
         catch (err) {
-            console.log("MusicPartManagerIterator: Exception." + err); // FIXME
+            Logging_1.Logging.log("MusicPartManagerIterator: " + err);
         }
     }
     Object.defineProperty(MusicPartManagerIterator.prototype, "EndReached", {

+ 6 - 3
dist/src/MusicalScore/MusicSheet.js

@@ -6,6 +6,7 @@ var Voice_1 = require("./VoiceData/Voice");
 var MusicSheetErrors_1 = require("../Common/DataObjects/MusicSheetErrors");
 var EngravingRules_1 = require("./Graphical/EngravingRules");
 var DrawingEnums_1 = require("./Graphical/DrawingEnums");
+var Logging_1 = require("../Common/Logging");
 // FIXME
 //type MusicSheetParameters = any;
 //type MultiTempoExpression = any;
@@ -46,7 +47,9 @@ var MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet>*/ = (fu
         this.hasBeenOpenedForTheFirstTime = false;
         this.currentEnrolledPosition = new fraction_1.Fraction(0, 1);
         this.rules = EngravingRules_1.EngravingRules.Rules;
-        // (*) this.playbackSettings = new PlaybackSettings(new Fraction(4, 4, false), 100);
+        this.playbackSettings = new PlaybackSettings();
+        // FIXME:
+        this.playbackSettings.rhythm = new fraction_1.Fraction(4, 4, false);
         this.userStartTempoInBPM = 100;
         this.pageWidth = 120;
         this.MusicPartManager = new MusicPartManager_1.MusicPartManager(this);
@@ -472,7 +475,7 @@ var MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet>*/ = (fu
                 return this.getFirstSourceMeasure().MeasureNumber;
             }
             catch (ex) {
-                console.log(/* FIXME LogLevel.NORMAL, */ "MusicSheet.FirstMeasureNumber: ", ex);
+                Logging_1.Logging.log("MusicSheet.FirstMeasureNumber: ", ex);
                 return 0;
             }
         },
@@ -485,7 +488,7 @@ var MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet>*/ = (fu
                 return this.getLastSourceMeasure().MeasureNumber;
             }
             catch (ex) {
-                console.log(/* FIXME LogLevel.NORMAL, */ "MusicSheet.LastMeasureNumber: ", ex);
+                Logging_1.Logging.log("MusicSheet.LastMeasureNumber: ", ex);
                 return 0;
             }
         },

+ 2 - 1
dist/src/MusicalScore/MusicSource/Repetition.js

@@ -7,6 +7,7 @@ var __extends = (this && this.__extends) || function (d, b) {
 var SourceMusicPart_1 = require("./SourceMusicPart");
 var fraction_1 = require("../../Common/DataObjects/fraction");
 var PartListEntry_1 = require("./PartListEntry");
+var Logging_1 = require("../../Common/Logging");
 var Repetition = (function (_super) {
     __extends(Repetition, _super);
     function Repetition(musicSheet, virtualOverallRepetition) {
@@ -107,7 +108,7 @@ var Repetition = (function (_super) {
                 }
             }
             catch (err) {
-                console.log("Repetition: Exception."); // FIXME
+                Logging_1.Logging.error("Repetition: Exception.", err);
             }
         }
     };

+ 11 - 11
dist/src/MusicalScore/ScoreIO/InstrumentReader.js

@@ -10,7 +10,7 @@ var Exceptions_1 = require("../Exceptions");
 var ClefInstruction_2 = require("../VoiceData/Instructions/ClefInstruction");
 var RhythmInstruction_2 = require("../VoiceData/Instructions/RhythmInstruction");
 var KeyInstruction_2 = require("../VoiceData/Instructions/KeyInstruction");
-var logging_1 = require("../../Common/logging");
+var Logging_1 = require("../../Common/Logging");
 var ClefInstruction_3 = require("../VoiceData/Instructions/ClefInstruction");
 var InstrumentReader = (function () {
     function InstrumentReader(repetitionInstructionReader, xmlMeasureList, instrument) {
@@ -83,7 +83,7 @@ var InstrumentReader = (function () {
                         if (xmlNode.element("staff") !== undefined) {
                             noteStaff = parseInt(xmlNode.element("staff").value, 10);
                             if (isNaN(noteStaff)) {
-                                logging_1.Logging.debug("InstrumentReader.readNextXmlMeasure.get staff number");
+                                Logging_1.Logging.debug("InstrumentReader.readNextXmlMeasure.get staff number");
                                 noteStaff = 1;
                             }
                         }
@@ -117,7 +117,7 @@ var InstrumentReader = (function () {
                         else {
                             var errorMsg = ITextTranslation_1.ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError", "Invalid Note Duration.");
                             this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                            logging_1.Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
+                            Logging_1.Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
                             continue;
                         }
                     }
@@ -169,7 +169,7 @@ var InstrumentReader = (function () {
                         this.divisions = parseInt(divisionsNode.value, 10);
                         if (isNaN(this.divisions)) {
                             var errorMsg = ITextTranslation_1.ITextTranslation.translateText("ReaderErrorMessages/DivisionError", "Invalid divisions value at Instrument: ");
-                            logging_1.Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
+                            Logging_1.Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
                             this.divisions = this.readDivisionsFromNotes();
                             if (this.divisions > 0) {
                                 this.musicSheet.SheetErrors.push(errorMsg + this.instrument.Name);
@@ -256,7 +256,7 @@ var InstrumentReader = (function () {
             }
             var errorMsg = ITextTranslation_1.ITextTranslation.translateText("ReaderErrorMessages/MeasureError", "Error while reading Measure.");
             this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-            logging_1.Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg, e);
+            Logging_1.Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg, e);
         }
         this.previousMeasure = this.currentMeasure;
         this.currentXmlMeasureIndex += 1;
@@ -424,7 +424,7 @@ var InstrumentReader = (function () {
                         errorMsg = ITextTranslation_1.ITextTranslation.translateText("ReaderErrorMessages/ClefLineError", "Invalid clef line given -> using default clef line.");
                         this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                         line = 2;
-                        logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+                        Logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
                     }
                 }
                 var signNode = nodeList.element("sign");
@@ -446,7 +446,7 @@ var InstrumentReader = (function () {
                         this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                         clefEnum = ClefInstruction_2.ClefEnum.G;
                         line = 2;
-                        logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, e);
+                        Logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, e);
                     }
                 }
                 var clefOctaveNode = nodeList.element("clef-octave-change");
@@ -485,7 +485,7 @@ var InstrumentReader = (function () {
                     errorMsg = ITextTranslation_1.ITextTranslation.translateText("ReaderErrorMessages/KeyError", "Invalid key found -> set to default.");
                     this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                     key = 0;
-                    logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+                    Logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
                 }
             }
             var keyEnum = KeyInstruction_2.KeyEnum.none;
@@ -501,7 +501,7 @@ var InstrumentReader = (function () {
                     errorMsg = ITextTranslation_1.ITextTranslation.translateText("ReaderErrorMessages/KeyError", "Invalid key found -> set to default.");
                     this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                     keyEnum = KeyInstruction_2.KeyEnum.major;
-                    logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+                    Logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
                 }
             }
             var keyInstruction = new KeyInstruction_1.KeyInstruction(undefined, key, keyEnum);
@@ -575,7 +575,7 @@ var InstrumentReader = (function () {
                     this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                     num = 4;
                     denom = 4;
-                    logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+                    Logging_1.Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
                 }
                 if ((num === 4 && denom === 4) || (num === 2 && denom === 2)) {
                     symbolEnum = RhythmInstruction_2.RhythmSymbolEnum.NONE;
@@ -818,7 +818,7 @@ var InstrumentReader = (function () {
                                 noteDuration = parseInt(durationNode.value, 10);
                             }
                             catch (ex) {
-                                logging_1.Logging.debug("InstrumentReader.readDivisionsFromNotes", ex);
+                                Logging_1.Logging.debug("InstrumentReader.readDivisionsFromNotes", ex);
                                 continue;
                             }
                             switch (type) {

+ 15 - 24
dist/src/MusicalScore/ScoreIO/MusicSheetReader.js

@@ -6,7 +6,7 @@ var InstrumentReader_1 = require("./InstrumentReader");
 var Instrument_1 = require("../Instrument");
 var ITextTranslation_1 = require("../Interfaces/ITextTranslation");
 var Exceptions_1 = require("../Exceptions");
-var logging_1 = require("../../Common/logging");
+var Logging_1 = require("../../Common/Logging");
 var RhythmInstruction_1 = require("../VoiceData/Instructions/RhythmInstruction");
 var RhythmInstruction_2 = require("../VoiceData/Instructions/RhythmInstruction");
 var SourceStaffEntry_1 = require("../VoiceData/SourceStaffEntry");
@@ -36,7 +36,7 @@ var MusicSheetReader /*implements IMusicSheetReader*/ = (function () {
             return this._createMusicSheet(root, path);
         }
         catch (e) {
-            logging_1.Logging.log("MusicSheetReader.CreateMusicSheet", e);
+            Logging_1.Logging.log("MusicSheetReader.CreateMusicSheet", e);
         }
     };
     MusicSheetReader /*implements IMusicSheetReader*/.prototype._removeFromArray = function (list, elem) {
@@ -92,11 +92,8 @@ var MusicSheetReader /*implements IMusicSheetReader*/ = (function () {
             throw new Exceptions_1.MusicSheetReadingException("Undefined partListNode");
         }
         var partInst = root.elements("part");
-        console.log(partInst.length + " parts");
         var partList = partlistNode.elements();
-        //Logging.debug("Starting initializeReading");
         this.initializeReading(partList, partInst, instrumentReaders);
-        //Logging.debug("Done initializeReading");
         var couldReadMeasure = true;
         this.currentFraction = new fraction_1.Fraction(0, 1);
         var guitarPro = false;
@@ -126,17 +123,11 @@ var MusicSheetReader /*implements IMusicSheetReader*/ = (function () {
                 }
             }
             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);
@@ -276,19 +267,19 @@ var MusicSheetReader /*implements IMusicSheetReader*/ = (function () {
     MusicSheetReader /*implements IMusicSheetReader*/.prototype.setSourceMeasureDuration = function (instrumentReaders, sourceMeasureCounter) {
         var activeRhythm = new fraction_1.Fraction(0, 1);
         var instrumentsMaxTieNoteFractions = [];
-        for (var idx = 0, len = instrumentReaders.length; idx < len; ++idx) {
-            var instrumentReader = instrumentReaders[idx];
+        for (var _i = 0, instrumentReaders_3 = instrumentReaders; _i < instrumentReaders_3.length; _i++) {
+            var instrumentReader = instrumentReaders_3[_i];
             instrumentsMaxTieNoteFractions.push(instrumentReader.MaxTieNoteFraction);
             var activeRythmMeasure = instrumentReader.ActiveRhythm.Rhythm;
-            if (activeRhythm < activeRythmMeasure) {
+            if (activeRhythm.lt(activeRythmMeasure)) {
                 activeRhythm = new fraction_1.Fraction(activeRythmMeasure.Numerator, activeRythmMeasure.Denominator, false);
             }
         }
         var instrumentsDurations = this.currentMeasure.calculateInstrumentsDuration(this.musicSheet, instrumentsMaxTieNoteFractions);
         var maxInstrumentDuration = new fraction_1.Fraction(0, 1);
-        for (var idx = 0, len = instrumentsDurations.length; idx < len; ++idx) {
-            var instrumentsDuration = instrumentsDurations[idx];
-            if (maxInstrumentDuration < instrumentsDuration) {
+        for (var _a = 0, instrumentsDurations_1 = instrumentsDurations; _a < instrumentsDurations_1.length; _a++) {
+            var instrumentsDuration = instrumentsDurations_1[_a];
+            if (maxInstrumentDuration.lt(instrumentsDuration)) {
                 maxInstrumentDuration = instrumentsDuration;
             }
         }
@@ -296,7 +287,7 @@ var MusicSheetReader /*implements IMusicSheetReader*/ = (function () {
             this.checkFractionsForEquivalence(maxInstrumentDuration, activeRhythm);
         }
         else {
-            if (maxInstrumentDuration < activeRhythm) {
+            if (maxInstrumentDuration.lt(activeRhythm)) {
                 maxInstrumentDuration = this.currentMeasure.reverseCheck(this.musicSheet, maxInstrumentDuration);
                 this.checkFractionsForEquivalence(maxInstrumentDuration, activeRhythm);
             }
@@ -310,7 +301,7 @@ var MusicSheetReader /*implements IMusicSheetReader*/ = (function () {
         for (var i = 0; i < instrumentsDurations.length; i++) {
             var instrumentsDuration = instrumentsDurations[i];
             if ((this.currentMeasure.ImplicitMeasure && instrumentsDuration !== maxInstrumentDuration) ||
-                instrumentsDuration !== activeRhythm &&
+                !fraction_1.Fraction.Equal(instrumentsDuration, activeRhythm) &&
                     !this.allInstrumentsHaveSameDuration(instrumentsDurations, maxInstrumentDuration)) {
                 var firstStaffIndexOfInstrument = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.musicSheet.Instruments[i]);
                 for (var staffIndex = 0; staffIndex < this.musicSheet.Instruments[i].Staves.length; staffIndex++) {
@@ -402,7 +393,7 @@ var MusicSheetReader /*implements IMusicSheetReader*/ = (function () {
                 this.musicSheet.Title = new Label_1.Label(filenameSplits[0]);
             }
             catch (ex) {
-                logging_1.Logging.log("MusicSheetReader.pushSheetLabels: ", ex);
+                Logging_1.Logging.log("MusicSheetReader.pushSheetLabels: ", ex);
             }
         }
     };
@@ -648,7 +639,7 @@ var MusicSheetReader /*implements IMusicSheetReader*/ = (function () {
                                                 subInstrument.volume = result / 127.0;
                                             }
                                             catch (ex) {
-                                                logging_1.Logging.debug("ExpressionReader.readExpressionParameters", "read volume", ex);
+                                                Logging_1.Logging.debug("ExpressionReader.readExpressionParameters", "read volume", ex);
                                             }
                                         }
                                         else if (instrumentElement.name === "pan") {
@@ -657,18 +648,18 @@ var MusicSheetReader /*implements IMusicSheetReader*/ = (function () {
                                                 subInstrument.pan = result / 64.0;
                                             }
                                             catch (ex) {
-                                                logging_1.Logging.debug("ExpressionReader.readExpressionParameters", "read pan", ex);
+                                                Logging_1.Logging.debug("ExpressionReader.readExpressionParameters", "read pan", ex);
                                             }
                                         }
                                     }
                                     catch (ex) {
-                                        logging_1.Logging.log("MusicSheetReader.createInstrumentGroups midi settings: ", ex);
+                                        Logging_1.Logging.log("MusicSheetReader.createInstrumentGroups midi settings: ", ex);
                                     }
                                 }
                             }
                         }
                         catch (ex) {
-                            logging_1.Logging.log("MusicSheetReader.createInstrumentGroups: ", ex);
+                            Logging_1.Logging.log("MusicSheetReader.createInstrumentGroups: ", ex);
                         }
                     }
                     if (instrument.SubInstruments.length === 0) {

+ 3 - 3
dist/src/MusicalScore/ScoreIO/VoiceGenerator.js

@@ -14,7 +14,7 @@ var Exceptions_1 = require("../Exceptions");
 var pitch_1 = require("../../Common/DataObjects/pitch");
 var pitch_2 = require("../../Common/DataObjects/pitch");
 var StaffEntryLink_1 = require("../VoiceData/StaffEntryLink");
-var logging_1 = require("../../Common/logging");
+var Logging_1 = require("../../Common/Logging");
 var pitch_3 = require("../../Common/DataObjects/pitch");
 var collectionUtil_1 = require("../../Util/collectionUtil");
 var VoiceGenerator = (function () {
@@ -275,7 +275,7 @@ var VoiceGenerator = (function () {
                             }
                         }
                         catch (ex) {
-                            logging_1.Logging.log("VoiceGenerator.addSingleNote read Step: ", ex.message);
+                            Logging_1.Logging.log("VoiceGenerator.addSingleNote read Step: ", ex.message);
                         }
                     }
                 }
@@ -299,7 +299,7 @@ var VoiceGenerator = (function () {
                 }
             }
             catch (ex) {
-                logging_1.Logging.log("VoiceGenerator.addSingleNote: ", ex);
+                Logging_1.Logging.log("VoiceGenerator.addSingleNote: ", ex);
             }
         }
         noteOctave -= pitch_3.Pitch.OctaveXmlDifference;

+ 2 - 1
dist/src/MusicalScore/SubInstrument.js

@@ -1,5 +1,6 @@
 "use strict";
 var ClefInstruction_1 = require("./VoiceData/Instructions/ClefInstruction");
+var Logging_1 = require("../Common/Logging");
 var SubInstrument = (function () {
     function SubInstrument(parentInstrument) {
         this.parentInstrument = parentInstrument;
@@ -46,7 +47,7 @@ var SubInstrument = (function () {
             }
         }
         catch (e) {
-            console.log("Error parsing MIDI Instrument. Default to Grand Piano."); // FIXME
+            Logging_1.Logging.error("Error parsing MIDI Instrument. Default to Grand Piano.");
         }
         return "unnamed";
     };

+ 2 - 2
dist/src/MusicalScore/VoiceData/Expressions/instantaniousDynamicExpression.js

@@ -8,7 +8,7 @@ var abstractExpression_1 = require("./abstractExpression");
 var dynamicExpressionSymbolEnum_1 = require("./dynamicExpressionSymbolEnum");
 //import {ArgumentOutOfRangeException} from "../../Exceptions";
 var Exceptions_1 = require("../../Exceptions");
-var logging_1 = require("../../../Common/logging");
+var Logging_1 = require("../../../Common/Logging");
 var InstantaniousDynamicExpression = (function (_super) {
     __extends(InstantaniousDynamicExpression, _super);
     function InstantaniousDynamicExpression(dynamicExpression, soundDynamics, placement, staffNumber) {
@@ -137,7 +137,7 @@ var InstantaniousDynamicExpression = (function (_super) {
         //    length += FontInfo.Info.getBoundingBox(symbol).Width;
         //}
         //return length;
-        logging_1.Logging.debug("[Andrea] instantaniousDynamicExpression: not implemented: calculateLength!");
+        Logging_1.Logging.debug("[Andrea] instantaniousDynamicExpression: not implemented: calculateLength!");
         return 0.0;
     };
     InstantaniousDynamicExpression.dynamicToRelativeVolumeDict = {

+ 5 - 3
dist/src/MusicalScore/VoiceData/SourceMeasure.js

@@ -391,12 +391,14 @@ var SourceMeasure = (function () {
         return undefined;
     };
     SourceMeasure.prototype.getLastSourceStaffEntryForInstrument = function (instrumentIndex) {
+        var entry;
         for (var i = 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;
     };
     return SourceMeasure;
 }());

+ 28 - 0
dist/src/OSMD/Cursor.d.ts

@@ -0,0 +1,28 @@
+import { MusicPartManager } from "../MusicalScore/MusicParts/MusicPartManager";
+import { OSMD } from "./OSMD";
+import { GraphicalMusicSheet } from "../MusicalScore/Graphical/GraphicalMusicSheet";
+export declare class Cursor {
+    constructor(container: HTMLElement, osmd: OSMD);
+    private container;
+    private osmd;
+    private iterator;
+    private graphic;
+    private hidden;
+    private cursorElement;
+    init(manager: MusicPartManager, graphic: GraphicalMusicSheet): void;
+    show(): void;
+    update(): void;
+    /**
+     * Hide the cursor
+     */
+    hide(): void;
+    /**
+     * Go to next entry
+     */
+    next(): void;
+    /**
+     * Go to previous entry. Not implemented.
+     */
+    prev(): void;
+    private updateStyle(width, color?);
+}

+ 118 - 0
dist/src/OSMD/Cursor.js

@@ -0,0 +1,118 @@
+"use strict";
+var Cursor = (function () {
+    function Cursor(container, osmd) {
+        this.hidden = true;
+        this.container = container;
+        this.osmd = osmd;
+        var curs = document.createElement("img");
+        curs.style.position = "absolute";
+        curs.style.zIndex = "-1";
+        this.cursorElement = curs;
+        container.appendChild(curs);
+    }
+    Cursor.prototype.init = function (manager, graphic) {
+        this.iterator = manager.getIterator();
+        this.graphic = graphic;
+        this.hidden = true;
+        this.hide();
+    };
+    Cursor.prototype.show = function () {
+        this.hidden = false;
+        this.update();
+        // Forcing the sheet to re-render is not necessary anymore
+        //this.osmd.render();
+    };
+    Cursor.prototype.update = function () {
+        // Should NEVER call this.osmd.render()
+        if (this.hidden) {
+            return;
+        }
+        this.graphic.Cursors.length = 0;
+        var iterator = this.iterator;
+        if (iterator.EndReached || iterator.CurrentVoiceEntries === undefined) {
+            return;
+        }
+        var x = 0, y = 0, height = 0;
+        for (var idx = 0, len = iterator.CurrentVoiceEntries.length; idx < len; ++idx) {
+            var voiceEntry = iterator.CurrentVoiceEntries[idx];
+            var measureIndex = voiceEntry.ParentSourceStaffEntry.VerticalContainerParent.ParentMeasure.MeasureNumber;
+            var staffIndex = voiceEntry.ParentSourceStaffEntry.ParentStaff.idInMusicSheet;
+            var gse = this.graphic.findGraphicalStaffEntryFromMeasureList(staffIndex, measureIndex, voiceEntry.ParentSourceStaffEntry);
+            if (idx === 0) {
+                x = gse.getX();
+                var musicSystem = gse.parentMeasure.parentMusicSystem;
+                y = musicSystem.PositionAndShape.AbsolutePosition.y + musicSystem.StaffLines[0].PositionAndShape.RelativePosition.y;
+                var endY = musicSystem.PositionAndShape.AbsolutePosition.y +
+                    musicSystem.StaffLines[musicSystem.StaffLines.length - 1].PositionAndShape.RelativePosition.y + 4.0;
+                height = endY - y;
+            }
+        }
+        // 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:
+        var cursorElement = this.cursorElement;
+        cursorElement.style.top = (y * 10.0 * this.osmd.zoom) + "px";
+        cursorElement.style.left = ((x - 1.5) * 10.0 * this.osmd.zoom) + "px";
+        cursorElement.height = (height * 10.0 * this.osmd.zoom);
+        var newWidth = 3 * 10.0 * this.osmd.zoom;
+        if (newWidth !== cursorElement.width) {
+            cursorElement.width = newWidth;
+            this.updateStyle(newWidth);
+        }
+        // Show cursors
+        // // Old cursor: this.graphic.Cursors.push(cursor);
+        this.cursorElement.style.display = "";
+    };
+    /**
+     * Hide the cursor
+     */
+    Cursor.prototype.hide = function () {
+        // 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
+     */
+    Cursor.prototype.next = function () {
+        this.iterator.moveToNext();
+        if (!this.hidden) {
+            this.show();
+        }
+    };
+    /**
+     * Go to previous entry. Not implemented.
+     */
+    Cursor.prototype.prev = function () {
+        // TODO
+        // Previous does not seem to be implemented in the MusicPartManager iterator...
+    };
+    Cursor.prototype.updateStyle = function (width, color) {
+        if (color === void 0) { color = "#33e02f"; }
+        // Create a dummy canvas to generate the gradient for the cursor
+        // FIXME This approach needs to be improved
+        var c = document.createElement("canvas");
+        c.width = this.cursorElement.width;
+        c.height = 1;
+        var ctx = c.getContext("2d");
+        ctx.globalAlpha = 0.5;
+        // Generate the gradient
+        var gradient = ctx.createLinearGradient(0, 0, this.cursorElement.width, 0);
+        gradient.addColorStop(0, "white"); // it was: "transparent"
+        gradient.addColorStop(0.2, color);
+        gradient.addColorStop(0.8, 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");
+    };
+    return Cursor;
+}());
+exports.Cursor = Cursor;

+ 44 - 0
dist/src/OSMD/OSMD.d.ts

@@ -0,0 +1,44 @@
+import { Cursor } from "./Cursor";
+export declare 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
+     * @autoResize automatically resize the sheet to full page width on window resize
+     */
+    constructor(container: string | HTMLElement, autoResize?: boolean);
+    cursor: Cursor;
+    zoom: number;
+    private container;
+    private heading;
+    private canvas;
+    private sheet;
+    private drawer;
+    private graphic;
+    /**
+     * 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
+     */
+    load(content: string | Document): void;
+    /**
+     * Render the music sheet in the container
+     */
+    render(): void;
+    /**
+     *
+     * @param url
+     */
+    private openURL(url);
+    /**
+     * Clear all the titles from the headings element
+     */
+    private resetHeadings();
+    /**
+     * Initialize this object to default values
+     * FIXME: Probably unnecessary
+     */
+    private reset();
+    /**
+     * Attach the appropriate handler to the window.onResize event
+     */
+    private autoResize();
+}

+ 167 - 0
dist/src/OSMD/OSMD.js

@@ -0,0 +1,167 @@
+"use strict";
+var Xml_1 = require("./../Common/FileIO/Xml");
+var VexFlowMusicSheetCalculator_1 = require("./../MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator");
+var MusicSheetReader_1 = require("./../MusicalScore/ScoreIO/MusicSheetReader");
+var GraphicalMusicSheet_1 = require("./../MusicalScore/Graphical/GraphicalMusicSheet");
+var VexFlowMusicSheetDrawer_1 = require("./../MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer");
+var Cursor_1 = require("./Cursor");
+var Mxl_1 = require("../Common/FileIO/Mxl");
+//import {Promise} from "es6-promise";
+var ResizeHandler_1 = require("./ResizeHandler");
+var OSMD = (function () {
+    /**
+     * 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
+     */
+    function OSMD(container, autoResize) {
+        if (autoResize === void 0) { autoResize = false; }
+        this.zoom = 1.0;
+        // Store container element
+        if (typeof container === "string") {
+            // ID passed
+            this.container = document.getElementById(container);
+        }
+        else if (container && "appendChild" in container) {
+            // Element passed
+            this.container = container;
+        }
+        if (!this.container) {
+            throw new Error("Please pass a valid div container to OSMD");
+        }
+        // Create the elements inside the container
+        this.heading = document.createElement("div");
+        this.canvas = document.createElement("canvas");
+        this.canvas.style.zIndex = "0";
+        var inner = 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_1.VexFlowMusicSheetDrawer(this.heading, this.canvas);
+        // Create the cursor
+        this.cursor = new Cursor_1.Cursor(inner, this);
+        if (autoResize) {
+            this.autoResize();
+        }
+    }
+    /**
+     * 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
+     */
+    OSMD.prototype.load = function (content) {
+        // Warning! This function is asynchronous! No error handling is done here.
+        // FIXME TODO Refactor with Promises
+        this.reset();
+        var path = "Unknown path";
+        if (typeof content === "string") {
+            var str = content;
+            if (str.substr(0, 4) === "http") {
+                // Retrieve the file at the url
+                path = str;
+                this.openURL(path);
+                return;
+            }
+            if (str.substr(0, 4) === "\x04\x03\x4b\x50") {
+                // This is a zip file, unpack it first
+                Mxl_1.openMxl(str).then(this.load, function (err) {
+                    throw new Error("OSMD: Invalid MXL file");
+                });
+                return;
+            }
+            if (str.substr(0, 5) === "<?xml") {
+                // Parse the string representing an xml file
+                var parser = new DOMParser();
+                content = parser.parseFromString(str, "text/xml");
+            }
+        }
+        if (!content || !content.nodeName) {
+            throw new Error("OSMD: Document provided is not valid");
+        }
+        var elem = content.getElementsByTagName("score-partwise")[0];
+        if (elem === undefined) {
+            throw new Error("OSMD: Document is not valid partwise MusicXML");
+        }
+        var score = new Xml_1.IXmlElement(elem);
+        var calc = new VexFlowMusicSheetCalculator_1.VexFlowMusicSheetCalculator();
+        var reader = new MusicSheetReader_1.MusicSheetReader();
+        this.sheet = reader.createMusicSheet(score, path);
+        this.graphic = new GraphicalMusicSheet_1.GraphicalMusicSheet(this.sheet, calc);
+        this.cursor.init(this.sheet.MusicPartManager, this.graphic);
+        return; // Promise.resolve();
+    };
+    /**
+     * Render the music sheet in the container
+     */
+    OSMD.prototype.render = function () {
+        this.resetHeadings();
+        if (!this.graphic) {
+            throw new Error("OSMD: Before rendering a music sheet, please load a MusicXML file");
+        }
+        var width = this.container.offsetWidth;
+        if (isNaN(width)) {
+            throw new Error("OSMD: Before rendering a music sheet, please give the container a width");
+        }
+        // Set page width
+        this.sheet.pageWidth = width / this.zoom / 10.0;
+        // Calculate again
+        this.graphic.reCalculate();
+        // Update Sheet Page
+        var height = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * 10.0 * this.zoom;
+        this.drawer.resize(width, height);
+        this.drawer.scale(this.zoom);
+        // Finally, draw
+        this.drawer.drawSheet(this.graphic);
+        // Update the cursor position
+        this.cursor.update();
+    };
+    /**
+     *
+     * @param url
+     */
+    OSMD.prototype.openURL = function (url) {
+        throw new Error("OSMD: Not implemented: Load sheet from URL");
+        //let JSZipUtils: any;
+        //let self: OSMD = this;
+        //JSZipUtils.getBinaryContent(url, function (err, data) {
+        //    if(err) {
+        //        throw err;
+        //    }
+        //    return self.load(data);
+        //});
+    };
+    /**
+     * Clear all the titles from the headings element
+     */
+    OSMD.prototype.resetHeadings = function () {
+        while (this.heading.firstChild) {
+            this.heading.removeChild(this.heading.firstChild);
+        }
+    };
+    /**
+     * Initialize this object to default values
+     * FIXME: Probably unnecessary
+     */
+    OSMD.prototype.reset = function () {
+        this.sheet = undefined;
+        this.graphic = undefined;
+        this.zoom = 1.0;
+        this.resetHeadings();
+    };
+    /**
+     * Attach the appropriate handler to the window.onResize event
+     */
+    OSMD.prototype.autoResize = function () {
+        var self = this;
+        ResizeHandler_1.handleResize(function () {
+            // empty
+        }, function () {
+            var width = Math.max(document.documentElement.clientWidth, document.body.scrollWidth, document.documentElement.scrollWidth, document.body.offsetWidth, document.documentElement.offsetWidth);
+            self.container.style.width = width + "px";
+            self.render();
+        });
+    };
+    return OSMD;
+}());
+exports.OSMD = OSMD;

+ 9 - 0
dist/src/OSMD/ResizeHandler.d.ts

@@ -0,0 +1,9 @@
+/**
+ * Created by acondolu on 15/07/16.
+ */
+/**
+ * Helper function for managing window's onResize events
+ * @param startCallback
+ * @param endCallback
+ */
+export declare function handleResize(startCallback: () => void, endCallback: () => void): void;

+ 43 - 0
dist/src/OSMD/ResizeHandler.js

@@ -0,0 +1,43 @@
+/**
+ * Created by acondolu on 15/07/16.
+ */
+"use strict";
+/**
+ * Helper function for managing window's onResize events
+ * @param startCallback
+ * @param endCallback
+ */
+function handleResize(startCallback, endCallback) {
+    "use strict";
+    var rtime;
+    var timeout = undefined;
+    var delta = 200;
+    function resizeEnd() {
+        //timeout = undefined;
+        window.clearTimeout(timeout);
+        if ((new Date()).getTime() - rtime < delta) {
+            timeout = window.setTimeout(resizeEnd, delta);
+        }
+        else {
+            endCallback();
+        }
+    }
+    function resizeStart() {
+        rtime = (new Date()).getTime();
+        if (!timeout) {
+            startCallback();
+            rtime = (new Date()).getTime();
+            timeout = window.setTimeout(resizeEnd, delta);
+        }
+    }
+    if (window.attachEvent) {
+        // Support IE<9
+        window.attachEvent("onresize", resizeStart);
+    }
+    else {
+        window.addEventListener("resize", resizeStart);
+    }
+    window.setTimeout(startCallback, 0);
+    window.setTimeout(endCallback, 1);
+}
+exports.handleResize = handleResize;

+ 2 - 1
dist/test/Common/DataObjects/fraction_Test.js

@@ -4,6 +4,7 @@
  */
 var fraction_1 = require("../../../src/Common/DataObjects/fraction");
 var Dictionary_1 = require("typescript-collections/dist/lib/Dictionary");
+var Logging_1 = require("../../../src/Common/Logging");
 describe("Fraction Unit Tests:", function () {
     describe("Construct Fraction, check properties", function () {
         var f1 = new fraction_1.Fraction(2, 6);
@@ -51,7 +52,7 @@ describe("Fraction Unit Tests:", function () {
                 var key = keys[i];
                 var value = values[i];
                 //console.log(values[i].toString() + "== " + dict.getValue(key));
-                console.log(values[i].toString() + "== " + dict.getValue(new fraction_1.Fraction(key.Numerator, key.Denominator)));
+                Logging_1.Logging.debug(values[i].toString() + "== " + dict.getValue(new fraction_1.Fraction(key.Numerator, key.Denominator)));
                 // chai.expect(dict.getValue(key)).to.equal(value);
                 chai.expect(dict.getValue(new fraction_1.Fraction(key.Numerator, key.Denominator))).to.equal(value);
             }

+ 0 - 0
dist/test/Common/OSMD/OSMD.d.ts


+ 18 - 0
dist/test/Common/OSMD/OSMD.js

@@ -0,0 +1,18 @@
+"use strict";
+var chai = require("chai");
+var OSMD_1 = require("../../../src/OSMD/OSMD");
+describe("OSMD Main Export", function () {
+    it("no container", function (done) {
+        chai.expect(function () {
+            return new OSMD_1.OSMD(undefined);
+        }).to.throw(/container/);
+        done();
+    });
+    it("container", function (done) {
+        var div = document.createElement("div");
+        chai.expect(function () {
+            return new OSMD_1.OSMD(div);
+        }).to.not.throw(Error);
+        done();
+    });
+});

+ 2 - 3
dist/test/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.js

@@ -4,7 +4,6 @@ var GraphicalMusicSheet_1 = require("../../../../src/MusicalScore/Graphical/Grap
 var MusicSheetReader_1 = require("../../../../src/MusicalScore/ScoreIO/MusicSheetReader");
 var VexFlowMusicSheetCalculator_1 = require("../../../../src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator");
 var TestUtils_1 = require("../../../Util/TestUtils");
-var VexFlowTextMeasurer_1 = require("../../../../src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer");
 var fraction_1 = require("../../../../src/Common/DataObjects/fraction");
 var DrawingEnums_1 = require("../../../../src/MusicalScore/Graphical/DrawingEnums");
 describe("VexFlow Music Sheet Drawer", function () {
@@ -20,12 +19,12 @@ describe("VexFlow Music Sheet Drawer", function () {
         gms.Cursors.push(gms.calculateCursorLineAtTimestamp(new fraction_1.Fraction(), DrawingEnums_1.OutlineAndFillStyleEnum.PlaybackCursor));
         // Create heading in the test page
         var h1 = document.createElement("h1");
-        h1.textContent = "VexFlowMusicSheetDrawer Output";
+        h1.textContent = "VexFlowMusicSheetDrawer Test Output";
         document.body.appendChild(h1);
         // Create the canvas in the document:
         var canvas = document.createElement("canvas");
         document.body.appendChild(canvas);
-        (new VexFlowMusicSheetDrawer_1.VexFlowMusicSheetDrawer(canvas, new VexFlowTextMeasurer_1.VexFlowTextMeasurer())).drawSheet(gms);
+        (new VexFlowMusicSheetDrawer_1.VexFlowMusicSheetDrawer(document.body, canvas)).drawSheet(gms);
         done();
     });
 });

+ 0 - 1
dist/test/MusicalScore/ScoreIO/MusicSheetReader.js

@@ -34,7 +34,6 @@ describe("Music Sheet Reader Tests", function () {
     });
     it("Measures", function (done) {
         chai.expect(sheet.SourceMeasures.length).to.equal(38);
-        console.log("First Measure: ", sheet.SourceMeasures[0]);
         done();
     });
     it("Instruments", function (done) {

+ 4 - 5
external/vexflow/vexflow.d.ts

@@ -3,7 +3,7 @@ declare namespace Vex {
     const RESOLUTION: any;
 
     export class Formatter {
-      constructor();
+      constructor(opts?: any);
 
       public hasMinTotalWidth: boolean;
       public minTotalWidth: number;
@@ -41,17 +41,15 @@ declare namespace Vex {
       public getNoteHeadBeginX(): number;
       public getNoteHeadEndX(): number;
       public addAccidental(index: number, accidental: Accidental): StaveNote;
+      public setStyle(style: any): void;
     }
 
     export class Stave {
       constructor(x: number, y: number, width: number, options: any);
 
-      public x: number;
-      public start_x: number;
-      public end_x: number;
-
       public setX(x: number): Stave;
       public setY(y: number): Stave;
+      public getX(): number;
       public addClef(clefSpec: string, size: any, annotation: any, position: any): void;
       public setEndClef(clefSpec: string, size: any, annotation: any): void;
       public getModifiers(): StaveModifier[];
@@ -60,6 +58,7 @@ declare namespace Vex {
       public setWidth(width: number): Stave;
       public getNoteStartX(): number;
       public getNoteEndX(): number;
+      public setNoteStartX(x: number): Stave;
       public format(): void;
       public getSpacingBetweenLines(): number;
       public getNumLines(): number;

+ 4 - 0
karma.conf.js

@@ -57,6 +57,10 @@ module.exports = function (config) {
         // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
         logLevel: config.LOG_INFO,
 
+        client: {
+            captureConsole: true
+        },
+
         // enable / disable watching file and executing tests whenever any file changes
         autoWatch: false,
 

+ 3 - 3
package.json

@@ -1,9 +1,9 @@
 {
   "name": "opensheetmusicdisplay",
-  "version": "0.0.1-alpha.0",
+  "version": "0.0.1-alpha.1",
   "description": "An open source JavaScript engine for displaying MusicXML based on VexFlow.",
-  "main": "dist/src/MusicSheetApi.js",
-  "typings": "dist/src/MusicSheetApi",
+  "main": "dist/src/OSMD/OSMD.js",
+  "typings": "dist/src/OSMD/OSMD",
   "scripts": {
     "test": "grunt npmtest",
     "postinstall": "typings install",

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

@@ -14,8 +14,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) => {
@@ -53,3 +52,17 @@ export function extractSheetFromMxl(data: string): Promise<any> {
     }
   );
 }
+
+export function openMxl(data: string): Promise<any> {
+    "use strict";
+    let zip: JSZip.JSZip = new JSZip();
+    // asynchronously load zip file and process it - with Promises
+    return zip.loadAsync(data).then(
+        (_: any) => {
+            return zip.file("META-INF/container.xml").async("string");
+        },
+        (err: any) => {
+            throw err;
+        }
+    );
+}

+ 16 - 0
src/Common/Logging.ts

@@ -0,0 +1,16 @@
+/* tslint:disable:no-console */
+
+export class Logging {
+    public static debug(...args: any[]): void {
+        console.debug("[OSMD] ", args.join(" "));
+    }
+    public static log(...args: any[]): void {
+        console.log("[OSMD] ", args.join(" "));
+    }
+    public static error(...args: any[]): void {
+        console.error("[OSMD] ", args.join(" "));
+    }
+    public static warn(...args: any[]): void {
+        console.warn("[OSMD] ", args.join(" "));
+    }
+}

+ 0 - 13
src/Common/logging.ts

@@ -1,13 +0,0 @@
-/**
- * Created by acondolu on 26/04/16.
- */
-
-
-export class Logging {
-    public static debug(...args: any[]): void {
-        console.log("[OSMD] DEBUG: ", args.join(" "));
-    }
-    public static log(...args: any[]): void {
-        console.log("[OSMD] ", args.join(" "));
-    }
-}

+ 0 - 110
src/MusicSheetAPI.ts

@@ -1,110 +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 {VexFlowTextMeasurer} from "./MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer";
-
-export class MusicSheetAPI {
-    constructor() {
-        this.free();
-    }
-
-    private canvas: HTMLCanvasElement;
-    private sheet: MusicSheet;
-    private drawer: VexFlowMusicSheetDrawer;
-    private graphic: GraphicalMusicSheet;
-    private width: number;
-    private zoom: number = 1.0;
-    private unit: number = 10.0;
-
-    /**
-     * Initialize this object to default values
-     */
-    public free(): void {
-        this.width = undefined;
-        this.canvas = undefined;
-        this.sheet = undefined;
-        this.drawer = undefined;
-        this.graphic = undefined;
-        this.zoom = 1.0;
-        this.unit = 10.0;
-    }
-
-    /**
-     * Load a MusicXML file
-     * @param doc is the root node of a MusicXML document
-     */
-    public load(doc: Document): void {
-        let elem: Element = doc.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, "*** unknown path ***");
-        this.graphic = new GraphicalMusicSheet(this.sheet, calc);
-        this.display();
-    }
-
-    /**
-     * Set the drawing canvas
-     * @param canvas
-     */
-    public setCanvas(canvas: HTMLCanvasElement): void {
-        this.canvas = canvas;
-        this.drawer = new VexFlowMusicSheetDrawer(canvas, new VexFlowTextMeasurer());
-    }
-
-    /**
-     * Set the canvas width
-     * @param width
-     */
-    public setWidth(width: number): void {
-        if (width === this.width) {
-            return;
-        }
-        this.width = width;
-        this.display();
-    }
-
-    /**
-     * Set the zoom
-     * @param k
-     */
-    public scale(k: number): void {
-        this.zoom = k;
-        this.display();
-    }
-
-    // FIXME: make the following private!
-    public display(): void {
-        if (this.width === undefined) {
-            return;
-        }
-        if (this.canvas === undefined) {
-            return;
-        }
-        if (this.sheet === undefined) {
-            return;
-        }
-        // Set page width
-        this.sheet.pageWidth = this.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(
-            this.width,
-            height
-        );
-        // Fix the label problem
-        // this.drawer.translate(0, 100);
-        this.drawer.scale(this.zoom);
-        // Finally, draw
-        this.drawer.drawSheet(this.graphic);
-    }
-}

+ 0 - 216
src/MusicalScore/Calculation/MeasureSizeCalculator.ts

@@ -1,216 +0,0 @@
-import Vex = require("vexflow");
-import StaveNote = Vex.Flow.StaveNote;
-
-// The type PositionAndShapeInfo is still to be ported in TypeScript
-export type PositionAndShapeInfo = any;
-
-/* TODO
- * Complete support for StaveModifiers
- * Take into account Ties and Slurs
- */
-
-/* Measure Size Calculator
- *  Given a stave, voices and a formatter, calculates
- *  through VexFlow the size of a measure.
- *  !!! before using this, call the methods
- *  !!! joinVoices and preCalculateMinTotalWidth
- *  !!! of the formatter!
- *
- * Usage:
- *   let stave: Vex.Flow.Stave = ...;
- *   let formatter = new Vex.Flow.Formatter()
- *   let voices: Vex.Flor.Voice[] = ...;
- *   formatter.preCalculateMinTotalWidth(voices);
- *   let calc = new MeasureSizeCalculator(stave, voices, formatter);
- *   calc.???
- */
-export class MeasureSizeCalculator {
-  private stave: Vex.Flow.Stave;
-  private voices: Vex.Flow.Voice[];
-  private formatter: any;
-
-  private offsetLeft: number;
-  private offsetRight: number;
-  private voicesWidth: number;
-  private topBorder: number;
-  private bottomBorder: number;
-
-  constructor(
-    stave: Vex.Flow.Stave,
-    voices: Vex.Flow.Voice[],
-    formatter: Vex.Flow.Formatter
-  ) {
-    this.stave = stave;
-    this.voices = voices;
-    this.formatter = formatter;
-    // the stave must be initialized with width, x, y 0
-    // the voices must be already joined and (pre)formatted
-    if (!formatter.hasMinTotalWidth) {
-      throw "Must first call Formatter.preCalculateMinTotalWidth " +
-      "with all the voices in the measure (vertical)";
-    }
-    this.format();
-  }
-
-  // Returns the shape of the note head at position _index_ inside _note_.
-  // Remember: in VexFlow, StaveNote correspond to PhonicScore's VoiceEntries.
-  //  public static getVexFlowNoteHeadShape(note: StaveNote, index: number): PositionAndShapeInfo {
-  //  // note_heads is not public in StaveNote, but we access it anyway...
-  //  let bb = note.note_heads[index].getBoundingBox();
-  //  let info: any = new PositionAndShapeInfo();
-  //  let x: number = bb.getX();
-  //  let y: number = bb.getY();
-  //  let w: number = bb.getW();
-  //  let h: number = bb.getH();
-  //  info.Left = info.Right = bb.getW() / 2;
-  //  info.Top = info.Bottom = bb.getH() / 2;
-  //  info.X = bb.getX() + info.Left;
-  //  info.Y = bb.getY() + info.Bottom;
-  //  return info;
-  //}
-
-  // Returns the shape of all the note heads inside a StaveNote.
-  // Remember: in VexFlow, StaveNote correspond to PhonicScore's VoiceEntries.
-  public static getVexFlowStaveNoteShape(note: StaveNote): PositionAndShapeInfo {
-    let info: PositionAndShapeInfo = {};
-    let bounds: any = note.getNoteHeadBounds();
-    let beginX: number = note.getNoteHeadBeginX();
-    let endX: number = note.getNoteHeadEndX();
-
-    info.Left = info.Right = (endX - beginX) / 2;
-    info.Top = info.Bottom = (bounds.y_top - bounds.y_bottom) / 2;
-    info.X = beginX + info.Left;
-    info.Y = bounds.y_bottom + info.Bottom;
-    return info;
-  }
-
-
-  public static getClefBoundingBox(clef: Vex.Flow.Clef): Vex.Flow.BoundingBox {
-    let clef2: any = clef;
-    clef2.placeGlyphOnLine(clef2.glyph, clef2.stave, clef2.clef.line);
-    let glyph: any = clef.glyph;
-    let posX: number = clef.x + glyph.x_shift;
-    let posY: number = clef.stave.getYForGlyphs() + glyph.y_shift;
-    let scale: number = glyph.scale;
-    let outline: any[] = glyph.metrics.outline;
-    let xmin: number = 0, xmax: number = 0, ymin: number = 0, ymax: number = 0;
-
-    function update(i: number): void {
-      let x: number = outline[i + 1];
-      let y: number = outline[i + 2];
-      xmin = Math.min(xmin, x);
-      xmax = Math.max(xmax, x);
-      ymin = Math.min(ymin, y);
-      ymax = Math.max(ymax, y);
-    }
-
-    for (let i: number = 0, len: number = outline.length; i < len; i += 3) {
-      switch (outline[i] as string) {
-        case "m": update(i); break;
-        case "l": update(i); break;
-        case "q": i += 2; update(i); break;
-        case "b": i += 4; update(i); break;
-        default: break;
-      }
-
-    }
-    return new Vex.Flow.BoundingBox(
-        posX + xmin * scale,
-        posY - ymin * scale,
-        (xmax - xmin) * scale,
-        (ymin - ymax) * scale
-    );
-  }
-
-
-  public static getKeySignatureBoundingBox(sig: any): Vex.Flow.BoundingBox {
-    // FIXME: Maybe use Vex.Flow.keySignature(this.keySpec);
-    let stave: Vex.Flow.Stave = sig.getStave();
-    let width: number = sig.getWidth();
-    let maxLine: number = 1;
-    let minLine: number = 1;
-    for (let acc of sig.accList) {
-      maxLine = Math.max(acc.line, maxLine);
-      minLine = Math.min(acc.line, minLine);
-    }
-    let y: number = sig.getStave().getYForLine(minLine);
-    let height: number = stave.getSpacingBetweenLines() * (maxLine - minLine);
-    let x: number = 0; // FIXME
-    return new Vex.Flow.BoundingBox(x, y, width, height);
-  }
-
-
-  public getWidth(): number {
-    // begin_modifiers + voices + end_modifiers
-    return this.offsetLeft + this.voicesWidth + this.offsetRight;
-    // = stave.end_x - stave.x
-  }
-
-  public getHeight(): number {
-    // FIXME this formula does not take into account
-    // other things like staves and ties!
-    return this.stave.getSpacingBetweenLines()
-      * (this.topBorder - this.bottomBorder);
-  }
-
-  // The following methods return a number
-  // where 0 is the upper line of the stave.
-
-  public getTopBorder(): number {
-    return this.topBorder;
-  }
-
-  public getBottomBorder(): number {
-    return this.bottomBorder;
-  }
-
-  private format(): void {
-    let stave: Vex.Flow.Stave = this.stave;
-    let voices: Vex.Flow.Voice[] = this.voices;
-    let voicesBoundingBox: Vex.Flow.BoundingBox;
-    let bb: Vex.Flow.BoundingBox;
-    // Compute widths
-    this.voicesWidth = this.formatter.minTotalWidth;
-    stave.setWidth(this.voicesWidth);
-    stave.format();
-    this.offsetLeft = stave.getNoteStartX() - stave.x;
-    this.offsetRight = stave.end_x - stave.getWidth() - stave.start_x;
-    // Compute heights
-    // Height is:
-    //// height of StaveModifiers + BoundingBox of notes + height of NoteMod's
-    for (let i: number = 0; i < this.voices.length; i ++) {
-      voices[i].setStave(stave);
-      bb = voices[i].getBoundingBox();
-      if (voicesBoundingBox === undefined) {
-        voicesBoundingBox = bb;
-      } else {
-        voicesBoundingBox = voicesBoundingBox.mergeWith(bb);
-      }
-    }
-    // TODO voicesBoundingBox.getW() should be similar to this.voicesWidth?
-    //console.log("this.width", this.voicesWidth);
-    //console.log("voicesBB", voicesBoundingBox.getW());
-    //this.height = voicesBoundingBox.getH(); FIXME
-
-    // Consider clefs
-    let clefs: Vex.Flow.Clef[] = stave.getModifiers(
-      Vex.Flow.Modifier.Position.LEFT,
-      Vex.Flow.Clef.category
-    );
-    for (let clef of clefs) {
-      voicesBoundingBox = voicesBoundingBox.mergeWith(
-        MeasureSizeCalculator.getClefBoundingBox(clef)
-      );
-    }
-
-    this.topBorder = Math.min(
-      0,
-      Math.floor(stave.getLineForY(voicesBoundingBox.getY()))
-    );
-    this.bottomBorder = Math.max(
-      stave.getNumLines(),
-      Math.ceil(stave.getLineForY(voicesBoundingBox.getY() + voicesBoundingBox.getH()))
-    );
-  }
-
-}

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

@@ -1,6 +1,6 @@
 import {PagePlacementEnum} from "./GraphicalMusicPage";
 //import {MusicSymbol} from "./MusicSymbol";
-import {Logging} from "../../Common/logging";
+import {Logging} from "../../Common/Logging";
 
 export class EngravingRules {
     private static rules: EngravingRules;

+ 11 - 12
src/MusicalScore/Graphical/GraphicalMusicSheet.ts

@@ -18,7 +18,7 @@ import {Instrument} from "../Instrument";
 import {BoundingBox} from "./BoundingBox";
 import {Note} from "../VoiceData/Note";
 import {MusicSheetCalculator} from "./MusicSheetCalculator";
-import {Logging} from "../../Common/logging";
+import {Logging} from "../../Common/Logging";
 import Dictionary from "typescript-collections/dist/lib/Dictionary";
 import {CollectionUtil} from "../../Util/collectionUtil";
 import {SelectionStartSymbol} from "./SelectionStartSymbol";
@@ -300,21 +300,20 @@ export class GraphicalMusicSheet {
 
     public getOrCreateVerticalContainer(timestamp: Fraction): VerticalGraphicalStaffEntryContainer {
         if (this.verticalGraphicalStaffEntryContainers.length === 0 ||
-            timestamp > CollectionUtil.getLastElement(this.verticalGraphicalStaffEntryContainers).AbsoluteTimestamp) {
+            timestamp .lt(CollectionUtil.getLastElement(this.verticalGraphicalStaffEntryContainers).AbsoluteTimestamp)) {
             let verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
                 new VerticalGraphicalStaffEntryContainer(this.numberOfStaves, timestamp);
             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);
         }

+ 7 - 7
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -49,7 +49,7 @@ import {AccidentalCalculator} from "./AccidentalCalculator";
 import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
 import {Staff} from "../VoiceData/Staff";
 import {OctaveShift} from "../VoiceData/Expressions/ContinuousExpressions/octaveShift";
-import {Logging} from "../../Common/logging";
+import {Logging} from "../../Common/Logging";
 import Dictionary from "typescript-collections/dist/lib/Dictionary";
 import {CollectionUtil} from "../../Util/collectionUtil";
 
@@ -266,12 +266,12 @@ export abstract class MusicSheetCalculator {
     }
 
     protected clearRecreatedObjects(): void {
-        Logging.log("clearRecreatedObjects not implemented");
+        Logging.debug("clearRecreatedObjects not implemented");
     }
 
     protected handleStaffEntryLink(graphicalStaffEntry: GraphicalStaffEntry,
                                    staffEntryLinks: StaffEntryLink[]): void {
-        Logging.log("handleStaffEntryLink not implemented");
+        Logging.debug("handleStaffEntryLink not implemented");
     }
 
     protected calculateMusicSystems(): void {
@@ -396,19 +396,19 @@ export abstract class MusicSheetCalculator {
     }
 
     protected updateSkyBottomLine(staffLine: StaffLine): void {
-        Logging.log("updateSkyBottomLine not implemented");
+        Logging.debug("updateSkyBottomLine not implemented");
     }
 
     protected calculateSkyBottomLine(staffLine: StaffLine): void {
-        Logging.log("calculateSkyBottomLine not implemented");
+        Logging.debug("calculateSkyBottomLine not implemented");
     }
 
     protected calculateMarkedAreas(): void {
-        Logging.log("calculateMarkedAreas not implemented");
+        Logging.debug("calculateMarkedAreas not implemented");
     }
 
     protected calculateComments(): void {
-        Logging.log("calculateComments not implemented");
+        Logging.debug("calculateComments not implemented");
     }
 
     protected optimizeStaffLineDynamicExpressionsPositions(): void {

+ 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";
     }
 }

+ 45 - 32
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -13,8 +13,8 @@ import {Beam} from "../../VoiceData/Beam";
 import {GraphicalNote} from "../GraphicalNote";
 import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
 import StaveConnector = Vex.Flow.StaveConnector;
-import StaveModifier = Vex.Flow.StaveModifier;
 import StaveNote = Vex.Flow.StaveNote;
+import {Logging} from "../../../Common/Logging";
 
 export class VexFlowMeasure extends StaffMeasure {
     constructor(staff: Staff, staffLine: StaffLine = undefined, sourceMeasure: SourceMeasure = undefined) {
@@ -29,8 +29,6 @@ export class VexFlowMeasure extends StaffMeasure {
     public vfVoices: { [voiceID: number]: Vex.Flow.Voice; } = {};
     // Call this function (if present) to x-format all the voices in the measure
     public formatVoices: (width: number) => void;
-    // The unit
-    public unit: number = 10.0;
 
     // The VexFlow Stave (one measure in one line)
     private stave: Vex.Flow.Stave;
@@ -39,7 +37,7 @@ export class VexFlowMeasure extends StaffMeasure {
     // Intermediate object to construct beams
     private beams: { [voiceID: number]: [Beam, VexFlowStaffEntry[]][]; } = {};
     // VexFlow Beams
-    private vfbeams: { [voiceID: number]: Vex.Flow.Beam[]; } = {};
+    private vfbeams: { [voiceID: number]: Vex.Flow.Beam[]; };
 
     // Sets the absolute coordinates of the VFStave on the canvas
     public setAbsoluteCoordinates(x: number, y: number): void {
@@ -53,19 +51,21 @@ export class VexFlowMeasure extends StaffMeasure {
     public resetLayout(): void {
         // Take into account some space for the begin and end lines of the stave
         // Will be changed when repetitions will be implemented
-        this.beginInstructionsWidth = 20 / this.unit;
-        this.endInstructionsWidth = 20 / this.unit;
+        //this.beginInstructionsWidth = 20 / 10.0;
+        //this.endInstructionsWidth = 20 / 10.0;
         this.stave = new Vex.Flow.Stave(0, 0, 0, {
             space_above_staff_ln: 0,
             space_below_staff_ln: 0,
         });
+        this.updateInstructionWidth();
     }
 
     public clean(): void {
         //this.beams = {};
-        //this.vfbeams = {};
+        //this.vfbeams = undefined;
         this.connectors = [];
-        console.log("clean!");
+        // Clean up instructions
+        this.resetLayout();
     }
 
     /**
@@ -78,9 +78,9 @@ export class VexFlowMeasure extends StaffMeasure {
         let vfline: any = VexFlowConverter.line(line);
         switch (vfline) {
             case Vex.Flow.StaveConnector.type.SINGLE:
-                return 1.0 / this.unit;
+                return 1.0 / 10.0;
             case Vex.Flow.StaveConnector.type.DOUBLE:
-                return 3.0 / this.unit;
+                return 3.0 / 10.0;
             default:
                 return 0;
         }
@@ -95,7 +95,7 @@ export class VexFlowMeasure extends StaffMeasure {
         this.octaveOffset = clef.OctaveOffset;
         let vfclef: string = VexFlowConverter.Clef(clef);
         this.stave.addClef(vfclef, undefined, undefined, Vex.Flow.Modifier.Position.BEGIN);
-        this.increaseBeginInstructionWidth();
+        this.updateInstructionWidth();
     }
 
     /**
@@ -124,7 +124,7 @@ export class VexFlowMeasure extends StaffMeasure {
             timeSig,
             Vex.Flow.Modifier.Position.BEGIN
         );
-        this.increaseBeginInstructionWidth();
+        this.updateInstructionWidth();
     }
 
     /**
@@ -135,7 +135,7 @@ export class VexFlowMeasure extends StaffMeasure {
     public addClefAtEnd(clef: ClefInstruction): void {
         let vfclef: string = VexFlowConverter.Clef(clef);
         this.stave.setEndClef(vfclef, undefined, undefined);
-        this.increaseEndInstructionWidth();
+        this.updateInstructionWidth();
     }
 
     /**
@@ -145,12 +145,14 @@ export class VexFlowMeasure extends StaffMeasure {
     public setWidth(width: number): void {
         super.setWidth(width);
         // Set the width of the Vex.Flow.Stave
-        this.stave.setWidth(width * this.unit);
+        this.stave.setWidth(width * 10.0);
+        // Force the width of the Begin Instructions
+        this.stave.setNoteStartX(this.beginInstructionsWidth * 10.0);
         // If this is the first stave in the vertical measure, call the format
         // method to set the width of all the voices
         if (this.formatVoices) {
             // The width of the voices does not include the instructions (StaveModifiers)
-            this.formatVoices((width - this.beginInstructionsWidth - this.endInstructionsWidth) * this.unit);
+            this.formatVoices((width - this.beginInstructionsWidth - this.endInstructionsWidth) * 10.0);
         }
     }
 
@@ -230,6 +232,10 @@ export class VexFlowMeasure extends StaffMeasure {
      * Complete the creation of VexFlow Beams in this measure
      */
     public finalizeBeams(): void {
+        // The following line resets the created Vex.Flow Beams and
+        // created them brand new. Is this needed? And more importantly,
+        // should the old beams be removed manually by the notes?
+        this.vfbeams = {};
         for (let voiceID in this.beams) {
             if (this.beams.hasOwnProperty(voiceID)) {
                 let vfbeams: Vex.Flow.Beam[] = this.vfbeams[voiceID];
@@ -244,7 +250,7 @@ export class VexFlowMeasure extends StaffMeasure {
                     if (notes.length > 1) {
                         vfbeams.push(new Vex.Flow.Beam(notes, true));
                     } else {
-                        console.log("Warning! Beam with no notes! Trying to ignore, but this is a serious problem.");
+                        Logging.log("Warning! Beam with no notes! Trying to ignore, but this is a serious problem.");
                     }
                 }
             }
@@ -258,8 +264,8 @@ export class VexFlowMeasure extends StaffMeasure {
             if (gnotes.hasOwnProperty(voiceID)) {
                 if (!(voiceID in vfVoices)) {
                     vfVoices[voiceID] = new Vex.Flow.Voice({
-                        beat_value: 4, //this.parentSourceMeasure.Duration.Denominator,
-                        num_beats: 3, //this.parentSourceMeasure.Duration.Numerator,
+                        beat_value: this.parentSourceMeasure.Duration.Denominator,
+                        num_beats: this.parentSourceMeasure.Duration.Numerator,
                         resolution: Vex.Flow.RESOLUTION,
                     }).setMode(Vex.Flow.Voice.Mode.SOFT);
                 }
@@ -285,20 +291,27 @@ export class VexFlowMeasure extends StaffMeasure {
         return this.stave;
     }
 
-    private increaseBeginInstructionWidth(): void {
-        let modifiers: StaveModifier[] = this.stave.getModifiers();
-        let modifier: StaveModifier = modifiers[modifiers.length - 1];
-        //let padding: number = modifier.getCategory() === "keysignatures" ? modifier.getPadding(2) : 0;
-        let padding: number = modifier.getPadding(20);
-        let width: number = modifier.getWidth();
-        this.beginInstructionsWidth += (padding + width) / this.unit;
-    }
+    //private increaseBeginInstructionWidth(): void {
+    //    let modifiers: StaveModifier[] = this.stave.getModifiers();
+    //    let modifier: StaveModifier = modifiers[modifiers.length - 1];
+    //    //let padding: number = modifier.getCategory() === "keysignatures" ? modifier.getPadding(2) : 0;
+    //    let padding: number = modifier.getPadding(20);
+    //    let width: number = modifier.getWidth();
+    //    this.beginInstructionsWidth += (padding + width) / 10.0;
+    //}
+    //
+    //private increaseEndInstructionWidth(): void {
+    //    let modifiers: StaveModifier[] = this.stave.getModifiers();
+    //    let modifier: StaveModifier = modifiers[modifiers.length - 1];
+    //    let padding: number = 0;
+    //    let width: number = modifier.getWidth();
+    //    this.endInstructionsWidth += (padding + width) / 10.0;
+    //
+    //}
 
-    private increaseEndInstructionWidth(): void {
-        let modifiers: StaveModifier[] = this.stave.getModifiers();
-        let modifier: StaveModifier = modifiers[modifiers.length - 1];
-        let padding: number = 0;
-        let width: number = modifier.getWidth();
-        this.endInstructionsWidth += (padding + width) / this.unit;
+    private updateInstructionWidth(): void {
+        this.stave.format();
+        this.beginInstructionsWidth = this.stave.getNoteStartX() / 10.0;
+        this.endInstructionsWidth = this.stave.getNoteEndX() / 10.0;
     }
 }

+ 8 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -26,6 +26,7 @@ import {VexFlowMeasure} from "./VexFlowMeasure";
 import {VexFlowTextMeasurer} from "./VexFlowTextMeasurer";
 
 import Vex = require("vexflow");
+import {Logging} from "../../../Common/Logging";
 
 export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     constructor() {
@@ -64,7 +65,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         }
         // Format the voices
         let allVoices: Vex.Flow.Voice[] = [];
-        let formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter();
+        let formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter({
+            align_rests: true,
+        });
         for (let measure of measures) {
             let mvoices:  { [voiceID: number]: Vex.Flow.Voice; } = (measure as VexFlowMeasure).vfVoices;
             let voices: Vex.Flow.Voice[] = [];
@@ -75,13 +78,15 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
                 }
             }
             if (voices.length === 0) {
-                console.warn("Found a measure with no voices... Continuing anyway.", mvoices);
+                Logging.warn("Found a measure with no voices... Continuing anyway.", mvoices);
                 continue;
             }
             formatter.joinVoices(voices);
         }
         let firstMeasure: VexFlowMeasure = measures[0] as VexFlowMeasure;
-        let width: number = formatter.preCalculateMinTotalWidth(allVoices) / firstMeasure.unit;
+        // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
+        // FIXME: a more relaxed formatting of voices
+        let width: number = formatter.preCalculateMinTotalWidth(allVoices) / 10.0 + 5.0;
         for (let measure of measures) {
             measure.minimumStaffEntriesWidth = width;
             (measure as VexFlowMeasure).formatVoices = undefined;

+ 21 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -2,23 +2,26 @@ import Vex = require("vexflow");
 import {MusicSheetDrawer} from "../MusicSheetDrawer";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {VexFlowMeasure} from "./VexFlowMeasure";
-import {ITextMeasurer} from "../../Interfaces/ITextMeasurer";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
 import {GraphicalLabel} from "../GraphicalLabel";
 import {VexFlowConverter} from "./VexFlowConverter";
+import {VexFlowTextMeasurer} from "./VexFlowTextMeasurer";
 
 export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
     private renderer: Vex.Flow.Renderer;
     private vfctx: Vex.Flow.CanvasContext;
     private ctx: CanvasRenderingContext2D;
+    private titles: HTMLElement;
+    private zoom: number = 1.0;
 
-    constructor(canvas: HTMLCanvasElement, textMeasurer: ITextMeasurer, isPreviewImageDrawer: boolean = false) {
-        super(textMeasurer, isPreviewImageDrawer);
+    constructor(titles: HTMLElement, canvas: HTMLCanvasElement, isPreviewImageDrawer: boolean = false) {
+        super(new VexFlowTextMeasurer(), isPreviewImageDrawer);
         this.renderer = new Vex.Flow.Renderer(canvas, Vex.Flow.Renderer.Backends.CANVAS);
         this.vfctx = this.renderer.getContext();
         // The following is a hack to retrieve the actual canvas' drawing context
         // Not supposed to work forever....
         this.ctx = (this.vfctx as any).vexFlowCanvasContext;
+        this.titles = titles;
     }
 
     /**
@@ -26,6 +29,7 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
      * @param k is the zoom factor
      */
     public scale(k: number): void {
+        this.zoom = k;
         this.vfctx.scale(k, k);
     }
 
@@ -71,6 +75,20 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
      */
     protected renderLabel(graphicalLabel: GraphicalLabel, layer: number, bitmapWidth: number,
                           bitmapHeight: number, heightInPixel: number, screenPosition: PointF2D): void {
+        if (screenPosition.y < 0) {
+            // Temportary solution for title labels
+            let div: HTMLElement = document.createElement("div");
+            div.style.fontSize = (graphicalLabel.Label.fontHeight * this.zoom * 10.0) + "px";
+            //span.style.width = (bitmapWidth * this.zoom * 1.1) + "px";
+            //span.style.height = (bitmapHeight * this.zoom * 1.1) + "px";
+            //span.style.overflow = "hidden";
+            div.style.fontFamily = "Times New Roman";
+            //span.style.marginLeft = (screenPosition.x * this.zoom) + "px";
+            div.style.textAlign = "center";
+            div.appendChild(document.createTextNode(graphicalLabel.Label.text));
+            this.titles.appendChild(div);
+            return;
+        }
         let ctx: CanvasRenderingContext2D = (this.vfctx as any).vexFlowCanvasContext;
         let old: string = ctx.font;
         ctx.font = VexFlowConverter.font(

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

@@ -12,4 +12,21 @@ export class VexFlowStaffEntry extends GraphicalStaffEntry {
     public graphicalNotes: { [voiceID: number]: GraphicalNote[]; } = {};
     // The corresponding VexFlow.StaveNotes
     public vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = {};
+
+    /**
+     *
+     * @returns {number} the x-position (in units) of this Staff Entry
+     */
+    public getX(): 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;
+            }
+        }
+        return x / n / 10.0;
+    }
 }

+ 2 - 1
src/MusicalScore/MusicParts/MusicPartManagerIterator.ts

@@ -14,6 +14,7 @@ import {ContinuousDynamicExpression} from "../VoiceData/Expressions/ContinuousEx
 import {InstantaniousDynamicExpression} from "../VoiceData/Expressions/instantaniousDynamicExpression";
 import {MultiTempoExpression} from "../VoiceData/Expressions/multiTempoExpression";
 import {AbstractExpression} from "../VoiceData/Expressions/abstractExpression";
+import {Logging} from "../../Common/Logging";
 
 export class MusicPartManagerIterator {
     constructor(manager: MusicPartManager, startTimestamp?: Fraction, endTimestamp?: Fraction) {
@@ -46,7 +47,7 @@ export class MusicPartManagerIterator {
             }
             this.currentTempoChangingExpression = this.activeTempoExpression;
         } catch (err) {
-            console.log("MusicPartManagerIterator: Exception." + err); // FIXME
+            Logging.log("MusicPartManagerIterator: " + err);
         }
 
     }

+ 6 - 3
src/MusicalScore/MusicSheet.ts

@@ -16,6 +16,7 @@ import {EngravingRules} from "./Graphical/EngravingRules";
 import {NoteState} from "./Graphical/DrawingEnums";
 import {Note} from "./VoiceData/Note";
 import {VoiceEntry} from "./VoiceData/VoiceEntry";
+import {Logging} from "../Common/Logging";
 
 // FIXME
 //type MusicSheetParameters = any;
@@ -39,7 +40,9 @@ export class PlaybackSettings {
 export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet>*/ {
     constructor() {
         this.rules = EngravingRules.Rules;
-        // (*) this.playbackSettings = new PlaybackSettings(new Fraction(4, 4, false), 100);
+        this.playbackSettings = new PlaybackSettings();
+        // FIXME:
+        this.playbackSettings.rhythm = new Fraction(4, 4, false);
         this.userStartTempoInBPM = 100;
         this.pageWidth = 120;
         this.MusicPartManager = new MusicPartManager(this);
@@ -385,7 +388,7 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         try {
             return this.getFirstSourceMeasure().MeasureNumber;
         } catch (ex) {
-            console.log(/* FIXME LogLevel.NORMAL, */ "MusicSheet.FirstMeasureNumber: ", ex);
+            Logging.log("MusicSheet.FirstMeasureNumber: ", ex);
             return 0;
         }
 
@@ -394,7 +397,7 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         try {
             return this.getLastSourceMeasure().MeasureNumber;
         } catch (ex) {
-            console.log(/* FIXME LogLevel.NORMAL, */ "MusicSheet.LastMeasureNumber: ", ex);
+            Logging.log("MusicSheet.LastMeasureNumber: ", ex);
             return 0;
         }
 

+ 2 - 1
src/MusicalScore/MusicSource/Repetition.ts

@@ -4,6 +4,7 @@ import {Fraction} from "../../Common/DataObjects/fraction";
 import {MusicSheet} from "../MusicSheet";
 import {RepetitionInstruction} from "../VoiceData/Instructions/RepetitionInstruction";
 import {PartListEntry} from "./PartListEntry";
+import {Logging} from "../../Common/Logging";
 
 export class Repetition extends PartListEntry /*implements IRepetition*/ {
     constructor(musicSheet: MusicSheet, virtualOverallRepetition: boolean) {
@@ -83,7 +84,7 @@ export class Repetition extends PartListEntry /*implements IRepetition*/ {
                     this.numberOfEndings = endingNumber;
                 }
             } catch (err) {
-                console.log("Repetition: Exception."); // FIXME
+                Logging.error("Repetition: Exception.", err);
             }
 
         }

+ 1 - 1
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -17,7 +17,7 @@ import {RhythmSymbolEnum} from "../VoiceData/Instructions/RhythmInstruction";
 import {KeyEnum} from "../VoiceData/Instructions/KeyInstruction";
 import {IXmlAttribute} from "../../Common/FileIO/Xml";
 import {ChordSymbolContainer} from "../VoiceData/ChordSymbolContainer";
-import {Logging} from "../../Common/logging";
+import {Logging} from "../../Common/Logging";
 import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
 //import Dictionary from "typescript-collections/dist/lib/Dictionary";
 

+ 7 - 19
src/MusicalScore/ScoreIO/MusicSheetReader.ts

@@ -6,7 +6,7 @@ import {IXmlElement} from "../../Common/FileIO/Xml";
 import {Instrument} from "../Instrument";
 import {ITextTranslation} from "../Interfaces/ITextTranslation";
 import {MusicSheetReadingException} from "../Exceptions";
-import {Logging} from "../../Common/logging";
+import {Logging} from "../../Common/Logging";
 import {IXmlAttribute} from "../../Common/FileIO/Xml";
 import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
 import {RhythmSymbolEnum} from "../VoiceData/Instructions/RhythmInstruction";
@@ -118,11 +118,8 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         }
 
         let partInst: IXmlElement[] = root.elements("part");
-        console.log(partInst.length + " parts");
         let partList: IXmlElement[] = partlistNode.elements();
-        //Logging.debug("Starting initializeReading");
         this.initializeReading(partList, partInst, instrumentReaders);
-        //Logging.debug("Done initializeReading");
         let couldReadMeasure: boolean = true;
         this.currentFraction = new Fraction(0, 1);
         let guitarPro: boolean = false;
@@ -152,22 +149,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 +311,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 +343,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]);

+ 1 - 1
src/MusicalScore/ScoreIO/VoiceGenerator.ts

@@ -22,7 +22,7 @@ import {NoteEnum} from "../../Common/DataObjects/pitch";
 import {Staff} from "../VoiceData/Staff";
 import {StaffEntryLink} from "../VoiceData/StaffEntryLink";
 import {VerticalSourceStaffEntryContainer} from "../VoiceData/VerticalSourceStaffEntryContainer";
-import {Logging} from "../../Common/logging";
+import {Logging} from "../../Common/Logging";
 import {Pitch} from "../../Common/DataObjects/pitch";
 import {IXmlAttribute} from "../../Common/FileIO/Xml";
 import {CollectionUtil} from "../../Util/collectionUtil";

+ 2 - 1
src/MusicalScore/SubInstrument.ts

@@ -1,5 +1,6 @@
 import {Instrument} from "./Instrument";
 import {MidiInstrument} from "./VoiceData/Instructions/ClefInstruction";
+import {Logging} from "../Common/Logging";
 
 export class SubInstrument {
     constructor(parentInstrument: Instrument) {
@@ -107,7 +108,7 @@ export class SubInstrument {
                 }
             }
         } catch (e) {
-            console.log("Error parsing MIDI Instrument. Default to Grand Piano."); // FIXME
+            Logging.error("Error parsing MIDI Instrument. Default to Grand Piano.");
         }
         return "unnamed";
     }

+ 1 - 1
src/MusicalScore/VoiceData/Expressions/instantaniousDynamicExpression.ts

@@ -3,7 +3,7 @@ import {MultiExpression} from "./multiExpression";
 import {DynamicExpressionSymbolEnum} from "./dynamicExpressionSymbolEnum";
 //import {ArgumentOutOfRangeException} from "../../Exceptions";
 import {InvalidEnumArgumentException} from "../../Exceptions";
-import {Logging} from "../../../Common/logging";
+import {Logging} from "../../../Common/Logging";
 
 export class InstantaniousDynamicExpression extends AbstractExpression {
     constructor(dynamicExpression: string, soundDynamics: number, placement: PlacementEnum, staffNumber: number) {

+ 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;
     }
 }

+ 149 - 0
src/OSMD/Cursor.ts

@@ -0,0 +1,149 @@
+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.getX();
+                let musicSystem: MusicSystem = gse.parentMeasure.parentMusicSystem;
+                y = musicSystem.PositionAndShape.AbsolutePosition.y + musicSystem.StaffLines[0].PositionAndShape.RelativePosition.y;
+                let endY: number = musicSystem.PositionAndShape.AbsolutePosition.y +
+                    musicSystem.StaffLines[musicSystem.StaffLines.length - 1].PositionAndShape.RelativePosition.y + 4.0;
+                height = endY - y;
+            }
+            // 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.style.top = (y * 10.0 * this.osmd.zoom) + "px";
+        cursorElement.style.left = ((x - 1.5) * 10.0 * this.osmd.zoom) + "px";
+        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);
+        }
+
+        // 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 = "#33e02f"): 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.2, color);
+        gradient.addColorStop(0.8, 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");
+    }
+}

+ 193 - 0
src/OSMD/OSMD.ts

@@ -0,0 +1,193 @@
+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";
+import {openMxl} from "../Common/FileIO/Mxl";
+//import {Promise} from "es6-promise";
+import {handleResize} from "./ResizeHandler";
+
+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
+     * @autoResize automatically resize the sheet to full page width on window resize
+     */
+    constructor(container: string|HTMLElement, autoResize: boolean = false) {
+        // Store container element
+        if (typeof container === "string") {
+            // ID passed
+            this.container = document.getElementById(<string>container);
+        } else if (container && "appendChild" in <any>container) {
+            // Element passed
+            this.container = <HTMLElement>container;
+        }
+        if (!this.container) {
+            throw new Error("Please pass a valid div container to OSMD");
+        }
+        // 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);
+        if (autoResize) {
+            this.autoResize();
+        }
+    }
+
+    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;
+
+    /**
+     * 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 {
+        // Warning! This function is asynchronous! No error handling is done here.
+        // FIXME TODO Refactor with Promises
+        this.reset();
+        let path: string = "Unknown path";
+        if (typeof content === "string") {
+            let str: string = <string>content;
+            if (str.substr(0, 4) === "http") {
+                // Retrieve the file at the url
+                path = str;
+                this.openURL(path);
+                return;
+            }
+            if (str.substr(0, 4) === "\x04\x03\x4b\x50") {
+                // This is a zip file, unpack it first
+                openMxl(str).then(
+                    this.load,
+                    (err: any) => {
+                        throw new Error("OSMD: Invalid MXL file");
+                    }
+                );
+                return;
+            }
+            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 || !(<any>content).nodeName) {
+            throw new Error("OSMD: Document provided is not valid");
+        }
+        let elem: Element = (<Document>content).getElementsByTagName("score-partwise")[0];
+        if (elem === undefined) {
+            throw new Error("OSMD: Document is not valid partwise MusicXML");
+        }
+        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);
+        return; // Promise.resolve();
+    }
+
+    /**
+     * 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 give the container a width");
+        }
+        // Set page width
+        this.sheet.pageWidth = width / this.zoom / 10.0;
+        // Calculate again
+        this.graphic.reCalculate();
+        // Update Sheet Page
+        let height: number = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * 10.0 * this.zoom;
+        this.drawer.resize(width, height);
+        this.drawer.scale(this.zoom);
+        // Finally, draw
+        this.drawer.drawSheet(this.graphic);
+        // Update the cursor position
+        this.cursor.update();
+    }
+
+    /**
+     *
+     * @param url
+     */
+    private openURL(url: string): string {
+        throw new Error("OSMD: Not implemented: Load sheet from URL");
+        //let JSZipUtils: any;
+        //let self: OSMD = this;
+        //JSZipUtils.getBinaryContent(url, function (err, data) {
+        //    if(err) {
+        //        throw err;
+        //    }
+        //    return self.load(data);
+        //});
+    }
+
+    /**
+     * Clear all the titles from the headings element
+     */
+    private resetHeadings(): void {
+        while (this.heading.firstChild) {
+            this.heading.removeChild(this.heading.firstChild);
+        }
+    }
+
+    /**
+     * Initialize this object to default values
+     * FIXME: Probably unnecessary
+     */
+    private reset(): void {
+        this.sheet = undefined;
+        this.graphic = undefined;
+        this.zoom = 1.0;
+        this.resetHeadings();
+    }
+
+    /**
+     * Attach the appropriate handler to the window.onResize event
+     */
+    private autoResize(): void {
+        let self: OSMD = this;
+        handleResize(
+            () => {
+                // empty
+            },
+            () => {
+                let width: number = Math.max(
+                    document.documentElement.clientWidth,
+                    document.body.scrollWidth,
+                    document.documentElement.scrollWidth,
+                    document.body.offsetWidth,
+                    document.documentElement.offsetWidth
+                );
+                self.container.style.width = width + "px";
+                self.render();
+            }
+        );
+    }
+}

+ 44 - 0
src/OSMD/ResizeHandler.ts

@@ -0,0 +1,44 @@
+/**
+ * Created by acondolu on 15/07/16.
+ */
+
+/**
+ * Helper function for managing window's onResize events
+ * @param startCallback
+ * @param endCallback
+ */
+export function handleResize(startCallback: () => void, endCallback: () => void): void {
+    "use strict";
+    let rtime: number;
+    let timeout: number = undefined;
+    let delta: number = 200;
+
+    function resizeEnd(): void {
+        //timeout = undefined;
+        window.clearTimeout(timeout);
+        if ((new Date()).getTime() - rtime < delta) {
+            timeout = window.setTimeout(resizeEnd, delta);
+        } else {
+            endCallback();
+        }
+    }
+
+    function resizeStart(): void {
+        rtime = (new Date()).getTime();
+        if (!timeout) {
+            startCallback();
+            rtime = (new Date()).getTime();
+            timeout = window.setTimeout(resizeEnd, delta);
+        }
+    }
+
+    if ((<any>window).attachEvent) {
+        // Support IE<9
+        (<any>window).attachEvent("onresize", resizeStart);
+    } else {
+        window.addEventListener("resize", resizeStart);
+    }
+
+    window.setTimeout(startCallback, 0);
+    window.setTimeout(endCallback, 1);
+}

+ 2 - 1
test/Common/DataObjects/fraction_Test.ts

@@ -3,6 +3,7 @@
  */
 import { Fraction } from "../../../src/Common/DataObjects/fraction";
 import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import {Logging} from "../../../src/Common/Logging";
 
 describe("Fraction Unit Tests:", () => {
     describe("Construct Fraction, check properties", () => {
@@ -57,7 +58,7 @@ describe("Fraction Unit Tests:", () => {
                 let value: Fraction = values[i];
 
                 //console.log(values[i].toString() + "== " + dict.getValue(key));
-                console.log(values[i].toString() + "== " + dict.getValue(new Fraction(key.Numerator, key.Denominator)));
+                Logging.debug(values[i].toString() + "== " + dict.getValue(new Fraction(key.Numerator, key.Denominator)));
                 // chai.expect(dict.getValue(key)).to.equal(value);
                 chai.expect(dict.getValue(new Fraction(key.Numerator, key.Denominator))).to.equal(value);
             }

+ 22 - 0
test/Common/OSMD/OSMD.ts

@@ -0,0 +1,22 @@
+import chai = require("chai");
+import {OSMD} from "../../../src/OSMD/OSMD";
+
+
+describe("OSMD Main Export", () => {
+
+    it("no container", (done: MochaDone) => {
+        chai.expect(() => {
+            return new OSMD(undefined);
+        }).to.throw(/container/);
+        done();
+    });
+
+    it("container", (done: MochaDone) => {
+        let div: HTMLElement = document.createElement("div");
+        chai.expect(() => {
+            return new OSMD(div);
+        }).to.not.throw(Error);
+        done();
+    });
+
+});

+ 0 - 90
test/MusicalScore/Calculation/MeasureSizeCalculator.ts

@@ -1,90 +0,0 @@
-import {
-  MeasureSizeCalculator,
-} from "../../../src/MusicalScore/Calculation/MeasureSizeCalculator.ts";
-
-import Vex = require("vexflow");
-
-describe("Measure Size Calculator Tests", () => {
-  // Initialization
-  let stave: Vex.Flow.Stave = new Vex.Flow.Stave(0, 0, 0, {});
-  let voices: Vex.Flow.Voice[];
-  let formatter: Vex.Flow.Formatter;
-  let voice: Vex.Flow.Voice;
-  let note: Vex.Flow.StaveNote;
-  let calc: MeasureSizeCalculator;
-
-  it("One note", (done: MochaDone) => {
-    formatter = new Vex.Flow.Formatter();
-    voice = new Vex.Flow.Voice(undefined);
-    note = new Vex.Flow.StaveNote({ keys: ["b/4"], "duration": "1" });
-    voice.addTickables([note]);
-    voices = [voice];
-
-    chai.expect(formatter.preCalculateMinTotalWidth(voices)).to.equal(22);
-
-    calc = new MeasureSizeCalculator(stave, voices, formatter);
-
-    chai.expect(calc.getBottomBorder()).to.equal(5);
-    done();
-  });
-
-  it("Four quarter notes", (done: MochaDone) => {
-    formatter = new Vex.Flow.Formatter();
-    voice = new Vex.Flow.Voice(undefined);
-    voice.addTickables([
-      new Vex.Flow.StaveNote({ keys: ["c/4"], "duration": "q" }),
-      new Vex.Flow.StaveNote({ keys: ["d/4"], "duration": "q" }),
-      new Vex.Flow.StaveNote({ keys: ["e/4"], "duration": "q" }),
-      new Vex.Flow.StaveNote({ keys: ["f/4"], "duration": "q" }),
-    ]);
-    voices = [voice];
-
-    chai.expect(formatter.preCalculateMinTotalWidth(voices)).to.equal(64);
-    calc = new MeasureSizeCalculator(stave, voices, formatter);
-
-    chai.expect(calc.getWidth()).to.equal(64);
-    chai.expect(calc.getBottomBorder()).to.equal(6);
-    chai.expect(calc.getTopBorder()).to.equal(0);
-    done();
-  });
-
-  it("Will certainly pass", (done: MochaDone) => {
-    let visual: (testfun: (r: any, ctx: any) => void ) => void;
-    visual = function(func: (r: any, ctx: any) => void): void {
-      let canvas: HTMLCanvasElement = document.createElement("canvas");
-      document.body.appendChild(canvas);
-      let renderer: any = new Vex.Flow.Renderer(
-        canvas,
-        Vex.Flow.Renderer.Backends.CANVAS
-      );
-      renderer.resize(300, 100);
-      let ctx: any = renderer.getContext();
-      ctx.setFont("Arial", 10, "").setBackgroundFillStyle("#eed");
-      func(renderer, ctx);
-    };
-
-    visual((renderer: any, ctx: any): void => {
-      renderer.resize(420, 120);
-      let stave2: Vex.Flow.Stave = new Vex.Flow.Stave(10, 0, 410, {});
-      stave2.setContext(ctx);
-      for (let t in Vex.Flow.Clef.types) {
-        if (Vex.Flow.Clef.types.hasOwnProperty(t)) {
-          let clef: Vex.Flow.Clef = new Vex.Flow.Clef(t);
-          stave2.addModifier(clef, Vex.Flow.Modifier.Position.BEGIN);
-          stave2.format();
-
-          clef.setStave(stave2);
-          let bb: Vex.Flow.BoundingBox =
-              MeasureSizeCalculator.getClefBoundingBox(clef);
-          //console.log(bb);
-          ctx.rect(bb.getX(), bb.getY(), bb.getW(), bb.getH());
-          ctx.stroke();
-        }
-      }
-      stave2.draw();
-    });
-
-    done();
-  });
-
-});

+ 2 - 3
test/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -5,7 +5,6 @@ import {MusicSheetReader} from "../../../../src/MusicalScore/ScoreIO/MusicSheetR
 import {VexFlowMusicSheetCalculator} from "../../../../src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator";
 import {TestUtils} from "../../../Util/TestUtils";
 import {IXmlElement} from "../../../../src/Common/FileIO/Xml";
-import {VexFlowTextMeasurer} from "../../../../src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer";
 import {Fraction} from "../../../../src/Common/DataObjects/fraction";
 import {OutlineAndFillStyleEnum} from "../../../../src/MusicalScore/Graphical/DrawingEnums";
 
@@ -24,12 +23,12 @@ describe("VexFlow Music Sheet Drawer", () => {
 
         // Create heading in the test page
         let h1: Element = document.createElement("h1");
-        h1.textContent = "VexFlowMusicSheetDrawer Output";
+        h1.textContent = "VexFlowMusicSheetDrawer Test Output";
         document.body.appendChild(h1);
         // Create the canvas in the document:
         let canvas: HTMLCanvasElement = document.createElement("canvas");
         document.body.appendChild(canvas);
-        (new VexFlowMusicSheetDrawer(canvas, new VexFlowTextMeasurer())).drawSheet(gms);
+        (new VexFlowMusicSheetDrawer(document.body, canvas)).drawSheet(gms);
         done();
     });
 

+ 0 - 1
test/MusicalScore/ScoreIO/MusicSheetReader.ts

@@ -44,7 +44,6 @@ describe("Music Sheet Reader Tests", () => {
 
     it("Measures", (done: MochaDone) => {
         chai.expect(sheet.SourceMeasures.length).to.equal(38);
-        console.log("First Measure: ", sheet.SourceMeasures[0]);
         done();
     });
 

+ 2 - 2
typings.json

@@ -1,10 +1,10 @@
 {
   "globalDependencies": {
     "chai": "registry:dt/chai#3.4.0+20160317120654",
-    "jszip": "registry:dt/jszip",
     "mocha": "registry:dt/mocha#2.2.5+20160317120654"
   },
   "dependencies": {
-    "es6-promise": "registry:npm/es6-promise#3.0.0+20160211003958"
+    "es6-promise": "registry:npm/es6-promise#3.0.0+20160211003958",
+    "jszip": "github:sebastianhaas/typed-jszip"
   }
 }