Przeglądaj źródła

Support for loading MusicXML files by URL

Andrea Condoluci 9 lat temu
rodzic
commit
8f20bf72bf

+ 3 - 2
dist/src/Common/FileIO/Mxl.d.ts

@@ -1,3 +1,4 @@
+import { IXmlElement } from "./Xml";
 import { Promise } from "es6-promise";
-export declare function extractSheetFromMxl(data: string): Promise<any>;
-export declare function openMxl(data: string): Promise<any>;
+export declare function MXLtoIXmlElement(data: string): Promise<IXmlElement>;
+export declare function MXLtoXMLstring(data: string): Promise<string>;

+ 12 - 5
dist/src/Common/FileIO/Mxl.js

@@ -12,7 +12,7 @@ var JSZip = require("jszip");
 //     // Handle it here.
 //   }
 // )
-function extractSheetFromMxl(data) {
+function MXLtoIXmlElement(data) {
     "use strict";
     var zip = new JSZip();
     // asynchronously load zip file and process it - with Promises
@@ -35,13 +35,13 @@ function extractSheetFromMxl(data) {
     }, function (err) {
         throw err;
     }).then(function (content) {
-        return es6_promise_1.Promise.resolve(content);
+        return content;
     }, function (err) {
         throw new Error("extractSheetFromMxl: " + err.message);
     });
 }
-exports.extractSheetFromMxl = extractSheetFromMxl;
-function openMxl(data) {
+exports.MXLtoIXmlElement = MXLtoIXmlElement;
+function MXLtoXMLstring(data) {
     "use strict";
     var zip = new JSZip();
     // asynchronously load zip file and process it - with Promises
@@ -49,6 +49,13 @@ function openMxl(data) {
         return zip.file("META-INF/container.xml").async("string");
     }, function (err) {
         throw err;
+    }).then(function (content) {
+        var parser = new DOMParser();
+        var doc = parser.parseFromString(content, "text/xml");
+        var rootFile = doc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
+        return zip.file(rootFile).async("string");
+    }, function (err) {
+        throw err;
     });
 }
-exports.openMxl = openMxl;
+exports.MXLtoXMLstring = MXLtoXMLstring;

+ 11 - 7
dist/src/Common/FileIO/Xml.js

@@ -2,6 +2,9 @@
 var IXmlElement = (function () {
     function IXmlElement(elem) {
         this.hasAttributes = false;
+        if (elem === undefined) {
+            throw new Error("IXmlElement: expected Element, got undefined");
+        }
         this.elem = elem;
         this.name = elem.nodeName.toLowerCase();
         if (elem.hasAttributes()) {
@@ -21,7 +24,7 @@ var IXmlElement = (function () {
         return this.elem.attributes.getNamedItem(attributeName);
     };
     IXmlElement.prototype.attributes = function () {
-        if (typeof this.attrs === "undefined") {
+        if (!this.attrs) {
             var attributes = this.elem.attributes;
             var attrs = [];
             for (var i = 0; i < attributes.length; i += 1) {
@@ -32,7 +35,13 @@ var IXmlElement = (function () {
         return this.attrs;
     };
     IXmlElement.prototype.element = function (elementName) {
-        return this.elements(elementName)[0];
+        var nodes = this.elem.childNodes;
+        for (var i = 0, length_1 = nodes.length; i < length_1; i += 1) {
+            var node = nodes[i];
+            if (node.nodeType === Node.ELEMENT_NODE && node.nodeName.toLowerCase() === elementName) {
+                return new IXmlElement(node);
+            }
+        }
     };
     IXmlElement.prototype.elements = function (nodeName) {
         var nodes = this.elem.childNodes;
@@ -41,13 +50,8 @@ var IXmlElement = (function () {
         if (!nameUnset) {
             nodeName = nodeName.toLowerCase();
         }
-        // console.log("-", nodeName, nodes.length, this.elem.childElementCount, this.elem.getElementsByTagName(nodeName).length);
-        // if (nodeName === "measure") {
-        //   console.log(this.elem);
-        // }
         for (var i = 0; i < nodes.length; i += 1) {
             var node = nodes[i];
-            //console.log("node: ", this.elem.nodeName, ">>", node.nodeName, node.nodeType === Node.ELEMENT_NODE);
             if (node.nodeType === Node.ELEMENT_NODE &&
                 (nameUnset || node.nodeName.toLowerCase() === nodeName)) {
                 ret.push(new IXmlElement(node));

+ 2 - 1
dist/src/OSMD/OSMD.d.ts

@@ -1,4 +1,5 @@
 import { Cursor } from "./Cursor";
+import { Promise } from "es6-promise";
 export declare class OSMD {
     /**
      * The easy way of displaying a MusicXML sheet music file
@@ -18,7 +19,7 @@ export declare class OSMD {
      * 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;
+    load(content: string | Document): Promise<{}>;
     /**
      * Render the music sheet in the container
      */

+ 26 - 15
dist/src/OSMD/OSMD.js

@@ -6,7 +6,7 @@ var GraphicalMusicSheet_1 = require("./../MusicalScore/Graphical/GraphicalMusicS
 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 es6_promise_1 = require("es6-promise");
 var ResizeHandler_1 = require("./ResizeHandler");
 var OSMD = (function () {
     /**
@@ -60,15 +60,16 @@ var OSMD = (function () {
             if (str.substr(0, 4) === "http") {
                 // Retrieve the file at the url
                 path = str;
-                this.openURL(path);
-                return;
+                return this.openURL(path).then(function (s) { return {}; }, function (exc) { throw exc; });
             }
-            if (str.substr(0, 4) === "\x04\x03\x4b\x50") {
+            if (str.substr(0, 4) === "\x50\x4b\x03\x04") {
                 // This is a zip file, unpack it first
-                Mxl_1.openMxl(str).then(this.load, function (err) {
+                var self_1 = this;
+                return Mxl_1.MXLtoXMLstring(str).then(function (str) {
+                    return self_1.load(str);
+                }, function (err) {
                     throw new Error("OSMD: Invalid MXL file");
                 });
-                return;
             }
             if (str.substr(0, 5) === "<?xml") {
                 // Parse the string representing an xml file
@@ -77,11 +78,19 @@ var OSMD = (function () {
             }
         }
         if (!content || !content.nodeName) {
-            throw new Error("OSMD: Document provided is not valid");
+            return es6_promise_1.Promise.reject(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 children = content.childNodes;
+        var elem;
+        for (var i = 0, length_1 = children.length; i < length_1; i += 1) {
+            var node = children[i];
+            if (node.nodeType === Node.ELEMENT_NODE && node.nodeName.toLowerCase() === "score-partwise") {
+                elem = node;
+                break;
+            }
+        }
+        if (!elem) {
+            return es6_promise_1.Promise.reject(new Error("OSMD: Document is not a valid 'partwise' MusicXML"));
         }
         var score = new Xml_1.IXmlElement(elem);
         var calc = new VexFlowMusicSheetCalculator_1.VexFlowMusicSheetCalculator();
@@ -89,7 +98,7 @@ var OSMD = (function () {
         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();
+        return es6_promise_1.Promise.resolve({});
     };
     /**
      * Render the music sheet in the container
@@ -100,9 +109,11 @@ var OSMD = (function () {
             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");
-        }
+        // Before introducing the following optimization (maybe irrelevant), tests
+        // have to be modified to ensure that width is > 0 when executed
+        //if (isNaN(width) || width === 0) {
+        //    return;
+        //}
         // Set page width
         this.sheet.pageWidth = width / this.zoom / 10.0;
         // Calculate again
@@ -121,7 +132,7 @@ var OSMD = (function () {
      * @param url
      */
     OSMD.prototype.openURL = function (url) {
-        throw new Error("OSMD: Not implemented: Load sheet from URL");
+        return es6_promise_1.Promise.reject(new Error("OSMD: Not implemented: Load sheet from URL"));
         //let JSZipUtils: any;
         //let self: OSMD = this;
         //JSZipUtils.getBinaryContent(url, function (err, data) {

+ 4 - 7
dist/test/Common/FileIO/Mxl.js

@@ -1,22 +1,19 @@
 "use strict";
 var Mxl_ts_1 = require("../../../src/Common/FileIO/Mxl.ts");
+var TestUtils_1 = require("../../Util/TestUtils");
 describe("MXL Tests", function () {
-    // Load the mxl file
-    function getSheet(filename) {
-        return (window.__raw__)[filename];
-    }
     // Generates a test for a mxl file name
     function testFile(scoreName) {
         it(scoreName, function (done) {
             // Load the xml file content
-            var mxl = getSheet("test/data/" + scoreName + ".mxl");
+            var mxl = TestUtils_1.TestUtils.getMXL(scoreName);
             chai.expect(mxl).to.not.be.undefined;
             // Extract XML from MXL
             // Warning: the sheet is loaded asynchronously,
             // (with Promises), thus we need a little fix
             // in the end with 'then(null, done)' to
             // make Mocha work asynchronously
-            Mxl_ts_1.extractSheetFromMxl(mxl).then(function (score) {
+            Mxl_ts_1.MXLtoIXmlElement(mxl).then(function (score) {
                 chai.expect(score).to.not.be.undefined;
                 chai.expect(score.name).to.equal("score-partwise");
                 done();
@@ -31,7 +28,7 @@ describe("MXL Tests", function () {
     }
     // Test failure
     it("Corrupted file", function (done) {
-        Mxl_ts_1.extractSheetFromMxl("").then(function (score) {
+        Mxl_ts_1.MXLtoIXmlElement("").then(function (score) {
             chai.expect(score).to.not.be.undefined;
             chai.expect(score.name).to.equal("score-partwise");
             done(new Error("Empty zip file was loaded correctly. How is that even possible?"));

+ 3 - 3
dist/test/Common/FileIO/Xml.js

@@ -6,11 +6,11 @@ var xmlTestData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
 <score-partwise>  <identification>    <encoding>      <software>Example Software name</software>      \
 <encoding-date>2016-04-04</encoding-date>      </encoding>    </identification>   <credit page=\"1\"> \
 <credit-words justify=\"center\" valign=\"top\">Example Credit Words</credit-words> </credit>  </score-partwise>";
-describe("XML Unit Tests", function () {
+describe("XML interface", function () {
     var parser = new DOMParser();
     var doc = parser.parseFromString(xmlTestData, "text/xml");
     var documentElement = new Xml_ts_1.IXmlElement(doc.documentElement);
-    it("IXmlElement Tests", function (done) {
+    it("test IXmlElement", function (done) {
         // Test name attribute
         chai.expect(documentElement.name).to.equal("score-partwise");
         // Test element method
@@ -22,7 +22,7 @@ describe("XML Unit Tests", function () {
             .element("software").value).to.equal("Example Software name");
         done();
     });
-    it("IXmlAttribute Tests", function (done) {
+    it("test IXmlAttribute", function (done) {
         // Test attributes method
         chai.expect(documentElement.element("credit").attributes()[0].name).to.equal("page");
         var creditWords = documentElement.element("credit").element("credit-words");

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

@@ -1,6 +1,7 @@
 "use strict";
 var chai = require("chai");
 var OSMD_1 = require("../../../src/OSMD/OSMD");
+var TestUtils_1 = require("../../Util/TestUtils");
 describe("OSMD Main Export", function () {
     it("no container", function (done) {
         chai.expect(function () {
@@ -15,4 +16,70 @@ describe("OSMD Main Export", function () {
         }).to.not.throw(Error);
         done();
     });
+    it("load MXL from string", function (done) {
+        var mxl = TestUtils_1.TestUtils.getMXL("MozartTrio");
+        var div = document.createElement("div");
+        var osmd = new OSMD_1.OSMD(div);
+        osmd.load(mxl).then(function (_) {
+            osmd.render();
+            done();
+        }, done);
+    });
+    it("load invalid MXL from string", function (done) {
+        var mxl = "\x50\x4b\x03\x04";
+        var div = document.createElement("div");
+        var osmd = new OSMD_1.OSMD(div);
+        osmd.load(mxl).then(function (_) {
+            done(new Error("Corrupted MXL appears to be loaded correctly"));
+        }, function (exc) {
+            if (exc.message.toLowerCase().match(/invalid/)) {
+                done();
+            }
+            else {
+                done(new Error("Unexpected error: " + exc.message));
+            }
+        });
+    });
+    it("load XML string", function (done) {
+        var score = TestUtils_1.TestUtils.getScore("MuzioClementi_SonatinaOpus36No1_Part1");
+        var xml = new XMLSerializer().serializeToString(score);
+        var div = document.createElement("div");
+        var osmd = new OSMD_1.OSMD(div);
+        osmd.load(xml).then(function (_) {
+            osmd.render();
+            done();
+        }, done);
+    });
+    it("load XML Document", function (done) {
+        var score = TestUtils_1.TestUtils.getScore("MuzioClementi_SonatinaOpus36No1_Part1");
+        var div = document.createElement("div");
+        var osmd = new OSMD_1.OSMD(div);
+        osmd.load(score).then(function (_) {
+            osmd.render();
+            done();
+        }, done);
+    });
+    it("load invalid XML string", function (done) {
+        var xml = "<?xml";
+        var div = document.createElement("div");
+        var osmd = new OSMD_1.OSMD(div);
+        osmd.load(xml).then(function (_) {
+            done(new Error("Corrupted XML appears to be loaded correctly"));
+        }, function (exc) {
+            if (exc.message.toLowerCase().match(/partwise/)) {
+                done();
+            }
+            else {
+                done(new Error("Unexpected error: " + exc.message));
+            }
+        });
+    });
+    it("render without loading", function (done) {
+        var div = document.createElement("div");
+        var osmd = new OSMD_1.OSMD(div);
+        chai.expect(function () {
+            return osmd.render();
+        }).to.throw(/load/);
+        done();
+    });
 });

+ 5 - 4
dist/test/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.js

@@ -4,17 +4,18 @@ 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 Xml_1 = require("../../../../src/Common/FileIO/Xml");
 var fraction_1 = require("../../../../src/Common/DataObjects/fraction");
 var DrawingEnums_1 = require("../../../../src/MusicalScore/Graphical/DrawingEnums");
 describe("VexFlow Music Sheet Drawer", function () {
     it(".drawSheet (Clementi pt. 1)", function (done) {
-        var path = "test/data/MuzioClementi_SonatinaOpus36No1_Part2.xml";
-        // "test/data/MuzioClementi_SonatinaOpus36No1_Part1.xml";
-        var score = TestUtils_1.TestUtils.getScore(path);
+        var score = TestUtils_1.TestUtils.getScore("MuzioClementi_SonatinaOpus36No1_Part1");
         chai.expect(score).to.not.be.undefined;
+        var partwise = TestUtils_1.TestUtils.getPartWiseElement(score);
+        chai.expect(partwise).to.not.be.undefined;
         var calc = new VexFlowMusicSheetCalculator_1.VexFlowMusicSheetCalculator();
         var reader = new MusicSheetReader_1.MusicSheetReader();
-        var sheet = reader.createMusicSheet(score, path);
+        var sheet = reader.createMusicSheet(new Xml_1.IXmlElement(partwise), "path");
         var gms = new GraphicalMusicSheet_1.GraphicalMusicSheet(sheet, calc);
         gms.Cursors.push(gms.calculateCursorLineAtTimestamp(new fraction_1.Fraction(), DrawingEnums_1.OutlineAndFillStyleEnum.PlaybackCursor));
         // Create heading in the test page

+ 7 - 4
dist/test/MusicalScore/ScoreCalculation/MusicSheetCalculator_Test.js

@@ -3,6 +3,7 @@
  * Created by Matthias on 21.06.2016.
  */
 var MusicSheetReader_1 = require("../../../src/MusicalScore/ScoreIO/MusicSheetReader");
+var Xml_1 = require("../../../src/Common/FileIO/Xml");
 var MusicSheetCalculator_1 = require("../../../src/MusicalScore/Graphical/MusicSheetCalculator");
 var VexFlowMusicSheetCalculator_1 = require("../../../src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator");
 var GraphicalMusicSheet_1 = require("../../../src/MusicalScore/Graphical/GraphicalMusicSheet");
@@ -10,7 +11,7 @@ var VexFlowTextMeasurer_1 = require("../../../src/MusicalScore/Graphical/VexFlow
 var TestUtils_1 = require("../../Util/TestUtils");
 describe("Music Sheet Calculator Tests", function () {
     // Initialize variables
-    var path = "test/data/MuzioClementi_SonatinaOpus36No1_Part1.xml";
+    var filename = "MuzioClementi_SonatinaOpus36No1_Part1";
     var reader = new MusicSheetReader_1.MusicSheetReader();
     var calculator = new VexFlowMusicSheetCalculator_1.VexFlowMusicSheetCalculator();
     var score;
@@ -26,10 +27,12 @@ describe("Music Sheet Calculator Tests", function () {
     });
     it("Do Calculation", function (done) {
         MusicSheetCalculator_1.MusicSheetCalculator.TextMeasurer = new VexFlowTextMeasurer_1.VexFlowTextMeasurer();
-        // Load the xml file
-        score = TestUtils_1.TestUtils.getScore(path);
+        // Load the XML file
+        var xml = TestUtils_1.TestUtils.getScore(filename);
+        chai.expect(xml).to.not.be.undefined;
+        score = new Xml_1.IXmlElement(TestUtils_1.TestUtils.getPartWiseElement(xml));
         chai.expect(score).to.not.be.undefined;
-        sheet = reader.createMusicSheet(score, path);
+        sheet = reader.createMusicSheet(score, "path-of-" + filename);
         var graphicalSheet = new GraphicalMusicSheet_1.GraphicalMusicSheet(sheet, calculator);
         graphicalSheet.reCalculate();
         done();

+ 13 - 2
dist/test/Util/TestUtils.d.ts

@@ -1,4 +1,15 @@
-import { IXmlElement } from "../../src/Common/FileIO/Xml";
+/**
+ * This class collects useful methods to interact with test data.
+ * During tests, XML and MXL documents are preprocessed by karma,
+ * and this is some helper code to retrieve them.
+ */
 export declare class TestUtils {
-    static getScore(path: string): IXmlElement;
+    static getScore(name: string): Document;
+    static getMXL(scoreName: string): string;
+    /**
+     * Retrieve from a XML document the first element with name "score-partwise"
+     * @param doc is the XML Document
+     * @returns {Element}
+     */
+    static getPartWiseElement(doc: Document): Element;
 }

+ 25 - 10
dist/test/Util/TestUtils.js

@@ -1,18 +1,33 @@
 "use strict";
-var Xml_1 = require("../../src/Common/FileIO/Xml");
+/**
+ * This class collects useful methods to interact with test data.
+ * During tests, XML and MXL documents are preprocessed by karma,
+ * and this is some helper code to retrieve them.
+ */
 var TestUtils = (function () {
     function TestUtils() {
     }
-    TestUtils.getScore = function (path) {
-        var doc = (window.__xml__)[path];
-        if (doc === undefined) {
-            return;
-        }
-        var elem = doc.getElementsByTagName("score-partwise")[0];
-        if (elem === undefined) {
-            return;
+    TestUtils.getScore = function (name) {
+        var path = "test/data/" + name + ".xml";
+        return (window.__xml__)[path];
+    };
+    TestUtils.getMXL = function (scoreName) {
+        var path = "test/data/" + scoreName + ".mxl";
+        return (window.__raw__)[path];
+    };
+    /**
+     * Retrieve from a XML document the first element with name "score-partwise"
+     * @param doc is the XML Document
+     * @returns {Element}
+     */
+    TestUtils.getPartWiseElement = function (doc) {
+        var nodes = doc.childNodes;
+        for (var i = 0, length_1 = nodes.length; i < length_1; i += 1) {
+            var node = nodes[i];
+            if (node.nodeType === Node.ELEMENT_NODE && node.nodeName.toLowerCase() === "score-partwise") {
+                return node;
+            }
         }
-        return new Xml_1.IXmlElement(elem);
     };
     return TestUtils;
 }());

+ 1 - 1
karma.conf.js

@@ -55,7 +55,7 @@ module.exports = function (config) {
 
         // level of logging
         // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
-        logLevel: config.LOG_INFO,
+        logLevel: config.LOG_ERROR,
 
         client: {
             captureConsole: true

+ 34 - 0
src/OSMD/AJAX.ts

@@ -0,0 +1,34 @@
+import {Promise} from "es6-promise";
+
+/**
+ * Retrive the content of the file at _url_
+ * @param url
+ * @returns {any}
+ */
+export function ajax(url: string): Promise<string> {
+    "use strict";
+    let xhttp: XMLHttpRequest;
+    if (XMLHttpRequest) {
+        xhttp = new XMLHttpRequest();
+    } else if (ActiveXObject) {
+        // for IE<7
+        xhttp = new ActiveXObject("Microsoft.XMLHTTP");
+    } else {
+        return Promise.reject(new Error("XMLHttp not supported."));
+    }
+    return new Promise((resolve: (value: string) => void, reject: (error: any) => void) => {
+        xhttp.onreadystatechange = () => {
+            if (xhttp.readyState === XMLHttpRequest.DONE) {
+                if (xhttp.status === 200) {
+                    resolve(xhttp.responseText);
+                } else {
+                    //reject(new Error("AJAX error: '" + xhttp.statusText + "'"));
+                    reject(new Error("Could not retrieve requested URL"));
+                }
+            }
+        };
+        xhttp.overrideMimeType("text/plain; charset=x-user-defined");
+        xhttp.open("GET", url, true);
+        xhttp.send();
+    });
+}

+ 11 - 29
src/OSMD/OSMD.ts

@@ -9,6 +9,7 @@ import {Cursor} from "./Cursor";
 import {MXLtoXMLstring} from "../Common/FileIO/Mxl";
 import {Promise} from "es6-promise";
 import {handleResize} from "./ResizeHandler";
+import {ajax} from "./AJAX";
 
 export class OSMD {
     /**
@@ -62,22 +63,12 @@ export class OSMD {
      */
     public load(content: string|Document): Promise<{}> {
         // 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;
-                return this.openURL(path).then(
-                    (s: string) => { return {}; },
-                    (exc: Error) => { throw exc; }
-                );
-            }
+            let self: OSMD = this;
             if (str.substr(0, 4) === "\x50\x4b\x03\x04") {
                 // This is a zip file, unpack it first
-                let self: OSMD = this;
                 return MXLtoXMLstring(str).then(
                     (str: string) => {
                         return self.load(str);
@@ -91,11 +82,18 @@ export class OSMD {
                 // Parse the string representing an xml file
                 let parser: DOMParser = new DOMParser();
                 content = parser.parseFromString(str, "text/xml");
+            } else if (str.length < 2083) {
+                // Assume now 'str' is a URL
+                // Retrieve the file at the given URL
+                return ajax(str).then(
+                    (s: string) => { return self.load(s); },
+                    (exc: Error) => { throw exc; }
+                );
             }
         }
 
         if (!content || !(<any>content).nodeName) {
-            return Promise.reject(new Error("OSMD: Document provided is not valid"));
+            return Promise.reject(new Error("OSMD: The document which was provided is invalid"));
         }
         let children: NodeList = (<Document>content).childNodes;
         let elem: Element;
@@ -112,7 +110,7 @@ export class OSMD {
         let score: IXmlElement = new IXmlElement(elem);
         let calc: MusicSheetCalculator = new VexFlowMusicSheetCalculator();
         let reader: MusicSheetReader = new MusicSheetReader();
-        this.sheet = reader.createMusicSheet(score, path);
+        this.sheet = reader.createMusicSheet(score, "Unknown path");
         this.graphic = new GraphicalMusicSheet(this.sheet, calc);
         this.cursor.init(this.sheet.MusicPartManager, this.graphic);
         return Promise.resolve({});
@@ -148,22 +146,6 @@ export class OSMD {
     }
 
     /**
-     *
-     * @param url
-     */
-    private openURL(url: string): Promise<string> {
-        return Promise.reject(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 {

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

@@ -78,6 +78,37 @@ describe("OSMD Main Export", () => {
         );
     });
 
+    it("load MXL Document by URL", (done: MochaDone) => {
+        let url: string = "base/test/data/MozartTrio.mxl";
+        let div: HTMLElement = document.createElement("div");
+        let osmd: OSMD = new OSMD(div);
+        osmd.load(url).then(
+            (_: {}) => {
+                osmd.render();
+                done();
+            },
+            done
+        );
+    });
+
+    it("load MXL Document by invalid URL", (done: MochaDone) => {
+        let url: string = "http://www.google.com";
+        let div: HTMLElement = document.createElement("div");
+        let osmd: OSMD = new OSMD(div);
+        osmd.load(url).then(
+            (_: {}) => {
+                done(new Error("Invalid URL appears to be loaded correctly"));
+            },
+            (exc: Error) => {
+                if (exc.message.toLowerCase().match(/url/)) {
+                    done();
+                } else {
+                    done(new Error("Unexpected error: " + exc.message));
+                }
+            }
+        );
+    });
+
     it("load invalid XML string", (done: MochaDone) => {
         let xml: string = "<?xml";
         let div: HTMLElement = document.createElement("div");

BIN
test/data/MozartTrio.mxl