Explorar o código

Merge branch 'release/0.3.0'

Sebastian Haas %!s(int64=7) %!d(string=hai) anos
pai
achega
6f69694591
Modificáronse 100 ficheiros con 6461 adicións e 4116 borrados
  1. 21 0
      .appveyor.yml
  2. 5 0
      .eslintignore
  3. 3 0
      .eslintrc.yml
  4. 0 3
      .gitignore
  5. 0 91
      .jshintrc
  6. 2 4
      .travis.yml
  7. 1 0
      AUTHORS
  8. 37 10
      CHANGELOG.md
  9. 0 172
      Gruntfile.js
  10. 5 4
      README.md
  11. 5 3
      bin/publish_gh_page.sh
  12. 71 48
      demo/index.html
  13. 46 26
      demo/index.js
  14. 116 15
      external/vexflow/vexflow.d.ts
  15. 48 13
      karma.conf.js
  16. 53 44
      package.json
  17. 394 338
      src/Common/DataObjects/Fraction.ts
  18. 15 14
      src/Common/DataObjects/Pitch.ts
  19. 11 11
      src/Common/FileIO/Mxl.ts
  20. 8 8
      src/Common/FileIO/Xml.ts
  21. 4 4
      src/Common/Logging.ts
  22. 11 0
      src/Common/Strings/StringUtil.ts
  23. 4 4
      src/MusicalScore/Graphical/AccidentalCalculator.ts
  24. 75 33
      src/MusicalScore/Graphical/BoundingBox.ts
  25. 39 1
      src/MusicalScore/Graphical/DrawingEnums.ts
  26. 4 1
      src/MusicalScore/Graphical/EngravingRules.ts
  27. 1 2
      src/MusicalScore/Graphical/GraphicalChordSymbolContainer.ts
  28. 5 5
      src/MusicalScore/Graphical/GraphicalLabel.ts
  29. 1 0
      src/MusicalScore/Graphical/GraphicalLyricEntry.ts
  30. 1 0
      src/MusicalScore/Graphical/GraphicalLyricWord.ts
  31. 97 97
      src/MusicalScore/Graphical/GraphicalMusicSheet.ts
  32. 10 5
      src/MusicalScore/Graphical/GraphicalNote.ts
  33. 47 40
      src/MusicalScore/Graphical/GraphicalStaffEntry.ts
  34. 4 4
      src/MusicalScore/Graphical/GraphicalStaffEntryLink.ts
  35. 370 147
      src/MusicalScore/Graphical/MusicSheetCalculator.ts
  36. 100 58
      src/MusicalScore/Graphical/MusicSheetDrawer.ts
  37. 48 46
      src/MusicalScore/Graphical/MusicSystem.ts
  38. 100 105
      src/MusicalScore/Graphical/MusicSystemBuilder.ts
  39. 10 10
      src/MusicalScore/Graphical/SelectionEndSymbol.ts
  40. 7 7
      src/MusicalScore/Graphical/SelectionStartSymbol.ts
  41. 30 10
      src/MusicalScore/Graphical/StaffLine.ts
  42. 1 1
      src/MusicalScore/Graphical/StaffLineActivitySymbol.ts
  43. 18 20
      src/MusicalScore/Graphical/StaffMeasure.ts
  44. 3 3
      src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.ts
  45. 66 0
      src/MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend.ts
  46. 71 0
      src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts
  47. 62 0
      src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts
  48. 275 57
      src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts
  49. 1 1
      src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts
  50. 27 13
      src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts
  51. 14 0
      src/MusicalScore/Graphical/VexFlow/VexFlowInstrumentBrace.ts
  52. 54 0
      src/MusicalScore/Graphical/VexFlow/VexFlowInstrumentBracket.ts
  53. 218 100
      src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts
  54. 338 214
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts
  55. 68 34
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts
  56. 28 8
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSystem.ts
  57. 25 13
      src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts
  58. 0 1
      src/MusicalScore/Graphical/VexFlow/VexFlowStaffLine.ts
  59. 1 1
      src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer.ts
  60. 24 22
      src/MusicalScore/Instrument.ts
  61. 8 0
      src/MusicalScore/Interfaces/IAfterSheetReadingModule.ts
  62. 2 1
      src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts
  63. 14 4
      src/MusicalScore/MusicParts/MusicPartManager.ts
  64. 66 34
      src/MusicalScore/MusicParts/MusicPartManagerIterator.ts
  65. 33 19
      src/MusicalScore/MusicSheet.ts
  66. 9 9
      src/MusicalScore/MusicSource/Repetition.ts
  67. 963 966
      src/MusicalScore/ScoreIO/InstrumentReader.ts
  68. 114 119
      src/MusicalScore/ScoreIO/MusicSheetReader.ts
  69. 31 0
      src/MusicalScore/ScoreIO/MusicSymbolModuleFactory.ts
  70. 195 0
      src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts
  71. 144 0
      src/MusicalScore/ScoreIO/MusicSymbolModules/ChordSymbolReader.ts
  72. 140 0
      src/MusicalScore/ScoreIO/MusicSymbolModules/LyricsReader.ts
  73. 99 0
      src/MusicalScore/ScoreIO/MusicSymbolModules/RepetitionCalculator.ts
  74. 380 0
      src/MusicalScore/ScoreIO/MusicSymbolModules/RepetitionInstructionReader.ts
  75. 825 797
      src/MusicalScore/ScoreIO/VoiceGenerator.ts
  76. 6 4
      src/MusicalScore/SubInstrument.ts
  77. 1 1
      src/MusicalScore/VoiceData/Expressions/AbstractExpression.ts
  78. 1 1
      src/MusicalScore/VoiceData/Expressions/AbstractTempoExpression.ts
  79. 3 3
      src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression.ts
  80. 3 3
      src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/ContinuousTempoExpression.ts
  81. 5 5
      src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/Slur.ts
  82. 2 2
      src/MusicalScore/VoiceData/Expressions/MultiExpression.ts
  83. 1 1
      src/MusicalScore/VoiceData/Expressions/MultiTempoExpression.ts
  84. 0 4
      src/MusicalScore/VoiceData/Expressions/UnknownExpression.ts
  85. 3 3
      src/MusicalScore/VoiceData/Instructions/ClefInstruction.ts
  86. 13 6
      src/MusicalScore/VoiceData/Instructions/KeyInstruction.ts
  87. 7 4
      src/MusicalScore/VoiceData/Instructions/RepetitionInstruction.ts
  88. 6 6
      src/MusicalScore/VoiceData/Instructions/RhythmInstruction.ts
  89. 8 1
      src/MusicalScore/VoiceData/Lyrics/LyricsEntry.ts
  90. 2 2
      src/MusicalScore/VoiceData/Lyrics/LyricsWord.ts
  91. 8 4
      src/MusicalScore/VoiceData/Note.ts
  92. 38 31
      src/MusicalScore/VoiceData/SourceMeasure.ts
  93. 9 9
      src/MusicalScore/VoiceData/SourceStaffEntry.ts
  94. 1 1
      src/MusicalScore/VoiceData/Tie.ts
  95. 1 1
      src/MusicalScore/VoiceData/Tuplet.ts
  96. 0 2
      src/MusicalScore/VoiceData/VerticalSourceStaffEntryContainer.ts
  97. 58 43
      src/MusicalScore/VoiceData/VoiceEntry.ts
  98. 0 159
      src/OSMD/Cursor.ts
  99. 0 0
      src/OpenSheetMusicDisplay/AJAX.ts
  100. 159 0
      src/OpenSheetMusicDisplay/Cursor.ts

+ 21 - 0
.appveyor.yml

@@ -0,0 +1,21 @@
+image: Visual Studio 2017
+environment:
+  matrix:
+    - nodejs_version: "6"
+    # - nodejs_version: "7"
+    # - nodejs_version: "8" 
+    - nodejs_version: "9"
+platform:
+  # - x86
+  - x64
+install:
+  - ps: Install-Product node $env:nodejs_version $env:platform
+  - npm install
+  - node --version
+  - npm --version
+  - npm run fix-memory-limit
+build_script:
+  - npm run lint
+  - npm run build
+test_script:
+  - npm run test

+ 5 - 0
.eslintignore

@@ -0,0 +1,5 @@
+node_modules
+dist
+build
+bin
+demo

+ 3 - 0
.eslintrc.yml

@@ -0,0 +1,3 @@
+extends: standard
+rules:
+  indent: [2, 4]

+ 0 - 3
.gitignore

@@ -23,9 +23,6 @@ lib-cov
 # Coverage directory used by tools like istanbul
 coverage
 
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
 # node-waf configuration
 .lock-wscript
 

+ 0 - 91
.jshintrc

@@ -1,91 +0,0 @@
-{
-    // JSHint Default Configuration File (as on JSHint website)
-    // See http://jshint.com/docs/ for more details
-
-    "maxerr"        : 50,       // {int} Maximum error before stopping
-
-    // Enforcing
-    "bitwise"       : true,     // true: Prohibit bitwise operators (&, |, ^, etc.)
-    "camelcase"     : true,     // true: Identifiers must be in camelCase
-    "curly"         : true,     // true: Require {} for every new block or scope
-    "eqeqeq"        : true,     // true: Require triple equals (===) for comparison
-    "forin"         : true,     // true: Require filtering for..in loops with obj.hasOwnProperty()
-    "freeze"        : true,     // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
-    "immed"         : false,    // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
-    "latedef"       : false,    // true: Require variables/functions to be defined before being used
-    "newcap"        : false,    // true: Require capitalization of all constructor functions e.g. `new F()`
-    "noarg"         : true,     // true: Prohibit use of `arguments.caller` and `arguments.callee`
-    "noempty"       : true,     // true: Prohibit use of empty blocks
-    "nonbsp"        : true,     // true: Prohibit "non-breaking whitespace" characters.
-    "nonew"         : false,    // true: Prohibit use of constructors for side-effects (without assignment)
-    "plusplus"      : false,    // true: Prohibit use of `++` and `--`
-    "quotmark"      : true,     // Quotation mark consistency:
-                                //   false    : do nothing (default)
-                                //   true     : ensure whatever is used is consistent
-                                //   "single" : require single quotes
-                                //   "double" : require double quotes
-    "undef"         : true,     // true: Require all non-global variables to be declared (prevents global leaks)
-    "unused"        : true,     // Unused variables:
-                                //   true     : all variables, last function parameter
-                                //   "vars"   : all variables only
-                                //   "strict" : all variables, all function parameters
-    "strict"        : true,     // true: Requires all functions run in ES5 Strict Mode
-    "maxparams"     : false,    // {int} Max number of formal params allowed per function
-    "maxdepth"      : false,    // {int} Max depth of nested blocks (within functions)
-    "maxstatements" : false,    // {int} Max number statements per function
-    "maxcomplexity" : false,    // {int} Max cyclomatic complexity per function
-    "maxlen"        : false,    // {int} Max number of characters per line
-    "varstmt"       : false,    // true: Disallow any var statements. Only `let` and `const` are allowed.
-
-    // Relaxing
-    "asi"           : false,     // true: Tolerate Automatic Semicolon Insertion (no semicolons)
-    "boss"          : false,     // true: Tolerate assignments where comparisons would be expected
-    "debug"         : false,     // true: Allow debugger statements e.g. browser breakpoints.
-    "eqnull"        : false,     // true: Tolerate use of `== null`
-    "esversion"     : 5,         // {int} Specify the ECMAScript version to which the code must adhere.
-    "moz"           : false,     // true: Allow Mozilla specific syntax (extends and overrides esnext features)
-                                 // (ex: `for each`, multiple try/catch, function expression…)
-    "evil"          : false,     // true: Tolerate use of `eval` and `new Function()`
-    "expr"          : false,     // true: Tolerate `ExpressionStatement` as Programs
-    "funcscope"     : false,     // true: Tolerate defining variables inside control statements
-    "globalstrict"  : false,     // true: Allow global "use strict" (also enables 'strict')
-    "iterator"      : false,     // true: Tolerate using the `__iterator__` property
-    "lastsemic"     : false,     // true: Tolerate omitting a semicolon for the last statement of a 1-line block
-    "laxbreak"      : false,     // true: Tolerate possibly unsafe line breakings
-    "laxcomma"      : false,     // true: Tolerate comma-first style coding
-    "loopfunc"      : false,     // true: Tolerate functions being defined in loops
-    "multistr"      : false,     // true: Tolerate multi-line strings
-    "noyield"       : false,     // true: Tolerate generator functions with no yield statement in them.
-    "notypeof"      : false,     // true: Tolerate invalid typeof operator values
-    "proto"         : false,     // true: Tolerate using the `__proto__` property
-    "scripturl"     : false,     // true: Tolerate script-targeted URLs
-    "shadow"        : false,     // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
-    "sub"           : false,     // true: Tolerate using `[]` notation when it can still be expressed in dot notation
-    "supernew"      : false,     // true: Tolerate `new function () { ... };` and `new Object;`
-    "validthis"     : false,     // true: Tolerate using this in a non-constructor function
-
-    // Environments
-    "browser"       : true,     // Web Browser (window, document, etc)
-    "browserify"    : false,    // Browserify (node.js code in the browser)
-    "couch"         : false,    // CouchDB
-    "devel"         : true,     // Development/debugging (alert, confirm, etc)
-    "dojo"          : false,    // Dojo Toolkit
-    "jasmine"       : false,    // Jasmine
-    "jquery"        : false,    // jQuery
-    "mocha"         : true,     // Mocha
-    "mootools"      : false,    // MooTools
-    "node"          : false,    // Node.js
-    "nonstandard"   : false,    // Widely adopted globals (escape, unescape, etc)
-    "phantom"       : false,    // PhantomJS
-    "prototypejs"   : false,    // Prototype and Scriptaculous
-    "qunit"         : false,    // QUnit
-    "rhino"         : false,    // Rhino
-    "shelljs"       : false,    // ShellJS
-    "typed"         : false,    // Globals for typed array constructions
-    "worker"        : false,    // Web Workers
-    "wsh"           : false,    // Windows Scripting Host
-    "yui"           : false,    // Yahoo User Interface
-
-    // Custom Globals
-    "globals"       : {}        // additional predefined global variables
-}

+ 2 - 4
.travis.yml

@@ -1,7 +1,7 @@
 sudo: false
 language: node_js
 node_js:
-- '5'
+- '6'
 notifications:
   email: false
   slack:
@@ -17,6 +17,7 @@ deploy:
     branch: master
     repo: opensheetmusicdisplay/opensheetmusicdisplay
 after_deploy:
+- openssl aes-256-cbc -K $encrypted_170846311824_key -iv $encrypted_170846311824_iv -in bin/gh_pages_deploy_key.enc -out bin/gh_pages_deploy_key -d
 - eval "$(ssh-agent -s)"
 - chmod 600 bin/gh_pages_deploy_key
 - ssh-add bin/gh_pages_deploy_key
@@ -24,6 +25,3 @@ after_deploy:
 - git config --global user.name "travis"
 - chmod +x ./bin/publish_gh_page.sh
 - ./bin/publish_gh_page.sh
-before_install:
-- openssl aes-256-cbc -K $encrypted_170846311824_key -iv $encrypted_170846311824_iv
-  -in bin/gh_pages_deploy_key.enc -out bin/gh_pages_deploy_key -d

+ 1 - 0
AUTHORS

@@ -2,3 +2,4 @@ Andrea Condoluci <a.condoluci@phonicscore.com> (http://llliii.ooo)
 Oliver Hörbinger <o.hoerbinger@phonicscore.com> ()
 Matthias Uiberacker <m.uiberacker@phonicscore.com> ()
 Sebastian Haas <s.haas@phonicscore.com> (http://sebastianhaas.at)
+Benjamin Giesinger <benjamin.giesinger@gmail.com> (http://benjamingiesinger.de)

+ 37 - 10
CHANGELOG.md

@@ -1,22 +1,50 @@
-# Change Log
-All notable changes to this project will be documented in this file.
+## [0.3.0](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.1.0...0.3.0) (2018-05-03)
 
-The format is based on [Keep a Changelog](http://keepachangelog.com/) 
-and this project adheres to [Semantic Versioning](http://semver.org/).
 
-## [Unreleased]
-### Added
-### Changed
-### Bugfixes
+### Bug Fixes
+
+* **logging:** fixed problems connected to loglevel type definition changes ([eea535d](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/eea535d))
+* corrected begin instructions width (begin modifiers) to work also for extra instruction measures. ([1509a81](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/1509a81))
+* fixed a bug in stem calculation which made all stems up for a single voice. ([aeb670e](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/aeb670e))
+* fixed all broken file references in demo file selector. ([3659fec](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/3659fec))
+* fixed showing the staveconnector of multi-staved instruments at the end of the system. ([46ca911](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/46ca911))
+* refined position of Articulations (always close to note head). Remaining problem: Beam calculations change initial stem direction. Articulation positions need to be set after beams. ([22de162](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/22de162))
+* using backend select value already for initial OSMD constructor ([97aad81](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/97aad81))
+
+
+### Features
+
+* **Articulations:** Added ArticulationReader - articulations are read from xml ([f529f45](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/f529f45))
+* **clef:** Improved conversion of ClefInstructions to VexFlow clefs. Lines are now respected during ([473c85a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/473c85a)), closes [#110](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/110)
+* **engraving:** allow to change system labels' right margin ([#131](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/131)) ([be03289](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/be03289))
+* implemented setting stem direction automatically from voice type (main or linked voice) ([e7f9e10](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/e7f9e10))
+* optional element mode in key signature ([e85117a](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/e85117a)), closes [#108](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues/108)
+
+
+### Styles
+
+* moved linting from grunt to npm script ([8dafc27](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/commit/8dafc27))
+
+
+### BREAKING CHANGES
+
+* Running `grunt lint` is no longer possible.
+
+
+## [0.2.0](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.1.0...0.2.0) (2017-04-08)
+
+
+## [0.1.0](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.1.0-beta.5...0.1.0) (2016-09-23)
 
-## [0.1.0] - 2016-09-23
 ### Added
 - Added Reset button for cursor for demo
 - Added more xml files for demo and testing
 - Added unit tests for reading and calculating the xml files
 - Added logo as favicon and as img for demo site
+
 ### Changed
 - html site layout of demo
+
 ### Bugfixes
 - Fixed cursor functionality in demo
 
@@ -92,7 +120,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ### Added
 - First public pre-release
 
-[Unreleased]: https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.1.0-beta.5...HEAD
 [0.1.0-beta.5]: https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.1.0-beta.4...0.1.0-beta.5
 [0.1.0-beta.4]: https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.1.0-beta.3...0.1.0-beta.4
 [0.1.0-beta.3]: https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/compare/0.1.0-beta.2...0.1.0-beta.3

+ 0 - 172
Gruntfile.js

@@ -1,172 +0,0 @@
-/*global module*/
-module.exports = function (grunt) {
-    'use strict';
-
-    // The banner on top of the build
-    var banner = '/**\n' +
-        ' * Open Sheet Music Display <%= pkg.version %> built on <%= grunt.template.today("yyyy-mm-dd") %>.\n' +
-        ' * Copyright (c) 2016 PhonicScore\n' +
-        ' *\n' +
-        ' * https://github.com/opensheetmusicdisplay/opensheetmusicdisplay\n' +
-        ' */\n',
-        typings = [
-            'typings/index.d.ts',
-            // Additional manual typings:
-            'external/vexflow/vexflow.d.ts'
-        ];
-
-    // Paths
-    var src = ['src/**/*.ts'];
-    var test = ['test/**/*.ts'];
-
-    // Grunt configuration following:
-    grunt.initConfig({
-        pkg: grunt.file.readJSON('package.json'),
-        banner: '',
-        // Build output directories
-        outputDir: {
-            build: 'build',
-            dist: 'dist'
-        },
-        // Browserify
-        browserify: {
-            dist: {
-                src: ['src/OSMD/OSMD.ts'],
-                dest: '<%= outputDir.build %>/osmd.js',
-                options: {
-                    banner: '<%= banner %>',
-                    browserifyOptions: {
-                        standalone: 'opensheetmusicdisplay'
-                    }
-                }
-            },
-            debug: {
-                src: ['src/OSMD/OSMD.ts'],
-                dest: '<%= outputDir.build %>/osmd-debug.js',
-                options: {
-                    banner: '<%= banner %>',
-                    browserifyOptions: {
-                        debug: true,
-                        standalone: 'opensheetmusicdisplay'
-                    }
-                }
-            },
-            test: {
-                src: [].concat(typings, src, test),
-                dest: '<%= outputDir.build %>/osmd-test.js',
-                options: {
-                    banner: '<%= banner %>',
-                    browserifyOptions: {
-                        debug: true
-                    }
-                }
-            },
-            options: {
-                plugin: ['tsify']
-            }
-        },
-        // Uglify
-        uglify: {
-            options: {
-                compress: {
-                    'drop_console': true
-                },
-                banner: banner,
-                mangle: true,
-                mangleProperties: true
-            },
-            bundle: {
-                files: {
-                    'build/osmd.min.js': ['build/osmd.js']
-                }
-            }
-        },
-        // Karma setup
-        karma: {
-            // For continuous integration
-            ci: {
-                configFile: 'karma.conf.js',
-                options: {
-                    browsers: ['PhantomJS']
-                }
-            },
-            firefox: {
-                configFile: 'karma.conf.js',
-                options: {
-                    singleRun: false,
-                    browsers: ['Firefox']
-                }
-            },
-            chrome: {
-                configFile: 'karma.conf.js',
-                options: {
-                    singleRun: false,
-                    browsers: ['Chrome']
-                }
-            }
-        },
-        // Typescript compilation for ES6 module (npm package)
-        ts: {
-          default : {
-            tsconfig: true
-          }
-        },
-        // Cleaning task setup
-        clean: {
-            options: {
-                force: true
-            },
-            all: {
-                src: [
-                    '<%= outputDir.build %>',
-                    '<%= outputDir.dist %>',
-                    '.tscache',
-                    'src/**/*.js', 'test/**/*.js' // if something went wrong, delete JS from TypeScript source directories
-                ]
-            }
-        },
-        copy: {
-            demo: {
-                files: [
-                    { src: ['*'], dest: '<%= outputDir.build %>/demo/sheets/', cwd: './test/data/', expand: true },
-                    { src: ['*.js', '*.css', '*.html', '*.ico'], cwd: './demo/', expand: true, dest: '<%= outputDir.build %>/demo/' },
-                    { src: ['osmd-debug.js'], cwd: './build/', expand: true, dest: '<%= outputDir.build %>/demo/' }
-                ]
-            }
-        },
-        // http-server
-        'http-server': {
-            'demo': {
-                root: 'build/demo',
-                port: 8000,
-                host: '0.0.0.0',
-                showDir : true,
-                autoIndex: true,
-                runInBackground: false,
-                openBrowser : true
-            }
-        }
-
-    });
-
-    // Load npm tasks
-    grunt.loadNpmTasks('grunt-browserify');
-    grunt.loadNpmTasks('grunt-contrib-clean');
-    grunt.loadNpmTasks('grunt-contrib-copy');
-    grunt.loadNpmTasks('grunt-contrib-uglify');
-    grunt.loadNpmTasks('grunt-contrib-watch');
-    grunt.loadNpmTasks('grunt-http-server');
-    grunt.loadNpmTasks('grunt-karma');
-    grunt.loadNpmTasks('grunt-ts');
-
-    // Build tasks
-    grunt.registerTask('build:demo',  'Builds the demo.',                            ['browserify:debug', 'copy:demo']);
-    grunt.registerTask('build:test',  'Builds the tests',                            ['browserify:test']);
-    grunt.registerTask('build:dist',  'Builds for distribution on npm and Bower.',   ['browserify:dist', 'uglify', 'ts']);
-
-    // Tests
-    grunt.registerTask('test',        'Runs unit, regression and e2e tests.',        ['build:test', 'karma:ci']);
-
-    // Default task (if grunt is run without any argument, used in contiuous integration)
-    grunt.registerTask('default',     'Default task, running all other tasks. (CI)', ['test', 'build:demo', 'build:dist']);
-};

+ 5 - 4
README.md

@@ -1,9 +1,10 @@
-<img alt="OSMD logo" src="http://opensheetmusicdisplay.org/assets/osmd_logo.svg" width="200"/>
+<img alt="OSMD logo" src="https://opensheetmusicdisplay.org/assets/osmd_logo.svg" width="200"/>
 
 # OpenSheetMusicDisplay
 
 [![Greenkeeper badge](https://badges.greenkeeper.io/opensheetmusicdisplay/opensheetmusicdisplay.svg)](https://greenkeeper.io/)
-[![Build Status](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay.svg?branch=master)](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay)
+[![Travis Build Status](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay.svg?branch=master)](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay)
+[![Appveyor Build status](https://ci.appveyor.com/api/projects/status/r88lnffso55nq1ko?svg=true)](https://ci.appveyor.com/project/sebastianhaas/opensheetmusicdisplay/branch/master)
 [![Dependency Status](https://david-dm.org/opensheetmusicdisplay/opensheetmusicdisplay.png)](https://david-dm.org/opensheetmusicdisplay/opensheetmusicdisplay)
 [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/opensheetmusicdisplay/opensheetmusicdisplay?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Code Climate](https://codeclimate.com/github/opensheetmusicdisplay/opensheetmusicdisplay/badges/gpa.svg)](https://codeclimate.com/github/opensheetmusicdisplay/opensheetmusicdisplay)
@@ -11,13 +12,13 @@
 
 https://www.opensheetmusicdisplay.org/
 
-OpenSheeMusicDisplay is the missing link between MusicXML and VexFlow. Built upon many years of experience in both sheet music interactivity and engraving, it is the perfect solution for app developers seeking to build digital sheet music services.
+OpenSheetMusicDisplay is the missing link between MusicXML and VexFlow. Built upon many years of experience in both sheet music interactivity and engraving, it is the perfect solution for app developers seeking to build digital sheet music services.
 
 MusicXML is the de facto standard for sharing sheet music on the internet. VexFlow is widely used for rendering sheet music. It features an extensive note sign library attributable to its open source nature.
 
 OpenSheetMusicDisplay brings the two together and offers an open source turnkey solution for your digital sheet music project.
 
-Written in [TypeScript](https://www.typescriptlang.org) and released under [MIT license](#license).
+Written in [TypeScript](https://www.typescriptlang.org) and released under [MIT license](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/blob/develop/LICENSE).
 
 
 <!--# <a name="license"></a>License

+ 5 - 3
bin/publish_gh_page.sh

@@ -1,8 +1,8 @@
 #!/bin/bash
 
 # Prepare files to be published
+npm run build
 npm run docs
-grunt build:demo
 
 # Clone github page
 git clone git@github.com:opensheetmusicdisplay/opensheetmusicdisplay.github.io.git
@@ -10,10 +10,12 @@ cd opensheetmusicdisplay.github.io
 git status
 
 # Copy class documentation
-rsync -a ../build/docs/* ./classdoc
+rsync -a ../build/docs/* ./classdoc/
 
 # Copy demo application
-rsync -a ../build/demo/* ./demo
+rsync -a ../build/demo.min.js ./demo/
+rm -rf ./demo/sheets
+rsync -a ../test/data/* ./demo/sheets/
 
 # Commit and push changes
 git status

+ 71 - 48
demo/index.html

@@ -3,60 +3,83 @@
 <head>
     <meta charset="utf-8">
 
-    <title>OpenSheetMusicDisplay Demo</title>
+    <title><%= htmlWebpackPlugin.options.title %></title>
     <meta name="description" content="A showcase for OpenSheetMusicDisplay.">
     <meta name="author" content="OpenSheetMusicDisplay contributors">
-
-    <!-- Include opensheetmusicdisplay -->
-    <script src="osmd-debug.js"></script>
-
-    <!-- Include code and styles for this demo -->
-    <script src="demo.js"></script>
-    <link href="demo.css" media="all" rel="stylesheet"/>
+    <!-- Demo js file is included automatically by webpack druing build -->
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>    
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.js" type="text/javascript"></script>    
+    <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.css" media="all" rel="stylesheet"/>
     <link rel="icon" href="./favicon.ico?" type="image/x-icon"/>
 </head>
 <body>
+<br>
+<h1 class="ui centered header">
+    <img src="./favicon.ico?" class="ui image">
+    <%= htmlWebpackPlugin.options.title %>
+</h1>
+<div class="ui three column grid container">
+    <div class="column">
+        <h3 class="ui header">Select a sample:</h3>
+        <select class="ui selection dropdown" style="width:100px"  id="select"></select>
+    </div>
+    <div class="column">
+        <h3 class="ui header">Render backend:</h3>
+        <select class="ui dropdown" id="backend-select" value="svg">
+            <option value="svg">SVG</option>
+            <option value="canvas">Canvas</option>>
+        </select>
+    </div>
+    <div class="column">
+        <h3 class="ui header">Cursor controls:</h3>
+        <div class="ui buttons">
+            <div class="ui animated fade button" id="show-cursor-btn">
+                <div class="visible content">Show</div>
+                <div class="hidden content">
+                    <i class="eye icon"></i>
+                </div>
+            </div>
+            <div class="ui animated fade button" id="hide-cursor-btn">
+                <div class="visible content">Hide</div>
+                <div class="hidden content">
+                    <i class="eye slash icon"></i>
+                </div>
+            </div>
+            <div class="ui animated fade button" id="next-cursor-btn">
+                <div class="visible content">Next</div>
+                <div class="hidden content">
+                    <i class="arrow right icon"></i>
+                </div>
+            </div>
+            <div class="ui animated fade button" id="reset-cursor-btn">
+                <div class="visible content">Reset</div>
+                <div class="hidden content">
+                    <i class="undo icon"></i>
+                </div>
+            </div>
+        </div>
+    </div>         
+    <div class="column">
+        <h3 class="ui header">Zoom controls:</h3>
+        <div class="ui buttons">
+                <div class="ui button" id="zoom-in-btn">
+                    <i class="search plus icon"></i>
+                </div>
+                <div class="ui button" id="zoom-out-btn">
+                    <i class="search minus icon"></i>
+                </div>
+        </div>
+    </div>
+    <div class="column">
+        <h3 class="ui header">Zoom factor:</h3>
+        <h4 class="ui header" id="zoom-str">???</h4>
+    </div>
+    <div class="column">
+            <h3 class="ui header">Current width:</h3>                
+            <h4 class="ui header" id="size-str">???</h4>
+    </div>
+</div>
 <table cellspacing="0" style="max-width:700px;">
-    <tr>
-        <td>
-            <img src="./favicon.ico?" style="width:64px;height:64px;"/>
-        </td>
-        <td>
-            <h1>OpenSheetMusicDisplay Demo</h1>
-        </td>
-    </tr>
-</table>
-<table cellspacing="0" style="max-width:700px;">
-    <tr>
-        <td valign="top">
-            <select id="select"></select>
-            <p> Select a sample from the list ... </p>
-            <p>... or just drop your MusicXML file on this page.</p>
-        </td>
-        <td valign="top" halign="right">
-            <p>Cursor controls:
-                <input type="button" value="show" id="show-cursor-btn"/>
-                <input type="button" value="hide" id="hide-cursor-btn"/>
-                <input type="button" value="next" id="next-cursor-btn"/>
-                <input type="button" value="reset" id="reset-cursor-btn"/>
-            </p>
-            <p>
-                Zoom controls:
-                <input type="button" value="zoom in" id="zoom-in-btn"/>
-                <input type="button" value="zoom out" id="zoom-out-btn"/>
-            </p>
-            <table cellspacing="0">
-                <tr>
-                    <td>
-                        <p>Zoom factor: <span id="zoom-str">???</span>%</p>
-                    </td>
-                    <td>
-                        <p>Current width: <span id="size-str">???</span>px</p>
-                    </td>
-                </tr>
-            </table>
-        </td>
-    </tr>
     <tr id="error-tr">
         <td></td>
         <td id="error-td"></td>

+ 46 - 26
demo/demo.js → demo/index.js

@@ -1,29 +1,34 @@
+import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMusicDisplay';
+
 /*jslint browser:true */
 (function () {
     "use strict";
-    var OSMD;
+    var openSheetMusicDisplay;
     // The folder of the demo files
-    var folder = "sheets/",
+    var folder = process.env.STATIC_FILES_SUBFOLDER ? process.env.STATIC_FILES_SUBFOLDER + "/" : "",
     // The available demos
         demos = {
+            "Beethoven - An die ferne Geliebte": "Beethoven_AnDieFerneGeliebte.xml",
             "M. Clementi - Sonatina Op.36 No.1 Pt.1": "MuzioClementi_SonatinaOpus36No1_Part1.xml",
             "M. Clementi - Sonatina Op.36 No.1 Pt.2": "MuzioClementi_SonatinaOpus36No1_Part2.xml",
             "M. Clementi - Sonatina Op.36 No.3 Pt.1": "MuzioClementi_SonatinaOpus36No3_Part1.xml",
             "M. Clementi - Sonatina Op.36 No.3 Pt.2": "MuzioClementi_SonatinaOpus36No3_Part2.xml",
+            "J.S. Bach - Praeludium In C Dur BWV846 1": "JohannSebastianBach_PraeludiumInCDur_BWV846_1.xml",
             "J.S. Bach - Air": "JohannSebastianBach_Air.xml",
-            "G.P. Telemann - Sonata, TWV 40:102 - 1. Dolce": "TelemannWV40.102_Sonate-Nr.1.1-Dolce.xml",
             "C. Gounod - Meditation": "CharlesGounod_Meditation.xml",
-            "J.S. Bach - Praeludium In C Dur BWV846 1": "JohannSebastianBach_PraeludiumInCDur_BWV846_1.xml",
             "J. Haydn - Concertante Cello": "JosephHaydn_ConcertanteCello.xml",
+            "Mozart - An Chloe": "Mozart_AnChloe.xml",
+            "Mozart - Das Veilchen": "Mozart_DasVeilchen.xml",
+            "Mozart - Trio": "MozartTrio.mxl",
             "S. Joplin - Elite Syncopations": "ScottJoplin_EliteSyncopations.xml",
             "S. Joplin - The Entertainer": "ScottJoplin_The_Entertainer.xml",
             "ActorPreludeSample": "ActorPreludeSample.xml",
-            "an chloe - mozart": "an chloe - mozart.xml",
-            "Beethoven - AnDieFerneGeliebte": "AnDieFerneGeliebte_Beethoven.xml",
-            "das veilchen - mozart": "das veilchen - mozart.xml",
-            "Dichterliebe01": "Dichterliebe01.xml",
-            "mandoline - debussy": "mandoline - debussy.xml",
-            "MozartTrio": "MozartTrio.mxl",
+            "R. Schumann - Dichterliebe": "Dichterliebe01.xml",
+            "C. Debussy - Mandoline": "Debussy_Mandoline.xml",
+            "France Levasseur - Parlez Mois": "Parlez-moi.mxl",
+            "Telemann - Sonate-Nr.1.1-Dolce": "TelemannWV40.102_Sonate-Nr.1.1-Dolce.xml",
+            "Telemann - Sonate-Nr.1.2-Allegro": "TelemannWV40.102_Sonate-Nr.1.2-Allegro-F-Dur.xml",
+            "Saltarello": "Saltarello.mxl",
         },
 
         zoom = 1.0,
@@ -40,7 +45,8 @@
         nextCursorBtn,
         resetCursorBtn,
         showCursorBtn,
-        hideCursorBtn;
+        hideCursorBtn,
+        backendSelect;
 
     // Initialization code
     function init() {
@@ -59,6 +65,7 @@
         resetCursorBtn = document.getElementById("reset-cursor-btn");
         showCursorBtn = document.getElementById("show-cursor-btn");
         hideCursorBtn = document.getElementById("hide-cursor-btn");
+        backendSelect = document.getElementById("backend-select");
 
         // Hide error
         error();
@@ -74,6 +81,9 @@
         }
         select.onchange = selectOnChange;
 
+        // Pre-select default music piece
+        select.value = "MuzioClementi_SonatinaOpus36No1_Part1.xml";
+
         custom.appendChild(document.createTextNode("Custom"));
 
         // Create zoom controls
@@ -87,8 +97,8 @@
         };
 
         // Create OSMD object and canvas
-        OSMD = new opensheetmusicdisplay.OSMD(canvas);
-        OSMD.setLogLevel('info');
+        openSheetMusicDisplay = new OpenSheetMusicDisplay(canvas, false, backendSelect.value);
+        openSheetMusicDisplay.setLogLevel('info');
         document.body.appendChild(canvas);
 
         // Set resize event handler
@@ -100,7 +110,7 @@
                 var width = document.body.clientWidth;
                 canvas.width = width;
                 try {
-                OSMD.render();
+                openSheetMusicDisplay.render();
                 } catch (e) {}
                 enable();
             }
@@ -109,20 +119,30 @@
         window.addEventListener("keydown", function(e) {
             var event = window.event ? window.event : e;
             if (event.keyCode === 39) {
-                OSMD.cursor.next();
+                openSheetMusicDisplay.cursor.next();
             }
         });
         nextCursorBtn.addEventListener("click", function() {
-            OSMD.cursor.next();
+            openSheetMusicDisplay.cursor.next();
         });
         resetCursorBtn.addEventListener("click", function() {
-            OSMD.cursor.reset();
+            openSheetMusicDisplay.cursor.reset();
         });
         hideCursorBtn.addEventListener("click", function() {
-            OSMD.cursor.hide();
+            openSheetMusicDisplay.cursor.hide();
         });
         showCursorBtn.addEventListener("click", function() {
-            OSMD.cursor.show();
+            openSheetMusicDisplay.cursor.show();
+        });
+
+        backendSelect.addEventListener("change", function(e) {
+            var value = e.target.value;
+            // clears the canvas element
+            canvas.innerHTML = "";
+            openSheetMusicDisplay = new OpenSheetMusicDisplay(canvas, false, value);
+            openSheetMusicDisplay.setLogLevel('info');
+            selectOnChange();
+
         });
     }
 
@@ -162,9 +182,9 @@
             str = folder + select.value;
         }
         zoom = 1.0;
-        OSMD.load(str).then(
+        openSheetMusicDisplay.load(str).then(
             function() {
-                return OSMD.render();
+                return openSheetMusicDisplay.render();
             },
             function(e) {
                 error("Error reading sheet: " + e);
@@ -173,7 +193,7 @@
             function() {
                 return onLoadingEnd(isCustom);
             }, function(e) {
-                error("Error rendering sheet: " + e);
+                error("Error rendering sheet: " + process.env.DEBUG ? e.stack : e);
                 onLoadingEnd(isCustom);
             }
         );
@@ -189,15 +209,15 @@
     }
 
     function logCanvasSize() {
-        size.innerHTML = canvas.offsetWidth;
-        zoomDiv.innerHTML = Math.floor(zoom * 100.0);
+        size.innerHTML = canvas.offsetWidth + "px";
+        zoomDiv.innerHTML = Math.floor(zoom * 100.0) + "%";
     }
 
     function scale() {
         disable();
         window.setTimeout(function(){
-            OSMD.zoom = zoom;
-            OSMD.render();
+            openSheetMusicDisplay.zoom = zoom;
+            openSheetMusicDisplay.render();
             enable();
         }, 0);
     }

+ 116 - 15
external/vexflow/vexflow.d.ts

@@ -1,4 +1,7 @@
+
+
 declare namespace Vex {
+
     export module Flow {
         const RESOLUTION: any;
 
@@ -27,6 +30,16 @@ declare namespace Vex {
             public getW(): number;
 
             public getH(): number;
+
+            public draw(ctx: Vex.Flow.RenderContext): void;
+        }
+
+        export class Tickable {
+            public reset(): void;
+
+            public setStave(stave: Stave);
+
+            public getBoundingBox(): BoundingBox;
         }
 
         export class Voice {
@@ -34,6 +47,10 @@ declare namespace Vex {
 
             public static Mode: any;
 
+            public context: RenderContext;
+
+            public tickables: Tickable[];
+
             public getBoundingBox(): BoundingBox;
 
             public setStave(stave: Stave): Voice;
@@ -47,7 +64,19 @@ declare namespace Vex {
             public draw(ctx: any, stave: Stave): void;
         }
 
-        export class StaveNote {
+        export class Note extends Tickable {
+        }
+
+        export class Stem {
+            public static UP: number;
+            public static DOWN: number;
+        }
+        export class StemmableNote extends Note {
+            public getStemDirection(): number;
+            public setStemDirection(direction: number): StemmableNote;
+        }
+
+        export class StaveNote extends StemmableNote {
             constructor(note_struct: any);
 
             public getNoteHeadBounds(): any;
@@ -56,10 +85,14 @@ declare namespace Vex {
 
             public getNoteHeadEndX(): number;
 
+            public getGlyphWidth(): number;
+
             public addAccidental(index: number, accidental: Accidental): StaveNote;
 
             public addAnnotation(index: number, annotation: Annotation): StaveNote;
 
+            public addModifier(index: number, modifier: Modifier): StaveNote;
+
             public setStyle(style: any): void;
 
             public addDotToAll(): void;
@@ -68,7 +101,7 @@ declare namespace Vex {
         export class StaveTie {
             constructor(notes_struct: any);
 
-            public setContext(ctx: CanvasContext): StaveTie;
+            public setContext(ctx: RenderContext): StaveTie;
 
             public draw(): void;
         }
@@ -82,6 +115,10 @@ declare namespace Vex {
 
             public getX(): number;
 
+            public setBegBarType(type: any): Stave;
+
+            public setEndBarType(type: any): Stave;
+
             public addClef(clefSpec: string, size: any, annotation: any, position: any): void;
 
             public setEndClef(clefSpec: string, size: any, annotation: any): void;
@@ -96,12 +133,16 @@ declare namespace Vex {
 
             public getNoteStartX(): number;
 
+            public getModifierXShift(): number;
+
             public getNoteEndX(): number;
 
             public setNoteStartX(x: number): Stave;
 
             public setKeySignature(keySpec: any, cancelKeySpec: any, position: any): Stave;
 
+            public setText(text: string, position: number, options: any): void;
+
             public format(): void;
 
             public getSpacingBetweenLines(): number;
@@ -110,8 +151,11 @@ declare namespace Vex {
 
             public getLineForY(y: number): number;
 
+            public getYForLine(y: number): number;
+
             public getModifiers(pos: any, cat: any): Clef[]; // FIXME
-            public setContext(ctx: CanvasContext): Stave;
+
+            public setContext(ctx: RenderContext): Stave;
 
             public addModifier(mod: any, pos: any): void;
 
@@ -128,9 +172,31 @@ declare namespace Vex {
             public getWidth(): number;
 
             public getPadding(index: number): number;
+
+            public getPosition(): number;
+
+            public setPosition(position: number): Modifier;
         }
 
+
         export class StaveModifier extends Modifier {
+            public static get Position() {
+                return {
+                    LEFT: 1,
+                    RIGHT: 2,
+                    ABOVE: 3,
+                    BELOW: 4,
+                    BEGIN: 5,
+                    END: 6,
+                };
+            }
+
+            public getPosition(): number;
+
+        }
+
+        export class Repetition extends StaveModifier {
+            constructor(type: any, x: number, y_shift: number);
         }
 
         export class Clef extends StaveModifier {
@@ -148,19 +214,24 @@ declare namespace Vex {
         }
 
         export class Renderer {
-            constructor(canvas: HTMLCanvasElement, backend: any);
+            constructor(canvas: HTMLElement, backend: number);
 
-            public static Backends: any;
+            public static Backends: {
+                CANVAS: number,
+                RAPHAEL: number,
+                SVG: number,
+                VML: number
+            };
 
             public resize(a: number, b: number): void;
 
-            public getContext(): CanvasContext;
+            public getContext(): CanvasContext | SVGContext;
         }
 
-        export class TimeSignature {
+        export class TimeSignature extends StaveModifier {
             constructor(timeSpec: string, customPadding?: any);
         }
-        export class KeySignature {
+        export class KeySignature extends StaveModifier {
             constructor(keySpec: string, cancelKeySpec: string, alterKeySpec?: string);
         }
 
@@ -172,24 +243,43 @@ declare namespace Vex {
             constructor(type: string);
         }
 
+        export class Articulation extends Modifier {
+            constructor(type: string);
+        }
+
         export class Beam {
             constructor(notes: StaveNote[], auto_stem: boolean);
 
-            public setContext(ctx: CanvasContext): Beam;
+            public setContext(ctx: RenderContext): Beam;
 
             public draw(): void;
         }
 
         export class Tuplet {
-            constructor(notes: StaveNote[]);
+            constructor(notes: StaveNote[], options: any);
 
-            public setContext(ctx: CanvasContext): Tuplet;
+            public setContext(ctx: RenderContext): Tuplet;
 
             public draw(): void;
         }
 
-        export class CanvasContext {
-            public scale(x: number, y: number): CanvasContext;
+        export class RenderContext {
+            public scale(x: number, y: number): RenderContext;
+            public fillRect(x: number, y: number, width: number, height: number): RenderContext
+            public fillText(text: string, x: number, y: number): RenderContext;
+            public setFont(family: string, size: number, weight: string): RenderContext;
+            public save(): RenderContext;
+            public restore(): RenderContext;
+        }
+
+        export class CanvasContext extends RenderContext {
+            public vexFlowCanvasContext: CanvasRenderingContext2D;
+        }
+
+        export class SVGContext extends RenderContext {
+            public svg: SVGElement;
+            public attributes: any;
+            public state: any;
         }
 
         export class StaveConnector {
@@ -199,11 +289,22 @@ declare namespace Vex {
 
             public setType(type: any): StaveConnector;
 
-            public setContext(ctx: CanvasContext): StaveConnector;
+            public setContext(ctx: RenderContext): StaveConnector;
+
+            public setXShift(shift: number): StaveConnector;
+
+            public top_stave: Stave;
+
+            public bottom_stave: Stave;
+
+            public thickness: number;
+
+            public width: number;
+
+            public x_shift: number;
 
             public draw(): void;
         }
-
     }
 }
 

+ 48 - 13
karma.conf.js

@@ -1,8 +1,7 @@
-// Karma configuration
-// Generated on Fri Feb 05 2016 12:36:08 GMT+0100 (CET)
-/*globals module*/
+var common = require('./webpack.common.js')
+
 module.exports = function (config) {
-    'use strict';
+    'use strict'
     config.set({
         // base path that will be used to resolve all patterns (eg. files, exclude)
         basePath: '',
@@ -15,13 +14,11 @@ module.exports = function (config) {
         exclude: [],
 
         files: [{
-            pattern: 'build/osmd-test.js'
-        }, {
             pattern: 'src/**/*.ts',
             included: false
         }, {
             pattern: 'test/**/*.ts',
-            included: false
+            included: true
         }, {
             pattern: 'test/data/*.xml',
             included: true
@@ -39,7 +36,35 @@ module.exports = function (config) {
         // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
         preprocessors: {
             'test/data/*.xml': ['xml2js'],
-            'test/data/*.mxl.base64': ['base64-to-js']
+            'test/data/*.mxl.base64': ['base64-to-js'],
+            // add webpack as preprocessor
+            'src/**/*.ts': ['webpack'],
+            'test/**/*.ts': ['webpack']
+        },
+
+        webpack: {
+            // karma watches the test entry points
+            // (you don't need to specify the entry option)
+            // webpack watches dependencies
+
+            // copy parts of webpack configuration to use minimal effort here
+            devtool: process.env.CI ? false : 'cheap-module-eval-source-map',
+            mode: process.env.CI ? 'production' : 'development',
+            module: {
+                rules: common.module.rules
+            },
+            resolve: common.resolve
+        },
+        webpackMiddleware: {
+            // webpack-dev-middleware configuration
+            // i. e.
+            noInfo: true
+        },
+
+        // Required for Firefox and Chorme to work
+        // see https://github.com/webpack-contrib/karma-webpack/issues/188
+        mime: {
+            'text/x-typescript': ['ts', 'tsx']
         },
 
         // test results reporter to use
@@ -49,7 +74,9 @@ module.exports = function (config) {
 
         // web server port
         port: 9876,
-
+        // timeout in ms:
+        browserNoActivityTimeout: 100000,
+        captureTimeout: 60000,
         // enable / disable colors in the output (reporters and logs)
         colors: true,
 
@@ -65,8 +92,16 @@ module.exports = function (config) {
         autoWatch: false,
 
         // start these browsers
-        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
-        browsers: [], // Will be overruled by karma grunt task options
+        browsers: [process.env.CI ? 'ChromeHeadlessNoSandbox' : 'ChromeHeadless'],
+
+        // For security reasons, Google Chrome is unable to provide sandboxing
+        // when it is running in container-based environments (e.g. CI).
+        customLaunchers: {
+            ChromeHeadlessNoSandbox: {
+                base: 'ChromeHeadless',
+                flags: ['--no-sandbox']
+            }
+        },
 
         // Continuous Integration mode
         // if true, Karma captures browsers, runs the tests and exits
@@ -75,5 +110,5 @@ module.exports = function (config) {
         // Concurrency level
         // how many browser should be started simultaneous
         concurrency: Infinity
-    });
-};
+    })
+}

+ 53 - 44
package.json

@@ -1,34 +1,34 @@
 {
   "name": "opensheetmusicdisplay",
-  "version": "0.2.1",
+  "version": "0.3.0",
   "description": "An open source JavaScript engine for displaying MusicXML based on VexFlow.",
-  "main": "dist/src/OSMD/OSMD.js",
-  "typings": "dist/src/OSMD/OSMD",
+  "main": "build/opensheetmusicdisplay.min.js",
+  "typings": "build/dist/src/OpenSheetMusicDisplay/OpenSheetMusicDisplay",
   "scripts": {
-    "docs": "typedoc --mode file --out build/docs --module commonjs --target ES5 --name opensheetmusicdisplay ./src",
-    "lint": "npm run jshint && npm run tslint",
-    "jshint": "jshint . --exclude node_modules,dist,build,bin,demo",
-    "test": "npm run lint && grunt test",
-    "tslint": "tslint --type-check --project tsconfig.json \"src/**/*.ts\" \"test/**/*.ts\"",
-    "typedoc": "typedoc --out ./build/docs --name OpenSheetMusicDisplay --module commonjs --target ES5 --mode file ./src/**/*.ts",
-    "postinstall": "rimraf typings",
-    "prepublish": "grunt build:dist"
+    "docs": "typedoc --out ./build/docs --name OpenSheetMusicDisplay --module commonjs --target ES5 --ignoreCompilerErrors --mode file ./src",
+    "eslint": "eslint .",
+    "tslint": "tslint --project tsconfig.json \"src/**/*.ts\" \"test/**/*.ts\"",
+    "lint": "npm-run-all eslint tslint",
+    "test": "karma start --single-run --no-auto-watch",
+    "test:watch": "karma start --no-single-run --auto-watch --browsers Chrome",
+    "prepare": "npm run build",
+    "build": "npm-run-all lint build:webpack",
+    "build:doc": "cross-env STATIC_FILES_SUBFOLDER=sheets npm run build",
+    "build:webpack": "webpack --progress --colors --config webpack.prod.js",
+    "build:webpack-dev": "webpack --progress --colors --config webpack.dev.js",
+    "start": "webpack-dev-server --progress --colors --config webpack.dev.js",
+    "fix-memory-limit": "increase-memory-limit"
   },
   "pre-commit": [
     "lint"
   ],
   "files": [
-    "dist",
+    "build/dist/src",
+    "build/opensheetmusicdisplay.min.js",
     "AUTHORS",
     "CHANGELOG.md",
     "README.md",
-    "karma.conf.js",
-    "src",
-    "external",
-    "demo",
-    "tsconfig.json",
-    "tslint.json",
-    "Gruntfile.js"
+    "external"
   ],
   "repository": {
     "type": "git",
@@ -48,47 +48,56 @@
   "homepage": "http://opensheetmusicdisplay.org",
   "dependencies": {
     "es6-promise": "^4.0.5",
+    "increase-memory-limit": "^1.0.6",
     "jszip": "^3.0.0",
-    "loglevel": "^1.4.1",
+    "loglevel": "^1.5.0",
     "shortid": "^2.2.6",
     "typescript-collections": "^1.1.2",
-    "vexflow": "^1.2.53"
+    "vexflow": "^1.2.84"
   },
   "devDependencies": {
-    "@types/chai": "^3.4.35",
+    "@types/chai": "^4.0.3",
     "@types/loglevel": "^1.4.29",
     "@types/mocha": "^2.2.40",
-    "browserify": "^14.0.0",
-    "chai": "^3.5.0",
+    "chai": "^4.1.0",
+    "clean-webpack-plugin": "^0.1.18",
+    "cross-env": "^5.1.3",
     "cz-conventional-changelog": "^2.0.0",
-    "grunt": "^1.0.1",
-    "grunt-browserify": "^5.0.0",
-    "grunt-contrib-clean": "^1.0.0",
-    "grunt-contrib-copy": "^1.0.0",
-    "grunt-contrib-uglify": "^2.0.0",
-    "grunt-contrib-watch": "^1.0.0",
-    "grunt-http-server": "",
-    "grunt-karma": "^2.0.0",
-    "grunt-ts": "^6.0.0-beta.3",
-    "http-server": "^0.9.0",
-    "jshint": "^2.9.4",
-    "karma": "^1.1.1",
+    "eslint": "^4.18.1",
+    "eslint-config-standard": "^11.0.0",
+    "eslint-plugin-import": "^2.9.0",
+    "eslint-plugin-node": "^6.0.1",
+    "eslint-plugin-promise": "^3.6.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "file-loader": "^1.1.8",
+    "html-webpack-plugin": "^3.1.0",
+    "http-server": "^0.11.0",
+    "jquery": "^3.2.1",
+    "karma": "^2.0.0",
     "karma-base64-to-js-preprocessor": "^0.0.1",
     "karma-chai": "^0.1.0",
-    "karma-chrome-launcher": "^2.0.0",
+    "karma-chrome-launcher": "^2.2.0",
     "karma-firefox-launcher": "^1.0.0",
     "karma-mocha": "^1.1.1",
     "karma-mocha-reporter": "^2.0.4",
-    "karma-phantomjs-launcher": "^1.0.1",
+    "karma-webpack": "^3.0.0",
     "karma-xml2js-preprocessor": "^0.0.3",
-    "mocha": "^3.0.1",
-    "phantomjs-prebuilt": "^2.1.8",
+    "mocha": "^4.1.0",
+    "npm-run-all": "^4.1.2",
     "pre-commit": "^1.2.2",
-    "rimraf": "^2.6.1",
+    "ts-loader": "^4.1.0",
     "tsify": "^3.0.0",
-    "tslint": "^5.0.0",
-    "typedoc": "^0.5.7",
-    "typescript": "^2.2.2"
+    "tslint": "^5.8.0",
+    "tslint-loader": "^3.5.3",
+    "typedoc": "^0.11.1",
+    "typescript": "^2.6.1",
+    "uglifyjs-webpack-plugin": "^1.2.4",
+    "underscore-template-loader": "^1.0.0",
+    "webpack": "^4.2.0",
+    "webpack-cli": "^2.0.13",
+    "webpack-dev-server": "3.1.1",
+    "webpack-merge": "^4.1.2",
+    "webpack-visualizer-plugin": "^0.1.11"
   },
   "config": {
     "commitizen": {

+ 394 - 338
src/Common/DataObjects/Fraction.ts

@@ -5,379 +5,435 @@
  * A class representing mathematical fractions, which have a numerator and a denominator.
  */
 export class Fraction {
-    private static maximumAllowedNumber: number = 46340;
-    private numerator: number = 0;
-    private denominator: number = 1;
-    private realValue: number;
-
-    /**
-     * Returns the maximum of two fractions (does not clone)
-     * @param f1
-     * @param f2
-     * @returns {Fraction}
-     */
-    public static max(f1: Fraction, f2: Fraction): Fraction {
-        if (f1.RealValue > f2.RealValue) {
-            return f1;
-        } else {
-            return f2;
-        }
+  private static maximumAllowedNumber: number = 46340; // sqrt(int.Max) --> signed int with 4 bytes (2^31)
+  private numerator: number = 0;
+  private denominator: number = 1;
+  private wholeValue: number = 0;
+  private realValue: number;
+
+  /**
+   * Returns the maximum of two fractions (does not clone)
+   * @param f1
+   * @param f2
+   * @returns {Fraction}
+   */
+  public static max(f1: Fraction, f2: Fraction): Fraction {
+    if (f1.RealValue > f2.RealValue) {
+      return f1;
+    } else {
+      return f2;
     }
-
-    public static Equal(f1: Fraction, f2: Fraction): boolean {
-        return f1.Denominator === f2.Denominator && f1.Numerator === f2.Numerator;
+  }
+
+  public static Equal(f1: Fraction, f2: Fraction): boolean {
+    return f1.wholeValue === f2.wholeValue && f1.Denominator === f2.Denominator && f1.Numerator === f2.Numerator;
+  }
+
+  /**
+   * The same as Fraction.clone
+   * @param fraction
+   * @returns {Fraction}
+   */
+  public static createFromFraction(fraction: Fraction): Fraction {
+    return new Fraction(fraction.numerator, fraction.denominator, fraction.wholeValue, false);
+  }
+
+  public static plus(f1: Fraction, f2: Fraction): Fraction {
+    const sum: Fraction = f1.clone();
+    sum.Add(f2);
+    return sum;
+  }
+
+  public static minus(f1: Fraction, f2: Fraction): Fraction {
+    const sum: Fraction = f1.clone();
+    sum.Sub(f2);
+    return sum;
+  }
+
+  private static greatestCommonDenominator(a: number, b: number): number {
+    if (a === 0) {
+      return b;
     }
 
-    /**
-     * The same as Fraction.clone
-     * @param fraction
-     * @returns {Fraction}
-     */
-    public static createFromFraction(fraction: Fraction): Fraction {
-        return new Fraction(fraction.numerator, fraction.denominator);
+    if (b === 1) {
+      return 1;
     }
 
-    public static plus (f1: Fraction, f2: Fraction): Fraction {
-        let sum: Fraction = f1.clone();
-        sum.Add(f2);
-        return sum;
+    while (b !== 0) {
+      if (a > b) {
+        a -= b;
+      } else {
+        b -= a;
+      }
     }
 
-    public static minus(f1: Fraction , f2: Fraction): Fraction {
-        let sum: Fraction = f1.clone();
-        sum.Sub(f2);
-        return sum;
+    return a;
+  }
+
+  /**
+   *
+   * @param numerator
+   * @param denominator
+   * @param wholeValue - the integer number, needed for values greater than 1
+   * @param simplify - If simplify is true, then the fraction is simplified
+   *      to make both the numerator and denominator coprime, and less than maximumAllowedNumber.
+   */
+  constructor(numerator: number = 0, denominator: number = 1, wholeValue: number = 0, simplify: boolean = true) {
+    this.numerator = numerator;
+    this.denominator = denominator;
+    this.wholeValue = wholeValue;
+
+    if (simplify) {
+      this.simplify();
     }
+    this.setRealValue();
+  }
 
-    private static greatestCommonDenominator(a: number, b: number): number {
-        if (a === 0) {
-            return b;
-        }
-
-        if (b === 1) {
-            return 1;
-        }
-
-        while (b !== 0) {
-            if (a > b) {
-                a -= b;
-            } else {
-                b -= a;
-            }
-        }
-
-        return a;
+  public toString(): string {
+    let result: string = this.numerator + "/" + this.denominator;
+    if (this.wholeValue !== 0) {
+      result = this.wholeValue + " " + result;
     }
 
-    /**
-     *
-     * @param numerator
-     * @param denominator
-     * @param simplify - If simplify is true, then the fraction is simplified
-     *      to make both the numerator and denominator coprime, and less than maximumAllowedNumber.
-     */
-    constructor(numerator: number = 0, denominator: number = 1, simplify: boolean = true) {
-        this.numerator = numerator;
-        this.denominator = denominator;
-
-        if (simplify) { this.simplify(); }
-        this.setRealValue();
-    }
+    return result;
+  }
 
-    public toString(): string {
-        return this.numerator + "/" + this.denominator;
-    }
+  public clone(): Fraction {
+    return new Fraction(this.numerator, this.denominator, this.wholeValue, false);
+  }
 
-    public clone(): Fraction {
-        return new Fraction(this.numerator, this.denominator, false);
-    }
+  public get Numerator(): number {
+    return this.numerator;
+  }
 
-    public get Numerator(): number {
-        return this.numerator;
+  public set Numerator(value: number) {
+    if (this.numerator !== value) {
+      this.numerator = value;
+      this.simplify();
+      this.setRealValue();
     }
+  }
 
-    public set Numerator(value: number) {
-        if (this.numerator !== value) {
-            this.numerator = value;
-            this.simplify();
-            this.setRealValue();
-        }
-    }
+  public get Denominator(): number {
+    return this.denominator;
+  }
 
-    public get Denominator(): number {
-        return this.denominator;
+  public set Denominator(value: number) {
+    if (this.denominator !== value) {
+      this.denominator = value;
+      // don't simplify in case of a GraceNote (need it in order to set the right symbol)
+      if (this.numerator !== 0) {
+        this.simplify();
+      }
+      this.setRealValue();
     }
+  }
 
-    public set Denominator(value: number) {
-        if (this.denominator !== value) {
-            this.denominator = value;
-            if (this.numerator !== 0) {
-                this.simplify();
-            }
-            this.setRealValue();
-        }
-    }
+  public get WholeValue(): number {
+    return this.wholeValue;
+  }
 
-    public get RealValue(): number {
-        return this.realValue;
+  public set WholeValue(value: number) {
+    if (this.wholeValue !== value) {
+      this.wholeValue = value;
+      this.setRealValue();
     }
-
-    public multiplyWithFactor(factor: number): void {
-        this.numerator *= factor;
-        this.denominator *= factor;
+  }
+
+  /**
+   * Returns the unified numerator where the whole value will be expanded
+   * with the denominator and added to the existing numerator.
+   */
+  public GetExpandedNumerator(): number {
+    return this.wholeValue * this.denominator + this.numerator;
+  }
+
+  public IsNegative(): boolean {
+    return this.realValue < 0;
+  }
+
+  public get RealValue(): number {
+    return this.realValue;
+  }
+
+  public expand(expansionValue: number): void {
+    this.numerator *= expansionValue;
+    this.denominator *= expansionValue;
+    if (this.wholeValue !== 0) {
+      this.numerator += this.wholeValue * this.denominator;
+      this.wholeValue = 0;
     }
-
-    public multiplyDenominatorWithFactor(factor: number): void {
-        this.denominator *= factor;
-        this.setRealValue();
+  }
+
+  // public multiplyDenominatorWithFactor(factor: number): void {
+  //   this.denominator *= factor;
+  //   this.setRealValue();
+  // }
+
+  public Add(fraction: Fraction): void {
+    // normally should check if denominator or fraction.denominator is 0 but in our case
+    // a zero denominator doesn't make sense
+    this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator +
+      (fraction.wholeValue * fraction.denominator + fraction.numerator) * this.denominator;
+    this.denominator = this.denominator * fraction.denominator;
+    this.wholeValue = 0;
+    this.simplify();
+    this.setRealValue();
+  }
+
+  public Sub(fraction: Fraction): void {
+    // normally should check if denominator or fraction.denominator is 0 but in our case
+    // a zero denominator doesn't make sense
+    this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator -
+      (fraction.wholeValue * fraction.denominator + fraction.numerator) * this.denominator;
+    this.denominator = this.denominator * fraction.denominator;
+    this.wholeValue = 0;
+    this.simplify();
+    this.setRealValue();
+  }
+  /**
+   * Brute Force quanization by searching incremental with the numerator until the denominator is
+   * smaller/equal than the desired one.
+   * @param maxAllowedDenominator
+   */
+  public Quantize(maxAllowedDenominator: number): Fraction {
+    if (this.denominator <= maxAllowedDenominator) {
+      return this;
     }
 
-    public Add(fraction: Fraction): void {
-        this.numerator = this.numerator * fraction.denominator + fraction.numerator * this.denominator;
-        this.denominator = this.denominator * fraction.denominator;
-        this.simplify();
-        this.setRealValue();
+    const upTestFraction: Fraction = new Fraction(this.numerator + 1, this.denominator, this.wholeValue);
 
+    while (upTestFraction.Denominator > maxAllowedDenominator) {
+      upTestFraction.Numerator++;
     }
 
-    public Sub(fraction: Fraction): void {
-        this.numerator = this.numerator * fraction.denominator - fraction.numerator * this.denominator;
-        this.denominator = this.denominator * fraction.denominator;
-        this.simplify();
-        this.setRealValue();
-    }
+    if (this.numerator > this.denominator) {
+      const downTestFraction: Fraction = new Fraction(this.numerator - 1, this.denominator, this.wholeValue);
 
-    public Quantize(maxAllowedDenominator: number): Fraction {
-        if (this.denominator <= maxAllowedDenominator) {
-            return this;
-        }
+      while (downTestFraction.Denominator > maxAllowedDenominator) {
+        downTestFraction.Numerator--;
+      }
 
-        let upTestFraction: Fraction = new Fraction(this.numerator + 1, this.denominator);
-
-        while (upTestFraction.Denominator > maxAllowedDenominator) {
-            upTestFraction.Numerator++;
-        }
-
-        if (this.numerator > this.denominator) {
-            let downTestFraction: Fraction = new Fraction(this.numerator - 1, this.denominator);
-
-            while (downTestFraction.Denominator > maxAllowedDenominator) {
-                downTestFraction.Numerator--;
-            }
-
-            if (downTestFraction.Denominator < upTestFraction.Denominator) {
-                return downTestFraction;
-            }
-        }
-        return upTestFraction;
+      if (downTestFraction.Denominator < upTestFraction.Denominator) {
+        return downTestFraction;
+      }
     }
-
-    public Equals(obj: Fraction): boolean {
-        return this.RealValue === obj.RealValue;
-    }
-
-    public CompareTo(obj: Fraction): number {
-        let diff: number = this.numerator * obj.Denominator - this.denominator * obj.Numerator;
-        // Return the sign of diff
-        return diff ? diff < 0 ? -1 : 1 : 0;
+    return upTestFraction;
+  }
+
+  public Equals(obj: Fraction): boolean {
+    return this.realValue === obj.realValue;
+  }
+
+  public CompareTo(obj: Fraction): number {
+    const diff: number = this.realValue - obj.realValue;
+    // Return the sign of diff
+    return diff ? diff < 0 ? -1 : 1 : 0;
+  }
+
+  public lt(frac: Fraction): boolean {
+    return this.realValue < frac.realValue;
+  }
+
+  public lte(frac: Fraction): boolean {
+    return this.realValue <= frac.realValue;
+  }
+
+  //public Equals(f: Fraction): boolean {
+  //    if (ReferenceEquals(this, f))
+  //        return true;
+  //    if (ReferenceEquals(f, undefined))
+  //        return false;
+  //    return <number>this.numerator * f.denominator === <number>f.numerator * this.denominator;
+  //}
+
+  private setRealValue(): void {
+    this.realValue = this.wholeValue + this.numerator / this.denominator;
+  }
+
+  private simplify(): void {
+    // don't simplify in case of a GraceNote (need it in order to set the right symbol)
+    if (this.numerator === 0) {
+      this.denominator = 1;
+      return;
     }
 
-    public lt(frac: Fraction): boolean {
-        return (this.numerator * frac.Denominator - this.denominator * frac.Numerator) < 0;
-    }
-    public lte(frac: Fraction): boolean {
-        return (this.numerator * frac.Denominator - this.denominator * frac.Numerator) <= 0;
-    }
+    // normally should check if denominator or fraction.denominator is 0 but in our case a zero denominator
+    // doesn't make sense. Could probably be optimized
+    const i: number = Fraction.greatestCommonDenominator(Math.abs(this.numerator), Math.abs(this.denominator));
 
-    //public Equals(f: Fraction): boolean {
-    //    if (ReferenceEquals(this, f))
-    //        return true;
-    //    if (ReferenceEquals(f, undefined))
-    //        return false;
-    //    return <number>this.numerator * f.denominator === <number>f.numerator * this.denominator;
-    //}
+    this.numerator /= i;
+    this.denominator /= i;
 
-    public GetInversion(): Fraction {
-        return new Fraction(this.denominator, this.numerator);
+    const whole: number = Math.floor(this.numerator / this.denominator);
+    if (whole !== 0) {
+      this.wholeValue += whole;
+      this.numerator -= whole * this.denominator;
+      if (this.numerator === 0) {
+        this.denominator = 1;
+      }
     }
-
-    private setRealValue(): void {
-        this.realValue = this.numerator / this.denominator;
+    if (this.denominator > Fraction.maximumAllowedNumber) {
+      const factor: number = <number>this.denominator / Fraction.maximumAllowedNumber;
+      this.numerator = <number>Math.round(this.numerator / factor);
+      this.denominator = <number>Math.round(this.denominator / factor);
     }
-
-    private simplify(): void {
-        if (this.numerator === 0) {
-            this.denominator = 1;
-            return;
-        }
-
-        let i: number = Fraction.greatestCommonDenominator(Math.abs(this.numerator), Math.abs(this.denominator));
-
-        this.numerator /= i;
-        this.denominator /= i;
-
-        if (this.denominator > Fraction.maximumAllowedNumber) {
-            let factor: number = this.denominator / Fraction.maximumAllowedNumber;
-            this.numerator = Math.round(this.numerator / factor);
-            this.denominator = Math.round(this.denominator / factor);
-        }
-        if (this.numerator > Fraction.maximumAllowedNumber) {
-            let factor: number = this.numerator / Fraction.maximumAllowedNumber;
-            this.numerator = Math.round(this.numerator / factor);
-            this.denominator = Math.round(this.denominator / factor);
-        }
+    if (this.numerator > Fraction.maximumAllowedNumber) {
+      const factor: number = <number>this.numerator / Fraction.maximumAllowedNumber;
+      this.numerator = <number>Math.round(this.numerator / factor);
+      this.denominator = <number>Math.round(this.denominator / factor);
     }
-
-
-
-    //private static equals(f1: Fraction, f2: Fraction): boolean {
-    //    return <number>f1.numerator * f2.denominator === <number>f2.numerator * f1.denominator;
-    //}
-    //
-    //public static ApproximateFractionFromValue(value: number, epsilonForPrecision: number): Fraction {
-    //    let n: number = 1;
-    //    let d: number = 1;
-    //    let fraction: number = n / d;
-    //    while (Math.abs(fraction - value) > epsilonForPrecision) {
-    //        if (fraction < value) {
-    //            n++;
-    //        }
-    //        else {
-    //            d++;
-    //            n = <number>Math.round(value * d);
-    //        }
-    //        fraction = n / <number>d;
-    //    }
-    //    return new Fraction(n, d);
-    //}
-    //public static GetEarlierTimestamp(m1: Fraction, m2: Fraction): Fraction {
-    //    if (m1 < m2)
-    //        return m1;
-    //    else return m2;
-    //}
-
-    //public static getFraction(value: number, denominatorPrecision: number): Fraction {
-    //    let numerator: number = <number>Math.round(value / (1.0 / denominatorPrecision));
-    //    return new Fraction(numerator, denominatorPrecision);
-    //}
-    //public static fractionMin(f1: Fraction, f2: Fraction): Fraction {
-    //    if (f1 < f2)
-    //        return f1;
-    //    else return f2;
-    //}
-
-    //public static GetMaxValue(): Fraction {
-    //    return new Fraction(Fraction.maximumAllowedNumber, 1);
-    //}
-    //public static get MaxAllowedNumerator(): number {
-    //    return Fraction.maximumAllowedNumber;
-    //}
-    //public static get MaxAllowedDenominator(): number {
-    //    return Fraction.maximumAllowedNumber;
-    //}
-    //public ToFloatingString(): string {
-    //    return this.RealValue.ToString();
-    //}
-    //public Compare(x: Fraction, y: Fraction): number {
-    //    if (x > y)
-    //        return 1;
-    //    if (x < y)
-    //        return -1;
-    //    return 0;
-    //}
-
-    //#region operators
-    //
-    //    // operator overloads must always come in pairs
-    //    // operator overload +
-    //    public static Fraction operator + (Fraction f1, Fraction f2)
-    //{
-    //    Fraction sum = new Fraction(f1);
-    //    sum.Add(f2);
-    //    return sum;
-    //}
-    //
-    //// operator overload -
-    //public static Fraction operator - (Fraction f1, Fraction f2)
-    //{
-    //    Fraction diff = new Fraction(f1);
-    //    diff.Sub(f2);
-    //    return diff;
-    //}
-    //
-    //// operator overloads must always come in pairs
-    //// operator overload >
-    //public static bool operator > (Fraction f1, Fraction f2)
-    //{
-    //    //return (long) f1.Numerator*f2._denominator > (long) f2._numerator*f1._denominator;
-    //    return f1.RealValue > f2.RealValue;
-    //}
-    //
-    //// operator overload <
-    //public static bool operator < (Fraction f1, Fraction f2)
-    //{
-    //    //return (long) f1._numerator*f2._denominator < (long) f2._numerator*f1._denominator;
-    //    return f1.RealValue < f2.RealValue;
-    //}
-    //
-    //// operator overload ==
-    //public static bool operator === (Fraction f1, Fraction f2)
-    //{
-    //    // code enhanced for performance
-    //    // System.Object.ReferenceEquals(f1, undefined) is better than if (f1 === undefined)
-    //    // and comparisons between booleans are quick
-    //    bool f1IsNull = System.Object.ReferenceEquals(f1, undefined);
-    //    bool f2IsNull = System.Object.ReferenceEquals(f2, undefined);
-    //
-    //    // method returns true when both are undefined, false when only the first is undefined, otherwise the result of equals
-    //    if (f1IsNull !== f2IsNull)
-    //        return false;
-    //
-    //    if (f1IsNull /*&& f2IsNull*/)
-    //        return true;
-    //
-    //    return equals(f1, f2);
-    //}
-    //
-    //// operator overload !=
-    //public static bool operator !== (Fraction f1, Fraction f2)
-    //{
-    //    return (!(f1 === f2));
-    //}
-    //
-    //// operator overload >=
-    //public static bool operator >= (Fraction f1, Fraction f2)
-    //{
-    //    return (!(f1 < f2));
-    //}
-    //
-    //// operator overload <=
-    //public static bool operator <= (Fraction f1,Fraction f2)
-    //{
-    //    return (!(f1 > f2));
-    //}
-    //
-    //public static Fraction operator / (Fraction f, int i)
-    //{
-    //    return new Fraction(f._numerator, f._denominator *= i);
-    //}
-    //
-    //public static Fraction operator / (Fraction f1, Fraction f2)
-    //{
-    //    let res = new Fraction(f1.Numerator*f2.Denominator, f1.Denominator*f2.Numerator);
-    //    return res.Denominator === 0 ? new Fraction(0, 1) : res;
-    //}
-    //
-    //public static Fraction operator * (Fraction f1, Fraction f2)
-    //{
-    //    return new Fraction(f1.Numerator*f2.Numerator, f1.Denominator*f2.Denominator);
-    //}
-    //
-    //public static Fraction operator % (Fraction f1, Fraction f2)
-    //{
-    //    let a = f1/f2;
-    //    return new Fraction(a.Numerator%a.Denominator, a.Denominator)*f2;
-    //}
-    //
-    //#endregion operators
+  }
+
+
+  //private static equals(f1: Fraction, f2: Fraction): boolean {
+  //    return <number>f1.numerator * f2.denominator === <number>f2.numerator * f1.denominator;
+  //}
+  //
+  //public static ApproximateFractionFromValue(value: number, epsilonForPrecision: number): Fraction {
+  //    let n: number = 1;
+  //    let d: number = 1;
+  //    let fraction: number = n / d;
+  //    while (Math.abs(fraction - value) > epsilonForPrecision) {
+  //        if (fraction < value) {
+  //            n++;
+  //        }
+  //        else {
+  //            d++;
+  //            n = <number>Math.round(value * d);
+  //        }
+  //        fraction = n / <number>d;
+  //    }
+  //    return new Fraction(n, d);
+  //}
+  //public static GetEarlierTimestamp(m1: Fraction, m2: Fraction): Fraction {
+  //    if (m1 < m2)
+  //        return m1;
+  //    else return m2;
+  //}
+
+  //public static getFraction(value: number, denominatorPrecision: number): Fraction {
+  //    let numerator: number = <number>Math.round(value / (1.0 / denominatorPrecision));
+  //    return new Fraction(numerator, denominatorPrecision);
+  //}
+  //public static fractionMin(f1: Fraction, f2: Fraction): Fraction {
+  //    if (f1 < f2)
+  //        return f1;
+  //    else return f2;
+  //}
+
+  //public static GetMaxValue(): Fraction {
+  //    return new Fraction(Fraction.maximumAllowedNumber, 1);
+  //}
+  //public static get MaxAllowedNumerator(): number {
+  //    return Fraction.maximumAllowedNumber;
+  //}
+  //public static get MaxAllowedDenominator(): number {
+  //    return Fraction.maximumAllowedNumber;
+  //}
+  //public ToFloatingString(): string {
+  //    return this.RealValue.ToString();
+  //}
+  //public Compare(x: Fraction, y: Fraction): number {
+  //    if (x > y)
+  //        return 1;
+  //    if (x < y)
+  //        return -1;
+  //    return 0;
+  //}
+
+  //#region operators
+  //
+  //    // operator overloads must always come in pairs
+  //    // operator overload +
+  //    public static Fraction operator + (Fraction f1, Fraction f2)
+  //{
+  //    Fraction sum = new Fraction(f1);
+  //    sum.Add(f2);
+  //    return sum;
+  //}
+  //
+  //// operator overload -
+  //public static Fraction operator - (Fraction f1, Fraction f2)
+  //{
+  //    Fraction diff = new Fraction(f1);
+  //    diff.Sub(f2);
+  //    return diff;
+  //}
+  //
+  //// operator overloads must always come in pairs
+  //// operator overload >
+  //public static bool operator > (Fraction f1, Fraction f2)
+  //{
+  //    //return (long) f1.Numerator*f2._denominator > (long) f2._numerator*f1._denominator;
+  //    return f1.RealValue > f2.RealValue;
+  //}
+  //
+  //// operator overload <
+  //public static bool operator < (Fraction f1, Fraction f2)
+  //{
+  //    //return (long) f1._numerator*f2._denominator < (long) f2._numerator*f1._denominator;
+  //    return f1.RealValue < f2.RealValue;
+  //}
+  //
+  //// operator overload ==
+  //public static bool operator === (Fraction f1, Fraction f2)
+  //{
+  //    // code enhanced for performance
+  //    // System.Object.ReferenceEquals(f1, undefined) is better than if (f1 === undefined)
+  //    // and comparisons between booleans are quick
+  //    bool f1IsNull = System.Object.ReferenceEquals(f1, undefined);
+  //    bool f2IsNull = System.Object.ReferenceEquals(f2, undefined);
+  //
+  //    // method returns true when both are undefined, false when only the first is undefined, otherwise the result of equals
+  //    if (f1IsNull !== f2IsNull)
+  //        return false;
+  //
+  //    if (f1IsNull /*&& f2IsNull*/)
+  //        return true;
+  //
+  //    return equals(f1, f2);
+  //}
+  //
+  //// operator overload !=
+  //public static bool operator !== (Fraction f1, Fraction f2)
+  //{
+  //    return (!(f1 === f2));
+  //}
+  //
+  //// operator overload >=
+  //public static bool operator >= (Fraction f1, Fraction f2)
+  //{
+  //    return (!(f1 < f2));
+  //}
+  //
+  //// operator overload <=
+  //public static bool operator <= (Fraction f1,Fraction f2)
+  //{
+  //    return (!(f1 > f2));
+  //}
+  //
+  //public static Fraction operator / (Fraction f, int i)
+  //{
+  //    return new Fraction(f._numerator, f._denominator *= i);
+  //}
+  //
+  //public static Fraction operator / (Fraction f1, Fraction f2)
+  //{
+  //    let res = new Fraction(f1.Numerator*f2.Denominator, f1.Denominator*f2.Numerator);
+  //    return res.Denominator === 0 ? new Fraction(0, 1) : res;
+  //}
+  //
+  //public static Fraction operator * (Fraction f1, Fraction f2)
+  //{
+  //    return new Fraction(f1.Numerator*f2.Numerator, f1.Denominator*f2.Denominator);
+  //}
+  //
+  //public static Fraction operator % (Fraction f1, Fraction f2)
+  //{
+  //    let a = f1/f2;
+  //    return new Fraction(a.Numerator%a.Denominator, a.Denominator)*f2;
+  //}
+  //
+  //#endregion operators
 }

+ 15 - 14
src/Common/DataObjects/Pitch.ts

@@ -14,7 +14,8 @@ export enum AccidentalEnum {
     FLAT = -1,
     NONE = 0,
     SHARP = 1,
-    DOUBLESHARP = 2
+    DOUBLESHARP = 2,
+    NATURAL = 3
 }
 
 // This class represents a musical note. The middle A (440 Hz) lies in the octave with the value 1.
@@ -64,7 +65,7 @@ export class Pitch {
      * @constructor
      */
     public static CalculateTransposedHalfTone(pitch: Pitch, transpose: number): { value: number; overflow: number; } {
-        let newHalfTone: number = <number>pitch.fundamentalNote + <number>pitch.accidental + transpose;
+        const newHalfTone: number = <number>pitch.fundamentalNote + <number>pitch.accidental + transpose;
         return Pitch.WrapAroundCheck(newHalfTone, 12);
     }
 
@@ -91,12 +92,12 @@ export class Pitch {
         let halftoneSteps: number;
         if (obj instanceof Pitch) {
             // obj is a pitch
-            let pitch: Pitch = obj;
+            const pitch: Pitch = obj;
             octaveSteps = pitch.octave - 1;
             halftoneSteps = <number>pitch.fundamentalNote - <number>NoteEnum.A + <number>pitch.accidental;
         } else if (typeof obj === "number") {
             // obj is a fractional key
-            let fractionalKey: number = obj;
+            const fractionalKey: number = obj;
             halftoneSteps = fractionalKey - 57.0;
         }
         // Return frequency:
@@ -109,9 +110,9 @@ export class Pitch {
     }
 
     public static fromFrequency(frequency: number): Pitch {
-        let key: number = Pitch.calcFractionalKey(frequency) + 0.5;
-        let octave: number = Math.floor(key / 12) - Pitch.octXmlDiff;
-        let halftone: number = Math.floor(key) % 12;
+        const key: number = Pitch.calcFractionalKey(frequency) + 0.5;
+        const octave: number = Math.floor(key / 12) - Pitch.octXmlDiff;
+        const halftone: number = Math.floor(key) % 12;
         let fundamentalNote: NoteEnum = <NoteEnum>halftone;
         let accidental: AccidentalEnum = AccidentalEnum.NONE;
         if (this.pitchEnumValues.indexOf(fundamentalNote) === -1) {
@@ -122,8 +123,8 @@ export class Pitch {
     }
 
     public static fromHalftone(halftone: number): Pitch {
-        let octave: number = <number>Math.floor(<number>halftone / 12) - Pitch.octXmlDiff;
-        let halftoneInOctave: number = halftone % 12;
+        const octave: number = <number>Math.floor(<number>halftone / 12) - Pitch.octXmlDiff;
+        const halftoneInOctave: number = halftone % 12;
         let fundamentalNote: NoteEnum = <NoteEnum>halftoneInOctave;
         let accidental: AccidentalEnum = AccidentalEnum.NONE;
         if (this.pitchEnumValues.indexOf(fundamentalNote) === -1) {
@@ -222,7 +223,7 @@ export class Pitch {
     }
 
     public OperatorEquals(p2: Pitch): boolean {
-        let p1: Pitch = this;
+        const p1: Pitch = this;
         // if (ReferenceEquals(p1, p2)) {
         //     return true;
         // }
@@ -233,13 +234,13 @@ export class Pitch {
     }
 
     public OperatorNotEqual(p2: Pitch): boolean {
-        let p1: Pitch = this;
+        const p1: Pitch = this;
         return !(p1 === p2);
     }
 
     // This method returns a new Pitch factor-Halftones higher than the current Pitch
     private getHigherPitchByTransposeFactor(factor: number): Pitch {
-        let noteEnumIndex: number = Pitch.pitchEnumValues.indexOf(this.fundamentalNote);
+        const noteEnumIndex: number = Pitch.pitchEnumValues.indexOf(this.fundamentalNote);
         let newOctave: number = this.octave;
         let newNoteEnum: NoteEnum;
         if (noteEnumIndex + factor > Pitch.pitchEnumValues.length - 1) {
@@ -252,7 +253,7 @@ export class Pitch {
     }
 
     private getLowerPitchByTransposeFactor(factor: number): Pitch {
-        let noteEnumIndex: number = Pitch.pitchEnumValues.indexOf(this.fundamentalNote);
+        const noteEnumIndex: number = Pitch.pitchEnumValues.indexOf(this.fundamentalNote);
         let newOctave: number = this.octave;
         let newNoteEnum: NoteEnum;
         if (noteEnumIndex - factor < 0) {
@@ -271,7 +272,7 @@ export class Pitch {
     }
 
     private getPreviousFundamentalNote(fundamental: NoteEnum): NoteEnum {
-        let i: number = Pitch.pitchEnumValues.indexOf(fundamental);
+        const i: number = Pitch.pitchEnumValues.indexOf(fundamental);
         if (i > 0) {
             return Pitch.pitchEnumValues[i - 1];
         } else {

+ 11 - 11
src/Common/FileIO/Mxl.ts

@@ -13,7 +13,7 @@ export class MXLHelper {
      * @constructor
      */
     public static MXLtoIXmlElement(data: string): Promise<IXmlElement> {
-        let zip: JSZip.JSZip = new JSZip();
+        const zip: JSZip.JSZip = new JSZip();
         // asynchronously load zip file and process it - with Promises
         return zip.loadAsync(data).then(
             (_: any) => {
@@ -24,9 +24,9 @@ export class MXLHelper {
             }
         ).then(
             (content: string) => {
-                let parser: DOMParser = new DOMParser();
-                let doc: Document = parser.parseFromString(content, "text/xml");
-                let rootFile: string = doc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
+                const parser: DOMParser = new DOMParser();
+                const doc: Document = parser.parseFromString(content, "text/xml");
+                const rootFile: string = doc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
                 return zip.file(rootFile).async("string");
             },
             (err: any) => {
@@ -34,9 +34,9 @@ export class MXLHelper {
             }
         ).then(
             (content: string) => {
-                let parser: DOMParser = new DOMParser();
-                let xml: Document = parser.parseFromString(content, "text/xml");
-                let doc: IXmlElement = new IXmlElement(xml.documentElement);
+                const parser: DOMParser = new DOMParser();
+                const xml: Document = parser.parseFromString(content, "text/xml");
+                const doc: IXmlElement = new IXmlElement(xml.documentElement);
                 return Promise.resolve(doc);
             },
             (err: any) => {
@@ -53,7 +53,7 @@ export class MXLHelper {
     }
 
     public static MXLtoXMLstring(data: string): Promise<string> {
-        let zip:  JSZip.JSZip = new JSZip();
+        const zip:  JSZip.JSZip = new JSZip();
         // asynchronously load zip file and process it - with Promises
         return zip.loadAsync(data).then(
             (_: any) => {
@@ -64,9 +64,9 @@ export class MXLHelper {
             }
         ).then(
             (content: string) => {
-                let parser: DOMParser = new DOMParser();
-                let doc: Document = parser.parseFromString(content, "text/xml");
-                let rootFile: string = doc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
+                const parser: DOMParser = new DOMParser();
+                const doc: Document = parser.parseFromString(content, "text/xml");
+                const rootFile: string = doc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
                 return zip.file(rootFile).async("string");
             },
             (err: any) => {

+ 8 - 8
src/Common/FileIO/Xml.ts

@@ -56,8 +56,8 @@ export class IXmlElement {
      */
     public attributes(): IXmlAttribute[] {
         if (!this.attrs) {
-            let attributes: NamedNodeMap = this.elem.attributes;
-            let attrs: IXmlAttribute[] = [];
+            const attributes: NamedNodeMap = this.elem.attributes;
+            const attrs: IXmlAttribute[] = [];
             for (let i: number = 0; i < attributes.length; i += 1) {
                 attrs.push(attributes[i]);
             }
@@ -72,9 +72,9 @@ export class IXmlElement {
      * @returns {IXmlElement}
      */
     public element(elementName: string): IXmlElement {
-        let nodes: NodeList = this.elem.childNodes;
+        const nodes: NodeList = this.elem.childNodes;
         for (let i: number = 0, length: number = nodes.length; i < length; i += 1) {
-            let node: Node = nodes[i];
+            const node: Node = nodes[i];
             if (node.nodeType === Node.ELEMENT_NODE && node.nodeName.toLowerCase() === elementName) {
                 return new IXmlElement(node as Element);
             }
@@ -87,14 +87,14 @@ export class IXmlElement {
      * @returns {IXmlElement[]}
      */
     public elements(nodeName?: string): IXmlElement[] {
-        let nodes: NodeList = this.elem.childNodes;
-        let ret: IXmlElement[] = [];
-        let nameUnset: boolean = nodeName === undefined;
+        const nodes: NodeList = this.elem.childNodes;
+        const ret: IXmlElement[] = [];
+        const nameUnset: boolean = nodeName === undefined;
         if (!nameUnset) {
             nodeName = nodeName.toLowerCase();
         }
         for (let i: number = 0; i < nodes.length; i += 1) {
-            let node: Node = nodes[i];
+            const node: Node = nodes[i];
             if (node.nodeType === Node.ELEMENT_NODE &&
                 (nameUnset || node.nodeName.toLowerCase() === nodeName)
             ) {

+ 4 - 4
src/Common/Logging.ts

@@ -7,15 +7,15 @@
  */
 export class Logging {
     public static debug(...args: any[]): void {
-        console.debug("[OSMD] ", args.join(" "));
+        console.debug("[opensheetmusicdisplay] ", args.join(" "));
     }
     public static log(...args: any[]): void {
-        console.log("[OSMD] ", args.join(" "));
+        console.log("[opensheetmusicdisplay] ", args.join(" "));
     }
     public static error(...args: any[]): void {
-        console.error("[OSMD] ", args.join(" "));
+        console.error("[opensheetmusicdisplay] ", args.join(" "));
     }
     public static warn(...args: any[]): void {
-        console.warn("[OSMD] ", args.join(" "));
+        console.warn("[opensheetmusicdisplay] ", args.join(" "));
     }
 }

+ 11 - 0
src/Common/Strings/StringUtil.ts

@@ -0,0 +1,11 @@
+export class StringUtil {
+  public static StringContainsSeparatedWord(str: string, word: string): boolean {
+    if (str === word ||
+      str.search(" " + word) !== -1 ||
+      str.search(word + " ") !== -1 ||
+      str.search(word + ".") !== -1) {
+      return true;
+    }
+    return false;
+  }
+}

+ 4 - 4
src/MusicalScore/Graphical/AccidentalCalculator.ts

@@ -34,7 +34,7 @@ export class AccidentalCalculator {
      */
     public doCalculationsAtEndOfMeasure(): void {
         this.currentInMeasureNoteAlterationsDict.clear();
-        for (let key of this.keySignatureNoteAlterationsDict.keys()) {
+        for (const key of this.keySignatureNoteAlterationsDict.keys()) {
             this.currentInMeasureNoteAlterationsDict.setValue(key, this.keySignatureNoteAlterationsDict.getValue(key));
         }
     }
@@ -43,7 +43,7 @@ export class AccidentalCalculator {
         if (pitch === undefined) {
             return;
         }
-        let pitchKey: number = <number>pitch.FundamentalNote + pitch.Octave * 12;
+        const pitchKey: number = <number>pitch.FundamentalNote + pitch.Octave * 12;
         /*let pitchKeyGivenInMeasureDict: boolean = this.currentInMeasureNoteAlterationsDict.containsKey(pitchKey);
         if (
             (pitchKeyGivenInMeasureDict && this.currentInMeasureNoteAlterationsDict.getValue(pitchKey) !== pitch.Accidental)
@@ -64,7 +64,7 @@ export class AccidentalCalculator {
             this.symbolFactory.addGraphicalAccidental(graphicalNote, pitch, grace, graceScalingFactor);
         }*/
 
-        let isInCurrentAlterationsToKeyList: boolean = this.currentAlterationsComparedToKeyInstructionList.indexOf(pitchKey) >= 0;
+        const isInCurrentAlterationsToKeyList: boolean = this.currentAlterationsComparedToKeyInstructionList.indexOf(pitchKey) >= 0;
         if (this.currentInMeasureNoteAlterationsDict.containsKey(pitchKey)) {
             if (isInCurrentAlterationsToKeyList) {
                 this.currentAlterationsComparedToKeyInstructionList.splice(this.currentAlterationsComparedToKeyInstructionList.indexOf(pitchKey), 1);
@@ -96,7 +96,7 @@ export class AccidentalCalculator {
     }
 
     private reactOnKeyInstructionChange(): void {
-        let noteEnums: NoteEnum[] = KeyInstruction.getNoteEnumList(this.activeKeyInstruction);
+        const noteEnums: NoteEnum[] = KeyInstruction.getNoteEnumList(this.activeKeyInstruction);
         let keyAccidentalType: AccidentalEnum;
         if (this.activeKeyInstruction.Key > 0) {
             keyAccidentalType = AccidentalEnum.SHARP;

+ 75 - 33
src/MusicalScore/Graphical/BoundingBox.ts

@@ -5,6 +5,9 @@ import {RectangleF2D} from "../../Common/DataObjects/RectangleF2D";
 
 /**
  * A bounding box delimits an area on the 2D plane.
+ * @param dataObject Graphical object where the bounding box will be attached
+ * @param parent Parent bounding box of an object in a higher hierarchy position
+ * @param connectChildToParent Create a child to parent relationship too. Will be true by default
  */
 export class BoundingBox {
     protected isSymbol: boolean = false;
@@ -14,9 +17,9 @@ export class BoundingBox {
     protected absolutePosition: PointF2D = new PointF2D();
     protected relativePosition: PointF2D = new PointF2D();
     protected size: SizeF2D = new SizeF2D();
-    protected marginSize: SizeF2D;
-    protected upperLeftCorner: PointF2D;
-    protected upperLeftMarginCorner: PointF2D;
+    protected marginSize: SizeF2D = new SizeF2D();
+    protected upperLeftCorner: PointF2D = new PointF2D();
+    protected upperLeftMarginCorner: PointF2D = new PointF2D();
     protected borderLeft: number = 0;
     protected borderRight: number = 0;
     protected borderTop: number = 0;
@@ -30,12 +33,20 @@ export class BoundingBox {
     protected childElements: BoundingBox[] = [];
     protected parent: BoundingBox;
     protected dataObject: Object;
-
+    /**
+     * Create a bounding box
+     * @param dataObject Graphical object where the bounding box will be attached
+     * @param parent Parent bounding box of an object in a higher hierarchy position
+     * @param connectChildToParent Create a child to parent relationship too. Will be true by default
+     */
     constructor(dataObject: Object = undefined, parent: BoundingBox = undefined) {
         this.parent = parent;
         this.dataObject = dataObject;
         this.xBordersHaveBeenSet = false;
         this.yBordersHaveBeenSet = false;
+        if (parent !== undefined) {
+            this.Parent = parent;
+        }
     }
 
     public get RelativePositionHasBeenSet(): boolean {
@@ -189,6 +200,12 @@ export class BoundingBox {
 
     public set Parent(value: BoundingBox) {
         this.parent = value;
+        if (this.parent.ChildElements.indexOf(this) > -1) {
+            console.error("BoundingBox of " + (this.dataObject.constructor as any).name +
+            " already in children list of " + (this.parent.dataObject.constructor as any).name + "'s BoundingBox");
+        } else {
+            this.parent.ChildElements.push(this);
+        }
     }
 
     public get DataObject(): Object {
@@ -205,19 +222,34 @@ export class BoundingBox {
     }
 
     /**
+     * Calculate the the absolute position by adding up all relative positions of all parents (including the own rel. pos.)
+     */
+    public calculateAbsolutePosition(): void {
+      this.absolutePosition.x = this.relativePosition.x;
+      this.absolutePosition.y = this.relativePosition.y;
+      let parent: BoundingBox = this.parent;
+      while (parent !== undefined) {
+        this.absolutePosition.x += parent.relativePosition.x;
+        this.absolutePosition.y += parent.relativePosition.y;
+        parent = parent.parent;
+      }
+    }
+
+    /**
      * This method calculates the Absolute Positions recursively
      */
     public calculateAbsolutePositionsRecursiveWithoutTopelement(): void {
         this.absolutePosition.x = 0.0;
         this.absolutePosition.y = 0.0;
         for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
-            let child: BoundingBox = this.ChildElements[idx];
+            const child: BoundingBox = this.ChildElements[idx];
             child.calculateAbsolutePositionsRecursive(this.absolutePosition.x, this.absolutePosition.y);
         }
     }
 
     /**
      * This method calculates the Absolute Positions recursively
+     * from the root element down to the leaf elements
      * @param x
      * @param y
      */
@@ -225,12 +257,22 @@ export class BoundingBox {
         this.absolutePosition.x = this.relativePosition.x + x;
         this.absolutePosition.y = this.relativePosition.y + y;
         for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
-            let child: BoundingBox = this.ChildElements[idx];
+            const child: BoundingBox = this.ChildElements[idx];
             child.calculateAbsolutePositionsRecursive(this.absolutePosition.x, this.absolutePosition.y);
         }
     }
 
     /**
+     * calculates the absolute positions of all children of this boundingBox
+     */
+    public calculateAbsolutePositionsOfChildren(): void {
+      for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
+        const child: BoundingBox = this.ChildElements[idx];
+        child.calculateAbsolutePositionsRecursive(this.absolutePosition.x, this.absolutePosition.y);
+      }
+    }
+
+    /**
      * This method calculates the BoundingBoxes
      */
     public calculateBoundingBox(): void {
@@ -238,7 +280,7 @@ export class BoundingBox {
             return;
         }
         for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
-            let childElement: BoundingBox = this.ChildElements[idx];
+            const childElement: BoundingBox = this.ChildElements[idx];
             childElement.calculateBoundingBox();
         }
 
@@ -266,7 +308,7 @@ export class BoundingBox {
 
         // ChildElements will have their borders calculated, so calculate current borders
         for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
-            let childElement: BoundingBox = this.ChildElements[idx];
+            const childElement: BoundingBox = this.ChildElements[idx];
             minLeft = Math.min(minLeft, childElement.relativePosition.x + childElement.borderLeft);
             maxRight = Math.max(maxRight, childElement.relativePosition.x + childElement.borderRight);
             minTop = Math.min(minTop, childElement.relativePosition.y + childElement.borderTop);
@@ -297,7 +339,7 @@ export class BoundingBox {
             return;
         }
         for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
-            let childElement: BoundingBox = this.ChildElements[idx];
+            const childElement: BoundingBox = this.ChildElements[idx];
             childElement.calculateTopBottomBorders();
         }
         let minTop: number = Number.MAX_VALUE;
@@ -311,7 +353,7 @@ export class BoundingBox {
             maxMarginBottom = this.borderMarginBottom;
         }
         for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
-            let childElement: BoundingBox = this.ChildElements[idx];
+            const childElement: BoundingBox = this.ChildElements[idx];
             minTop = Math.min(minTop, childElement.relativePosition.y + childElement.borderTop);
             maxBottom = Math.max(maxBottom, childElement.relativePosition.y + childElement.borderBottom);
             minMarginTop = Math.min(minMarginTop, childElement.relativePosition.y + childElement.borderMarginTop);
@@ -363,9 +405,9 @@ export class BoundingBox {
      * @returns {boolean}
      */
     public collisionDetection(psi: BoundingBox): boolean {
-        let overlapWidth: number = Math.min(this.AbsolutePosition.x + this.borderRight, psi.absolutePosition.x + psi.borderRight)
+        const overlapWidth: number = Math.min(this.AbsolutePosition.x + this.borderRight, psi.absolutePosition.x + psi.borderRight)
             - Math.max(this.AbsolutePosition.x + this.borderLeft, psi.absolutePosition.x + psi.borderLeft);
-        let overlapHeight: number = Math.min(this.AbsolutePosition.y + this.borderBottom, psi.absolutePosition.y + psi.borderBottom)
+        const overlapHeight: number = Math.min(this.AbsolutePosition.y + this.borderBottom, psi.absolutePosition.y + psi.borderBottom)
             - Math.max(this.AbsolutePosition.y + this.borderTop, psi.absolutePosition.y + psi.borderTop);
         if (overlapWidth > 0 && overlapHeight > 0) {
             return true;
@@ -379,14 +421,14 @@ export class BoundingBox {
      * @returns {boolean}
      */
     public liesInsideBorders(psi: BoundingBox): boolean {
-        let leftBorderInside: boolean = (this.AbsolutePosition.x + this.borderLeft) <= (psi.absolutePosition.x + psi.borderLeft)
+        const leftBorderInside: boolean = (this.AbsolutePosition.x + this.borderLeft) <= (psi.absolutePosition.x + psi.borderLeft)
             && (psi.absolutePosition.x + psi.borderLeft) <= (this.AbsolutePosition.x + this.borderRight);
-        let rightBorderInside: boolean = (this.AbsolutePosition.x + this.borderLeft) <= (psi.absolutePosition.x + psi.borderRight)
+        const rightBorderInside: boolean = (this.AbsolutePosition.x + this.borderLeft) <= (psi.absolutePosition.x + psi.borderRight)
             && (psi.absolutePosition.x + psi.borderRight) <= (this.AbsolutePosition.x + this.borderRight);
         if (leftBorderInside && rightBorderInside) {
-            let topBorderInside: boolean = (this.AbsolutePosition.y + this.borderTop) <= (psi.absolutePosition.y + psi.borderTop)
+            const topBorderInside: boolean = (this.AbsolutePosition.y + this.borderTop) <= (psi.absolutePosition.y + psi.borderTop)
                 && (psi.absolutePosition.y + psi.borderTop) <= (this.AbsolutePosition.y + this.borderBottom);
-            let bottomBorderInside: boolean = (this.AbsolutePosition.y + this.borderTop) <= (psi.absolutePosition.y + psi.borderBottom)
+            const bottomBorderInside: boolean = (this.AbsolutePosition.y + this.borderTop) <= (psi.absolutePosition.y + psi.borderBottom)
                 && (psi.absolutePosition.y + psi.borderBottom) <= (this.AbsolutePosition.y + this.borderBottom);
             if (topBorderInside && bottomBorderInside) {
                 return true;
@@ -396,9 +438,9 @@ export class BoundingBox {
     }
 
     public pointLiesInsideBorders(position: PointF2D): boolean {
-        let xInside: boolean = (this.AbsolutePosition.x + this.borderLeft) <= position.x && position.x <= (this.AbsolutePosition.x + this.borderRight);
+        const xInside: boolean = (this.AbsolutePosition.x + this.borderLeft) <= position.x && position.x <= (this.AbsolutePosition.x + this.borderRight);
         if (xInside) {
-            let yInside: boolean = (this.AbsolutePosition.y + this.borderTop) <= position.y && position.y <= (this.AbsolutePosition.y + this.borderBottom);
+            const yInside: boolean = (this.AbsolutePosition.y + this.borderTop) <= position.y && position.y <= (this.AbsolutePosition.y + this.borderBottom);
             if (yInside) {
                 return true;
             }
@@ -412,9 +454,9 @@ export class BoundingBox {
      * @returns {boolean}
      */
     public marginCollisionDetection(psi: BoundingBox): boolean {
-        let overlapWidth: number = Math.min(this.AbsolutePosition.x + this.borderMarginRight, psi.absolutePosition.x + psi.borderMarginRight)
+        const overlapWidth: number = Math.min(this.AbsolutePosition.x + this.borderMarginRight, psi.absolutePosition.x + psi.borderMarginRight)
             - Math.max(this.AbsolutePosition.x + this.borderMarginLeft, psi.absolutePosition.x + psi.borderMarginLeft);
-        let overlapHeight: number = Math.min(this.AbsolutePosition.y + this.borderMarginBottom, psi.absolutePosition.y + psi.borderMarginBottom)
+        const overlapHeight: number = Math.min(this.AbsolutePosition.y + this.borderMarginBottom, psi.absolutePosition.y + psi.borderMarginBottom)
             - Math.max(this.AbsolutePosition.y + this.borderMarginTop, psi.absolutePosition.y + psi.borderMarginTop);
         if (overlapWidth > 0 && overlapHeight > 0) {
             return true;
@@ -428,14 +470,14 @@ export class BoundingBox {
      * @returns {boolean}
      */
     public liesInsideMargins(psi: BoundingBox): boolean {
-        let leftMarginInside: boolean = (this.AbsolutePosition.x + this.borderMarginLeft) <= (psi.absolutePosition.x + psi.borderMarginLeft)
+        const leftMarginInside: boolean = (this.AbsolutePosition.x + this.borderMarginLeft) <= (psi.absolutePosition.x + psi.borderMarginLeft)
             && (psi.absolutePosition.x + psi.borderMarginLeft) <= (this.AbsolutePosition.x + this.borderMarginRight);
-        let rightMarginInside: boolean = (this.AbsolutePosition.x + this.borderMarginLeft) <= (psi.absolutePosition.x + psi.borderMarginRight)
+        const rightMarginInside: boolean = (this.AbsolutePosition.x + this.borderMarginLeft) <= (psi.absolutePosition.x + psi.borderMarginRight)
             && (psi.absolutePosition.x + psi.borderMarginRight) <= (this.AbsolutePosition.x + this.borderMarginRight);
         if (leftMarginInside && rightMarginInside) {
-            let topMarginInside: boolean = (this.AbsolutePosition.y + this.borderMarginTop) <= (psi.absolutePosition.y + psi.borderMarginTop)
+            const topMarginInside: boolean = (this.AbsolutePosition.y + this.borderMarginTop) <= (psi.absolutePosition.y + psi.borderMarginTop)
                 && (psi.absolutePosition.y + psi.borderMarginTop) <= (this.AbsolutePosition.y + this.borderMarginBottom);
-            let bottomMarginInside: boolean = (this.AbsolutePosition.y + this.borderMarginTop) <= (psi.absolutePosition.y + psi.borderMarginBottom)
+            const bottomMarginInside: boolean = (this.AbsolutePosition.y + this.borderMarginTop) <= (psi.absolutePosition.y + psi.borderMarginBottom)
                 && (psi.absolutePosition.y + psi.borderMarginBottom) <= (this.AbsolutePosition.y + this.borderMarginBottom);
             if (topMarginInside && bottomMarginInside) {
                 return true;
@@ -445,10 +487,10 @@ export class BoundingBox {
     }
 
     public pointLiesInsideMargins(position: PointF2D): boolean {
-        let xInside: boolean = (this.AbsolutePosition.x + this.borderMarginLeft) <= position.x
+        const xInside: boolean = (this.AbsolutePosition.x + this.borderMarginLeft) <= position.x
             && position.x <= (this.AbsolutePosition.x + this.borderMarginRight);
         if (xInside) {
-            let yInside: boolean = (this.AbsolutePosition.y + this.borderMarginTop) <= position.y
+            const yInside: boolean = (this.AbsolutePosition.y + this.borderMarginTop) <= position.y
                 && position.y <= (this.AbsolutePosition.y + this.borderMarginBottom);
             if (yInside) {
                 return true;
@@ -489,13 +531,13 @@ export class BoundingBox {
     }
 
     public getClickedObjectOfType<T>(clickPosition: PointF2D): T {
-        let obj: Object = this.dataObject;
+        const obj: Object = this.dataObject;
         if (this.pointLiesInsideBorders(clickPosition) && (<T>obj !== undefined)) {
             return (obj as T);
         }
         for (let idx: number = 0, len: number = this.childElements.length; idx < len; ++idx) {
-            let psi: BoundingBox = this.childElements[idx];
-            let innerObject: Object = psi.getClickedObjectOfType<T>(clickPosition);
+            const psi: BoundingBox = this.childElements[idx];
+            const innerObject: Object = psi.getClickedObjectOfType<T>(clickPosition);
             if (innerObject !== undefined) {
                 return (innerObject as T);
             }
@@ -516,8 +558,8 @@ export class BoundingBox {
             }
             // FIXME Andrea: add here "return []"?
         }
-        let result: T[] = [];
-        for (let child of this.childElements) {
+        const result: T[] = [];
+        for (const child of this.childElements) {
             result.concat(child.getObjectsInRegion<T>(region, liesInside));
         }
         return result;
@@ -582,7 +624,7 @@ export class BoundingBox {
 
         // perform check for all children iteratively and return border from children symbols
         for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
-            let childElement: BoundingBox = this.ChildElements[idx];
+            const childElement: BoundingBox = this.ChildElements[idx];
             childElement.calculateMarginPositionAlongDirection(toBePlaced, direction);
         }
     }
@@ -633,7 +675,7 @@ export class BoundingBox {
 
         // perform check for all children iteratively and return border from children symbols
         for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
-            let childElement: BoundingBox = this.ChildElements[idx];
+            const childElement: BoundingBox = this.ChildElements[idx];
             childElement.calculatePositionAlongDirection(toBePlaced, direction);
         }
     }

+ 39 - 1
src/MusicalScore/Graphical/DrawingEnums.ts

@@ -1,3 +1,5 @@
+import * as Collections from "typescript-collections";
+
 /**
  * The supported styles to draw a rectangle on the music sheet
  */
@@ -37,13 +39,49 @@ export enum OutlineAndFillStyleEnum {
     Comment10
 }
 
+// tslint:disable-next-line:max-line-length A linebreak would be more confusing here
+export const OUTLINE_AND_FILL_STYLE_DICT: Collections.Dictionary<OutlineAndFillStyleEnum, string> = new Collections.Dictionary<OutlineAndFillStyleEnum, string>();
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.BaseWritingColor, "Thistle");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.FollowingCursor, "Aqua");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.AlternativeFollowingCursor, "Azure");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.PlaybackCursor, "Bisque");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Highlighted, "CadetBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.ErrorUnderlay, "DarkBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Selected, "DarkGoldenRod");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.SelectionSymbol, "BlanchedAlmond");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.DebugColor1, "Chartreuse");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.DebugColor2, "DarkGreen");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.DebugColor3, "DarkOrange");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.SplitScreenDivision, "FireBrick");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.GreyTransparentOverlay, "DarkSalmon");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea1, "DarkSeaGreen");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea2, "DarkOrchid");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea3, "Aquamarine");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea4, "DarkKhaki");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea5, "ForestGreen");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea6, "AliceBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea7, "DeepPink");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea8, "Coral");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea9, "DarkOliveGreen");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea10, "Chocolate");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment1, "DodgerBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment2, "Blue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment3, "Beige");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment4, "Crimson");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment5, "Fuchsia");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment6, "Brown");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment7, "BlanchedAlmond");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment8, "CornflowerBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment9, "Cornsilk");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment10, "DarkGrey");
+
 export enum StyleSets {
     MarkedArea,
     Comment
 }
 
 /**
- * The layers which one can draw on (not suppoerted)
+ * The layers which one can draw on (not supported)
  */
 export enum GraphicalLayers {
     Background,

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

@@ -442,6 +442,9 @@ export class EngravingRules {
     public get SystemLabelsRightMargin(): number {
         return this.systemLabelsRightMargin;
     }
+    public set SystemLabelsRightMargin(value: number) {
+        this.systemLabelsRightMargin = value;
+    }
     public get MinimumAllowedDistanceBetweenSystems(): number {
         return this.minimumAllowedDistanceBetweenSystems;
     }
@@ -1162,7 +1165,7 @@ export class EngravingRules {
         this.factorOne = new Array(this.bezierCurveStepSize);
         this.factorTwo = new Array(this.bezierCurveStepSize);
         for (let i: number = 0; i < this.bezierCurveStepSize; i++) {
-            let t: number = i / this.bezierCurveStepSize;
+            const t: number = i / this.bezierCurveStepSize;
             this.tPower3[i] = Math.pow(t, 3);
             this.oneMinusTPower3[i] = Math.pow((1 - t), 3);
             this.factorOne[i] = 3 * Math.pow((1 - t), 2) * t;

+ 1 - 2
src/MusicalScore/Graphical/GraphicalChordSymbolContainer.ts

@@ -22,9 +22,8 @@ export class GraphicalChordSymbolContainer extends GraphicalObject {
         return this.graphicalLabel;
     }
     private calculateLabel(textHeight: number, transposeHalftones: number): void {
-        let text: string = ChordSymbolContainer.calculateChordText(this.chordSymbolContainer, transposeHalftones);
+        const text: string = ChordSymbolContainer.calculateChordText(this.chordSymbolContainer, transposeHalftones);
         this.graphicalLabel = new GraphicalLabel(new Label(text), textHeight, TextAlignment.CenterBottom, this.boundingBox);
         this.graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
-        this.boundingBox.ChildElements.push(this.graphicalLabel.PositionAndShape);
     }
 }

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

@@ -33,13 +33,13 @@ export class GraphicalLabel extends Clickable {
         if (this.Label.text.trim() === "") {
             return;
         }
-        let labelMarginBorderFactor: number = EngravingRules.Rules.LabelMarginBorderFactor;
+        const labelMarginBorderFactor: number = EngravingRules.Rules.LabelMarginBorderFactor;
 
-        let widthToHeightRatio: number =
+        const widthToHeightRatio: number =
             MusicSheetCalculator.TextMeasurer.computeTextWidthToHeightRatio(this.Label.text, this.Label.font, this.Label.fontStyle);
-        let height: number = this.Label.fontHeight;
-        let width: number = height * widthToHeightRatio;
-        let psi: BoundingBox = this.PositionAndShape;
+        const height: number = this.Label.fontHeight;
+        const width: number = height * widthToHeightRatio;
+        const psi: BoundingBox = this.PositionAndShape;
 
         switch (this.Label.textAlignment) {
             case TextAlignment.CenterBottom:

+ 1 - 0
src/MusicalScore/Graphical/GraphicalLyricEntry.ts

@@ -27,6 +27,7 @@ export class GraphicalLyricEntry {
         this.graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, staffHeight);
     }
 
+    // FIXME: This should actually be called LyricsEntry or be a function
     public get GetLyricsEntry(): LyricsEntry {
         return this.lyricsEntry;
     }

+ 1 - 0
src/MusicalScore/Graphical/GraphicalLyricWord.ts

@@ -35,6 +35,7 @@ export class GraphicalLyricWord {
     }
 
     private initialize(): void {
+        // FIXME: This is actually not needed in Javascript as we have dynamic memory allication?
         for (let i: number = 0; i < this.lyricWord.Syllables.length; i++) {
             this.graphicalLyricsEntries.push(undefined);
         }

+ 97 - 97
src/MusicalScore/Graphical/GraphicalMusicSheet.ts

@@ -167,8 +167,8 @@ export class GraphicalMusicSheet {
      */
     public static transformRelativeToAbsolutePosition(graphicalMusicSheet: GraphicalMusicSheet): void {
         for (let i: number = 0; i < graphicalMusicSheet.MusicPages.length; i++) {
-            let pageAbsolute: PointF2D = graphicalMusicSheet.MusicPages[i].setMusicPageAbsolutePosition(i, graphicalMusicSheet.ParentMusicSheet.rules);
-            let page: GraphicalMusicPage = graphicalMusicSheet.MusicPages[i];
+            const pageAbsolute: PointF2D = graphicalMusicSheet.MusicPages[i].setMusicPageAbsolutePosition(i, graphicalMusicSheet.ParentMusicSheet.rules);
+            const page: GraphicalMusicPage = graphicalMusicSheet.MusicPages[i];
             page.PositionAndShape.calculateAbsolutePositionsRecursive(pageAbsolute.x, pageAbsolute.y);
         }
     }
@@ -189,9 +189,9 @@ export class GraphicalMusicSheet {
 
     public EnforceRedrawOfMusicSystems(): void {
         for (let idx: number = 0, len: number = this.musicPages.length; idx < len; ++idx) {
-            let graphicalMusicPage: GraphicalMusicPage = this.musicPages[idx];
+            const graphicalMusicPage: GraphicalMusicPage = this.musicPages[idx];
             for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
-                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
                 musicSystem.needsToBeRedrawn = true;
             }
         }
@@ -199,7 +199,7 @@ export class GraphicalMusicSheet {
 
     public getClickedObject<T>(positionOnMusicSheet: PointF2D): T {
         for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
-            let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
+            const graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
             return graphicalMusicPage.PositionAndShape.getClickedObjectOfType<T>(positionOnMusicSheet);
         }
         return undefined;
@@ -215,9 +215,9 @@ export class GraphicalMusicSheet {
      */
     public findGraphicalStaffEntryFromMeasureList(staffIndex: number, measureIndex: number, sourceStaffEntry: SourceStaffEntry): GraphicalStaffEntry {
         for (let i: number = measureIndex; i < this.measureList.length; i++) {
-            let graphicalMeasure: StaffMeasure = this.measureList[i][staffIndex];
+            const graphicalMeasure: StaffMeasure = this.measureList[i][staffIndex];
             for (let idx: number = 0, len: number = graphicalMeasure.staffEntries.length; idx < len; ++idx) {
-                let graphicalStaffEntry: GraphicalStaffEntry = graphicalMeasure.staffEntries[idx];
+                const graphicalStaffEntry: GraphicalStaffEntry = graphicalMeasure.staffEntries[idx];
                 if (graphicalStaffEntry.sourceStaffEntry === sourceStaffEntry) {
                     return graphicalStaffEntry;
                 }
@@ -234,12 +234,12 @@ export class GraphicalMusicSheet {
      * @returns {any}
      */
     public findNextGraphicalStaffEntry(staffIndex: number, measureIndex: number, graphicalStaffEntry: GraphicalStaffEntry): GraphicalStaffEntry {
-        let graphicalMeasure: StaffMeasure = graphicalStaffEntry.parentMeasure;
-        let graphicalStaffEntryIndex: number = graphicalMeasure.staffEntries.indexOf(graphicalStaffEntry);
+        const graphicalMeasure: StaffMeasure = graphicalStaffEntry.parentMeasure;
+        const graphicalStaffEntryIndex: number = graphicalMeasure.staffEntries.indexOf(graphicalStaffEntry);
         if (graphicalStaffEntryIndex < graphicalMeasure.staffEntries.length - 1) {
             return graphicalMeasure.staffEntries[graphicalStaffEntryIndex + 1];
         } else if (measureIndex < this.measureList.length - 1) {
-            let nextMeasure: StaffMeasure = this.measureList[measureIndex + 1][staffIndex];
+            const nextMeasure: StaffMeasure = this.measureList[measureIndex + 1][staffIndex];
             if (nextMeasure.staffEntries.length > 0) {
                 return nextMeasure.staffEntries[0];
             }
@@ -248,8 +248,8 @@ export class GraphicalMusicSheet {
     }
 
     public getFirstVisibleMeasuresListFromIndeces(start: number, end: number): StaffMeasure[] {
-        let graphicalMeasures: StaffMeasure[] = [];
-        let numberOfStaves: number = this.measureList[0].length;
+        const graphicalMeasures: StaffMeasure[] = [];
+        const numberOfStaves: number = this.measureList[0].length;
         for (let i: number = start; i <= end; i++) {
             for (let j: number = 0; j < numberOfStaves; j++) {
                 if (this.measureList[i][j].isVisible()) {
@@ -262,7 +262,7 @@ export class GraphicalMusicSheet {
     }
 
     public orderMeasuresByStaffLine(measures: StaffMeasure[]): StaffMeasure[][] {
-        let orderedMeasures: StaffMeasure[][] = [];
+        const orderedMeasures: StaffMeasure[][] = [];
         let mList: StaffMeasure[] = [];
         orderedMeasures.push(mList);
         for (let i: number = 0; i < measures.length; i++) {
@@ -289,14 +289,14 @@ export class GraphicalMusicSheet {
      * @returns {ClefInstruction[]}
      */
     public initializeActiveClefs(): ClefInstruction[] {
-        let activeClefs: ClefInstruction[] = [];
-        let firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
+        const activeClefs: ClefInstruction[] = [];
+        const firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
         if (firstSourceMeasure !== undefined) {
             for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
                 let clef: ClefInstruction = new ClefInstruction();
                 if (firstSourceMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
                     for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
-                        let abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
+                        const abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
                         if (abstractNotationInstruction instanceof ClefInstruction) {
                             clef = <ClefInstruction>abstractNotationInstruction;
 
@@ -310,11 +310,11 @@ export class GraphicalMusicSheet {
     }
 
     public GetMainKey(): KeyInstruction {
-        let firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
+        const firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
         if (firstSourceMeasure !== undefined) {
             for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
                 for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
-                    let abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
+                    const abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
                     if (abstractNotationInstruction instanceof KeyInstruction) {
                         return <KeyInstruction>abstractNotationInstruction;
                     }
@@ -332,14 +332,14 @@ export class GraphicalMusicSheet {
     public getOrCreateVerticalContainer(timestamp: Fraction): VerticalGraphicalStaffEntryContainer {
         if (this.verticalGraphicalStaffEntryContainers.length === 0 ||
             (CollectionUtil.getLastElement(this.verticalGraphicalStaffEntryContainers).AbsoluteTimestamp).lt(timestamp)) {
-            let verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
+            const verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
                 new VerticalGraphicalStaffEntryContainer(this.numberOfStaves, timestamp);
             this.verticalGraphicalStaffEntryContainers.push(verticalGraphicalStaffEntryContainer);
             return verticalGraphicalStaffEntryContainer;
         }
         for (let i: number = this.verticalGraphicalStaffEntryContainers.length - 1; i >= 0; i--) {
             if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp.lt(timestamp)) {
-                let verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
+                const verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
                     new VerticalGraphicalStaffEntryContainer(this.numberOfStaves, timestamp);
                 this.verticalGraphicalStaffEntryContainers.splice(i + 1, 0, verticalGraphicalStaffEntryContainer);
                 return verticalGraphicalStaffEntryContainer;
@@ -361,10 +361,10 @@ export class GraphicalMusicSheet {
      * @constructor
      */
     public GetVerticalContainerFromTimestamp(timestamp: Fraction, startIndex: number = 0): VerticalGraphicalStaffEntryContainer {
-        let index: number = CollectionUtil.binarySearch(this.verticalGraphicalStaffEntryContainers,
-                                                        new VerticalGraphicalStaffEntryContainer(0, timestamp),
-                                                        VerticalGraphicalStaffEntryContainer.compareByTimestamp,
-                                                        startIndex);
+        const index: number = CollectionUtil.binarySearch(this.verticalGraphicalStaffEntryContainers,
+                                                          new VerticalGraphicalStaffEntryContainer(0, timestamp),
+                                                          VerticalGraphicalStaffEntryContainer.compareByTimestamp,
+                                                          startIndex);
         if (index >= 0) {
             return this.verticalGraphicalStaffEntryContainers[index];
         }
@@ -378,7 +378,7 @@ export class GraphicalMusicSheet {
      * @constructor
      */
     public GetInterpolatedIndexInVerticalContainers(musicTimestamp: Fraction): number {
-        let containers: VerticalGraphicalStaffEntryContainer[] = this.verticalGraphicalStaffEntryContainers;
+        const containers: VerticalGraphicalStaffEntryContainer[] = this.verticalGraphicalStaffEntryContainers;
         let leftIndex: number = 0;
         let rightIndex: number = containers.length - 1;
         let foundIndex: number;
@@ -386,7 +386,7 @@ export class GraphicalMusicSheet {
         let rightTS: Fraction = undefined;
         if (musicTimestamp.lte(containers[containers.length - 1].AbsoluteTimestamp)) {
             while (rightIndex - leftIndex > 1) {
-                let middleIndex: number = Math.floor((rightIndex + leftIndex) / 2);
+                const middleIndex: number = Math.floor((rightIndex + leftIndex) / 2);
                 if (containers[leftIndex].AbsoluteTimestamp.Equals(musicTimestamp)) {
                     rightIndex = leftIndex;
                     break;
@@ -413,8 +413,8 @@ export class GraphicalMusicSheet {
             rightTS = Fraction.plus(this.getLongestStaffEntryDuration(containers.length - 1), leftTS);
             rightIndex = containers.length;
         }
-        let diff: number = rightTS.RealValue - leftTS.RealValue;
-        let diffTS: number = rightTS.RealValue - musicTimestamp.RealValue;
+        const diff: number = rightTS.RealValue - leftTS.RealValue;
+        const diffTS: number = rightTS.RealValue - musicTimestamp.RealValue;
 
         // estimate the interpolated index
         foundIndex = rightIndex - (diffTS / diff);
@@ -428,18 +428,18 @@ export class GraphicalMusicSheet {
      * @returns {number[]}
      */
     public getVisibleStavesIndecesFromSourceMeasure(visibleMeasures: StaffMeasure[]): number[] {
-        let visibleInstruments: Instrument[] = [];
-        let visibleStavesIndeces: number[] = [];
+        const visibleInstruments: Instrument[] = [];
+        const visibleStavesIndeces: number[] = [];
         for (let idx: number = 0, len: number = visibleMeasures.length; idx < len; ++idx) {
-            let graphicalMeasure: StaffMeasure = visibleMeasures[idx];
-            let instrument: Instrument = graphicalMeasure.ParentStaff.ParentInstrument;
+            const graphicalMeasure: StaffMeasure = visibleMeasures[idx];
+            const instrument: Instrument = graphicalMeasure.ParentStaff.ParentInstrument;
             if (visibleInstruments.indexOf(instrument) === -1) {
                 visibleInstruments.push(instrument);
             }
         }
         for (let idx: number = 0, len: number = visibleInstruments.length; idx < len; ++idx) {
-            let instrument: Instrument = visibleInstruments[idx];
-            let index: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(instrument);
+            const instrument: Instrument = visibleInstruments[idx];
+            const index: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(instrument);
             for (let j: number = 0; j < instrument.Staves.length; j++) {
                 visibleStavesIndeces.push(index + j);
             }
@@ -467,7 +467,7 @@ export class GraphicalMusicSheet {
         inListIndex = 0;
         for (; measureIndex < this.measureList.length; measureIndex++) {
             for (let idx: number = 0, len: number = this.measureList[measureIndex].length; idx < len; ++idx) {
-                let measure: StaffMeasure = this.measureList[measureIndex][idx];
+                const measure: StaffMeasure = this.measureList[measureIndex][idx];
                 if (measure === graphicalMeasure) {
                     return true;
                 }
@@ -477,11 +477,11 @@ export class GraphicalMusicSheet {
     }
 
     public GetNearesNote(clickPosition: PointF2D, maxClickDist: PointF2D): GraphicalNote {
-        let initialSearchArea: number = 10;
-        let foundNotes: GraphicalNote[] = [];
+        const initialSearchArea: number = 10;
+        const foundNotes: GraphicalNote[] = [];
 
         // Prepare search area
-        let region: BoundingBox = new BoundingBox();
+        const region: BoundingBox = new BoundingBox();
         region.BorderLeft = clickPosition.x - initialSearchArea;
         region.BorderTop = clickPosition.y - initialSearchArea;
         region.BorderRight = clickPosition.x + initialSearchArea;
@@ -490,14 +490,14 @@ export class GraphicalMusicSheet {
 
         // Search for StaffEntries in region
         for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
-            let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
-            let entries: GraphicalNote[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalNote>(region);
+            const graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
+            const entries: GraphicalNote[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalNote>(region);
             //let entriesArr: GraphicalNote[] = __as__<GraphicalNote[]>(entries, GraphicalNote[]) ? ? entries;
             if (entries === undefined) {
                 continue;
             } else {
                 for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
-                    let note: GraphicalNote = entries[idx2];
+                    const note: GraphicalNote = entries[idx2];
                     if (Math.abs(note.PositionAndShape.AbsolutePosition.x - clickPosition.x) < maxClickDist.x
                         && Math.abs(note.PositionAndShape.AbsolutePosition.y - clickPosition.y) < maxClickDist.y) {
                         foundNotes.push(note);
@@ -509,15 +509,15 @@ export class GraphicalMusicSheet {
         // Get closest entry
         let closest: GraphicalNote = undefined;
         for (let idx: number = 0, len: number = foundNotes.length; idx < len; ++idx) {
-            let note: GraphicalNote = foundNotes[idx];
+            const note: GraphicalNote = foundNotes[idx];
             if (closest === undefined) {
                 closest = note;
             } else {
                 if (note.parentStaffEntry.relInMeasureTimestamp === undefined) {
                     continue;
                 }
-                let deltaNew: number = this.CalculateDistance(note.PositionAndShape.AbsolutePosition, clickPosition);
-                let deltaOld: number = this.CalculateDistance(closest.PositionAndShape.AbsolutePosition, clickPosition);
+                const deltaNew: number = this.CalculateDistance(note.PositionAndShape.AbsolutePosition, clickPosition);
+                const deltaOld: number = this.CalculateDistance(closest.PositionAndShape.AbsolutePosition, clickPosition);
                 if (deltaNew < deltaOld) {
                     closest = note;
                 }
@@ -532,23 +532,23 @@ export class GraphicalMusicSheet {
     }
 
     public GetClickableLabel(clickPosition: PointF2D): GraphicalLabel {
-        let initialSearchAreaX: number = 4;
-        let initialSearchAreaY: number = 4;
+        const initialSearchAreaX: number = 4;
+        const initialSearchAreaY: number = 4;
         // Prepare search area
-        let region: BoundingBox = new BoundingBox();
+        const region: BoundingBox = new BoundingBox();
         region.BorderLeft = clickPosition.x - initialSearchAreaX;
         region.BorderTop = clickPosition.y - initialSearchAreaY;
         region.BorderRight = clickPosition.x + initialSearchAreaX;
         region.BorderBottom = clickPosition.y + initialSearchAreaY;
         region.AbsolutePosition = new PointF2D(0, 0);
         for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
-            let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
-            let entries: GraphicalLabel[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalLabel>(region);
+            const graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
+            const entries: GraphicalLabel[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalLabel>(region);
             if (entries.length !== 1) {
                 continue;
             } else {
                 for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
-                    let clickedLabel: GraphicalLabel = entries[idx2];
+                    const clickedLabel: GraphicalLabel = entries[idx2];
                     return clickedLabel;
                 }
             }
@@ -557,10 +557,10 @@ export class GraphicalMusicSheet {
     }
 
     public GetNearestStaffEntry(clickPosition: PointF2D): GraphicalStaffEntry {
-        let initialSearchArea: number = 10;
-        let foundEntries: GraphicalStaffEntry[] = [];
+        const initialSearchArea: number = 10;
+        const foundEntries: GraphicalStaffEntry[] = [];
         // Prepare search area
-        let region: BoundingBox = new BoundingBox(undefined);
+        const region: BoundingBox = new BoundingBox(undefined);
         region.BorderLeft = clickPosition.x - initialSearchArea;
         region.BorderTop = clickPosition.y - initialSearchArea;
         region.BorderRight = clickPosition.x + initialSearchArea;
@@ -568,13 +568,13 @@ export class GraphicalMusicSheet {
         region.AbsolutePosition = new PointF2D(0, 0);
         // Search for StaffEntries in region
         for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
-            let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
-            let entries: GraphicalStaffEntry[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalStaffEntry>(region, false);
+            const graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
+            const entries: GraphicalStaffEntry[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalStaffEntry>(region, false);
             if (entries === undefined || entries.length === 0) {
                 continue;
             } else {
                 for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
-                    let gse: GraphicalStaffEntry = entries[idx2];
+                    const gse: GraphicalStaffEntry = entries[idx2];
                     foundEntries.push(gse);
                 }
             }
@@ -582,15 +582,15 @@ export class GraphicalMusicSheet {
         // Get closest entry
         let closest: GraphicalStaffEntry = undefined;
         for (let idx: number = 0, len: number = foundEntries.length; idx < len; ++idx) {
-            let gse: GraphicalStaffEntry = foundEntries[idx];
+            const gse: GraphicalStaffEntry = foundEntries[idx];
             if (closest === undefined) {
                 closest = gse;
             } else {
                 if (gse.relInMeasureTimestamp === undefined) {
                     continue;
                 }
-                let deltaNew: number = this.CalculateDistance(gse.PositionAndShape.AbsolutePosition, clickPosition);
-                let deltaOld: number = this.CalculateDistance(closest.PositionAndShape.AbsolutePosition, clickPosition);
+                const deltaNew: number = this.CalculateDistance(gse.PositionAndShape.AbsolutePosition, clickPosition);
+                const deltaOld: number = this.CalculateDistance(closest.PositionAndShape.AbsolutePosition, clickPosition);
                 if (deltaNew < deltaOld) {
                     closest = gse;
                 }
@@ -605,7 +605,7 @@ export class GraphicalMusicSheet {
     }
 
     public GetPossibleCommentAnchor(clickPosition: PointF2D): SourceStaffEntry {
-        let entry: GraphicalStaffEntry = this.GetNearestStaffEntry(clickPosition);
+        const entry: GraphicalStaffEntry = this.GetNearestStaffEntry(clickPosition);
         if (entry === undefined) {
             return undefined;
         }
@@ -614,8 +614,8 @@ export class GraphicalMusicSheet {
 
     public getClickedObjectOfType<T>(positionOnMusicSheet: PointF2D): T {
         for (let idx: number = 0, len: number = this.musicPages.length; idx < len; ++idx) {
-            let page: GraphicalMusicPage = this.musicPages[idx];
-            let o: Object = page.PositionAndShape.getClickedObjectOfType<T>(positionOnMusicSheet);
+            const page: GraphicalMusicPage = this.musicPages[idx];
+            const o: Object = page.PositionAndShape.getClickedObjectOfType<T>(positionOnMusicSheet);
             if (o !== undefined) {
                 return (o as T);
             }
@@ -624,7 +624,7 @@ export class GraphicalMusicSheet {
     }
 
     public tryGetTimestampFromPosition(positionOnMusicSheet: PointF2D): Fraction {
-        let entry: GraphicalStaffEntry = this.getClickedObjectOfType<GraphicalStaffEntry>(positionOnMusicSheet);
+        const entry: GraphicalStaffEntry = this.getClickedObjectOfType<GraphicalStaffEntry>(positionOnMusicSheet);
         if (entry === undefined) {
             return undefined;
         }
@@ -643,7 +643,7 @@ export class GraphicalMusicSheet {
 
     public tryGetTimeStampFromPosition(positionOnMusicSheet: PointF2D): Fraction {
         try {
-            let entry: GraphicalStaffEntry = this.GetNearestStaffEntry(positionOnMusicSheet);
+            const entry: GraphicalStaffEntry = this.GetNearestStaffEntry(positionOnMusicSheet);
             if (entry === undefined) {
                 return undefined;
             }
@@ -664,11 +664,11 @@ export class GraphicalMusicSheet {
      * @returns {GraphicalStaffEntry}
      */
     public getStaffEntry(index: number): GraphicalStaffEntry {
-        let container: VerticalGraphicalStaffEntryContainer = this.VerticalGraphicalStaffEntryContainers[index];
+        const container: VerticalGraphicalStaffEntryContainer = this.VerticalGraphicalStaffEntryContainers[index];
         let staffEntry: GraphicalStaffEntry = undefined;
         try {
             for (let idx: number = 0, len: number = container.StaffEntries.length; idx < len; ++idx) {
-                let entry: GraphicalStaffEntry = container.StaffEntries[idx];
+                const entry: GraphicalStaffEntry = container.StaffEntries[idx];
                 if (entry === undefined || !entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
                     continue;
                 }
@@ -695,9 +695,9 @@ export class GraphicalMusicSheet {
      */
     public GetPreviousVisibleContainerIndex(index: number): number {
         for (let i: number = index - 1; i >= 0; i--) {
-            let entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
+            const entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
             for (let idx: number = 0, len: number = entries.length; idx < len; ++idx) {
-                let entry: GraphicalStaffEntry = entries[idx];
+                const entry: GraphicalStaffEntry = entries[idx];
                 if (entry !== undefined && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
                     return i;
                 }
@@ -714,9 +714,9 @@ export class GraphicalMusicSheet {
      */
     public GetNextVisibleContainerIndex(index: number): number {
         for (let i: number = index + 1; i < this.verticalGraphicalStaffEntryContainers.length; ++i) {
-            let entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
+            const entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
             for (let idx: number = 0, len: number = entries.length; idx < len; ++idx) {
-                let entry: GraphicalStaffEntry = entries[idx];
+                const entry: GraphicalStaffEntry = entries[idx];
                 if (entry !== undefined && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
                     return i;
                 }
@@ -746,7 +746,7 @@ export class GraphicalMusicSheet {
 
     public findClosestRightStaffEntry(fractionalIndex: number, returnOnlyVisibleEntries: boolean): GraphicalStaffEntry {
         let foundEntry: GraphicalStaffEntry = undefined;
-        let rightIndex: number = <number>Math.max(0, Math.ceil(fractionalIndex));
+        const rightIndex: number = <number>Math.max(0, Math.ceil(fractionalIndex));
         for (let i: number = rightIndex; i < this.VerticalGraphicalStaffEntryContainers.length; i++) {
             foundEntry = this.getStaffEntry(i);
             if (foundEntry !== undefined) {
@@ -763,23 +763,23 @@ export class GraphicalMusicSheet {
     }
 
     public calculateCursorLineAtTimestamp(musicTimestamp: Fraction, styleEnum: OutlineAndFillStyleEnum): GraphicalLine {
-        let result: [number, MusicSystem] = this.calculateXPositionFromTimestamp(musicTimestamp);
-        let xPos: number = result[0];
-        let correspondingMusicSystem: MusicSystem = result[1];
+        const result: [number, MusicSystem] = this.calculateXPositionFromTimestamp(musicTimestamp);
+        const xPos: number = result[0];
+        const correspondingMusicSystem: MusicSystem = result[1];
         if (correspondingMusicSystem === undefined || correspondingMusicSystem.StaffLines.length === 0) {
             return undefined;
         }
-        let yCoordinate: number = correspondingMusicSystem.PositionAndShape.AbsolutePosition.y;
-        let height: number = CollectionUtil.last(correspondingMusicSystem.StaffLines).PositionAndShape.RelativePosition.y + 4;
+        const yCoordinate: number = correspondingMusicSystem.PositionAndShape.AbsolutePosition.y;
+        const height: number = CollectionUtil.last(correspondingMusicSystem.StaffLines).PositionAndShape.RelativePosition.y + 4;
         return new GraphicalLine(new PointF2D(xPos, yCoordinate), new PointF2D(xPos, yCoordinate + height), 3, styleEnum);
     }
 
     public calculateXPositionFromTimestamp(timeStamp: Fraction): [number, MusicSystem] {
         let currentMusicSystem: MusicSystem = undefined;
-        let fractionalIndex: number = this.GetInterpolatedIndexInVerticalContainers(timeStamp);
-        let previousStaffEntry: GraphicalStaffEntry = this.findClosestLeftStaffEntry(fractionalIndex, true);
-        let nextStaffEntry: GraphicalStaffEntry = this.findClosestRightStaffEntry(fractionalIndex, true);
-        let currentTimeStamp: number = timeStamp.RealValue;
+        const fractionalIndex: number = this.GetInterpolatedIndexInVerticalContainers(timeStamp);
+        const previousStaffEntry: GraphicalStaffEntry = this.findClosestLeftStaffEntry(fractionalIndex, true);
+        const nextStaffEntry: GraphicalStaffEntry = this.findClosestRightStaffEntry(fractionalIndex, true);
+        const currentTimeStamp: number = timeStamp.RealValue;
         if (previousStaffEntry === undefined && nextStaffEntry === undefined) {
             return [0, undefined];
         }
@@ -806,7 +806,7 @@ export class GraphicalMusicSheet {
             } else if (nextStaffEntry === undefined) {
                 previousStaffEntryPositionX = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
                 nextStaffEntryPositionX = currentMusicSystem.GetRightBorderAbsoluteXPosition();
-                let sm: SourceMeasure = previousStaffEntry.parentMeasure.parentSourceMeasure;
+                const sm: SourceMeasure = previousStaffEntry.parentMeasure.parentSourceMeasure;
                 fraction = (currentTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue) / (
                     Fraction.plus(sm.AbsoluteTimestamp, sm.Duration).RealValue - previousStaffEntry.getAbsoluteTimestamp().RealValue);
             } else {
@@ -820,24 +820,24 @@ export class GraphicalMusicSheet {
                 }
             }
             fraction = Math.min(1, Math.max(0, fraction));
-            let interpolatedXPosition: number = previousStaffEntryPositionX + fraction * (nextStaffEntryPositionX - previousStaffEntryPositionX);
+            const interpolatedXPosition: number = previousStaffEntryPositionX + fraction * (nextStaffEntryPositionX - previousStaffEntryPositionX);
             return [interpolatedXPosition, currentMusicSystem];
         } else {
-            let nextSystemLeftBorderTimeStamp: number = nextStaffEntry.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.RealValue;
+            const nextSystemLeftBorderTimeStamp: number = nextStaffEntry.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.RealValue;
             let fraction: number;
             let interpolatedXPosition: number;
             if (currentTimeStamp < nextSystemLeftBorderTimeStamp) {
                 currentMusicSystem = previousStaffEntryMusicSystem;
-                let previousStaffEntryPositionX: number = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
-                let previousSystemRightBorderX: number = currentMusicSystem.GetRightBorderAbsoluteXPosition();
+                const previousStaffEntryPositionX: number = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
+                const previousSystemRightBorderX: number = currentMusicSystem.GetRightBorderAbsoluteXPosition();
                 fraction = (currentTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue) /
                     (nextSystemLeftBorderTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue);
                 fraction = Math.min(1, Math.max(0, fraction));
                 interpolatedXPosition = previousStaffEntryPositionX + fraction * (previousSystemRightBorderX - previousStaffEntryPositionX);
             } else {
                 currentMusicSystem = nextStaffEntryMusicSystem;
-                let nextStaffEntryPositionX: number = nextStaffEntry.PositionAndShape.AbsolutePosition.x;
-                let nextSystemLeftBorderX: number = currentMusicSystem.GetLeftBorderAbsoluteXPosition();
+                const nextStaffEntryPositionX: number = nextStaffEntry.PositionAndShape.AbsolutePosition.x;
+                const nextSystemLeftBorderX: number = currentMusicSystem.GetLeftBorderAbsoluteXPosition();
                 fraction = (currentTimeStamp - nextSystemLeftBorderTimeStamp) /
                     (nextStaffEntry.getAbsoluteTimestamp().RealValue - nextSystemLeftBorderTimeStamp);
                 fraction = Math.min(1, Math.max(0, fraction));
@@ -850,7 +850,7 @@ export class GraphicalMusicSheet {
     public GetNumberOfVisibleInstruments(): number {
         let visibleInstrumentCount: number = 0;
         for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
-            let instrument: Instrument = this.musicSheet.Instruments[idx];
+            const instrument: Instrument = this.musicSheet.Instruments[idx];
             if (instrument.Visible === true) {
                 visibleInstrumentCount++;
             }
@@ -861,7 +861,7 @@ export class GraphicalMusicSheet {
     public GetNumberOfFollowedInstruments(): number {
         let followedInstrumentCount: number = 0;
         for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
-            let instrument: Instrument = this.musicSheet.Instruments[idx];
+            const instrument: Instrument = this.musicSheet.Instruments[idx];
             if (instrument.Following === true) {
                 followedInstrumentCount++;
             }
@@ -874,16 +874,16 @@ export class GraphicalMusicSheet {
     }
 
     public GetGraphicalFromSourceStaffEntry(sourceStaffEntry: SourceStaffEntry): GraphicalStaffEntry {
-        let graphicalMeasure: StaffMeasure = this.GetGraphicalFromSourceMeasure(sourceStaffEntry.VerticalContainerParent.ParentMeasure)
+        const graphicalMeasure: StaffMeasure = this.GetGraphicalFromSourceMeasure(sourceStaffEntry.VerticalContainerParent.ParentMeasure)
             [sourceStaffEntry.ParentStaff.idInMusicSheet];
         return graphicalMeasure.findGraphicalStaffEntryFromTimestamp(sourceStaffEntry.Timestamp);
     }
 
     public GetGraphicalNoteFromSourceNote(note: Note, containingGse: GraphicalStaffEntry): GraphicalNote {
         for (let idx: number = 0, len: number = containingGse.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = containingGse.notes[idx];
+            const graphicalNotes: GraphicalNote[] = containingGse.notes[idx];
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                const graphicalNote: GraphicalNote = graphicalNotes[idx2];
                 if (graphicalNote.sourceNote === note) {
                     return graphicalNote;
                 }
@@ -893,8 +893,8 @@ export class GraphicalMusicSheet {
     }
 
     private CalculateDistance(pt1: PointF2D, pt2: PointF2D): number {
-        let deltaX: number = pt1.x - pt2.x;
-        let deltaY: number = pt1.y - pt2.y;
+        const deltaX: number = pt1.x - pt2.x;
+        const deltaY: number = pt1.y - pt2.y;
         return (deltaX * deltaX) + (deltaY * deltaY);
     }
 
@@ -906,14 +906,14 @@ export class GraphicalMusicSheet {
     private getLongestStaffEntryDuration(index: number): Fraction {
         let maxLength: Fraction = new Fraction(0, 1);
         for (let idx: number = 0, len: number = this.verticalGraphicalStaffEntryContainers[index].StaffEntries.length; idx < len; ++idx) {
-            let graphicalStaffEntry: GraphicalStaffEntry = this.verticalGraphicalStaffEntryContainers[index].StaffEntries[idx];
+            const graphicalStaffEntry: GraphicalStaffEntry = this.verticalGraphicalStaffEntryContainers[index].StaffEntries[idx];
             if (graphicalStaffEntry === undefined) {
                 continue;
             }
             for (let idx2: number = 0, len2: number = graphicalStaffEntry.notes.length; idx2 < len2; ++idx2) {
-                let graphicalNotes: GraphicalNote[] = graphicalStaffEntry.notes[idx2];
+                const graphicalNotes: GraphicalNote[] = graphicalStaffEntry.notes[idx2];
                 for (let idx3: number = 0, len3: number = graphicalNotes.length; idx3 < len3; ++idx3) {
-                    let note: GraphicalNote = graphicalNotes[idx3];
+                    const note: GraphicalNote = graphicalNotes[idx3];
                     if (maxLength.lt(note.graphicalNoteLength)) {
                         maxLength = note.graphicalNoteLength;
                     }

+ 10 - 5
src/MusicalScore/Graphical/GraphicalNote.ts

@@ -38,7 +38,7 @@ export class GraphicalNote extends GraphicalObject {
 
     public get ParentList(): GraphicalNote[] {
         for (let idx: number = 0, len: number = this.parentStaffEntry.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.parentStaffEntry.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.parentStaffEntry.notes[idx];
             if (graphicalNotes.indexOf(this) !== -1) {
                 return graphicalNotes;
             }
@@ -60,10 +60,15 @@ export class GraphicalNote extends GraphicalObject {
      * @returns {number}
      */
     private calculateNumberOfNeededDots(fraction: Fraction): number {
-        let dotCount: number = 0;
-        if (this.sourceNote === undefined || this.sourceNote.NoteTuplet === undefined) {
-            dotCount = Math.floor(Math.log(fraction.Numerator) / Math.LN2);
+      let num: number = 1;
+      let product: number = 2;
+      const expandedNumerator: number = fraction.GetExpandedNumerator();
+      if (this.sourceNote === undefined || this.sourceNote.NoteTuplet === undefined) {
+        while (product < expandedNumerator) {
+          num++;
+          product = <number>Math.pow(2, num);
         }
-        return Math.min(3, dotCount);
+      }
+      return Math.min(3, num - 1);
     }
 }

+ 47 - 40
src/MusicalScore/Graphical/GraphicalStaffEntry.ts

@@ -6,7 +6,6 @@ import {Note} from "../VoiceData/Note";
 import {Slur} from "../VoiceData/Expressions/ContinuousExpressions/Slur";
 import {Voice} from "../VoiceData/Voice";
 import {VoiceEntry} from "../VoiceData/VoiceEntry";
-import {LinkedVoice} from "../VoiceData/LinkedVoice";
 import {GraphicalTie} from "./GraphicalTie";
 import {GraphicalObject} from "./GraphicalObject";
 import {StaffMeasure} from "./StaffMeasure";
@@ -69,12 +68,16 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
         return this.lyricsEntries;
     }
 
+    public set LyricsEntries(value: GraphicalLyricEntry[]) {
+        this.lyricsEntries = value;
+    }
+
     /**
      * Calculate the absolute Timestamp.
      * @returns {Fraction}
      */
     public getAbsoluteTimestamp(): Fraction {
-        let result: Fraction = this.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.clone();
+        const result: Fraction = this.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.clone();
         if (this.relInMeasureTimestamp !== undefined) {
             result.Add(this.relInMeasureTimestamp);
         }
@@ -88,10 +91,10 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      */
     public findEndTieGraphicalNoteFromNote(tieNote: Note): GraphicalNote {
         for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.notes[idx];
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
-                let note: Note = graphicalNote.sourceNote;
+                const graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                const note: Note = graphicalNote.sourceNote;
                 if (note.Pitch !== undefined && note.Pitch.FundamentalNote === tieNote.Pitch.FundamentalNote
                     && note.Pitch.Octave === tieNote.Pitch.Octave && note.getAbsoluteTimestamp().Equals(tieNote.getAbsoluteTimestamp())) {
                     return graphicalNote;
@@ -109,10 +112,10 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      */
     public findEndTieGraphicalNoteFromNoteWithStartingSlur(tieNote: Note, slur: Slur): GraphicalNote {
         for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.notes[idx];
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
-                let note: Note = graphicalNote.sourceNote;
+                const graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                const note: Note = graphicalNote.sourceNote;
                 if (note.NoteTie !== undefined && note.NoteSlurs.indexOf(slur) !== -1) {
                     return graphicalNote;
                 }
@@ -128,10 +131,10 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      */
     public findEndTieGraphicalNoteFromNoteWithEndingSlur(tieNote: Note): GraphicalNote {
         for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.notes[idx];
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
-                let note: Note = graphicalNote.sourceNote;
+                const graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                const note: Note = graphicalNote.sourceNote;
                 if (
                     note.Pitch !== undefined && note.Pitch.FundamentalNote === tieNote.Pitch.FundamentalNote
                     && note.Pitch.Octave === tieNote.Pitch.Octave && this.getAbsoluteTimestamp().Equals(tieNote.getAbsoluteTimestamp())
@@ -145,9 +148,9 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
 
     public findGraphicalNoteFromGraceNote(graceNote: Note): GraphicalNote {
         for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.notes[idx];
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                const graphicalNote: GraphicalNote = graphicalNotes[idx2];
                 if (graphicalNote.sourceNote === graceNote) {
                     return graphicalNote;
                 }
@@ -158,9 +161,9 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
 
     public findGraphicalNoteFromNote(baseNote: Note): GraphicalNote {
         for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.notes[idx];
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                const graphicalNote: GraphicalNote = graphicalNotes[idx2];
                 if (graphicalNote.sourceNote === baseNote && this.getAbsoluteTimestamp().Equals(baseNote.getAbsoluteTimestamp())) {
                     return graphicalNote;
                 }
@@ -171,7 +174,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
 
     public getGraphicalNoteDurationFromVoice(voice: Voice): Fraction {
         for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.notes[idx];
             if (graphicalNotes[0].sourceNote.ParentVoiceEntry.ParentVoice === voice) {
                 return graphicalNotes[0].graphicalNoteLength;
             }
@@ -186,9 +189,9 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
     public findLinkedNotes(notLinkedNotes: GraphicalNote[]): void {
         if (this.sourceStaffEntry !== undefined && this.sourceStaffEntry.Link !== undefined) {
             for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-                let graphicalNotes: GraphicalNote[] = this.notes[idx];
+                const graphicalNotes: GraphicalNote[] = this.notes[idx];
                 for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                    let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                    const graphicalNote: GraphicalNote = graphicalNotes[idx2];
                     if (graphicalNote.parentStaffEntry === this) {
                         notLinkedNotes.push(graphicalNote);
                     }
@@ -204,9 +207,9 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      */
     public findVoiceEntryGraphicalNotes(voiceEntry: VoiceEntry): GraphicalNote[] {
         for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.notes[idx];
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                const graphicalNote: GraphicalNote = graphicalNotes[idx2];
                 if (graphicalNote.sourceNote.ParentVoiceEntry === voiceEntry) {
                     return graphicalNotes;
                 }
@@ -223,7 +226,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
     public isVoiceEntryPartOfLinkedVoiceEntry(voiceEntry: VoiceEntry): boolean {
         if (this.sourceStaffEntry.Link !== undefined) {
             for (let idx: number = 0, len: number = this.sourceStaffEntry.Link.LinkStaffEntries.length; idx < len; ++idx) {
-                let sEntry: SourceStaffEntry = this.sourceStaffEntry.Link.LinkStaffEntries[idx];
+                const sEntry: SourceStaffEntry = this.sourceStaffEntry.Link.LinkStaffEntries[idx];
                 if (sEntry.VoiceEntries.indexOf(voiceEntry) !== -1 && sEntry !== this.sourceStaffEntry) {
                     return true;
                 }
@@ -232,16 +235,6 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
         return false;
     }
 
-    public getMainVoice(): Voice {
-        for (let idx: number = 0, len: number = this.sourceStaffEntry.VoiceEntries.length; idx < len; ++idx) {
-            let voiceEntry: VoiceEntry = this.sourceStaffEntry.VoiceEntries[idx];
-            if (!(voiceEntry.ParentVoice instanceof LinkedVoice)) {
-                return voiceEntry.ParentVoice;
-            }
-        }
-        return this.notes[0][0].sourceNote.ParentVoiceEntry.ParentVoice;
-    }
-
     /**
      * Return the [[StaffEntry]]'s Minimum NoteLength.
      * @returns {Fraction}
@@ -249,11 +242,11 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
     public findStaffEntryMinNoteLength(): Fraction {
         let minLength: Fraction = new Fraction(Number.MAX_VALUE, 1);
         for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.notes[idx];
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
-                let calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
-                if (calNoteLen.lt(minLength) && calNoteLen.Numerator > 0) {
+                const graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                const calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
+                if (calNoteLen.lt(minLength) && calNoteLen.GetExpandedNumerator() > 0) {
                     minLength = calNoteLen;
                 }
             }
@@ -264,11 +257,11 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
     public findStaffEntryMaxNoteLength(): Fraction {
         let maxLength: Fraction = new Fraction(0, 1);
         for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
-            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            const graphicalNotes: GraphicalNote[] = this.notes[idx];
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
-                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
-                let calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
-                if (maxLength.lt(calNoteLen)  && calNoteLen.Numerator > 0) {
+                const graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                const calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
+                if (maxLength.lt(calNoteLen)  && calNoteLen.GetExpandedNumerator() > 0) {
                     maxLength = calNoteLen;
                 }
             }
@@ -305,7 +298,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
      */
     public findOrCreateGraphicalNotesListFromGraphicalNote(graphicalNote: GraphicalNote): GraphicalNote[] {
         let graphicalNotes: GraphicalNote[];
-        let tieStartSourceStaffEntry: SourceStaffEntry = graphicalNote.sourceNote.ParentStaffEntry;
+        const tieStartSourceStaffEntry: SourceStaffEntry = graphicalNote.sourceNote.ParentStaffEntry;
         if (this.sourceStaffEntry !== tieStartSourceStaffEntry) {
             graphicalNotes = this.findOrCreateGraphicalNotesListFromVoiceEntry(graphicalNote.sourceNote.ParentVoiceEntry);
         } else {
@@ -349,4 +342,18 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
             }
         }
     }
+
+    // FIXME: implement
+    public hasOnlyRests(): boolean {
+        const hasOnlyRests: boolean = true;
+        for (const graphicalNotes of this.notes) {
+            for (const graphicalNote of graphicalNotes) {
+                const note: Note = graphicalNote.sourceNote;
+                if (!note.isRest()) {
+                    return false;
+                }
+            }
+        }
+        return hasOnlyRests;
+    }
 }

+ 4 - 4
src/MusicalScore/Graphical/GraphicalStaffEntryLink.ts

@@ -38,13 +38,13 @@ export class GraphicalStaffEntryLink {
      */
     public getLinkedStaffEntriesGraphicalNotes(graphicalStaffEntry: GraphicalStaffEntry): GraphicalNote[] {
         if (this.graphicalLinkedStaffEntries.indexOf(graphicalStaffEntry) !== -1) {
-            let notes: GraphicalNote[] = [];
+            const notes: GraphicalNote[] = [];
             for (let idx: number = 0, len: number = this.graphicalLinkedStaffEntries.length; idx < len; ++idx) {
-                let graphicalLinkedStaffEntry: GraphicalStaffEntry = this.graphicalLinkedStaffEntries[idx];
+                const graphicalLinkedStaffEntry: GraphicalStaffEntry = this.graphicalLinkedStaffEntries[idx];
                 for (let idx2: number = 0, len2: number = graphicalLinkedStaffEntry.notes.length; idx2 < len2; ++idx2) {
-                    let graphicalNotes: GraphicalNote[] = graphicalLinkedStaffEntry.notes[idx2];
+                    const graphicalNotes: GraphicalNote[] = graphicalLinkedStaffEntry.notes[idx2];
                     for (let idx3: number = 0, len3: number = graphicalNotes.length; idx3 < len3; ++idx3) {
-                        let graphicalNote: GraphicalNote = graphicalNotes[idx3];
+                        const graphicalNote: GraphicalNote = graphicalNotes[idx3];
                         if (graphicalNote.sourceNote.ParentStaffEntry.Link !== undefined
                             && graphicalNote.sourceNote.ParentVoiceEntry === this.staffEntryLink.GetVoiceEntry) {
                             notes.push(graphicalNote);

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 370 - 147
src/MusicalScore/Graphical/MusicSheetCalculator.ts


+ 100 - 58
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -67,8 +67,8 @@ export abstract class MusicSheetDrawer {
         this.rules = graphicalMusicSheet.ParentMusicSheet.Rules;
         this.drawSplitScreenLine();
         if (this.drawingParameters.drawCursors) {
-            for (let line of graphicalMusicSheet.Cursors) {
-                let psi: BoundingBox = new BoundingBox(line);
+            for (const line of graphicalMusicSheet.Cursors) {
+                const psi: BoundingBox = new BoundingBox(line);
                 psi.AbsolutePosition = line.Start;
                 psi.BorderBottom = line.End.y - line.Start.y;
                 psi.BorderRight = line.Width / 2.0;
@@ -83,7 +83,7 @@ export abstract class MusicSheetDrawer {
             this.drawScrollIndicator();
         }
         // Draw all the pages
-        for (let page of this.graphicalMusicSheet.MusicPages) {
+        for (const page of this.graphicalMusicSheet.MusicPages) {
             this.drawPage(page);
         }
     }
@@ -95,33 +95,33 @@ export abstract class MusicSheetDrawer {
     }
 
     public drawLineAsVerticalRectangle(line: GraphicalLine, layer: number): void {
-        let lineStart: PointF2D = line.Start;
-        let lineWidth: number = line.Width;
+        const lineStart: PointF2D = line.Start;
+        const lineWidth: number = line.Width;
         let rectangle: RectangleF2D = new RectangleF2D(lineStart.x - lineWidth / 2, lineStart.y, lineWidth, line.End.y - lineStart.y);
         rectangle = this.applyScreenTransformationForRect(rectangle);
         this.renderRectangle(rectangle, layer, line.styleId);
     }
 
     public drawLineAsHorizontalRectangleWithOffset(line: GraphicalLine, offset: PointF2D, layer: number): void {
-        let start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
-        let end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
-        let width: number = line.Width;
+        const start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
+        const end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
+        const width: number = line.Width;
         let rectangle: RectangleF2D = new RectangleF2D(start.x, end.y - width / 2, end.x - start.x, width);
         rectangle = this.applyScreenTransformationForRect(rectangle);
         this.renderRectangle(rectangle, layer, line.styleId);
     }
 
     public drawLineAsVerticalRectangleWithOffset(line: GraphicalLine, offset: PointF2D, layer: number): void {
-        let start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
-        let end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
-        let width: number = line.Width;
+        const start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
+        const end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
+        const width: number = line.Width;
         let rectangle: RectangleF2D = new RectangleF2D(start.x, start.y, width, end.y - start.y);
         rectangle = this.applyScreenTransformationForRect(rectangle);
         this.renderRectangle(rectangle, layer, line.styleId);
     }
 
     public drawRectangle(rect: GraphicalRectangle, layer: number): void {
-        let psi: BoundingBox = rect.PositionAndShape;
+        const psi: BoundingBox = rect.PositionAndShape;
         let rectangle: RectangleF2D = new RectangleF2D(psi.AbsolutePosition.x, psi.AbsolutePosition.y, psi.BorderRight, psi.BorderBottom);
         rectangle = this.applyScreenTransformationForRect(rectangle);
         this.renderRectangle(rectangle, layer, <number>rect.style);
@@ -135,15 +135,15 @@ export abstract class MusicSheetDrawer {
         if (!this.isVisible(graphicalLabel.PositionAndShape)) {
             return;
         }
-        let label: Label = graphicalLabel.Label;
+        const label: Label = graphicalLabel.Label;
         if (label.text.trim() === "") {
             return;
         }
-        let screenPosition: PointF2D = this.applyScreenTransformation(graphicalLabel.PositionAndShape.AbsolutePosition);
-        let heightInPixel: number = this.calculatePixelDistance(label.fontHeight);
-        let widthInPixel: number = heightInPixel * this.textMeasurer.computeTextWidthToHeightRatio(label.text, label.font, label.fontStyle);
-        let bitmapWidth: number = <number>Math.ceil(widthInPixel);
-        let bitmapHeight: number = <number>Math.ceil(heightInPixel * 1.2);
+        const screenPosition: PointF2D = this.applyScreenTransformation(graphicalLabel.PositionAndShape.AbsolutePosition);
+        const heightInPixel: number = this.calculatePixelDistance(label.fontHeight);
+        const widthInPixel: number = heightInPixel * this.textMeasurer.computeTextWidthToHeightRatio(label.text, label.font, label.fontStyle);
+        const bitmapWidth: number = <number>Math.ceil(widthInPixel);
+        const bitmapHeight: number = <number>Math.ceil(heightInPixel * 1.2);
         switch (label.textAlignment) {
             case TextAlignment.LeftTop:
                 break;
@@ -186,8 +186,8 @@ export abstract class MusicSheetDrawer {
     }
 
     protected applyScreenTransformations(points: PointF2D[]): PointF2D[] {
-        let transformedPoints: PointF2D[] = [];
-        for (let point of points) {
+        const transformedPoints: PointF2D[] = [];
+        for (const point of points) {
             transformedPoints.push(this.applyScreenTransformation(point));
         }
         return transformedPoints;
@@ -201,7 +201,7 @@ export abstract class MusicSheetDrawer {
         // empty
     }
 
-    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number): void {
+    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number, alpha: number = 1): void {
         throw new Error("not implemented");
     }
 
@@ -239,7 +239,7 @@ export abstract class MusicSheetDrawer {
         // empty
     }
 
-    protected drawInstrumentBracket(bracket: GraphicalObject, system: MusicSystem): void {
+    protected drawInstrumentBrace(brace: GraphicalObject, system: MusicSystem): void {
         // empty
     }
 
@@ -252,21 +252,21 @@ export abstract class MusicSheetDrawer {
     }
 
     protected drawMusicSystem(system: MusicSystem): void {
-        let absBoundingRectWithMargin: RectangleF2D = this.getSystemAbsBoundingRect(system);
-        let systemBoundingBoxInPixels: RectangleF2D = this.getSytemBoundingBoxInPixels(absBoundingRectWithMargin);
+        const absBoundingRectWithMargin: RectangleF2D = this.getSystemAbsBoundingRect(system);
+        const systemBoundingBoxInPixels: RectangleF2D = this.getSytemBoundingBoxInPixels(absBoundingRectWithMargin);
         this.drawMusicSystemComponents(system, systemBoundingBoxInPixels, absBoundingRectWithMargin);
     }
 
     protected getSytemBoundingBoxInPixels(absBoundingRectWithMargin: RectangleF2D): RectangleF2D {
-        let systemBoundingBoxInPixels: RectangleF2D = this.applyScreenTransformationForRect(absBoundingRectWithMargin);
+        const systemBoundingBoxInPixels: RectangleF2D = this.applyScreenTransformationForRect(absBoundingRectWithMargin);
         systemBoundingBoxInPixels.x = Math.round(systemBoundingBoxInPixels.x);
         systemBoundingBoxInPixels.y = Math.round(systemBoundingBoxInPixels.y);
         return systemBoundingBoxInPixels;
     }
 
     protected getSystemAbsBoundingRect(system: MusicSystem): RectangleF2D {
-        let relBoundingRect: RectangleF2D = system.PositionAndShape.BoundingRectangle;
-        let absBoundingRectWithMargin: RectangleF2D = new RectangleF2D(
+        const relBoundingRect: RectangleF2D = system.PositionAndShape.BoundingRectangle;
+        const absBoundingRectWithMargin: RectangleF2D = new RectangleF2D(
             system.PositionAndShape.AbsolutePosition.x + system.PositionAndShape.BorderLeft - 1,
             system.PositionAndShape.AbsolutePosition.y + system.PositionAndShape.BorderTop - 1,
             (relBoundingRect.width + 6), (relBoundingRect.height + 2)
@@ -276,8 +276,8 @@ export abstract class MusicSheetDrawer {
 
     protected drawMusicSystemComponents(musicSystem: MusicSystem, systemBoundingBoxInPixels: RectangleF2D,
                                         absBoundingRectWithMargin: RectangleF2D): void {
-        let selectStartSymb: SelectionStartSymbol = this.graphicalMusicSheet.SelectionStartSymbol;
-        let selectEndSymb: SelectionEndSymbol = this.graphicalMusicSheet.SelectionEndSymbol;
+        const selectStartSymb: SelectionStartSymbol = this.graphicalMusicSheet.SelectionStartSymbol;
+        const selectEndSymb: SelectionEndSymbol = this.graphicalMusicSheet.SelectionEndSymbol;
         if (this.drawingParameters.drawSelectionStartSymbol) {
             if (selectStartSymb !== undefined && this.isVisible(selectStartSymb.PositionAndShape)) {
                 this.drawSelectionStartSymbol(selectStartSymb);
@@ -288,29 +288,29 @@ export abstract class MusicSheetDrawer {
                 this.drawSelectionEndSymbol(selectEndSymb);
             }
         }
-        for (let staffLine of musicSystem.StaffLines) {
+        for (const staffLine of musicSystem.StaffLines) {
             this.drawStaffLine(staffLine);
         }
-        for (let systemLine of musicSystem.SystemLines) {
+        for (const systemLine of musicSystem.SystemLines) {
             this.drawSystemLineObject(systemLine);
         }
         if (musicSystem === musicSystem.Parent.MusicSystems[0] && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
-            for (let label of musicSystem.Labels) {
+            for (const label of musicSystem.Labels) {
                 this.drawLabel(label, <number>GraphicalLayers.Notes);
             }
         }
-        for (let bracket of musicSystem.InstrumentBrackets) {
-            this.drawInstrumentBracket(bracket, musicSystem);
+        for (const bracket of musicSystem.InstrumentBrackets) {
+            this.drawInstrumentBrace(bracket, musicSystem);
         }
-        for (let bracket of musicSystem.GroupBrackets) {
+        for (const bracket of musicSystem.GroupBrackets) {
             this.drawGroupBracket(bracket, musicSystem);
         }
         if (!this.leadSheet) {
-            for (let measureNumberLabel of musicSystem.MeasureNumberLabels) {
+            for (const measureNumberLabel of musicSystem.MeasureNumberLabels) {
                 this.drawLabel(measureNumberLabel, <number>GraphicalLayers.Notes);
             }
         }
-        for (let staffLine of musicSystem.StaffLines) {
+        for (const staffLine of musicSystem.StaffLines) {
             this.drawStaffLineSymbols(staffLine);
         }
         if (this.drawingParameters.drawMarkedAreas) {
@@ -331,9 +331,22 @@ export abstract class MusicSheetDrawer {
     }
 
     protected drawStaffLine(staffLine: StaffLine): void {
-        for (let measure of staffLine.Measures) {
+        for (const measure of staffLine.Measures) {
             this.drawMeasure(measure);
         }
+
+        if (staffLine.LyricsDashes.length > 0) {
+            this.drawDashes(staffLine.LyricsDashes);
+        }
+    }
+
+    /**
+     * Draw all dashes to the canvas
+     * @param lyricsDashes Array of lyric dashes to be drawn
+     * @param layer Number of the layer that the lyrics should be drawn in
+     */
+    protected drawDashes(lyricsDashes: GraphicalLabel[]): void {
+        lyricsDashes.forEach(dash => this.drawLabel(dash, <number>GraphicalLayers.Notes));
     }
 
     // protected drawSlur(slur: GraphicalSlur, abs: PointF2D): void {
@@ -342,17 +355,17 @@ export abstract class MusicSheetDrawer {
 
     protected drawOctaveShift(staffLine: StaffLine, graphicalOctaveShift: GraphicalOctaveShift): void {
         this.drawSymbol(graphicalOctaveShift.octaveSymbol, MusicSymbolDrawingStyle.Normal, graphicalOctaveShift.PositionAndShape.AbsolutePosition);
-        let absolutePos: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
+        const absolutePos: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
         if (graphicalOctaveShift.dashesStart.x < graphicalOctaveShift.dashesEnd.x) {
-            let horizontalLine: GraphicalLine = new GraphicalLine(graphicalOctaveShift.dashesStart, graphicalOctaveShift.dashesEnd,
-                                                                  this.rules.OctaveShiftLineWidth);
+            const horizontalLine: GraphicalLine = new GraphicalLine(graphicalOctaveShift.dashesStart, graphicalOctaveShift.dashesEnd,
+                                                                    this.rules.OctaveShiftLineWidth);
             this.drawLineAsHorizontalRectangleWithOffset(horizontalLine, absolutePos, <number>GraphicalLayers.Notes);
         }
         if (!graphicalOctaveShift.endsOnDifferentStaffLine || graphicalOctaveShift.isSecondPart) {
             let verticalLine: GraphicalLine;
-            let dashEnd: PointF2D = graphicalOctaveShift.dashesEnd;
-            let octShiftVertLineLength: number = this.rules.OctaveShiftVerticalLineLength;
-            let octShiftLineWidth: number = this.rules.OctaveShiftLineWidth;
+            const dashEnd: PointF2D = graphicalOctaveShift.dashesEnd;
+            const octShiftVertLineLength: number = this.rules.OctaveShiftVerticalLineLength;
+            const octShiftLineWidth: number = this.rules.OctaveShiftLineWidth;
             if (graphicalOctaveShift.octaveSymbol === MusicSymbol.VA8 || graphicalOctaveShift.octaveSymbol === MusicSymbol.MA15) {
                 verticalLine = new GraphicalLine(dashEnd, new PointF2D(dashEnd.x, dashEnd.y + octShiftVertLineLength), octShiftLineWidth);
             } else {
@@ -364,7 +377,7 @@ export abstract class MusicSheetDrawer {
 
     protected drawStaffLines(staffLine: StaffLine): void {
         if (staffLine.StaffLines !== undefined) {
-            let position: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
+            const position: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
             for (let i: number = 0; i < 5; i++) {
                 this.drawLineAsHorizontalRectangleWithOffset(staffLine.StaffLines[i], position, <number>GraphicalLayers.Notes);
             }
@@ -407,20 +420,49 @@ export abstract class MusicSheetDrawer {
         if (!this.isVisible(page.PositionAndShape)) {
             return;
         }
-        for (let system of page.MusicSystems) {
+
+        for (const system of page.MusicSystems) {
             if (this.isVisible(system.PositionAndShape)) {
                 this.drawMusicSystem(system);
             }
         }
         if (page === page.Parent.MusicPages[0]) {
-            for (let label of page.Labels) {
+            for (const label of page.Labels) {
                 this.drawLabel(label, <number>GraphicalLayers.Notes);
             }
         }
+        // Draw bounding boxes for debug purposes. This has to be at the end because only
+        // then all the calculations and recalculations are done
+        if (process.env.DRAW_BOUNDING_BOX_ELEMENT) {
+            this.drawBoundingBoxes(page.PositionAndShape, 0, process.env.DRAW_BOUNDING_BOX_ELEMENT);
+        }
+
+    }
+
+    /**
+     * Draw bounding boxes aroung GraphicalObjects
+     * @param startBox Bounding Box that is used as a staring point to recursively go through all child elements
+     * @param layer Layer to draw to
+     * @param type Type of element to show bounding boxes for as string.
+     */
+    private drawBoundingBoxes(startBox: BoundingBox, layer: number = 0, type: string = "all"): void {
+        const dataObjectString: string = (startBox.DataObject.constructor as any).name;
+        if (startBox.BoundingRectangle !== undefined && (dataObjectString === type || type === "all")) {
+            const relBoundingRect: RectangleF2D = startBox.BoundingRectangle;
+            let tmpRect: RectangleF2D = new RectangleF2D(startBox.AbsolutePosition.x + startBox.BorderLeft,
+                                                         startBox.AbsolutePosition.y + startBox.BorderTop ,
+                                                         (relBoundingRect.width + 0), (relBoundingRect.height + 0));
+            tmpRect = this.applyScreenTransformationForRect(tmpRect);
+            this.renderRectangle(tmpRect, <number>GraphicalLayers.Background, layer, 0.5);
+            this.renderLabel(new GraphicalLabel(new Label(dataObjectString), 1.2, TextAlignment.CenterCenter),
+                             layer, tmpRect.width, tmpRect.height, tmpRect.height, new PointF2D(tmpRect.x, tmpRect.y + 12));
+        }
+        layer++;
+        startBox.ChildElements.forEach(bb => this.drawBoundingBoxes(bb, layer, type));
     }
 
     private drawMarkedAreas(system: MusicSystem): void {
-        for (let markedArea of system.GraphicalMarkedAreas) {
+        for (const markedArea of system.GraphicalMarkedAreas) {
             if (markedArea !== undefined) {
                 if (markedArea.systemRectangle !== undefined) {
                     this.drawRectangle(markedArea.systemRectangle, <number>GraphicalLayers.Background);
@@ -439,7 +481,7 @@ export abstract class MusicSheetDrawer {
     }
 
     private drawComment(system: MusicSystem): void {
-        for (let comment of system.GraphicalComments) {
+        for (const comment of system.GraphicalComments) {
             if (comment !== undefined) {
                 if (comment.settings !== undefined) {
                     this.drawLabel(comment.settings, <number>GraphicalLayers.Comment);
@@ -452,10 +494,10 @@ export abstract class MusicSheetDrawer {
     }
 
     private drawStaffLineSymbols(staffLine: StaffLine): void {
-        let parentInst: Instrument = staffLine.ParentStaff.ParentInstrument;
-        let absX: number = staffLine.PositionAndShape.AbsolutePosition.x;
-        let absY: number = staffLine.PositionAndShape.AbsolutePosition.y + 2;
-        let borderRight: number = staffLine.PositionAndShape.BorderRight;
+        const parentInst: Instrument = staffLine.ParentStaff.ParentInstrument;
+        const absX: number = staffLine.PositionAndShape.AbsolutePosition.x;
+        const absY: number = staffLine.PositionAndShape.AbsolutePosition.y + 2;
+        const borderRight: number = staffLine.PositionAndShape.BorderRight;
         if (parentInst.highlight && this.drawingParameters.drawHighlights) {
             this.drawLineAsHorizontalRectangle(
                 new GraphicalLine(
@@ -488,14 +530,14 @@ export abstract class MusicSheetDrawer {
                 break;
         }
         if (drawSymbols) {
-            let p: PointF2D = new PointF2D(absX + borderRight + 2, absY);
+            const p: PointF2D = new PointF2D(absX + borderRight + 2, absY);
             this.drawSymbol(symbol, style, p);
         }
         if (this.drawingParameters.drawErrors) {
-            for (let measure of staffLine.Measures) {
-                let measurePSI: BoundingBox = measure.PositionAndShape;
-                let absXPSI: number = measurePSI.AbsolutePosition.x;
-                let absYPSI: number = measurePSI.AbsolutePosition.y + 2;
+            for (const measure of staffLine.Measures) {
+                const measurePSI: BoundingBox = measure.PositionAndShape;
+                const absXPSI: number = measurePSI.AbsolutePosition.x;
+                const absYPSI: number = measurePSI.AbsolutePosition.y + 2;
                 if (measure.hasError && this.graphicalMusicSheet.ParentMusicSheet.DrawErroneousMeasures) {
                     this.drawLineAsHorizontalRectangle(
                         new GraphicalLine(

+ 48 - 46
src/MusicalScore/Graphical/MusicSystem.ts

@@ -14,7 +14,6 @@ import {PointF2D} from "../../Common/DataObjects/PointF2D";
 import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
 import {SystemLinesEnum} from "./SystemLinesEnum";
 import Dictionary from "typescript-collections/dist/lib/Dictionary";
-import {CollectionUtil} from "../../Util/CollectionUtil";
 import {GraphicalComment} from "./GraphicalComment";
 import {GraphicalMarkedArea} from "./GraphicalMarkedArea";
 import {SystemLine} from "./SystemLine";
@@ -58,6 +57,11 @@ export abstract class MusicSystem extends GraphicalObject {
         this.parent = value;
     }
 
+    public get NextSystem(): MusicSystem {
+        const idxInParent: number = this.Parent.MusicSystems.indexOf(this);
+        return idxInParent !== this.Parent.MusicSystems.length ? this.Parent.MusicSystems[idxInParent + 1] : undefined;
+    }
+
     public get StaffLines(): StaffLine[] {
         return this.staffLines;
     }
@@ -112,15 +116,14 @@ export abstract class MusicSystem extends GraphicalObject {
         if (this === this.parent.MusicSystems[0] && this.parent === this.parent.Parent.MusicPages[0]) {
             xPosition = this.maxLabelLength + systemLabelsRightMargin - lineWidth / 2;
         }
-        let top: StaffMeasure = this.staffLines[0].Measures[0];
+        const top: StaffMeasure = this.staffLines[0].Measures[0];
         let bottom: StaffMeasure = undefined;
         if (this.staffLines.length > 1) {
             bottom = this.staffLines[this.staffLines.length - 1].Measures[0];
         }
-        let leftSystemLine: SystemLine = this.createSystemLine(xPosition, lineWidth, SystemLinesEnum.SingleThin,
-                                                               SystemLinePosition.MeasureBegin, this, top, bottom);
+        const leftSystemLine: SystemLine = this.createSystemLine(xPosition, lineWidth, SystemLinesEnum.SingleThin,
+                                                                 SystemLinePosition.MeasureBegin, this, top, bottom);
         this.SystemLines.push(leftSystemLine);
-        this.boundingBox.ChildElements.push(leftSystemLine.PositionAndShape);
         leftSystemLine.PositionAndShape.RelativePosition = new PointF2D(xPosition, 0);
         leftSystemLine.PositionAndShape.BorderLeft = 0;
         leftSystemLine.PositionAndShape.BorderRight = lineWidth;
@@ -140,22 +143,22 @@ export abstract class MusicSystem extends GraphicalObject {
      */
     public createVerticalLineForMeasure(xPosition: number, lineWidth: number, lineType: SystemLinesEnum, linePosition: SystemLinePosition,
                                         measureIndex: number, measure: StaffMeasure): void {
-        let staffLine: StaffLine = measure.ParentStaffLine;
-        let staffLineRelative: PointF2D = new PointF2D(staffLine.PositionAndShape.RelativePosition.x,
-                                                       staffLine.PositionAndShape.RelativePosition.y);
-        let staves: Staff[] = staffLine.ParentStaff.ParentInstrument.Staves;
+        const staffLine: StaffLine = measure.ParentStaffLine;
+        const staffLineRelative: PointF2D = new PointF2D(staffLine.PositionAndShape.RelativePosition.x,
+                                                         staffLine.PositionAndShape.RelativePosition.y);
+        const staves: Staff[] = staffLine.ParentStaff.ParentInstrument.Staves;
         if (staffLine.ParentStaff === staves[0]) {
             let bottomMeasure: StaffMeasure = undefined;
             if (staves.length > 1) {
                 bottomMeasure = this.getBottomStaffLine(staffLine).Measures[measureIndex];
             }
-            let singleVerticalLineAfterMeasure: SystemLine = this.createSystemLine(xPosition, lineWidth, lineType, linePosition, this, measure, bottomMeasure);
-            let systemXPosition: number = staffLineRelative.x + xPosition;
+            const singleVerticalLineAfterMeasure: SystemLine = this.createSystemLine(xPosition, lineWidth, lineType,
+                                                                                     linePosition, this, measure, bottomMeasure);
+            const systemXPosition: number = staffLineRelative.x + xPosition;
             singleVerticalLineAfterMeasure.PositionAndShape.RelativePosition = new PointF2D(systemXPosition, 0);
             singleVerticalLineAfterMeasure.PositionAndShape.BorderLeft = 0;
             singleVerticalLineAfterMeasure.PositionAndShape.BorderRight = lineWidth;
             this.SystemLines.push(singleVerticalLineAfterMeasure);
-            this.boundingBox.ChildElements.push(singleVerticalLineAfterMeasure.PositionAndShape);
         }
     }
 
@@ -185,7 +188,7 @@ export abstract class MusicSystem extends GraphicalObject {
 
     public AddStaffMeasures(graphicalMeasures: StaffMeasure[]): void {
         for (let idx: number = 0, len: number = graphicalMeasures.length; idx < len; ++idx) {
-            let graphicalMeasure: StaffMeasure = graphicalMeasures[idx];
+            const graphicalMeasure: StaffMeasure = graphicalMeasures[idx];
             graphicalMeasure.parentMusicSystem = this;
         }
         this.graphicalMeasures.push(graphicalMeasures);
@@ -196,7 +199,7 @@ export abstract class MusicSystem extends GraphicalObject {
     }
 
     public GetSystemsLastTimeStamp(): Fraction {
-        let m: SourceMeasure = this.graphicalMeasures[this.graphicalMeasures.length - 1][0].parentSourceMeasure;
+        const m: SourceMeasure = this.graphicalMeasures[this.graphicalMeasures.length - 1][0].parentSourceMeasure;
         return Fraction.plus(m.AbsoluteTimestamp, m.Duration);
     }
 
@@ -207,11 +210,11 @@ export abstract class MusicSystem extends GraphicalObject {
      */
     public createInstrumentBrackets(instruments: Instrument[], staffHeight: number): void {
         for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
-            let instrument: Instrument = instruments[idx];
+            const instrument: Instrument = instruments[idx];
             if (instrument.Staves.length > 1) {
                 let firstStaffLine: StaffLine = undefined, lastStaffLine: StaffLine = undefined;
                 for (let idx2: number = 0, len2: number = this.staffLines.length; idx2 < len2; ++idx2) {
-                    let staffLine: StaffLine = this.staffLines[idx2];
+                    const staffLine: StaffLine = this.staffLines[idx2];
                     if (staffLine.ParentStaff === instrument.Staves[0]) {
                         firstStaffLine = staffLine;
                     }
@@ -234,27 +237,28 @@ export abstract class MusicSystem extends GraphicalObject {
      */
     public createGroupBrackets(instrumentGroups: InstrumentalGroup[], staffHeight: number, recursionDepth: number): void {
         for (let idx: number = 0, len: number = instrumentGroups.length; idx < len; ++idx) {
-            let instrumentGroup: InstrumentalGroup = instrumentGroups[idx];
+            const instrumentGroup: InstrumentalGroup = instrumentGroups[idx];
             if (instrumentGroup.InstrumentalGroups.length < 1) {
                 continue;
             }
-            let instrument1: Instrument = this.findFirstVisibleInstrumentInInstrumentalGroup(instrumentGroup);
-            let instrument2: Instrument = this.findLastVisibleInstrumentInInstrumentalGroup(instrumentGroup);
+            const instrument1: Instrument = this.findFirstVisibleInstrumentInInstrumentalGroup(instrumentGroup);
+            const instrument2: Instrument = this.findLastVisibleInstrumentInInstrumentalGroup(instrumentGroup);
             if (instrument1 === undefined || instrument2 === undefined) {
                 continue;
             }
-            let firstStaffLine: StaffLine = undefined, lastStaffLine: StaffLine = undefined;
+            let firstStaffLine: StaffLine = undefined;
+            let lastStaffLine: StaffLine = undefined;
             for (let idx2: number = 0, len2: number = this.staffLines.length; idx2 < len2; ++idx2) {
-                let staffLine: StaffLine = this.staffLines[idx2];
+                const staffLine: StaffLine = this.staffLines[idx2];
                 if (staffLine.ParentStaff === instrument1.Staves[0]) {
                     firstStaffLine = staffLine;
                 }
-                if (staffLine.ParentStaff === CollectionUtil.last(instrument2.Staves)) {
+                if (staffLine.ParentStaff === instrument2.Staves[0]) {
                     lastStaffLine = staffLine;
                 }
             }
-            if (firstStaffLine !== undefined && firstStaffLine !== undefined) {
-                this.createGroupBracket(firstStaffLine, firstStaffLine, recursionDepth);
+            if (firstStaffLine !== undefined && lastStaffLine !== undefined) {
+                this.createGroupBracket(firstStaffLine, lastStaffLine, recursionDepth);
             }
             if (instrumentGroup.InstrumentalGroups.length < 1) {
                 continue;
@@ -271,26 +275,24 @@ export abstract class MusicSystem extends GraphicalObject {
      */
     public createMusicSystemLabel(instrumentLabelTextHeight: number, systemLabelsRightMargin: number, labelMarginBorderFactor: number): void {
         if (this.parent === this.parent.Parent.MusicPages[0] && this === this.parent.MusicSystems[0]) {
-            let instruments: Instrument[] = this.parent.Parent.ParentMusicSheet.getVisibleInstruments();
+            const instruments: Instrument[] = this.parent.Parent.ParentMusicSheet.getVisibleInstruments();
             for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
-                let instrument: Instrument = instruments[idx];
-                let graphicalLabel: GraphicalLabel = new GraphicalLabel(
+                const instrument: Instrument = instruments[idx];
+                const graphicalLabel: GraphicalLabel = new GraphicalLabel(
                     instrument.NameLabel, instrumentLabelTextHeight, TextAlignment.LeftCenter, this.boundingBox
                 );
                 graphicalLabel.setLabelPositionAndShapeBorders();
                 this.labels.setValue(graphicalLabel, instrument);
-                this.boundingBox.ChildElements.push(graphicalLabel.PositionAndShape);
-
                 // X-Position will be 0 (Label starts at the same PointF_2D with MusicSystem)
                 // Y-Position will be calculated after the y-Spacing
-                graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+                // graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
             }
 
             // calculate maxLabelLength (needed for X-Spacing)
             this.maxLabelLength = 0.0;
-            let labels: GraphicalLabel[] = this.labels.keys();
+            const labels: GraphicalLabel[] = this.labels.keys();
             for (let idx: number = 0, len: number = labels.length; idx < len; ++idx) {
-                let label: GraphicalLabel = labels[idx];
+                const label: GraphicalLabel = labels[idx];
                 if (label.PositionAndShape.Size.width > this.maxLabelLength) {
                     this.maxLabelLength = label.PositionAndShape.Size.width;
                 }
@@ -310,7 +312,7 @@ export abstract class MusicSystem extends GraphicalObject {
                 for (let i: number = 0; i < this.staffLines.length; i++) {
                     if (this.staffLines[i].ParentStaff.ParentInstrument === value) {
                         for (let j: number = i; j < this.staffLines.length; j++) {
-                            let staffLine: StaffLine = this.staffLines[j];
+                            const staffLine: StaffLine = this.staffLines[j];
                             if (staffLine.ParentStaff.ParentInstrument !== value) {
                                 break;
                             }
@@ -337,18 +339,18 @@ export abstract class MusicSystem extends GraphicalObject {
         let second: boolean = false;
         for (let i: number = 0; i < this.staffLines.length - 1; i++) {
             for (let idx: number = 0, len: number = this.staffLines[i].Measures.length; idx < len; ++idx) {
-                let measure: StaffMeasure = this.staffLines[i].Measures[idx];
+                const measure: StaffMeasure = this.staffLines[i].Measures[idx];
                 for (let idx2: number = 0, len2: number = measure.staffEntries.length; idx2 < len2; ++idx2) {
-                    let staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
+                    const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
                     if (staffEntry.sourceStaffEntry.Link !== undefined) {
                         first = true;
                     }
                 }
             }
             for (let idx: number = 0, len: number = this.staffLines[i + 1].Measures.length; idx < len; ++idx) {
-                let measure: StaffMeasure = this.staffLines[i + 1].Measures[idx];
+                const measure: StaffMeasure = this.staffLines[i + 1].Measures[idx];
                 for (let idx2: number = 0, len2: number = measure.staffEntries.length; idx2 < len2; ++idx2) {
-                    let staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
+                    const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
                     if (staffEntry.sourceStaffEntry.Link !== undefined) {
                         second = true;
                     }
@@ -362,9 +364,9 @@ export abstract class MusicSystem extends GraphicalObject {
     }
 
     public getBottomStaffLine(topStaffLine: StaffLine): StaffLine {
-        let staves: Staff[] = topStaffLine.ParentStaff.ParentInstrument.Staves;
-        let last: Staff = staves[staves.length - 1];
-        for (let line of topStaffLine.ParentMusicSystem.staffLines) {
+        const staves: Staff[] = topStaffLine.ParentStaff.ParentInstrument.Staves;
+        const last: Staff = staves[staves.length - 1];
+        for (const line of topStaffLine.ParentMusicSystem.staffLines) {
             if (line.ParentStaff === last) {
                 return line;
             }
@@ -403,11 +405,11 @@ export abstract class MusicSystem extends GraphicalObject {
     protected calcBracketsWidth(): number {
         let width: number = 0;
         for (let idx: number = 0, len: number = this.GroupBrackets.length; idx < len; ++idx) {
-            let groupBracket: GraphicalObject = this.GroupBrackets[idx];
+            const groupBracket: GraphicalObject = this.GroupBrackets[idx];
             width = Math.max(width, groupBracket.PositionAndShape.Size.width);
         }
         for (let idx2: number = 0, len2: number = this.InstrumentBrackets.length; idx2 < len2; ++idx2) {
-            let instrumentBracket: GraphicalObject = this.InstrumentBrackets[idx2];
+            const instrumentBracket: GraphicalObject = this.InstrumentBrackets[idx2];
             width = Math.max(width, instrumentBracket.PositionAndShape.Size.width);
         }
         return width;
@@ -423,7 +425,7 @@ export abstract class MusicSystem extends GraphicalObject {
 
     private findFirstVisibleInstrumentInInstrumentalGroup(instrumentalGroup: InstrumentalGroup): Instrument {
         for (let idx: number = 0, len: number = instrumentalGroup.InstrumentalGroups.length; idx < len; ++idx) {
-            let groupOrInstrument: InstrumentalGroup = instrumentalGroup.InstrumentalGroups[idx];
+            const groupOrInstrument: InstrumentalGroup = instrumentalGroup.InstrumentalGroups[idx];
             if (groupOrInstrument instanceof Instrument) {
                 if ((<Instrument>groupOrInstrument).Visible === true) {
                     return <Instrument>groupOrInstrument;
@@ -456,13 +458,13 @@ export abstract class MusicSystem extends GraphicalObject {
      */
     private updateMusicSystemStaffLineXPosition(systemLabelsRightMargin: number): void {
         for (let idx: number = 0, len: number = this.StaffLines.length; idx < len; ++idx) {
-            let staffLine: StaffLine = this.StaffLines[idx];
-            let relative: PointF2D = staffLine.PositionAndShape.RelativePosition;
+            const staffLine: StaffLine = this.StaffLines[idx];
+            const relative: PointF2D = staffLine.PositionAndShape.RelativePosition;
             relative.x = this.maxLabelLength + systemLabelsRightMargin;
             staffLine.PositionAndShape.RelativePosition = relative;
             staffLine.PositionAndShape.BorderRight = this.boundingBox.Size.width - this.maxLabelLength - systemLabelsRightMargin;
             for (let i: number = 0; i < staffLine.StaffLines.length; i++) {
-                let lineEnd: PointF2D = new PointF2D(staffLine.PositionAndShape.Size.width, staffLine.StaffLines[i].End.y);
+                const lineEnd: PointF2D = new PointF2D(staffLine.PositionAndShape.Size.width, staffLine.StaffLines[i].End.y);
                 staffLine.StaffLines[i].End = lineEnd;
             }
         }

+ 100 - 105
src/MusicalScore/Graphical/MusicSystemBuilder.ts

@@ -62,7 +62,7 @@ export class MusicSystemBuilder {
 
     public buildMusicSystems(): void {
         let previousMeasureEndsSystem: boolean = false;
-        let systemMaxWidth: number = this.getFullPageSystemWidth();
+        const systemMaxWidth: number = this.getFullPageSystemWidth();
         this.measureListIndex = 0;
         this.currentSystemParams = new SystemBuildParameters();
 
@@ -85,14 +85,14 @@ export class MusicSystemBuilder {
 
         // go through measures and add to system until system gets too long -> finish system and start next system.
         while (this.measureListIndex < numberOfMeasures) {
-            let staffMeasures: StaffMeasure[] = this.measureList[this.measureListIndex];
+            const staffMeasures: StaffMeasure[] = this.measureList[this.measureListIndex];
             for (let idx: number = 0, len: number = staffMeasures.length; idx < len; ++idx) {
                 staffMeasures[idx].resetLayout();
             }
-            let sourceMeasure: SourceMeasure = staffMeasures[0].parentSourceMeasure;
-            let sourceMeasureEndsSystem: boolean = sourceMeasure.BreakSystemAfter;
-            let isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
-            let isFirstSourceMeasure: boolean = sourceMeasure === this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
+            const sourceMeasure: SourceMeasure = staffMeasures[0].parentSourceMeasure;
+            const sourceMeasureEndsSystem: boolean = sourceMeasure.BreakSystemAfter;
+            const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
+            const isFirstSourceMeasure: boolean = sourceMeasure === this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
             let currentMeasureBeginInstructionsWidth: number = this.rules.MeasureLeftMargin;
             let currentMeasureEndInstructionsWidth: number = 0;
 
@@ -101,7 +101,7 @@ export class MusicSystemBuilder {
             // 1. the begin instructions (clef, Key, Rhythm),
             // 2. the staff entries (= notes) and
             // 3. the end instructions (actually only clefs)
-            let measureStartLine: SystemLinesEnum = this.getMeasureStartLine();
+            const measureStartLine: SystemLinesEnum = this.getMeasureStartLine();
             currentMeasureBeginInstructionsWidth += this.getLineWidth(staffMeasures[0], measureStartLine, isSystemStartMeasure);
             if (!this.leadSheet) {
                 currentMeasureBeginInstructionsWidth += this.addBeginInstructions(staffMeasures, isSystemStartMeasure, isFirstSourceMeasure);
@@ -113,20 +113,20 @@ export class MusicSystemBuilder {
             }
 
             // take into account the LineWidth after each Measure
-            let measureEndLine: SystemLinesEnum = this.getMeasureEndLine();
+            const measureEndLine: SystemLinesEnum = this.getMeasureEndLine();
             currentMeasureEndInstructionsWidth += this.getLineWidth(staffMeasures[0], measureEndLine, isSystemStartMeasure);
             let nextMeasureBeginInstructionWidth: number = this.rules.MeasureLeftMargin;
 
             // Check if there are key or rhythm change instructions within the next measure:
             if (this.measureListIndex + 1 < this.measureList.length) {
-                let nextStaffMeasures: StaffMeasure[] = this.measureList[this.measureListIndex + 1];
-                let nextSourceMeasure: SourceMeasure = nextStaffMeasures[0].parentSourceMeasure;
+                const nextStaffMeasures: StaffMeasure[] = this.measureList[this.measureListIndex + 1];
+                const nextSourceMeasure: SourceMeasure = nextStaffMeasures[0].parentSourceMeasure;
                 if (nextSourceMeasure.hasBeginInstructions()) {
                     nextMeasureBeginInstructionWidth += this.addBeginInstructions(nextStaffMeasures, false, false);
                 }
             }
-            let totalMeasureWidth: number = currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth + currentMeasureVarWidth;
-            let measureFitsInSystem: boolean = this.currentSystemParams.currentWidth + totalMeasureWidth + nextMeasureBeginInstructionWidth < systemMaxWidth;
+            const totalMeasureWidth: number = currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth + currentMeasureVarWidth;
+            const measureFitsInSystem: boolean = this.currentSystemParams.currentWidth + totalMeasureWidth + nextMeasureBeginInstructionWidth < systemMaxWidth;
             if (isSystemStartMeasure || measureFitsInSystem) {
                 this.addMeasureToSystem(
                     staffMeasures, measureStartLine, measureEndLine, totalMeasureWidth,
@@ -153,7 +153,7 @@ export class MusicSystemBuilder {
      */
     private setMeasureWidth(staffMeasures: StaffMeasure[], width: number, beginInstrWidth: number, endInstrWidth: number): void {
         for (let idx: number = 0, len: number = staffMeasures.length; idx < len; ++idx) {
-            let measure: StaffMeasure = staffMeasures[idx];
+            const measure: StaffMeasure = staffMeasures[idx];
             measure.setWidth(width);
             if (beginInstrWidth > 0) {
                 measure.beginInstructionsWidth = beginInstrWidth;
@@ -203,11 +203,11 @@ export class MusicSystemBuilder {
      * (this should be refactored at some point to not use a combined end/start line but always separated lines)
      */
     private adaptRepetitionLineWithIfNeeded(): void {
-        let systemMeasures: MeasureBuildParameters[] = this.currentSystemParams.systemMeasures;
+        const systemMeasures: MeasureBuildParameters[] = this.currentSystemParams.systemMeasures;
         if (systemMeasures.length >= 1) {
-            let measures: StaffMeasure[] =
+            const measures: StaffMeasure[] =
                 this.currentSystemParams.currentSystem.GraphicalMeasures[this.currentSystemParams.currentSystem.GraphicalMeasures.length - 1];
-            let measureParams: MeasureBuildParameters = systemMeasures[systemMeasures.length - 1];
+            const measureParams: MeasureBuildParameters = systemMeasures[systemMeasures.length - 1];
             let diff: number = 0.0;
             if (measureParams.endLine === SystemLinesEnum.DotsBoldBoldDots) {
                 measureParams.endLine = SystemLinesEnum.DotsThinBold;
@@ -215,7 +215,7 @@ export class MusicSystemBuilder {
             }
             this.currentSystemParams.currentSystemFixWidth -= diff;
             for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
-                let measure: StaffMeasure = measures[idx];
+                const measure: StaffMeasure = measures[idx];
                 measure.endInstructionsWidth -= diff;
             }
         }
@@ -242,7 +242,7 @@ export class MusicSystemBuilder {
      * @returns {GraphicalMusicPage}
      */
     private createMusicPage(): GraphicalMusicPage {
-        let page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
+        const page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
         this.graphicalMusicSheet.MusicPages.push(page);
         page.PositionAndShape.BorderLeft = 0.0;
         page.PositionAndShape.BorderRight = this.graphicalMusicSheet.ParentMusicSheet.pageWidth;
@@ -257,10 +257,8 @@ export class MusicSystemBuilder {
      * @returns {MusicSystem}
      */
     private initMusicSystem(): MusicSystem {
-        let musicSystem: MusicSystem = this.symbolFactory.createMusicSystem(this.currentMusicPage, this.globalSystemIndex++);
+        const musicSystem: MusicSystem = this.symbolFactory.createMusicSystem(this.currentMusicPage, this.globalSystemIndex++);
         this.currentMusicPage.MusicSystems.push(musicSystem);
-        let boundingBox: BoundingBox = musicSystem.PositionAndShape;
-        this.currentMusicPage.PositionAndShape.ChildElements.push(boundingBox);
         return musicSystem;
     }
 
@@ -274,28 +272,28 @@ export class MusicSystemBuilder {
     }
 
     private layoutSystemStaves(): void {
-        let systemWidth: number = this.getFullPageSystemWidth();
-        let musicSystem: MusicSystem = this.currentSystemParams.currentSystem;
-        let boundingBox: BoundingBox = musicSystem.PositionAndShape;
+        const systemWidth: number = this.getFullPageSystemWidth();
+        const musicSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        const boundingBox: BoundingBox = musicSystem.PositionAndShape;
         boundingBox.BorderLeft = 0.0;
         boundingBox.BorderRight = systemWidth;
         boundingBox.BorderTop = 0.0;
-        let staffList: Staff[] = [];
-        let instruments: Instrument[] = this.graphicalMusicSheet.ParentMusicSheet.Instruments;
+        const staffList: Staff[] = [];
+        const instruments: Instrument[] = this.graphicalMusicSheet.ParentMusicSheet.Instruments;
         for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
-            let instrument: Instrument = instruments[idx];
+            const instrument: Instrument = instruments[idx];
             if (instrument.Voices.length === 0 || !instrument.Visible) {
                 continue;
             }
             for (let idx2: number = 0, len2: number = instrument.Staves.length; idx2 < len2; ++idx2) {
-                let staff: Staff = instrument.Staves[idx2];
+                const staff: Staff = instrument.Staves[idx2];
                 staffList.push(staff);
             }
         }
         let multiLyrics: boolean = false;
         if (this.leadSheet) {
             for (let idx: number = 0, len: number = staffList.length; idx < len; ++idx) {
-                let staff: Staff = staffList[idx];
+                const staff: Staff = staffList[idx];
                 if (staff.ParentInstrument.LyricVersesNumbers.length > 1) {
                     multiLyrics = true;
                     break;
@@ -331,11 +329,10 @@ export class MusicSystemBuilder {
      */
     private addStaffLineToMusicSystem(musicSystem: MusicSystem, relativeYPosition: number, staff: Staff): void {
         if (musicSystem !== undefined) {
-            let staffLine: StaffLine = this.symbolFactory.createStaffLine(musicSystem, staff);
+            const staffLine: StaffLine = this.symbolFactory.createStaffLine(musicSystem, staff);
             musicSystem.StaffLines.push(staffLine);
-            let boundingBox: BoundingBox = staffLine.PositionAndShape;
-            musicSystem.PositionAndShape.ChildElements.push(boundingBox);
-            let relativePosition: PointF2D = new PointF2D();
+            const boundingBox: BoundingBox = staffLine.PositionAndShape;
+            const relativePosition: PointF2D = new PointF2D();
             if (musicSystem.Parent.MusicSystems[0] === musicSystem && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
                 relativePosition.x = this.rules.FirstSystemMargin;
             } else {
@@ -352,10 +349,10 @@ export class MusicSystemBuilder {
             boundingBox.BorderTop = 0.0;
             boundingBox.BorderBottom = this.rules.StaffHeight;
             for (let i: number = 0; i < 5; i++) {
-                let start: PointF2D = new PointF2D();
+                const start: PointF2D = new PointF2D();
                 start.x = 0.0;
                 start.y = i * this.rules.StaffHeight / 4;
-                let end: PointF2D = new PointF2D();
+                const end: PointF2D = new PointF2D();
                 end.x = staffLine.PositionAndShape.Size.width;
                 end.y = i * this.rules.StaffHeight / 4;
                 if (this.leadSheet) {
@@ -371,12 +368,12 @@ export class MusicSystemBuilder {
      * @param measureList
      */
     private initializeActiveInstructions(measureList: StaffMeasure[]): void {
-        let firstSourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
+        const firstSourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
         if (firstSourceMeasure !== undefined) {
             this.visibleStaffIndices = this.graphicalMusicSheet.getVisibleStavesIndecesFromSourceMeasure(measureList);
             for (let i: number = 0, len: number = this.visibleStaffIndices.length; i < len; i++) {
-                let staffIndex: number = this.visibleStaffIndices[i];
-                let graphicalMeasure: StaffMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(firstSourceMeasure, staffIndex);
+                const staffIndex: number = this.visibleStaffIndices[i];
+                const graphicalMeasure: StaffMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(firstSourceMeasure, staffIndex);
                 this.activeClefs[i] = <ClefInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[0];
                 let keyInstruction: KeyInstruction = KeyInstruction.copy(
                     <KeyInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[1]);
@@ -408,17 +405,17 @@ export class MusicSystemBuilder {
      * @returns {number}
      */
     private addBeginInstructions(measures: StaffMeasure[], isSystemFirstMeasure: boolean, isFirstSourceMeasure: boolean): number {
-        let measureCount: number = measures.length;
+        const measureCount: number = measures.length;
         if (measureCount === 0) {
             return 0;
         }
         let totalBeginInstructionLengthX: number = 0.0;
-        let sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
+        const sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
         for (let idx: number = 0; idx < measureCount; ++idx) {
-            let measure: StaffMeasure = measures[idx];
-            let staffIndex: number = this.visibleStaffIndices[idx];
-            let beginInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.FirstInstructionsStaffEntries[staffIndex];
-            let beginInstructionLengthX: number = this.AddInstructionsAtMeasureBegin(
+            const measure: StaffMeasure = measures[idx];
+            const staffIndex: number = this.visibleStaffIndices[idx];
+            const beginInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.FirstInstructionsStaffEntries[staffIndex];
+            const beginInstructionLengthX: number = this.AddInstructionsAtMeasureBegin(
                 beginInstructionsStaffEntry, measure,
                 idx, isFirstSourceMeasure,
                 isSystemFirstMeasure
@@ -434,17 +431,17 @@ export class MusicSystemBuilder {
      * @returns {number}
      */
     private addEndInstructions(measures: StaffMeasure[]): number {
-        let measureCount: number = measures.length;
+        const measureCount: number = measures.length;
         if (measureCount === 0) {
             return 0;
         }
         let totalEndInstructionLengthX: number = 0.5;
-        let sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
+        const sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
         for (let idx: number = 0; idx < measureCount; idx++) {
-            let measure: StaffMeasure = measures[idx];
-            let staffIndex: number = this.visibleStaffIndices[idx];
-            let endInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.LastInstructionsStaffEntries[staffIndex];
-            let endInstructionLengthX: number = this.addInstructionsAtMeasureEnd(endInstructionsStaffEntry, measure);
+            const measure: StaffMeasure = measures[idx];
+            const staffIndex: number = this.visibleStaffIndices[idx];
+            const endInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.LastInstructionsStaffEntries[staffIndex];
+            const endInstructionLengthX: number = this.addInstructionsAtMeasureEnd(endInstructionsStaffEntry, measure);
             totalEndInstructionLengthX = Math.max(totalEndInstructionLengthX, endInstructionLengthX);
         }
         return totalEndInstructionLengthX;
@@ -458,7 +455,7 @@ export class MusicSystemBuilder {
         let currentRhythm: RhythmInstruction = undefined;
         if (firstEntry !== undefined) {
             for (let idx: number = 0, len: number = firstEntry.Instructions.length; idx < len; ++idx) {
-                let abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
+                const abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
                 if (abstractNotationInstruction instanceof ClefInstruction) {
                     currentClef = <ClefInstruction>abstractNotationInstruction;
                 } else if (abstractNotationInstruction instanceof KeyInstruction) {
@@ -490,7 +487,7 @@ export class MusicSystemBuilder {
         }
         if (currentKey !== undefined) {
             currentKey = this.transposeKeyInstruction(currentKey, measure);
-            let previousKey: KeyInstruction = isSystemStartMeasure ? undefined : this.activeKeys[visibleStaffIdx];
+            const previousKey: KeyInstruction = isSystemStartMeasure ? undefined : this.activeKeys[visibleStaffIdx];
             measure.addKeyAtBegin(currentKey, previousKey, currentClef);
             keyAdded = true;
         }
@@ -512,9 +509,9 @@ export class MusicSystemBuilder {
             return 0;
         }
         for (let idx: number = 0, len: number = lastEntry.Instructions.length; idx < len; ++idx) {
-            let abstractNotationInstruction: AbstractNotationInstruction = lastEntry.Instructions[idx];
+            const abstractNotationInstruction: AbstractNotationInstruction = lastEntry.Instructions[idx];
             if (abstractNotationInstruction instanceof ClefInstruction) {
-                let activeClef: ClefInstruction = <ClefInstruction>abstractNotationInstruction;
+                const activeClef: ClefInstruction = <ClefInstruction>abstractNotationInstruction;
                 measure.addClefAtEnd(activeClef);
             }
         }
@@ -530,11 +527,11 @@ export class MusicSystemBuilder {
      */
     private updateActiveClefs(measure: SourceMeasure, staffMeasures: StaffMeasure[]): void {
         for (let visStaffIdx: number = 0, len: number = staffMeasures.length; visStaffIdx < len; visStaffIdx++) {
-            let staffIndex: number = this.visibleStaffIndices[visStaffIdx];
-            let firstEntry: SourceStaffEntry = measure.FirstInstructionsStaffEntries[staffIndex];
+            const staffIndex: number = this.visibleStaffIndices[visStaffIdx];
+            const firstEntry: SourceStaffEntry = measure.FirstInstructionsStaffEntries[staffIndex];
             if (firstEntry !== undefined) {
                 for (let idx: number = 0, len2: number = firstEntry.Instructions.length; idx < len2; ++idx) {
-                    let abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
+                    const abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
                     if (abstractNotationInstruction instanceof ClefInstruction) {
                         this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
                     } else if (abstractNotationInstruction instanceof KeyInstruction) {
@@ -544,23 +541,23 @@ export class MusicSystemBuilder {
                     }
                 }
             }
-            let entries: SourceStaffEntry[] = measure.getEntriesPerStaff(staffIndex);
+            const entries: SourceStaffEntry[] = measure.getEntriesPerStaff(staffIndex);
             for (let idx: number = 0, len2: number = entries.length; idx < len2; ++idx) {
-                let staffEntry: SourceStaffEntry = entries[idx];
+                const staffEntry: SourceStaffEntry = entries[idx];
                 if (staffEntry.Instructions !== undefined) {
                     for (let idx2: number = 0, len3: number = staffEntry.Instructions.length; idx2 < len3; ++idx2) {
-                        let abstractNotationInstruction: AbstractNotationInstruction = staffEntry.Instructions[idx2];
+                        const abstractNotationInstruction: AbstractNotationInstruction = staffEntry.Instructions[idx2];
                         if (abstractNotationInstruction instanceof ClefInstruction) {
                             this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
                         }
                     }
                 }
             }
-            let lastEntry: SourceStaffEntry = measure.LastInstructionsStaffEntries[staffIndex];
+            const lastEntry: SourceStaffEntry = measure.LastInstructionsStaffEntries[staffIndex];
             if (lastEntry !== undefined) {
-                let instructions: AbstractNotationInstruction[] = lastEntry.Instructions;
+                const instructions: AbstractNotationInstruction[] = lastEntry.Instructions;
                 for (let idx: number = 0, len3: number = instructions.length; idx < len3; ++idx) {
-                    let abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
+                    const abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
                     if (abstractNotationInstruction instanceof ClefInstruction) {
                         this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
                     }
@@ -574,23 +571,23 @@ export class MusicSystemBuilder {
      * @param measures
      */
     private checkAndCreateExtraInstructionMeasure(measures: StaffMeasure[]): void {
-        let firstStaffEntries: SourceStaffEntry[] = measures[0].parentSourceMeasure.FirstInstructionsStaffEntries;
-        let visibleInstructionEntries: SourceStaffEntry[] = [];
+        const firstStaffEntries: SourceStaffEntry[] = measures[0].parentSourceMeasure.FirstInstructionsStaffEntries;
+        const visibleInstructionEntries: SourceStaffEntry[] = [];
         for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
-            let measure: StaffMeasure = measures[idx];
+            const measure: StaffMeasure = measures[idx];
             visibleInstructionEntries.push(firstStaffEntries[measure.ParentStaff.idInMusicSheet]);
         }
         let maxMeasureWidth: number = 0;
         for (let visStaffIdx: number = 0, len: number = visibleInstructionEntries.length; visStaffIdx < len; ++visStaffIdx) {
-            let sse: SourceStaffEntry = visibleInstructionEntries[visStaffIdx];
+            const sse: SourceStaffEntry = visibleInstructionEntries[visStaffIdx];
             if (sse === undefined) {
                 continue;
             }
-            let instructions: AbstractNotationInstruction[] = sse.Instructions;
+            const instructions: AbstractNotationInstruction[] = sse.Instructions;
             let keyInstruction: KeyInstruction = undefined;
             let rhythmInstruction: RhythmInstruction = undefined;
             for (let idx2: number = 0, len2: number = instructions.length; idx2 < len2; ++idx2) {
-                let instruction: AbstractNotationInstruction = instructions[idx2];
+                const instruction: AbstractNotationInstruction = instructions[idx2];
                 if (instruction instanceof KeyInstruction && (<KeyInstruction>instruction).Key !== this.activeKeys[visStaffIdx].Key) {
                     keyInstruction = <KeyInstruction>instruction;
                 }
@@ -599,7 +596,7 @@ export class MusicSystemBuilder {
                 }
             }
             if (keyInstruction !== undefined || rhythmInstruction !== undefined) {
-                let measureWidth: number = this.addExtraInstructionMeasure(visStaffIdx, keyInstruction, rhythmInstruction);
+                const measureWidth: number = this.addExtraInstructionMeasure(visStaffIdx, keyInstruction, rhythmInstruction);
                 maxMeasureWidth = Math.max(maxMeasureWidth, measureWidth);
             }
         }
@@ -614,9 +611,9 @@ export class MusicSystemBuilder {
     }
 
     private addExtraInstructionMeasure(visStaffIdx: number, keyInstruction: KeyInstruction, rhythmInstruction: RhythmInstruction): number {
-        let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
-        let measures: StaffMeasure[] = [];
-        let measure: StaffMeasure = this.symbolFactory.createExtraStaffMeasure(currentSystem.StaffLines[visStaffIdx]);
+        const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        const measures: StaffMeasure[] = [];
+        const measure: StaffMeasure = this.symbolFactory.createExtraStaffMeasure(currentSystem.StaffLines[visStaffIdx]);
         measures.push(measure);
         if (keyInstruction !== undefined) {
             measure.addKeyAtBegin(keyInstruction, this.activeKeys[visStaffIdx], this.activeClefs[visStaffIdx]);
@@ -627,11 +624,10 @@ export class MusicSystemBuilder {
         measure.PositionAndShape.BorderLeft = 0.0;
         measure.PositionAndShape.BorderTop = 0.0;
         measure.PositionAndShape.BorderBottom = this.rules.StaffHeight;
-        let width: number = this.rules.MeasureLeftMargin + measure.beginInstructionsWidth + this.rules.MeasureRightMargin;
+        const width: number = this.rules.MeasureLeftMargin + measure.beginInstructionsWidth + this.rules.MeasureRightMargin;
         measure.PositionAndShape.BorderRight = width;
         currentSystem.StaffLines[visStaffIdx].Measures.push(measure);
         measure.ParentStaffLine = currentSystem.StaffLines[visStaffIdx];
-        currentSystem.StaffLines[visStaffIdx].PositionAndShape.ChildElements.push(measure.PositionAndShape);
         return width;
     }
 
@@ -641,16 +637,15 @@ export class MusicSystemBuilder {
      */
     private addStaveMeasuresToSystem(staffMeasures: StaffMeasure[]): void {
         if (staffMeasures[0] !== undefined) {
-            let gmeasures: StaffMeasure[] = [];
+            const gmeasures: StaffMeasure[] = [];
             for (let i: number = 0; i < staffMeasures.length; i++) {
                 gmeasures.push(staffMeasures[i]);
             }
-            let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+            const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
             for (let visStaffIdx: number = 0; visStaffIdx < this.numberOfVisibleStaffLines; visStaffIdx++) {
-                let measure: StaffMeasure = gmeasures[visStaffIdx];
+                const measure: StaffMeasure = gmeasures[visStaffIdx];
                 currentSystem.StaffLines[visStaffIdx].Measures.push(measure);
                 measure.ParentStaffLine = currentSystem.StaffLines[visStaffIdx];
-                currentSystem.StaffLines[visStaffIdx].PositionAndShape.ChildElements.push(measure.PositionAndShape);
             }
             currentSystem.AddStaffMeasures(gmeasures);
         }
@@ -661,10 +656,10 @@ export class MusicSystemBuilder {
      * @returns {SystemLinesEnum}
      */
     private getMeasureStartLine(): SystemLinesEnum {
-        let thisMeasureBeginsLineRep: boolean = this.thisMeasureBeginsLineRepetition();
+        const thisMeasureBeginsLineRep: boolean = this.thisMeasureBeginsLineRepetition();
         if (thisMeasureBeginsLineRep) {
-            let isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
-            let isGlobalFirstMeasure: boolean = this.measureListIndex === 0;
+            const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
+            const isGlobalFirstMeasure: boolean = this.measureListIndex === 0;
             if (this.previousMeasureEndsLineRepetition() && !isSystemStartMeasure) {
                 return SystemLinesEnum.DotsBoldBoldDots;
             }
@@ -714,7 +709,7 @@ export class MusicSystemBuilder {
             return false;
         }
         for (let idx: number = 0, len: number = this.measureList[this.measureListIndex - 1].length; idx < len; ++idx) {
-            let measure: StaffMeasure = this.measureList[this.measureListIndex - 1][idx];
+            const measure: StaffMeasure = this.measureList[this.measureListIndex - 1][idx];
             if (measure.endsWithLineRepetition()) {
                 return true;
             }
@@ -728,7 +723,7 @@ export class MusicSystemBuilder {
      */
     private thisMeasureBeginsLineRepetition(): boolean {
         for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
-            let measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
+            const measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
             if (measure.beginsWithLineRepetition()) {
                 return true;
             }
@@ -741,12 +736,12 @@ export class MusicSystemBuilder {
      * @returns {boolean}
      */
     private nextMeasureBeginsLineRepetition(): boolean {
-        let nextMeasureIndex: number = this.measureListIndex + 1;
+        const nextMeasureIndex: number = this.measureListIndex + 1;
         if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length) {
             return false;
         }
         for (let idx: number = 0, len: number = this.measureList[nextMeasureIndex].length; idx < len; ++idx) {
-            let measure: StaffMeasure = this.measureList[nextMeasureIndex][idx];
+            const measure: StaffMeasure = this.measureList[nextMeasureIndex][idx];
             if (measure.beginsWithLineRepetition()) {
                 return true;
             }
@@ -760,7 +755,7 @@ export class MusicSystemBuilder {
      */
     private thisMeasureEndsLineRepetition(): boolean {
         for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
-            let measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
+            const measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
             if (measure.endsWithLineRepetition()) {
                 return true;
             }
@@ -773,12 +768,12 @@ export class MusicSystemBuilder {
      * @returns {boolean}
      */
     private nextMeasureBeginsWordRepetition(): boolean {
-        let nextMeasureIndex: number = this.measureListIndex + 1;
+        const nextMeasureIndex: number = this.measureListIndex + 1;
         if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length) {
             return false;
         }
         for (let idx: number = 0, len: number = this.measureList[nextMeasureIndex].length; idx < len; ++idx) {
-            let measure: StaffMeasure = this.measureList[nextMeasureIndex][idx];
+            const measure: StaffMeasure = this.measureList[nextMeasureIndex][idx];
             if (measure.beginsWithWordRepetition()) {
                 return true;
             }
@@ -792,7 +787,7 @@ export class MusicSystemBuilder {
      */
     private thisMeasureEndsWordRepetition(): boolean {
         for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
-            let measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
+            const measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
             if (measure.endsWithWordRepetition()) {
                 return true;
             }
@@ -811,7 +806,7 @@ export class MusicSystemBuilder {
     private getNextMeasureKeyInstruction(): KeyInstruction {
         if (this.measureListIndex < this.measureList.length - 1) {
             for (let visIndex: number = 0; visIndex < this.measureList[this.measureListIndex].length; visIndex++) {
-                let sourceMeasure: SourceMeasure = this.measureList[this.measureListIndex + 1][visIndex].parentSourceMeasure;
+                const sourceMeasure: SourceMeasure = this.measureList[this.measureListIndex + 1][visIndex].parentSourceMeasure;
                 if (sourceMeasure === undefined) {
                     return undefined;
                 }
@@ -832,9 +827,9 @@ export class MusicSystemBuilder {
             return 1.0;
         }
         let systemEndX: number;
-        let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
         systemEndX = currentSystem.StaffLines[0].PositionAndShape.Size.width;
-        let scalingFactor: number = (systemEndX - systemFixWidth) / systemVarWidth;
+        const scalingFactor: number = (systemEndX - systemFixWidth) / systemVarWidth;
         return scalingFactor;
     }
 
@@ -849,17 +844,17 @@ export class MusicSystemBuilder {
         if (isPartEndingSystem) {
             scalingFactor = Math.min(scalingFactor, this.rules.LastSystemMaxScalingFactor);
         }
-        let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
         for (let visStaffIdx: number = 0, len: number = currentSystem.StaffLines.length; visStaffIdx < len; ++visStaffIdx) {
-            let staffLine: StaffLine = currentSystem.StaffLines[visStaffIdx];
+            const staffLine: StaffLine = currentSystem.StaffLines[visStaffIdx];
             let currentXPosition: number = 0.0;
             for (let measureIndex: number = 0; measureIndex < staffLine.Measures.length; measureIndex++) {
-                let measure: StaffMeasure = staffLine.Measures[measureIndex];
+                const measure: StaffMeasure = staffLine.Measures[measureIndex];
                 measure.setPositionInStaffline(currentXPosition);
                 measure.setWidth(measure.beginInstructionsWidth + measure.minimumStaffEntriesWidth * scalingFactor + measure.endInstructionsWidth);
                 if (measureIndex < this.currentSystemParams.systemMeasures.length) {
-                    let startLine: SystemLinesEnum = this.currentSystemParams.systemMeasures[measureIndex].beginLine;
-                    let lineWidth: number = measure.getLineWidth(SystemLinesEnum.BoldThinDots);
+                    const startLine: SystemLinesEnum = this.currentSystemParams.systemMeasures[measureIndex].beginLine;
+                    const lineWidth: number = measure.getLineWidth(SystemLinesEnum.BoldThinDots);
                     switch (startLine) {
                         case SystemLinesEnum.BoldThinDots:
                             let xPosition: number = currentXPosition;
@@ -874,14 +869,14 @@ export class MusicSystemBuilder {
                 }
                 measure.staffEntriesScaleFactor = scalingFactor;
                 measure.layoutSymbols();
-                let nextMeasureHasRepStartLine: boolean = measureIndex + 1 < this.currentSystemParams.systemMeasures.length
+                const nextMeasureHasRepStartLine: boolean = measureIndex + 1 < this.currentSystemParams.systemMeasures.length
                     && this.currentSystemParams.systemMeasures[measureIndex + 1].beginLine === SystemLinesEnum.BoldThinDots;
                 if (!nextMeasureHasRepStartLine) {
                     let endLine: SystemLinesEnum = SystemLinesEnum.SingleThin;
                     if (measureIndex < this.currentSystemParams.systemMeasures.length) {
                         endLine = this.currentSystemParams.systemMeasures[measureIndex].endLine;
                     }
-                    let lineWidth: number = measure.getLineWidth(endLine);
+                    const lineWidth: number = measure.getLineWidth(endLine);
                     let xPos: number = measure.PositionAndShape.RelativePosition.x + measure.PositionAndShape.BorderRight - lineWidth;
                     if (endLine === SystemLinesEnum.DotsBoldBoldDots) {
                         xPos -= lineWidth / 2;
@@ -901,14 +896,14 @@ export class MusicSystemBuilder {
      * the [[StaffLine]]'s Width and the 5 [[StaffLine]]s length.
      */
     private decreaseMusicSystemBorders(): void {
-        let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
-        let bb: BoundingBox = CollectionUtil.last(currentSystem.StaffLines[0].Measures).PositionAndShape;
-        let width: number = bb.RelativePosition.x + bb.Size.width;
+        const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        const bb: BoundingBox = CollectionUtil.last(currentSystem.StaffLines[0].Measures).PositionAndShape;
+        const width: number = bb.RelativePosition.x + bb.Size.width;
         for (let idx: number = 0, len: number = currentSystem.StaffLines.length; idx < len; ++idx) {
-            let staffLine: StaffLine = currentSystem.StaffLines[idx];
+            const staffLine: StaffLine = currentSystem.StaffLines[idx];
             staffLine.PositionAndShape.BorderRight = width;
             for (let idx2: number = 0, len2: number = staffLine.StaffLines.length; idx2 < len2; ++idx2) {
-                let graphicalLine: GraphicalLine = staffLine.StaffLines[idx2];
+                const graphicalLine: GraphicalLine = staffLine.StaffLines[idx2];
                 graphicalLine.End = new PointF2D(width, graphicalLine.End.y);
             }
         }

+ 10 - 10
src/MusicalScore/Graphical/SelectionEndSymbol.ts

@@ -11,10 +11,10 @@ export class SelectionEndSymbol extends GraphicalObject {
 
     constructor(system: MusicSystem, xPosition: number) {
         super();
-        let xCoordinate: number = xPosition;
-        let yCoordinate: number = system.PositionAndShape.AbsolutePosition.y;
-        let lineThickness: number = 0.4;
-        let height: number = CollectionUtil.last(system.StaffLines).PositionAndShape.RelativePosition.y + 4;
+        const xCoordinate: number = xPosition;
+        const yCoordinate: number = system.PositionAndShape.AbsolutePosition.y;
+        const lineThickness: number = 0.4;
+        const height: number = CollectionUtil.last(system.StaffLines).PositionAndShape.RelativePosition.y + 4;
         this.verticalLine = new GraphicalLine(
             new PointF2D(xCoordinate, yCoordinate),
             new PointF2D(xCoordinate, yCoordinate + height),
@@ -22,9 +22,9 @@ export class SelectionEndSymbol extends GraphicalObject {
             OutlineAndFillStyleEnum.SelectionSymbol
         );
         for (let idx: number = 0, len: number = system.StaffLines.length; idx < len; ++idx) {
-            let staffLine: StaffLine = system.StaffLines[idx];
-            let anchor: PointF2D = new PointF2D(xCoordinate, yCoordinate + staffLine.PositionAndShape.RelativePosition.y);
-            let arrowPoints: PointF2D[] = new Array(3);
+            const staffLine: StaffLine = system.StaffLines[idx];
+            const anchor: PointF2D = new PointF2D(xCoordinate, yCoordinate + staffLine.PositionAndShape.RelativePosition.y);
+            const arrowPoints: PointF2D[] = new Array(3);
             anchor.y -= .2;
             arrowPoints[0].x = anchor.x - 3;
             arrowPoints[0].y = anchor.y + 1.2;
@@ -33,11 +33,11 @@ export class SelectionEndSymbol extends GraphicalObject {
             arrowPoints[2].x = anchor.x - 2;
             arrowPoints[2].y = anchor.y + 2;
             this.arrows.push(arrowPoints);
-            let linePoints: PointF2D[] = new Array(8);
-            let arrowThickness: number = .8;
+            const linePoints: PointF2D[] = new Array(8);
+            const arrowThickness: number = .8;
             anchor.x -= .1;
             anchor.y += .3;
-            let hilfsVar: number = .2;
+            const hilfsVar: number = .2;
             linePoints[0].x = anchor.x - 2;
             linePoints[0].y = anchor.y + 1.5 - hilfsVar;
             linePoints[1].x = anchor.x - 1;

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

@@ -11,10 +11,10 @@ export class SelectionStartSymbol extends GraphicalObject {
 
     constructor(system: MusicSystem, xPosition: number) {
         super();
-        let xCoordinate: number = xPosition;
-        let yCoordinate: number = system.PositionAndShape.AbsolutePosition.y;
-        let lineThickness: number = 0.4;
-        let height: number = CollectionUtil.last(system.StaffLines).PositionAndShape.RelativePosition.y + 4;
+        const xCoordinate: number = xPosition;
+        const yCoordinate: number = system.PositionAndShape.AbsolutePosition.y;
+        const lineThickness: number = 0.4;
+        const height: number = CollectionUtil.last(system.StaffLines).PositionAndShape.RelativePosition.y + 4;
         this.verticalLine = new GraphicalLine(
             new PointF2D(xCoordinate, yCoordinate),
             new PointF2D(xCoordinate, yCoordinate + height),
@@ -22,9 +22,9 @@ export class SelectionStartSymbol extends GraphicalObject {
             OutlineAndFillStyleEnum.SelectionSymbol
         );
         for (let idx: number = 0, len: number = system.StaffLines.length; idx < len; ++idx) {
-            let staffLine: StaffLine = system.StaffLines[idx];
-            let anchor: PointF2D = new PointF2D(xCoordinate, yCoordinate + staffLine.PositionAndShape.RelativePosition.y);
-            let arrowPoints: PointF2D[] = new Array(7);
+            const staffLine: StaffLine = system.StaffLines[idx];
+            const anchor: PointF2D = new PointF2D(xCoordinate, yCoordinate + staffLine.PositionAndShape.RelativePosition.y);
+            const arrowPoints: PointF2D[] = new Array(7);
             arrowPoints[0].x = anchor.x + 4;
             arrowPoints[0].y = anchor.y + 2;
             arrowPoints[1].x = anchor.x + 2.5;

+ 30 - 10
src/MusicalScore/Graphical/StaffLine.ts

@@ -8,6 +8,7 @@ import {StaffMeasure} from "./StaffMeasure";
 import {MusicSystem} from "./MusicSystem";
 import {StaffLineActivitySymbol} from "./StaffLineActivitySymbol";
 import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {GraphicalLabel} from "./GraphicalLabel";
 
 /**
  * A StaffLine contains the [[Measure]]s in one line of the music sheet
@@ -20,6 +21,8 @@ export abstract class StaffLine extends GraphicalObject {
     protected parentStaff: Staff;
     protected skyLine: number[];
     protected bottomLine: number[];
+    protected lyricLines: GraphicalLine[] = [];
+    protected lyricsDashes: GraphicalLabel[] = [];
 
     constructor(parentSystem: MusicSystem, parentStaff: Staff) {
         super();
@@ -44,6 +47,27 @@ export abstract class StaffLine extends GraphicalObject {
         this.staffLines = value;
     }
 
+    public get NextStaffLine(): StaffLine {
+        const idxInParent: number = this.parentMusicSystem.StaffLines.indexOf(this);
+        return idxInParent !== this.parentMusicSystem.StaffLines.length ? this.parentMusicSystem.StaffLines[idxInParent + 1] : undefined;
+    }
+
+    public get LyricLines(): GraphicalLine[] {
+        return this.lyricLines;
+    }
+
+    public set LyricLines(value: GraphicalLine[]) {
+        this.lyricLines = value;
+    }
+
+    public get LyricsDashes(): GraphicalLabel[] {
+        return this.lyricsDashes;
+    }
+
+    public set LyricsDashes(value: GraphicalLabel[]) {
+        this.lyricsDashes = value;
+    }
+
     public get ParentMusicSystem(): MusicSystem {
         return this.parentMusicSystem;
     }
@@ -77,11 +101,11 @@ export abstract class StaffLine extends GraphicalObject {
     }
 
     public addActivitySymbolClickArea(): void {
-        let activitySymbol: StaffLineActivitySymbol = new StaffLineActivitySymbol(this);
-        let staffLinePsi: BoundingBox = this.PositionAndShape;
+        const activitySymbol: StaffLineActivitySymbol = new StaffLineActivitySymbol(this);
+        const staffLinePsi: BoundingBox = this.PositionAndShape;
         activitySymbol.PositionAndShape.RelativePosition =
             new PointF2D(staffLinePsi.RelativePosition.x + staffLinePsi.BorderRight + 0.5, staffLinePsi.RelativePosition.y + 0.5);
-        this.parentMusicSystem.PositionAndShape.ChildElements.push(activitySymbol.PositionAndShape);
+        activitySymbol.PositionAndShape.Parent = this.parentMusicSystem.PositionAndShape;
     }
 
     /**
@@ -89,7 +113,7 @@ export abstract class StaffLine extends GraphicalObject {
      * @returns {boolean}
      */
     public isPartOfMultiStaffInstrument(): boolean {
-        let instrument: Instrument = this.parentStaff.ParentInstrument;
+        const instrument: Instrument = this.parentStaff.ParentInstrument;
         if (instrument.Staves.length > 1) {
             return true;
         }
@@ -103,17 +127,13 @@ export abstract class StaffLine extends GraphicalObject {
      */
     public findClosestStaffEntry(xPosition: number): GraphicalStaffEntry {
         let closestStaffentry: GraphicalStaffEntry = undefined;
-        let difference: number = Number.MAX_VALUE;
         for (let idx: number = 0, len: number = this.Measures.length; idx < len; ++idx) {
-            let graphicalMeasure: StaffMeasure = this.Measures[idx];
+            const graphicalMeasure: StaffMeasure = this.Measures[idx];
             for (let idx2: number = 0, len2: number = graphicalMeasure.staffEntries.length; idx2 < len2; ++idx2) {
-                let graphicalStaffEntry: GraphicalStaffEntry = graphicalMeasure.staffEntries[idx2];
+                const graphicalStaffEntry: GraphicalStaffEntry = graphicalMeasure.staffEntries[idx2];
                 if (
                     Math.abs(graphicalStaffEntry.PositionAndShape.RelativePosition.x - xPosition + graphicalMeasure.PositionAndShape.RelativePosition.x) < 5.0
                 ) {
-                    difference = Math.abs(
-                        graphicalStaffEntry.PositionAndShape.RelativePosition.x - xPosition + graphicalMeasure.PositionAndShape.RelativePosition.x
-                    );
                     closestStaffentry = graphicalStaffEntry;
                 }
             }

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

@@ -7,7 +7,7 @@ export class StaffLineActivitySymbol extends GraphicalObject {
     constructor(staffLine: StaffLine) {
         super();
         this.parentStaffLine = staffLine;
-        let staffLinePsi: BoundingBox = staffLine.PositionAndShape;
+        const staffLinePsi: BoundingBox = staffLine.PositionAndShape;
         this.boundingBox = new BoundingBox(this, staffLinePsi);
         this.boundingBox.BorderRight = 6;
         this.boundingBox.BorderBottom = 4.5;

+ 18 - 20
src/MusicalScore/Graphical/StaffMeasure.ts

@@ -182,7 +182,7 @@ export abstract class StaffMeasure extends GraphicalObject {
 
     public findGraphicalStaffEntryFromTimestamp(relativeTimestamp: Fraction): GraphicalStaffEntry {
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
-            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            const graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
             if (graphicalStaffEntry.relInMeasureTimestamp.Equals(relativeTimestamp)) {
                 return graphicalStaffEntry;
             }
@@ -197,7 +197,7 @@ export abstract class StaffMeasure extends GraphicalObject {
      */
     public findGraphicalStaffEntryFromVerticalContainerTimestamp(absoluteTimestamp: Fraction): GraphicalStaffEntry {
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
-            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            const graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
             if (graphicalStaffEntry.sourceStaffEntry.VerticalContainerParent.getAbsoluteTimestamp().Equals(absoluteTimestamp)) {
                 return graphicalStaffEntry;
             }
@@ -210,9 +210,9 @@ export abstract class StaffMeasure extends GraphicalObject {
      * @returns {boolean}
      */
     public hasSameDurationWithSourceMeasureParent(): boolean {
-        let duration: Fraction = new Fraction(0, 1);
+        const duration: Fraction = new Fraction(0, 1);
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
-            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            const graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
             duration.Add(graphicalStaffEntry.findStaffEntryMinNoteLength());
         }
         return duration.Equals(this.parentSourceMeasure.Duration);
@@ -226,11 +226,11 @@ export abstract class StaffMeasure extends GraphicalObject {
         if (this.staffEntries.length === 0) {
             return false;
         }
-        let voices: Voice[] = [];
+        const voices: Voice[] = [];
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
-            let staffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            const staffEntry: GraphicalStaffEntry = this.staffEntries[idx];
             for (let idx2: number = 0, len2: number = staffEntry.sourceStaffEntry.VoiceEntries.length; idx2 < len2; ++idx2) {
-                let voiceEntry: VoiceEntry = staffEntry.sourceStaffEntry.VoiceEntries[idx2];
+                const voiceEntry: VoiceEntry = staffEntry.sourceStaffEntry.VoiceEntries[idx2];
                 if (voices.indexOf(voiceEntry.ParentVoice) < 0) {
                     voices.push(voiceEntry.ParentVoice);
                 }
@@ -248,23 +248,23 @@ export abstract class StaffMeasure extends GraphicalObject {
 
     public getGraphicalMeasureDurationFromStaffEntries(): Fraction {
         let duration: Fraction = new Fraction(0, 1);
-        let voices: Voice[] = [];
+        const voices: Voice[] = [];
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
-            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            const graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
             for (let idx2: number = 0, len2: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx2 < len2; ++idx2) {
-                let voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx2];
+                const voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx2];
                 if (voices.indexOf(voiceEntry.ParentVoice) < 0) {
                     voices.push(voiceEntry.ParentVoice);
                 }
             }
         }
         for (let idx: number = 0, len: number = voices.length; idx < len; ++idx) {
-            let voice: Voice = voices[idx];
-            let voiceDuration: Fraction = new Fraction(0, 1);
+            const voice: Voice = voices[idx];
+            const voiceDuration: Fraction = new Fraction(0, 1);
             for (let idx2: number = 0, len2: number = this.staffEntries.length; idx2 < len2; ++idx2) {
-                let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx2];
+                const graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx2];
                 for (let idx3: number = 0, len3: number = graphicalStaffEntry.notes.length; idx3 < len3; ++idx3) {
-                    let graphicalNotes: GraphicalNote[] = graphicalStaffEntry.notes[idx3];
+                    const graphicalNotes: GraphicalNote[] = graphicalStaffEntry.notes[idx3];
                     if (graphicalNotes.length > 0 && graphicalNotes[0].sourceNote.ParentVoiceEntry.ParentVoice === voice) {
                         voiceDuration.Add(graphicalNotes[0].graphicalNoteLength);
                     }
@@ -279,7 +279,6 @@ export abstract class StaffMeasure extends GraphicalObject {
 
     public addGraphicalStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
         this.staffEntries.push(graphicalStaffEntry);
-        this.PositionAndShape.ChildElements.push(graphicalStaffEntry.PositionAndShape);
     }
 
     /**
@@ -301,12 +300,11 @@ export abstract class StaffMeasure extends GraphicalObject {
                     }
                 }
             }
-            this.PositionAndShape.ChildElements.push(staffEntry.PositionAndShape);
         }
     }
 
     public beginsWithLineRepetition(): boolean {
-        let sourceMeasure: SourceMeasure = this.parentSourceMeasure;
+        const sourceMeasure: SourceMeasure = this.parentSourceMeasure;
         if (sourceMeasure === undefined) {
             return false;
         }
@@ -318,7 +316,7 @@ export abstract class StaffMeasure extends GraphicalObject {
      * @returns {boolean}
      */
     public endsWithLineRepetition(): boolean {
-        let sourceMeasure: SourceMeasure = this.parentSourceMeasure;
+        const sourceMeasure: SourceMeasure = this.parentSourceMeasure;
         if (sourceMeasure === undefined) {
             return false;
         }
@@ -330,7 +328,7 @@ export abstract class StaffMeasure extends GraphicalObject {
      * @returns {boolean}
      */
     public beginsWithWordRepetition(): boolean {
-        let sourceMeasure: SourceMeasure = this.parentSourceMeasure;
+        const sourceMeasure: SourceMeasure = this.parentSourceMeasure;
         if (sourceMeasure === undefined) {
             return false;
         }
@@ -341,7 +339,7 @@ export abstract class StaffMeasure extends GraphicalObject {
      * Check if this Measure is a Repetition Ending.
      */
     public endsWithWordRepetition(): boolean {
-        let sourceMeasure: SourceMeasure = this.parentSourceMeasure;
+        const sourceMeasure: SourceMeasure = this.parentSourceMeasure;
         if (sourceMeasure === undefined) {
             return false;
         }

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

@@ -38,8 +38,8 @@ export class VerticalGraphicalStaffEntryContainer {
     }
 
     public static compareByTimestamp(x: VerticalGraphicalStaffEntryContainer, y: VerticalGraphicalStaffEntryContainer): number {
-        let xValue: number = x.absoluteTimestamp.RealValue;
-        let yValue: number = y.absoluteTimestamp.RealValue;
+        const xValue: number = x.absoluteTimestamp.RealValue;
+        const yValue: number = y.absoluteTimestamp.RealValue;
 
         if (xValue < yValue) {
             return -1;
@@ -56,7 +56,7 @@ export class VerticalGraphicalStaffEntryContainer {
      */
     public getFirstNonNullStaffEntry(): GraphicalStaffEntry {
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
-            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            const graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
             if (graphicalStaffEntry !== undefined) {
                 return graphicalStaffEntry;
             }

+ 66 - 0
src/MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend.ts

@@ -0,0 +1,66 @@
+import Vex = require("vexflow");
+
+import {VexFlowBackend} from "./VexFlowBackend";
+import {FontStyles} from "../../../Common/Enums/FontStyles";
+import {Fonts} from "../../../Common/Enums/Fonts";
+import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
+import {PointF2D} from "../../../Common/DataObjects/PointF2D";
+import {VexFlowConverter} from "./VexFlowConverter";
+
+export class CanvasVexFlowBackend extends VexFlowBackend {
+
+    public getBackendType(): number {
+        return Vex.Flow.Renderer.Backends.CANVAS;
+    }
+
+    public initialize(container: HTMLElement): void {
+        this.canvas = document.createElement("canvas");
+        this.inner = document.createElement("div");
+        this.inner.style.position = "relative";
+        this.canvas.style.zIndex = "0";
+        this.inner.appendChild(this.canvas);
+        container.appendChild(this.inner);
+        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getBackendType());
+        this.ctx = <Vex.Flow.CanvasContext>this.renderer.getContext();
+        this.canvasRenderingCtx = this.ctx.vexFlowCanvasContext;
+
+    }
+
+    public getContext(): Vex.Flow.CanvasContext {
+        return this.ctx;
+    }
+
+    public clear(): void {
+        // Doesn't need to do anything
+    }
+
+    public scale(k: number): void {
+        this.ctx.scale(k, k);
+    }
+
+    public translate(x: number, y: number): void {
+        this.canvasRenderingCtx.translate(x, y);
+    }
+    public renderText(fontHeight: number, fontStyle: FontStyles, font: Fonts, text: string,
+                      heightInPixel: number, screenPosition: PointF2D): void  {
+        const old: string = this.canvasRenderingCtx.font;
+        this.canvasRenderingCtx.font = VexFlowConverter.font(
+            fontHeight,
+            fontStyle,
+            font
+        );
+        this.canvasRenderingCtx.fillText(text, screenPosition.x, screenPosition.y + heightInPixel);
+        this.canvasRenderingCtx.font = old;
+    }
+    public renderRectangle(rectangle: RectangleF2D, styleId: number, alpha: number = 1): void {
+        const old: string | CanvasGradient | CanvasPattern = this.canvasRenderingCtx.fillStyle;
+        this.canvasRenderingCtx.fillStyle = VexFlowConverter.style(styleId);
+        this.canvasRenderingCtx.globalAlpha = alpha;
+        this.ctx.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+        this.canvasRenderingCtx.fillStyle = old;
+        this.canvasRenderingCtx.globalAlpha = 1;
+    }
+
+    private ctx: Vex.Flow.CanvasContext;
+    private canvasRenderingCtx: CanvasRenderingContext2D;
+}

+ 71 - 0
src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts

@@ -0,0 +1,71 @@
+import Vex = require("vexflow");
+
+import {VexFlowBackend} from "./VexFlowBackend";
+import {VexFlowConverter} from "./VexFlowConverter";
+import {FontStyles} from "../../../Common/Enums/FontStyles";
+import {Fonts} from "../../../Common/Enums/Fonts";
+import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
+import {PointF2D} from "../../../Common/DataObjects/PointF2D";
+
+export class SvgVexFlowBackend extends VexFlowBackend {
+
+    public getBackendType(): number {
+        return Vex.Flow.Renderer.Backends.SVG;
+    }
+
+    public initialize(container: HTMLElement): void {
+        this.canvas = document.createElement("div");
+        this.inner = this.canvas;
+        this.inner.style.position = "relative";
+        this.canvas.style.zIndex = "0";
+        container.appendChild(this.inner);
+        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getBackendType());
+        this.ctx = <Vex.Flow.SVGContext>this.renderer.getContext();
+
+    }
+
+    public getContext(): Vex.Flow.SVGContext {
+        return this.ctx;
+    }
+
+    public clear(): void {
+        const { svg } = this.ctx;
+        if (!svg) {
+            return;
+        }
+        // removes all children from the SVG element,
+        // effectively clearing the SVG viewport
+        while (svg.lastChild) {
+            svg.removeChild(svg.lastChild);
+        }
+    }
+
+    public scale(k: number): void {
+        this.ctx.scale(k, k);
+    }
+
+    public translate(x: number, y: number): void {
+        // TODO: implement this
+    }
+    public renderText(fontHeight: number, fontStyle: FontStyles, font: Fonts, text: string,
+                      heightInPixel: number, screenPosition: PointF2D): void {
+        this.ctx.save();
+
+        this.ctx.setFont("Times New Roman", fontHeight, VexFlowConverter.fontStyle(fontStyle));
+        // font size is set by VexFlow in `pt`. This overwrites the font so it's set to px instead
+        this.ctx.attributes["font-size"] = `${fontHeight}px`;
+        this.ctx.state["font-size"] = `${fontHeight}px`;
+        this.ctx.fillText(text, screenPosition.x, screenPosition.y + heightInPixel);
+        this.ctx.restore();
+    }
+    public renderRectangle(rectangle: RectangleF2D, styleId: number, alpha: number = 1): void {
+        this.ctx.save();
+        this.ctx.attributes.fill = VexFlowConverter.style(styleId);
+        this.ctx.attributes["fill-opacity"] = alpha;
+        this.ctx.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+        this.ctx.restore();
+        this.ctx.attributes["fill-opacity"] = 1;
+    }
+
+    private ctx: Vex.Flow.SVGContext;
+}

+ 62 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts

@@ -0,0 +1,62 @@
+import * as Vex from "vexflow";
+import {FontStyles} from "../../../Common/Enums/FontStyles";
+import {Fonts} from "../../../Common/Enums/Fonts";
+import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
+import {PointF2D} from "../../../Common/DataObjects/PointF2D";
+
+export class VexFlowBackends {
+  public static CANVAS: 0;
+  public static RAPHAEL: 1;
+  public static SVG: 2;
+  public static VML: 3;
+
+}
+
+export abstract class VexFlowBackend {
+
+  public abstract initialize(container: HTMLElement): void;
+
+  public getInnerElement(): HTMLElement {
+    return this.inner;
+  }
+
+  public getCanvas(): HTMLElement {
+    return this.canvas;
+  }
+
+  public getRenderer(): Vex.Flow.Renderer {
+    return this.renderer;
+  }
+
+  public abstract getContext(): Vex.Flow.RenderContext;
+
+  // public abstract setWidth(width: number): void;
+  // public abstract setHeight(height: number): void;
+
+  public abstract scale(k: number): void;
+
+  public resize(x: number, y: number): void {
+    this.renderer.resize(x, y);
+  }
+
+  public abstract clear(): void;
+
+  public abstract translate(x: number, y: number): void;
+  public abstract renderText(fontHeight: number, fontStyle: FontStyles, font: Fonts, text: string,
+                             heightInPixel: number, screenPosition: PointF2D): void;
+  /**
+   * Renders a rectangle with the given style to the screen.
+   * It is given in screen coordinates.
+   * @param rectangle the rect in screen coordinates
+   * @param layer is the current rendering layer. There are many layers on top of each other to which can be rendered. Not needed for now.
+   * @param styleId the style id
+   * @param alpha alpha value between 0 and 1
+   */
+  public abstract renderRectangle(rectangle: RectangleF2D, styleId: number, alpha: number): void;
+
+  public abstract getBackendType(): number;
+
+  protected renderer: Vex.Flow.Renderer;
+  protected inner: HTMLElement;
+  protected canvas: HTMLElement;
+}

+ 275 - 57
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -14,7 +14,10 @@ import {GraphicalNote} from "../GraphicalNote";
 import {SystemLinesEnum} from "../SystemLinesEnum";
 import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {Fonts} from "../../../Common/Enums/Fonts";
-import {OutlineAndFillStyleEnum} from "../DrawingEnums";
+import {OutlineAndFillStyleEnum, OUTLINE_AND_FILL_STYLE_DICT} from "../DrawingEnums";
+import {Logging} from "../../../Common/Logging";
+import { ArticulationEnum, StemDirectionType } from "../../VoiceData/VoiceEntry";
+import { SystemLinePosition } from "../SystemLinePosition";
 
 /**
  * Helper class, which contains static methods which actually convert
@@ -43,22 +46,53 @@ export class VexFlowConverter {
      * @param fraction a fraction representing the duration of a note
      * @returns {string}
      */
-    public static duration(fraction: Fraction): string {
-        let dur: number = fraction.RealValue;
-        if (dur >= 1) {
-            return "w";
-        } else if (dur < 1 && dur >= 0.5) {
-            return "h";
-        } else if (dur < 0.5 && dur >= 0.25) {
-            return "q";
-        } else if (dur < 0.25 && dur >= 0.125) {
-            return "8";
-        } else if (dur < 0.125 && dur >= 0.0625) {
-            return "16";
-        } else if (dur < 0.0625 && dur >= 0.03125) {
-            return "32";
+    public static duration(fraction: Fraction, isTuplet: boolean): string {
+      const dur: number = fraction.RealValue;
+
+      if (dur >= 1) {
+          return "w";
+      } else if (dur < 1 && dur >= 0.5) {
+        // change to the next higher straight note to get the correct note display type
+        if (isTuplet) {
+          return "w";
+        }
+        return "h";
+      } else if (dur < 0.5 && dur >= 0.25) {
+        // change to the next higher straight note to get the correct note display type
+        if (isTuplet && dur > 0.25) {
+          return "h";
+        }
+        return "q";
+      } else if (dur < 0.25 && dur >= 0.125) {
+        // change to the next higher straight note to get the correct note display type
+        if (isTuplet && dur > 0.125) {
+          return "q";
+        }
+        return "8";
+      } else if (dur < 0.125 && dur >= 0.0625) {
+        // change to the next higher straight note to get the correct note display type
+        if (isTuplet && dur > 0.0625) {
+          return "8";
         }
-        return "128";
+        return "16";
+      } else if (dur < 0.0625 && dur >= 0.03125) {
+        // change to the next higher straight note to get the correct note display type
+        if (isTuplet && dur > 0.03125) {
+          return "16";
+        }
+        return "32";
+      } else if (dur < 0.03125 && dur >= 0.015625) {
+        // change to the next higher straight note to get the correct note display type
+        if (isTuplet && dur > 0.015625) {
+          return "32";
+        }
+        return "64";
+      }
+
+      if (isTuplet) {
+        return "64";
+      }
+      return "128";
     }
 
     /**
@@ -68,10 +102,10 @@ export class VexFlowConverter {
      * @returns {string[]}
      */
     public static pitch(pitch: Pitch, clef: ClefInstruction): [string, string, ClefInstruction] {
-        let fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
+        const fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
         // The octave seems to need a shift of three FIXME?
-        let octave: number = pitch.Octave - clef.OctaveOffset + 3;
-        let acc: string = VexFlowConverter.accidental(pitch.Accidental);
+        const octave: number = pitch.Octave - clef.OctaveOffset + 3;
+        const acc: string = VexFlowConverter.accidental(pitch.Accidental);
         return [fund + "n/" + octave, acc, clef];
     }
 
@@ -110,22 +144,23 @@ export class VexFlowConverter {
      */
     public static StaveNote(notes: GraphicalNote[]): Vex.Flow.StaveNote {
         let keys: string[] = [];
-        let accidentals: string[] = [];
-        let frac: Fraction = notes[0].graphicalNoteLength;
-        let duration: string = VexFlowConverter.duration(frac);
+        const accidentals: string[] = [];
+        const frac: Fraction = notes[0].graphicalNoteLength;
+        const isTuplet: boolean = notes[0].sourceNote.NoteTuplet !== undefined;
+        let duration: string = VexFlowConverter.duration(frac, isTuplet);
         let vfClefType: string = undefined;
         let numDots: number = 0;
-        for (let note of notes) {
-            let res: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
-            if (res === undefined) {
-                keys = ["b/4"];
-                duration += "r";
-                break;
+        for (const note of notes) {
+            const pitch: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
+            if (pitch === undefined) { // if it is a rest:
+              keys = ["b/4"];
+              duration += "r";
+              break;
             }
-            keys.push(res[0]);
-            accidentals.push(res[1]);
+            keys.push(pitch[0]);
+            accidentals.push(pitch[1]);
             if (!vfClefType) {
-                let vfClef: {type: string, annotation: string} = VexFlowConverter.Clef(res[2]);
+                const vfClef: {type: string, annotation: string} = VexFlowConverter.Clef(pitch[2]);
                 vfClefType = vfClef.type;
             }
             if (numDots < note.numberOfDots) {
@@ -136,16 +171,23 @@ export class VexFlowConverter {
             duration += "d";
         }
 
-        let vfnote: Vex.Flow.StaveNote = new Vex.Flow.StaveNote({
+        const vfnote: Vex.Flow.StaveNote = new Vex.Flow.StaveNote({
             auto_stem: true,
             clef: vfClefType,
             duration: duration,
-            duration_override: {
-                denominator: frac.Denominator,
-                numerator: frac.Numerator,
-            },
             keys: keys,
         });
+        const wantedStemDirection: StemDirectionType = notes[0].sourceNote.ParentVoiceEntry.StemDirection;
+        switch (wantedStemDirection) {
+            case(StemDirectionType.Up):
+                vfnote.setStemDirection(Vex.Flow.Stem.UP);
+                break;
+            case (StemDirectionType.Down):
+                vfnote.setStemDirection(Vex.Flow.Stem.DOWN);
+                break;
+            default:
+                break;
+        }
 
         for (let i: number = 0, len: number = notes.length; i < len; i += 1) {
             (notes[i] as VexFlowGraphicalNote).setIndex(vfnote, i);
@@ -156,33 +198,164 @@ export class VexFlowConverter {
         for (let i: number = 0, len: number = numDots; i < len; ++i) {
             vfnote.addDotToAll();
         }
-
         return vfnote;
     }
 
+    public static generateArticulations(vfnote: Vex.Flow.StaveNote, articulations: ArticulationEnum[]): void {
+        // Articulations:
+        let vfArtPosition: number = Vex.Flow.Modifier.Position.ABOVE;
+
+        if (vfnote.getStemDirection() === Vex.Flow.Stem.UP) {
+            vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
+        }
+
+        for (const articulation of articulations) {
+            // tslint:disable-next-line:switch-default
+            let vfArt: Vex.Flow.Articulation = undefined;
+            switch (articulation) {
+                case ArticulationEnum.accent: {
+                    vfArt = new Vex.Flow.Articulation("a>");
+                    break;
+                }
+                case ArticulationEnum.downbow: {
+                    vfArt = new Vex.Flow.Articulation("am");
+                    break;
+                }
+                case ArticulationEnum.fermata: {
+                    vfArt = new Vex.Flow.Articulation("a@a");
+                    vfArtPosition = Vex.Flow.Modifier.Position.ABOVE;
+                    break;
+                }
+                case ArticulationEnum.invertedfermata: {
+                    vfArt = new Vex.Flow.Articulation("a@u");
+                    vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
+                    break;
+                }
+                case ArticulationEnum.lefthandpizzicato: {
+                    vfArt = new Vex.Flow.Articulation("a+");
+                    break;
+                }
+                case ArticulationEnum.snappizzicato: {
+                    vfArt = new Vex.Flow.Articulation("ao");
+                    break;
+                }
+                case ArticulationEnum.staccatissimo: {
+                    vfArt = new Vex.Flow.Articulation("av");
+                    break;
+                }
+                case ArticulationEnum.staccato: {
+                    vfArt = new Vex.Flow.Articulation("a.");
+                    break;
+                }
+                case ArticulationEnum.tenuto: {
+                    vfArt = new Vex.Flow.Articulation("a-");
+                    break;
+                }
+                case ArticulationEnum.upbow: {
+                    vfArt = new Vex.Flow.Articulation("a|");
+                    break;
+                }
+                case ArticulationEnum.strongaccent: {
+                    vfArt = new Vex.Flow.Articulation("a^");
+                    break;
+                }
+                default: {
+                    break;
+                }
+            }
+            if (vfArt !== undefined) {
+                vfArt.setPosition(vfArtPosition);
+                vfnote.addModifier(0, vfArt);
+            }
+        }
+    }
+
     /**
-     * Convert a ClefInstruction to a string representing a clef type in VexFlow
-     * @param clef
-     * @returns {string}
-     * @constructor
+     * Convert a ClefInstruction to a string represention of a clef type in VexFlow.
+     *
+     * @param clef The OSMD object to be converted representing the clef
+     * @param size The VexFlow size to be used. Can be `default` or `small`. As soon as
+     *             #118 is done, this parameter will be dispensable.
+     * @returns    A string representation of a VexFlow clef
+     * @see        https://github.com/0xfe/vexflow/blob/master/src/clef.js
+     * @see        https://github.com/0xfe/vexflow/blob/master/tests/clef_tests.js
      */
-    public static Clef(clef: ClefInstruction): {type: string, annotation: string} {
+    public static Clef(clef: ClefInstruction, size: string = "default"): { type: string, size: string, annotation: string } {
         let type: string;
-        let annotation: string = undefined;
+        let annotation: string;
 
+        // Make sure size is either "default" or "small"
+        if (size !== "default" && size !== "small") {
+            Logging.warn(`Invalid VexFlow clef size "${size}" specified. Using "default".`);
+            size = "default";
+        }
+
+        /*
+         * For all of the following conversions, OSMD uses line numbers 1-5 starting from
+         * the bottom, while VexFlow uses 0-4 starting from the top.
+         */
         switch (clef.ClefType) {
+
+            // G Clef
             case ClefEnum.G:
-                type = "treble";
+                switch (clef.Line) {
+                    case 1:
+                        type = "french"; // VexFlow line 4
+                        break;
+                    case 2:
+                        type = "treble"; // VexFlow line 3
+                        break;
+                    default:
+                        type = "treble";
+                        Logging.error(`Clef ${ClefEnum[clef.ClefType]} on line ${clef.Line} not supported by VexFlow. Using default value "${type}".`);
+                }
                 break;
+
+            // F Clef
             case ClefEnum.F:
-                type = "bass";
+                switch (clef.Line) {
+                  case 4:
+                      type = "bass"; // VexFlow line 1
+                      break;
+                  case 3:
+                      type = "baritone-f"; // VexFlow line 2
+                      break;
+                  case 5:
+                      type = "subbass"; // VexFlow line 0
+                      break;
+                  default:
+                      type = "bass";
+                      Logging.error(`Clef ${ClefEnum[clef.ClefType]} on line ${clef.Line} not supported by VexFlow. Using default value "${type}".`);
+                }
                 break;
+
+            // C Clef
             case ClefEnum.C:
-                type = "alto";
+                switch (clef.Line) {
+                  case 3:
+                      type = "alto"; // VexFlow line 2
+                      break;
+                  case 4:
+                      type = "tenor"; // VexFlow line 1
+                      break;
+                  case 1:
+                      type = "soprano"; // VexFlow line 4
+                      break;
+                  case 2:
+                      type = "mezzo-soprano"; // VexFlow line 3
+                      break;
+                  default:
+                      type = "alto";
+                      Logging.error(`Clef ${ClefEnum[clef.ClefType]} on line ${clef.Line} not supported by VexFlow. Using default value "${type}".`);
+                }
                 break;
+
+            // Percussion Clef
             case ClefEnum.percussion:
                 type = "percussion";
                 break;
+
+            // TAB Clef
             case ClefEnum.TAB:
                 type = "tab";
                 break;
@@ -198,7 +371,7 @@ export class VexFlowConverter {
                 break;
             default:
         }
-        return {type, annotation};
+        return { type, size, annotation };
     }
 
     /**
@@ -253,21 +426,23 @@ export class VexFlowConverter {
      * @param lineType
      * @returns {any}
      */
-    public static line(lineType: SystemLinesEnum): any {
-        // TODO Not all line types are correctly mapped!
+    public static line(lineType: SystemLinesEnum, linePosition: SystemLinePosition): any {
         switch (lineType) {
             case SystemLinesEnum.SingleThin:
-                return Vex.Flow.StaveConnector.type.SINGLE;
+                if (linePosition === SystemLinePosition.MeasureBegin) {
+                    return Vex.Flow.StaveConnector.type.SINGLE;
+                }
+                return Vex.Flow.StaveConnector.type.SINGLE_RIGHT;
             case SystemLinesEnum.DoubleThin:
                 return Vex.Flow.StaveConnector.type.DOUBLE;
             case SystemLinesEnum.ThinBold:
-                return Vex.Flow.StaveConnector.type.SINGLE;
+                return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_RIGHT;
             case SystemLinesEnum.BoldThinDots:
-                return Vex.Flow.StaveConnector.type.DOUBLE;
+                return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_LEFT;
             case SystemLinesEnum.DotsThinBold:
-                return Vex.Flow.StaveConnector.type.DOUBLE;
+                return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_RIGHT;
             case SystemLinesEnum.DotsBoldBoldDots:
-                return Vex.Flow.StaveConnector.type.DOUBLE;
+                return Vex.Flow.StaveConnector.type.BOLD_DOUBLE_RIGHT;
             case SystemLinesEnum.None:
                 return Vex.Flow.StaveConnector.type.NONE;
             default:
@@ -284,7 +459,7 @@ export class VexFlowConverter {
     public static font(fontSize: number, fontStyle: FontStyles = FontStyles.Regular, font: Fonts = Fonts.TimesNewRoman): string {
         let style: string = "normal";
         let weight: string = "normal";
-        let family: string = "'Times New Roman'";
+        const family: string = "'Times New Roman'";
 
         switch (fontStyle) {
             case FontStyles.Bold:
@@ -316,12 +491,55 @@ export class VexFlowConverter {
     }
 
     /**
+     * Converts the style into a string that VexFlow RenderContext can understand
+     * as the weight of the font
+     */
+    public static fontStyle(style: FontStyles): string {
+        switch (style) {
+            case FontStyles.Bold:
+                return "bold";
+            case FontStyles.Italic:
+                return "italic";
+            case FontStyles.BoldItalic:
+                return "italic bold";
+            default:
+                return "normal";
+        }
+    }
+
+    /**
      * Convert OutlineAndFillStyle to CSS properties
      * @param styleId
      * @returns {string}
      */
     public static style(styleId: OutlineAndFillStyleEnum): string {
-        // TODO To be implemented
-        return "lightgreen";
+        const ret: string = OUTLINE_AND_FILL_STYLE_DICT.getValue(styleId);
+        return ret;
     }
 }
+
+export enum VexFlowRepetitionType {
+    NONE = 1,         // no coda or segno
+    CODA_LEFT = 2,    // coda at beginning of stave
+    CODA_RIGHT = 3,   // coda at end of stave
+    SEGNO_LEFT = 4,   // segno at beginning of stave
+    SEGNO_RIGHT = 5,  // segno at end of stave
+    DC = 6,           // D.C. at end of stave
+    DC_AL_CODA = 7,   // D.C. al coda at end of stave
+    DC_AL_FINE = 8,   // D.C. al Fine end of stave
+    DS = 9,           // D.S. at end of stave
+    DS_AL_CODA = 10,  // D.S. al coda at end of stave
+    DS_AL_FINE = 11,  // D.S. al Fine at end of stave
+    FINE = 12,        // Fine at end of stave
+}
+
+export enum VexFlowBarlineType {
+    SINGLE = 1,
+    DOUBLE = 2,
+    END = 3,
+    REPEAT_BEGIN = 4,
+    REPEAT_END = 5,
+    REPEAT_BOTH = 6,
+    NONE = 7,
+}
+

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

@@ -36,7 +36,7 @@ export class VexFlowGraphicalNote extends GraphicalNote {
      */
     public setPitch(pitch: Pitch): void {
         if (this.vfnote) {
-            let acc: string = VexFlowConverter.accidental(pitch.Accidental);
+            const acc: string = VexFlowConverter.accidental(pitch.Accidental);
             if (acc) {
                 alert(acc);
                 this.vfnote[0].addAccidental(this.vfnote[1], new Vex.Flow.Accidental(acc));

+ 27 - 13
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts

@@ -16,9 +16,12 @@ import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
 import {OctaveEnum} from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import {GraphicalNote} from "../GraphicalNote";
 import {Pitch} from "../../../Common/DataObjects/Pitch";
-import {TechnicalInstruction} from "../../VoiceData/Instructions/TechnicalInstruction";
 import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
 import {Fraction} from "../../../Common/DataObjects/Fraction";
+import {GraphicalChordSymbolContainer} from "../GraphicalChordSymbolContainer";
+import {GraphicalLabel} from "../GraphicalLabel";
+import {EngravingRules} from "../EngravingRules";
+import { TechnicalInstruction } from "../../VoiceData/Instructions/TechnicalInstruction";
 
 export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
     /**
@@ -95,11 +98,11 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
     public createNote(note: Note, graphicalStaffEntry: GraphicalStaffEntry,
                       activeClef: ClefInstruction, octaveShift: OctaveEnum = OctaveEnum.NONE,  graphicalNoteLength: Fraction = undefined): GraphicalNote {
         // Creates the note:
-        let graphicalNote: GraphicalNote = new VexFlowGraphicalNote(note, graphicalStaffEntry, activeClef, octaveShift, graphicalNoteLength);
+        const graphicalNote: GraphicalNote = new VexFlowGraphicalNote(note, graphicalStaffEntry, activeClef, octaveShift, graphicalNoteLength);
         if (note.ParentVoiceEntry !== undefined) {
             // Adds the note to the right (graphical) voice (mynotes)
-            let voiceID: number = note.ParentVoiceEntry.ParentVoice.VoiceId;
-            let mynotes: { [id: number]: GraphicalNote[]; } = (graphicalStaffEntry as VexFlowStaffEntry).graphicalNotes;
+            const voiceID: number = note.ParentVoiceEntry.ParentVoice.VoiceId;
+            const mynotes: { [id: number]: GraphicalNote[]; } = (graphicalStaffEntry as VexFlowStaffEntry).graphicalNotes;
             if (!(voiceID in mynotes)) {
                 mynotes[voiceID] = [];
             }
@@ -132,7 +135,7 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
      */
     public addGraphicalAccidental(graphicalNote: GraphicalNote, pitch: Pitch, grace: boolean, graceScalingFactor: number): void {
         // ToDo: set accidental here from pitch.Accidental
-        let note: VexFlowGraphicalNote = (graphicalNote as VexFlowGraphicalNote);
+        const note: VexFlowGraphicalNote = (graphicalNote as VexFlowGraphicalNote);
         note.setPitch(pitch);
     }
 
@@ -147,14 +150,7 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
         return;
     }
 
-    /**
-     * Adds a technical instruction at the given staff entry.
-     * @param technicalInstruction
-     * @param graphicalStaffEntry
-     */
-    public createGraphicalTechnicalInstruction(technicalInstruction: TechnicalInstruction, graphicalStaffEntry: GraphicalStaffEntry): void {
-        return;
-    }
+
 
     /**
      * Adds a clef change within a measure before the given staff entry.
@@ -172,6 +168,24 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
      * @param transposeHalftones
      */
     public createChordSymbol(sourceStaffEntry: SourceStaffEntry, graphicalStaffEntry: GraphicalStaffEntry, transposeHalftones: number): void {
+      const graphicalChordSymbolContainer: GraphicalChordSymbolContainer =
+        new GraphicalChordSymbolContainer(sourceStaffEntry.ChordContainer,
+                                          graphicalStaffEntry.PositionAndShape,
+                                          EngravingRules.Rules.ChordSymbolTextHeight,
+                                          transposeHalftones);
+      const graphicalLabel: GraphicalLabel = graphicalChordSymbolContainer.GetGraphicalLabel;
+      graphicalLabel.setLabelPositionAndShapeBorders();
+      graphicalChordSymbolContainer.PositionAndShape.calculateBoundingBox();
+      graphicalStaffEntry.graphicalChordContainer = graphicalChordSymbolContainer;
+    }
+
+    /**
+     * Adds a technical instruction at the given staff entry.
+     * @param technicalInstruction
+     * @param graphicalStaffEntry
+     */
+    public createGraphicalTechnicalInstruction(technicalInstruction: TechnicalInstruction, graphicalStaffEntry: GraphicalStaffEntry): void {
         return;
     }
+
 }

+ 14 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowInstrumentBrace.ts

@@ -0,0 +1,14 @@
+import Vex = require("vexflow");
+import { VexFlowInstrumentBracket } from "./VexFlowInstrumentBracket";
+import { VexFlowStaffLine } from "./VexFlowStaffLine";
+
+/**
+ * Class that defines a instrument bracket at the beginning of a line.
+ */
+export class VexFlowInstrumentBrace extends VexFlowInstrumentBracket {
+
+    constructor(firstVexFlowStaffLine: VexFlowStaffLine, lastVexFlowStaffLine: VexFlowStaffLine, depth: number = 0) {
+        super(firstVexFlowStaffLine, lastVexFlowStaffLine, depth);
+        this.vexflowConnector.setType(Vex.Flow.StaveConnector.type.BRACE);
+    }
+}

+ 54 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowInstrumentBracket.ts

@@ -0,0 +1,54 @@
+import Vex = require("vexflow");
+import {GraphicalObject} from "../GraphicalObject";
+import {VexFlowStaffLine} from "./VexFlowStaffLine";
+import { BoundingBox } from "../BoundingBox";
+import { VexFlowMeasure } from "./VexFlowMeasure";
+import { unitInPixels } from "./VexFlowMusicSheetDrawer";
+
+/**
+ * Class that defines a instrument bracket at the beginning of a line.
+ */
+export class VexFlowInstrumentBracket extends GraphicalObject {
+
+    protected vexflowConnector: Vex.Flow.StaveConnector;
+
+    constructor(firstVexFlowStaffLine: VexFlowStaffLine, lastVexFlowStaffLine: VexFlowStaffLine, depth: number = 0) {
+        super();
+        this.PositionAndShape = new BoundingBox(this, firstVexFlowStaffLine.ParentMusicSystem.PositionAndShape);
+        const firstVexMeasure: VexFlowMeasure = firstVexFlowStaffLine.Measures[0] as VexFlowMeasure;
+        const lastVexMeasure: VexFlowMeasure = lastVexFlowStaffLine.Measures[0] as VexFlowMeasure;
+        this.addConnector(firstVexMeasure.getVFStave(), lastVexMeasure.getVFStave(), Vex.Flow.StaveConnector.type.BRACKET, depth);
+    }
+
+    /**
+     * Render the bracket using the given backend
+     * @param ctx Render Vexflow context
+     */
+    public draw(ctx: Vex.Flow.RenderContext): void {
+        // Draw vexflow brace. This sets the positions inside the connector.
+        this.vexflowConnector.setContext(ctx).draw();
+        // Set bounding box
+        const con: Vex.Flow.StaveConnector = this.vexflowConnector;
+        // First line in first stave
+        const topY: number = con.top_stave.getYForLine(0);
+        // Last line in last stave
+        const botY: number = con.bottom_stave.getYForLine(con.bottom_stave.getNumLines() - 1) + con.thickness;
+        // Set bounding box position and size in OSMD units
+        this.PositionAndShape.AbsolutePosition.x = (con.top_stave.getX() - 2 + con.x_shift) / unitInPixels;
+        this.PositionAndShape.AbsolutePosition.y = topY / unitInPixels;
+        this.PositionAndShape.Size.height = (botY - topY) / unitInPixels;
+        this.PositionAndShape.Size.width = 12 / unitInPixels; // width is always 12 -> vexflow implementation
+    }
+    /**
+     * Adds a connector between two staves
+     *
+     * @param {Stave} stave1: First stave
+     * @param {Stave} stave2: Second stave
+     * @param {Flow.StaveConnector.type} type: Type of connector
+     */
+    private addConnector(stave1: Vex.Flow.Stave, stave2: Vex.Flow.Stave, type: any, depth: number): void {
+        this.vexflowConnector = new Vex.Flow.StaveConnector(stave1, stave2)
+        .setType(type)
+        .setXShift(depth * -5);
+    }
+}

+ 218 - 100
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -7,7 +7,7 @@ import {SystemLinesEnum} from "../SystemLinesEnum";
 import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
 import {KeyInstruction} from "../../VoiceData/Instructions/KeyInstruction";
 import {RhythmInstruction} from "../../VoiceData/Instructions/RhythmInstruction";
-import {VexFlowConverter} from "./VexFlowConverter";
+import {VexFlowConverter, VexFlowRepetitionType, VexFlowBarlineType} from "./VexFlowConverter";
 import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
 import {Beam} from "../../VoiceData/Beam";
 import {GraphicalNote} from "../GraphicalNote";
@@ -17,6 +17,9 @@ import StaveNote = Vex.Flow.StaveNote;
 import {Logging} from "../../../Common/Logging";
 import {unitInPixels} from "./VexFlowMusicSheetDrawer";
 import {Tuplet} from "../../VoiceData/Tuplet";
+import { RepetitionInstructionEnum } from "../../VoiceData/Instructions/RepetitionInstruction";
+import { SystemLinePosition } from "../SystemLinePosition";
+import { StemDirectionType } from "../../VoiceData/VoiceEntry";
 
 export class VexFlowMeasure extends StaffMeasure {
     constructor(staff: Staff, staffLine: StaffLine = undefined, sourceMeasure: SourceMeasure = undefined) {
@@ -33,8 +36,10 @@ export class VexFlowMeasure extends StaffMeasure {
     public formatVoices: (width: number) => void;
     // The VexFlow Ties in the measure
     public vfTies: Vex.Flow.StaveTie[] = [];
+    // The repetition instructions given as words or symbols (coda, dal segno..)
+    public vfRepetitionWords: Vex.Flow.Repetition[] = [];
 
-    // The VexFlow Stave (one measure in one line)
+    // The VexFlow Stave (= one measure in a staffline)
     private stave: Vex.Flow.Stave;
     // VexFlow StaveConnectors (vertical lines)
     private connectors: Vex.Flow.StaveConnector[] = [];
@@ -76,18 +81,25 @@ export class VexFlowMeasure extends StaffMeasure {
     }
 
     /**
-     * returns the x-width of a given measure line.
+     * returns the x-width (in units) of a given measure line {SystemLinesEnum}.
      * @param line
-     * @returns {SystemLinesEnum} the x-width
+     * @returns the x-width in osmd units
      */
     public getLineWidth(line: SystemLinesEnum): number {
-        // FIXME: See values in VexFlow's stavebarline.js
-        let vfline: any = VexFlowConverter.line(line);
-        switch (vfline) {
-            case Vex.Flow.StaveConnector.type.SINGLE:
-                return 1.0 / unitInPixels;
-            case Vex.Flow.StaveConnector.type.DOUBLE:
-                return 3.0 / unitInPixels;
+        switch (line) {
+            // return 0 for the normal lines, as the line width will be considered at the updateInstructionWidth() method using the stavemodifiers.
+            // case SystemLinesEnum.SingleThin:
+            //     return 5.0 / unitInPixels;
+            // case SystemLinesEnum.DoubleThin:
+            //     return 5.0 / unitInPixels;
+            //     case SystemLinesEnum.ThinBold:
+            //     return 5.0 / unitInPixels;
+            // but just add a little extra space for repetitions (cosmetics):
+            case SystemLinesEnum.BoldThinDots:
+            case SystemLinesEnum.DotsThinBold:
+                return 10.0 / unitInPixels;
+            case SystemLinesEnum.DotsBoldBoldDots:
+                return 10.0 / unitInPixels;
             default:
                 return 0;
         }
@@ -100,8 +112,8 @@ export class VexFlowMeasure extends StaffMeasure {
      */
     public addClefAtBegin(clef: ClefInstruction): void {
         this.octaveOffset = clef.OctaveOffset;
-        let vfclef: {type: string, annotation: string} = VexFlowConverter.Clef(clef);
-        this.stave.addClef(vfclef.type, undefined, vfclef.annotation, Vex.Flow.Modifier.Position.BEGIN);
+        const vfclef: { type: string, size: string, annotation: string } = VexFlowConverter.Clef(clef, "default");
+        this.stave.addClef(vfclef.type, vfclef.size, vfclef.annotation, Vex.Flow.Modifier.Position.BEGIN);
         this.updateInstructionWidth();
     }
 
@@ -127,7 +139,7 @@ export class VexFlowMeasure extends StaffMeasure {
      * @param rhythm
      */
     public addRhythmAtBegin(rhythm: RhythmInstruction): void {
-        let timeSig: Vex.Flow.TimeSignature = VexFlowConverter.TimeSignature(rhythm);
+        const timeSig: Vex.Flow.TimeSignature = VexFlowConverter.TimeSignature(rhythm);
         this.stave.addModifier(
             timeSig,
             Vex.Flow.Modifier.Position.BEGIN
@@ -141,11 +153,108 @@ export class VexFlowMeasure extends StaffMeasure {
      * @param clef
      */
     public addClefAtEnd(clef: ClefInstruction): void {
-        let vfclef: {type: string, annotation: string} = VexFlowConverter.Clef(clef);
-        this.stave.setEndClef(vfclef.type, "small", vfclef.annotation);
+        const vfclef: { type: string, size: string, annotation: string } = VexFlowConverter.Clef(clef, "small");
+        this.stave.setEndClef(vfclef.type, vfclef.size, vfclef.annotation);
         this.updateInstructionWidth();
     }
 
+    public addMeasureLine(lineType: SystemLinesEnum, linePosition: SystemLinePosition): void {
+        switch (linePosition) {
+            case SystemLinePosition.MeasureBegin:
+                switch (lineType) {
+                    case SystemLinesEnum.BoldThinDots:
+                        this.stave.setBegBarType(VexFlowBarlineType.REPEAT_BEGIN);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case SystemLinePosition.MeasureEnd:
+                switch (lineType) {
+                    case SystemLinesEnum.DotsBoldBoldDots:
+                        this.stave.setEndBarType(VexFlowBarlineType.REPEAT_BOTH);
+                        break;
+                    case SystemLinesEnum.DotsThinBold:
+                        this.stave.setEndBarType(VexFlowBarlineType.REPEAT_END);
+                        break;
+                    case SystemLinesEnum.DoubleThin:
+                        this.stave.setEndBarType(VexFlowBarlineType.DOUBLE);
+                        break;
+                    case SystemLinesEnum.ThinBold:
+                        this.stave.setEndBarType(VexFlowBarlineType.END);
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Adds a measure number to the top left corner of the measure
+     * This method is not used currently in favor of the calculateMeasureNumberPlacement
+     * method in the MusicSheetCalculator.ts
+     */
+    public addMeasureNumber(): void {
+        const text: string = this.MeasureNumber.toString();
+        const position: number = Vex.Flow.StaveModifier.Position.ABOVE;
+        const options: any = {
+            justification: 1,
+            shift_x: 0,
+            shift_y: 0,
+          };
+
+        this.stave.setText(text, position, options);
+    }
+
+    public addWordRepetition(repetitionInstruction: RepetitionInstructionEnum): void {
+        let instruction: VexFlowRepetitionType = undefined;
+        let position: any = Vex.Flow.Modifier.Position.END;
+        switch (repetitionInstruction) {
+          case RepetitionInstructionEnum.Segno:
+            // create Segno Symbol:
+            instruction = VexFlowRepetitionType.SEGNO_LEFT;
+            position = Vex.Flow.Modifier.Position.BEGIN;
+            break;
+          case RepetitionInstructionEnum.Coda:
+            // create Coda Symbol:
+            instruction = VexFlowRepetitionType.CODA_LEFT;
+            position = Vex.Flow.Modifier.Position.BEGIN;
+            break;
+          case RepetitionInstructionEnum.DaCapo:
+            instruction = VexFlowRepetitionType.DC;
+            break;
+          case RepetitionInstructionEnum.DalSegno:
+            instruction = VexFlowRepetitionType.DS;
+            break;
+          case RepetitionInstructionEnum.Fine:
+            instruction = VexFlowRepetitionType.FINE;
+            break;
+          case RepetitionInstructionEnum.ToCoda:
+            //instruction = "To Coda";
+            break;
+          case RepetitionInstructionEnum.DaCapoAlFine:
+            instruction = VexFlowRepetitionType.DC_AL_FINE;
+            break;
+          case RepetitionInstructionEnum.DaCapoAlCoda:
+            instruction = VexFlowRepetitionType.DC_AL_CODA;
+            break;
+          case RepetitionInstructionEnum.DalSegnoAlFine:
+            instruction = VexFlowRepetitionType.DS_AL_FINE;
+            break;
+          case RepetitionInstructionEnum.DalSegnoAlCoda:
+            instruction = VexFlowRepetitionType.DS_AL_CODA;
+            break;
+          default:
+            break;
+        }
+        if (instruction !== undefined) {
+            this.stave.addModifier(new Vex.Flow.Repetition(instruction, 0, 0), position);
+        }
+    }
+
     /**
      * Sets the overall x-width of the measure.
      * @param width
@@ -181,54 +290,55 @@ export class VexFlowMeasure extends StaffMeasure {
      * Draw this measure on a VexFlow CanvasContext
      * @param ctx
      */
-    public draw(ctx: Vex.Flow.CanvasContext): void {
-        // 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((this.PositionAndShape.BorderRight - this.beginInstructionsWidth - this.endInstructionsWidth) * unitInPixels);
-        }
+    public draw(ctx: Vex.Flow.RenderContext): void {
 
-        // Force the width of the Begin Instructions
-        this.stave.setNoteStartX(this.stave.getX() + unitInPixels * this.beginInstructionsWidth);
         // Draw stave lines
         this.stave.setContext(ctx).draw();
         // Draw all voices
-        for (let voiceID in this.vfVoices) {
+        for (const voiceID in this.vfVoices) {
             if (this.vfVoices.hasOwnProperty(voiceID)) {
                 this.vfVoices[voiceID].draw(ctx, this.stave);
             }
         }
         // Draw beams
-        for (let voiceID in this.vfbeams) {
+        for (const voiceID in this.vfbeams) {
             if (this.vfbeams.hasOwnProperty(voiceID)) {
-                for (let beam of this.vfbeams[voiceID]) {
+                for (const beam of this.vfbeams[voiceID]) {
                     beam.setContext(ctx).draw();
                 }
             }
         }
 
         // Draw tuplets
-        for (let voiceID in this.vftuplets) {
+        for (const voiceID in this.vftuplets) {
             if (this.vftuplets.hasOwnProperty(voiceID)) {
-                for (let tuplet of this.vftuplets[voiceID]) {
+                for (const tuplet of this.vftuplets[voiceID]) {
                     tuplet.setContext(ctx).draw();
                 }
             }
         }
 
         // Draw ties
-        for (let tie of this.vfTies) {
+        for (const tie of this.vfTies) {
             tie.setContext(ctx).draw();
         }
 
         // Draw vertical lines
-        for (let connector of this.connectors) {
+        for (const connector of this.connectors) {
             connector.setContext(ctx).draw();
         }
+    }
 
-        // now we can finally set the vexflow x positions back into the osmd object model:
-        this.setStaffEntriesXPositions();
+    public format(): void {
+        // 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((this.PositionAndShape.BorderRight - this.beginInstructionsWidth - this.endInstructionsWidth) * unitInPixels);
+        }
+
+        // Force the width of the Begin Instructions
+        this.stave.setNoteStartX(this.stave.getX() + unitInPixels * this.beginInstructionsWidth);
     }
 
     /**
@@ -237,13 +347,13 @@ export class VexFlowMeasure extends StaffMeasure {
      * @param beam
      */
     public handleBeam(graphicalNote: GraphicalNote, beam: Beam): void {
-        let voiceID: number = graphicalNote.sourceNote.ParentVoiceEntry.ParentVoice.VoiceId;
+        const voiceID: number = graphicalNote.sourceNote.ParentVoiceEntry.ParentVoice.VoiceId;
         let beams: [Beam, VexFlowStaffEntry[]][] = this.beams[voiceID];
         if (beams === undefined) {
             beams = this.beams[voiceID] = [];
         }
         let data: [Beam, VexFlowStaffEntry[]];
-        for (let mybeam of beams) {
+        for (const mybeam of beams) {
             if (mybeam[0] === beam) {
                 data = mybeam;
             }
@@ -252,20 +362,21 @@ export class VexFlowMeasure extends StaffMeasure {
             data = [beam, []];
             beams.push(data);
         }
-        let parent: VexFlowStaffEntry = graphicalNote.parentStaffEntry as VexFlowStaffEntry;
+        const parent: VexFlowStaffEntry = graphicalNote.parentStaffEntry as VexFlowStaffEntry;
         if (data[1].indexOf(parent) < 0) {
             data[1].push(parent);
         }
     }
 
     public handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet): void {
-        let voiceID: number = graphicalNote.sourceNote.ParentVoiceEntry.ParentVoice.VoiceId;
+        const voiceID: number = graphicalNote.sourceNote.ParentVoiceEntry.ParentVoice.VoiceId;
+        tuplet = graphicalNote.sourceNote.NoteTuplet;
         let tuplets: [Tuplet, VexFlowStaffEntry[]][] = this.tuplets[voiceID];
         if (tuplets === undefined) {
             tuplets = this.tuplets[voiceID] = [];
         }
         let currentTupletBuilder: [Tuplet, VexFlowStaffEntry[]];
-        for (let t of tuplets) {
+        for (const t of tuplets) {
             if (t[0] === tuplet) {
                 currentTupletBuilder = t;
             }
@@ -274,7 +385,7 @@ export class VexFlowMeasure extends StaffMeasure {
             currentTupletBuilder = [tuplet, []];
             tuplets.push(currentTupletBuilder);
         }
-        let parent: VexFlowStaffEntry = graphicalNote.parentStaffEntry as VexFlowStaffEntry;
+        const parent: VexFlowStaffEntry = graphicalNote.parentStaffEntry as VexFlowStaffEntry;
         if (currentTupletBuilder[1].indexOf(parent) < 0) {
             currentTupletBuilder[1].push(parent);
         }
@@ -288,25 +399,32 @@ export class VexFlowMeasure extends StaffMeasure {
         // 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) {
+        for (const voiceID in this.beams) {
             if (this.beams.hasOwnProperty(voiceID)) {
                 let vfbeams: Vex.Flow.Beam[] = this.vfbeams[voiceID];
                 if (vfbeams === undefined) {
                     vfbeams = this.vfbeams[voiceID] = [];
                 }
-                for (let beam of this.beams[voiceID]) {
-                    let notes: Vex.Flow.StaveNote[] = [];
-                    for (let entry of beam[1]) {
-                        notes.push((<VexFlowStaffEntry>entry).vfNotes[voiceID]);
+                for (const beam of this.beams[voiceID]) {
+                    const notes: Vex.Flow.StaveNote[] = [];
+                    const staffEntries: VexFlowStaffEntry[] = beam[1];
+                    const autoStemBeam: boolean = staffEntries[0].graphicalNotes[voiceID][0].sourceNote.
+                                                    ParentVoiceEntry.StemDirection === StemDirectionType.Undefined;
+                    for (const entry of staffEntries) {
+                        const note: Vex.Flow.StaveNote = (<VexFlowStaffEntry>entry).vfNotes[voiceID];
+                        if (note !== undefined) {
+                          notes.push(note);
+                        }
                     }
                     if (notes.length > 1) {
-                        vfbeams.push(new Vex.Flow.Beam(notes, true));
+                        const vfBeam: Vex.Flow.Beam = new Vex.Flow.Beam(notes, autoStemBeam);
+                        vfbeams.push(vfBeam);
                         // just a test for coloring the notes:
                         // for (let note of notes) {
                         //     (<Vex.Flow.StaveNote> note).setStyle({fillStyle: "green", strokeStyle: "green"});
                         // }
                     } else {
-                        Logging.log("Warning! Beam with no notes! Trying to ignore, but this is a serious problem.");
+                        Logging.log("Warning! Beam with no notes!");
                     }
                 }
             }
@@ -319,21 +437,27 @@ export class VexFlowMeasure extends StaffMeasure {
     public finalizeTuplets(): void {
         // The following line resets the created Vex.Flow Tuplets and
         // created them brand new. Is this needed? And more importantly,
-        // should the old tuplets be removed manually by the notes?
+        // should the old tuplets be removed manually from the notes?
         this.vftuplets = {};
-        for (let voiceID in this.tuplets) {
+        for (const voiceID in this.tuplets) {
             if (this.tuplets.hasOwnProperty(voiceID)) {
                 let vftuplets: Vex.Flow.Tuplet[] = this.vftuplets[voiceID];
                 if (vftuplets === undefined) {
                     vftuplets = this.vftuplets[voiceID] = [];
                 }
-                for (let tuplet of this.tuplets[voiceID]) {
-                    let notes: Vex.Flow.StaveNote[] = [];
-                    for (let entry of tuplet[1]) {
-                        notes.push((<VexFlowStaffEntry>entry).vfNotes[voiceID]);
+                for (const tupletBuilder of this.tuplets[voiceID]) {
+                    const tupletStaveNotes: Vex.Flow.StaveNote[] = [];
+                    const tupletStaffEntries: VexFlowStaffEntry[] = tupletBuilder[1];
+                    for (const tupletStaffEntry of tupletStaffEntries) {
+                      tupletStaveNotes.push((tupletStaffEntry).vfNotes[voiceID]);
                     }
-                    if (notes.length > 1) {
-                        vftuplets.push(new Vex.Flow.Tuplet(notes));
+                    if (tupletStaveNotes.length > 1) {
+                      const notesOccupied: number = 2;
+                      vftuplets.push(new Vex.Flow.Tuplet( tupletStaveNotes,
+                                                          {
+                                                            notes_occupied: notesOccupied,
+                                                            num_notes: tupletStaveNotes.length //, location: -1, ratioed: true
+                                                          }));
                     } else {
                         Logging.log("Warning! Tuplet with no notes! Trying to ignore, but this is a serious problem.");
                     }
@@ -348,13 +472,13 @@ export class VexFlowMeasure extends StaffMeasure {
 
     public staffMeasureCreatedCalculations(): void {
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
-            let graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
+            const graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
 
             // create vex flow Notes:
-            let gnotes: { [voiceID: number]: GraphicalNote[]; } = graphicalStaffEntry.graphicalNotes;
-            for (let voiceID in gnotes) {
+            const gnotes: { [voiceID: number]: GraphicalNote[]; } = graphicalStaffEntry.graphicalNotes;
+            for (const voiceID in gnotes) {
                 if (gnotes.hasOwnProperty(voiceID)) {
-                    let vfnote: StaveNote = VexFlowConverter.StaveNote(gnotes[voiceID]);
+                    const vfnote: StaveNote = VexFlowConverter.StaveNote(gnotes[voiceID]);
                     (graphicalStaffEntry as VexFlowStaffEntry).vfNotes[voiceID] = vfnote;
                 }
             }
@@ -364,11 +488,11 @@ export class VexFlowMeasure extends StaffMeasure {
         this.finalizeTuplets();
 
         for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
-            let graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
-            let gnotes: { [voiceID: number]: GraphicalNote[]; } = graphicalStaffEntry.graphicalNotes;
+            const graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
+            const gnotes: { [voiceID: number]: GraphicalNote[]; } = graphicalStaffEntry.graphicalNotes;
             // create vex flow voices and add tickables to it:
-            let vfVoices: { [voiceID: number]: Vex.Flow.Voice; } = this.vfVoices;
-            for (let voiceID in gnotes) {
+            const vfVoices: { [voiceID: number]: Vex.Flow.Voice; } = this.vfVoices;
+            for (const voiceID in gnotes) {
                 if (gnotes.hasOwnProperty(voiceID)) {
                     if (!(voiceID in vfVoices)) {
                         vfVoices[voiceID] = new Vex.Flow.Voice({
@@ -382,6 +506,22 @@ export class VexFlowMeasure extends StaffMeasure {
                 }
             }
         }
+        this.createArticulations();
+    }
+
+    private createArticulations(): void {
+        for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
+            const graphicalStaffEntry: VexFlowStaffEntry = (this.staffEntries[idx] as VexFlowStaffEntry);
+
+            // create vex flow Notes:
+            const gnotes: { [voiceID: number]: GraphicalNote[]; } = graphicalStaffEntry.graphicalNotes;
+            for (const voiceID in gnotes) {
+                if (gnotes.hasOwnProperty(voiceID)) {
+                    const vfnote: StaveNote = (graphicalStaffEntry as VexFlowStaffEntry).vfNotes[voiceID];
+                    VexFlowConverter.generateArticulations(vfnote, gnotes[voiceID][0].sourceNote.ParentVoiceEntry.Articulations);
+                }
+            }
+        }
     }
 
     /**
@@ -390,7 +530,7 @@ export class VexFlowMeasure extends StaffMeasure {
      * @param lineType
      */
     public lineTo(top: VexFlowMeasure, lineType: any): void {
-        let connector: StaveConnector = new Vex.Flow.StaveConnector(top.getVFStave(), this.stave);
+        const connector: StaveConnector = new Vex.Flow.StaveConnector(top.getVFStave(), this.stave);
         connector.setType(lineType);
         this.connectors.push(connector);
     }
@@ -403,47 +543,25 @@ 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) / UnitInPixels;
-    //}
-    //
-    //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) / UnitInPixels;
-    //
-    //}
-
     /**
      * After re-running the formatting on the VexFlow Stave, update the
      * space needed by Instructions (in VexFlow: StaveModifiers)
      */
     private updateInstructionWidth(): void {
-        this.beginInstructionsWidth = (this.stave.getNoteStartX() - this.stave.getX()) / unitInPixels;
-        this.endInstructionsWidth = (this.stave.getX() + this.stave.getWidth() - this.stave.getNoteEndX()) / unitInPixels;
-    }
-
-    /**
-     * sets the vexflow x positions back into the bounding boxes of the staff entries in the osmd object model.
-     * The positions are needed for cursor placement and mouse/tap interactions
-     */
-    private setStaffEntriesXPositions(): void {
-        for (let idx3: number = 0, len3: number = this.staffEntries.length; idx3 < len3; ++idx3) {
-            let gse: VexFlowStaffEntry = (<VexFlowStaffEntry> this.staffEntries[idx3]);
-            let measure: StaffMeasure = gse.parentMeasure;
-            let x: number =
-                gse.getX() -
-                measure.PositionAndShape.RelativePosition.x -
-                measure.ParentStaffLine.PositionAndShape.RelativePosition.x -
-                measure.parentMusicSystem.PositionAndShape.RelativePosition.x;
-            gse.PositionAndShape.RelativePosition.x = x;
+        let beginInstructionsWidth: number = 0;
+        let endInstructionsWidth: number = 0;
+        const modifiers: Vex.Flow.StaveModifier[] = this.stave.getModifiers();
+        for (const mod of modifiers) {
+            if (mod.getPosition() === Vex.Flow.StaveModifier.Position.BEGIN) {
+                beginInstructionsWidth += mod.getWidth() + mod.getPadding(undefined);
+            } else if (mod.getPosition() === Vex.Flow.StaveModifier.Position.END) {
+                endInstructionsWidth += mod.getWidth() + mod.getPadding(undefined);
+            }
         }
+
+        this.beginInstructionsWidth = beginInstructionsWidth / unitInPixels;
+        this.endInstructionsWidth = endInstructionsWidth / unitInPixels;
+        //this.beginInstructionsWidth =  (this.stave.getNoteStartX() - this.stave.getX()) / unitInPixels;
+        //this.endInstructionsWidth = (this.stave.getX() + this.stave.getWidth() - this.stave.getNoteEndX()) / unitInPixels;
     }
 }

+ 338 - 214
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -16,12 +16,10 @@ import {Beam} from "../../VoiceData/Beam";
 import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
 import {OctaveEnum} from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import {Fraction} from "../../../Common/DataObjects/Fraction";
-import {LyricsEntry} from "../../VoiceData/Lyrics/LyricsEntry";
 import {LyricWord} from "../../VoiceData/Lyrics/LyricsWord";
 import {OrnamentContainer} from "../../VoiceData/OrnamentContainer";
 import {ArticulationEnum} from "../../VoiceData/VoiceEntry";
 import {Tuplet} from "../../VoiceData/Tuplet";
-import Dictionary from "typescript-collections/dist/lib/Dictionary";
 import {VexFlowMeasure} from "./VexFlowMeasure";
 import {VexFlowTextMeasurer} from "./VexFlowTextMeasurer";
 
@@ -29,252 +27,378 @@ import Vex = require("vexflow");
 import {Logging} from "../../../Common/Logging";
 import {unitInPixels} from "./VexFlowMusicSheetDrawer";
 import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
+import {TechnicalInstruction} from "../../VoiceData/Instructions/TechnicalInstruction";
+import {GraphicalLyricEntry} from "../GraphicalLyricEntry";
+import {GraphicalLabel} from "../GraphicalLabel";
+import {LyricsEntry} from "../../VoiceData/Lyrics/LyricsEntry";
+import {GraphicalLyricWord} from "../GraphicalLyricWord";
+import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
 
 export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
-    constructor() {
-        super(new VexFlowGraphicalSymbolFactory());
-        let a: LyricsEntry = new LyricsEntry(undefined, undefined, undefined);
-        a = a;
-        MusicSheetCalculator.TextMeasurer = new VexFlowTextMeasurer();
+  constructor() {
+    super(new VexFlowGraphicalSymbolFactory());
+    MusicSheetCalculator.TextMeasurer = new VexFlowTextMeasurer();
+  }
+
+  protected clearRecreatedObjects(): void {
+    super.clearRecreatedObjects();
+    for (const staffMeasures of this.graphicalMusicSheet.MeasureList) {
+      for (const staffMeasure of staffMeasures) {
+        (<VexFlowMeasure>staffMeasure).clean();
+      }
     }
-
-    protected clearRecreatedObjects(): void {
-        super.clearRecreatedObjects();
-        for (let staffMeasures of this.graphicalMusicSheet.MeasureList) {
-            for (let staffMeasure of staffMeasures) {
-                (<VexFlowMeasure>staffMeasure).clean();
+  }
+
+    protected formatMeasures(): void {
+        for (const staffMeasures of this.graphicalMusicSheet.MeasureList) {
+            for (const staffMeasure of staffMeasures) {
+                (<VexFlowMeasure>staffMeasure).format();
+                for (const staffEntry of staffMeasure.staffEntries) {
+                    (<VexFlowStaffEntry>staffEntry).calculateXPosition();
+                }
             }
         }
     }
 
-    //protected clearSystemsAndMeasures(): void {
-    //    for (let measure of measures) {
-    //
-    //    }
-    //}
-
-    /**
-     * Calculates the x layout of the staff entries within the staff measures belonging to one source measure.
-     * All staff entries are x-aligned throughout all vertically aligned staff measures.
-     * This method is called within calculateXLayout.
-     * The staff entries are aligned with minimum needed x distances.
-     * The MinimumStaffEntriesWidth of every measure will be set - needed for system building.
-     * @param measures
-     * @returns the minimum required x width of the source measure (=list of staff measures)
-     */
-    protected calculateMeasureXLayout(measures: StaffMeasure[]): number {
-        // Finalize beams
-        /*for (let measure of measures) {
-            (measure as VexFlowMeasure).finalizeBeams();
-            (measure as VexFlowMeasure).finalizeTuplets();
-        }*/
-        // Format the voices
-        let allVoices: Vex.Flow.Voice[] = [];
-        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[] = [];
-            for (let voiceID in mvoices) {
-                if (mvoices.hasOwnProperty(voiceID)) {
-                    voices.push(mvoices[voiceID]);
-                    allVoices.push(mvoices[voiceID]);
+  //protected clearSystemsAndMeasures(): void {
+  //    for (let measure of measures) {
+  //
+  //    }
+  //}
+
+  /**
+   * Calculates the x layout of the staff entries within the staff measures belonging to one source measure.
+   * All staff entries are x-aligned throughout all vertically aligned staff measures.
+   * This method is called within calculateXLayout.
+   * The staff entries are aligned with minimum needed x distances.
+   * The MinimumStaffEntriesWidth of every measure will be set - needed for system building.
+   * @param measures
+   * @returns the minimum required x width of the source measure (=list of staff measures)
+   */
+  protected calculateMeasureXLayout(measures: StaffMeasure[]): number {
+    // Finalize beams
+    /*for (let measure of measures) {
+     (measure as VexFlowMeasure).finalizeBeams();
+     (measure as VexFlowMeasure).finalizeTuplets();
+     }*/
+    // Format the voices
+    const allVoices: Vex.Flow.Voice[] = [];
+    const formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter({align_rests: true,
+    });
+
+    for (const measure of measures) {
+        const mvoices:  { [voiceID: number]: Vex.Flow.Voice; } = (measure as VexFlowMeasure).vfVoices;
+        const voices: Vex.Flow.Voice[] = [];
+        for (const voiceID in mvoices) {
+            if (mvoices.hasOwnProperty(voiceID)) {
+                voices.push(mvoices[voiceID]);
+                allVoices.push(mvoices[voiceID]);
 
-                }
             }
-            if (voices.length === 0) {
-                Logging.warn("Found a measure with no voices... Continuing anyway.", mvoices);
-                continue;
-            }
-            formatter.joinVoices(voices);
         }
+        if (voices.length === 0) {
+            Logging.warn("Found a measure with no voices... Continuing anyway.", mvoices);
+            continue;
+        }
+        formatter.joinVoices(voices);
+    }
 
-        let width: number = 200;
-        if (allVoices.length > 0) {
-            let firstMeasure: VexFlowMeasure = measures[0] as VexFlowMeasure;
-            // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
-            // FIXME: a more relaxed formatting of voices
-            width = formatter.preCalculateMinTotalWidth(allVoices) / unitInPixels + 5.0;
-            for (let measure of measures) {
-                measure.minimumStaffEntriesWidth = width;
-                (measure as VexFlowMeasure).formatVoices = undefined;
+    let width: number = 200;
+    if (allVoices.length > 0) {
+        // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
+        // FIXME: a more relaxed formatting of voices
+        width = formatter.preCalculateMinTotalWidth(allVoices) / unitInPixels + 5.0;
+        // firstMeasure.formatVoices = (w: number) => {
+        //     formatter.format(allVoices, w);
+        // };
+        for (const measure of measures) {
+            measure.minimumStaffEntriesWidth = width;
+            if (measure !== measures[0]) {
+        (measure as VexFlowMeasure).formatVoices = undefined;
+            } else {
+                (measure as VexFlowMeasure).formatVoices = (w: number) => {
+                    formatter.format(allVoices, w);
+                };
             }
-            firstMeasure.formatVoices = (w: number) => {
-                formatter.format(allVoices, w);
-            };
         }
-
-        return width;
     }
-
-    protected createGraphicalTie(tie: Tie, startGse: GraphicalStaffEntry, endGse: GraphicalStaffEntry,
-                                 startNote: GraphicalNote, endNote: GraphicalNote): GraphicalTie {
-        return new GraphicalTie(tie, startNote, endNote);
+    return width;
+  }
+
+  protected createGraphicalTie(tie: Tie, startGse: GraphicalStaffEntry, endGse: GraphicalStaffEntry,
+                               startNote: GraphicalNote, endNote: GraphicalNote): GraphicalTie {
+    return new GraphicalTie(tie, startNote, endNote);
+  }
+
+
+  protected updateStaffLineBorders(staffLine: StaffLine): void {
+    return;
+  }
+
+  protected staffMeasureCreatedCalculations(measure: StaffMeasure): void {
+    (measure as VexFlowMeasure).staffMeasureCreatedCalculations();
+  }
+
+  /**
+   * Can be used to calculate articulations, stem directions, helper(ledger) lines, and overlapping note x-displacement.
+   * Is Excecuted per voice entry of a staff entry.
+   * After that layoutStaffEntry is called.
+   * @param voiceEntry
+   * @param graphicalNotes
+   * @param graphicalStaffEntry
+   * @param hasPitchedNote
+   * @param isGraceStaffEntry
+   */
+  protected layoutVoiceEntry(voiceEntry: VoiceEntry, graphicalNotes: GraphicalNote[], graphicalStaffEntry: GraphicalStaffEntry,
+                             hasPitchedNote: boolean, isGraceStaffEntry: boolean): void {
+    return;
+  }
+
+  /**
+   * Do all layout calculations that have to be done per staff entry, like dots, ornaments, arpeggios....
+   * This method is called after the voice entries are handled by layoutVoiceEntry().
+   * @param graphicalStaffEntry
+   */
+  protected layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
+    (graphicalStaffEntry.parentMeasure as VexFlowMeasure).layoutStaffEntry(graphicalStaffEntry);
+  }
+
+  /**
+   * calculates the y positions of the staff lines within a system and
+   * furthermore the y positions of the systems themselves.
+   */
+  protected calculateSystemYLayout(): void {
+    for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+      const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+      if (!this.leadSheet) {
+        let globalY: number = this.rules.PageTopMargin + this.rules.TitleTopDistance + this.rules.SheetTitleHeight +
+          this.rules.TitleBottomDistance;
+        for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+          const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+          // calculate y positions of stafflines within system
+          let y: number = 0;
+          for (const line of musicSystem.StaffLines) {
+            line.PositionAndShape.RelativePosition.y = y;
+            y += 10;
+          }
+          // set y positions of systems using the previous system and a fixed distance.
+          musicSystem.PositionAndShape.BorderBottom = y + 0;
+          musicSystem.PositionAndShape.RelativePosition.x = this.rules.PageLeftMargin + this.rules.SystemLeftMargin;
+          musicSystem.PositionAndShape.RelativePosition.y = globalY;
+          globalY += y + 5;
+        }
+      }
     }
+  }
+
+  /**
+   * Is called at the begin of the method for creating the vertically aligned staff measures belonging to one source measure.
+   */
+  protected initStaffMeasuresCreation(): void {
+    return;
+  }
+
+  /**
+   * add here all given articulations to the VexFlowGraphicalStaffEntry and prepare them for rendering.
+   * @param articulations
+   * @param voiceEntry
+   * @param graphicalStaffEntry
+   */
+  protected layoutArticulationMarks(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
+    // uncomment this when implementing:
+    // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);
+
+    return;
+  }
 
-
-    protected updateStaffLineBorders(staffLine: StaffLine): void {
-        return;
+    /**
+     * Calculate the shape (Bezier curve) for this tie.
+     * @param tie
+     * @param tieIsAtSystemBreak
+     */
+  protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean): void {
+    const startNote: VexFlowGraphicalNote = (tie.StartNote as VexFlowGraphicalNote);
+    let vfStartNote: Vex.Flow.StaveNote = undefined;
+    if (startNote !== undefined) {
+      vfStartNote = startNote.vfnote[0];
     }
 
-    protected calculateMeasureNumberPlacement(musicSystem: MusicSystem): void {
-        return;
+    const endNote: VexFlowGraphicalNote = (tie.EndNote as VexFlowGraphicalNote);
+    let vfEndNote: Vex.Flow.StaveNote = undefined;
+    if (endNote !== undefined) {
+      vfEndNote = endNote.vfnote[0];
     }
 
-    protected staffMeasureCreatedCalculations(measure: StaffMeasure): void {
-        (measure as VexFlowMeasure).staffMeasureCreatedCalculations();
-    }
 
-    /**
-     * Can be used to calculate articulations, stem directions, helper(ledger) lines, and overlapping note x-displacement.
-     * Is Excecuted per voice entry of a staff entry.
-     * After that layoutStaffEntry is called.
-     * @param voiceEntry
-     * @param graphicalNotes
-     * @param graphicalStaffEntry
-     * @param hasPitchedNote
-     * @param isGraceStaffEntry
-     */
-    protected layoutVoiceEntry(voiceEntry: VoiceEntry, graphicalNotes: GraphicalNote[], graphicalStaffEntry: GraphicalStaffEntry,
-                               hasPitchedNote: boolean, isGraceStaffEntry: boolean): void {
-        return;
+    if (tieIsAtSystemBreak) {
+      // split tie into two ties:
+      const vfTie1: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
+        first_note: vfStartNote,
+      });
+      const measure1: VexFlowMeasure = (startNote.parentStaffEntry.parentMeasure as VexFlowMeasure);
+      measure1.vfTies.push(vfTie1);
+
+      const vfTie2: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
+        last_note: vfEndNote,
+      });
+      const measure2: VexFlowMeasure = (endNote.parentStaffEntry.parentMeasure as VexFlowMeasure);
+      measure2.vfTies.push(vfTie2);
+    } else {
+      const vfTie: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
+        first_note: vfStartNote,
+        last_note: vfEndNote,
+      });
+      const measure: VexFlowMeasure = (endNote.parentStaffEntry.parentMeasure as VexFlowMeasure);
+      measure.vfTies.push(vfTie);
     }
+  }
 
     /**
-     * Do all layout calculations that have to be done per staff entry, like dots, ornaments, arpeggios....
-     * This method is called after the voice entries are handled by layoutVoiceEntry().
-     * @param graphicalStaffEntry
+     * Calculate a single OctaveShift for a [[MultiExpression]].
+     * @param sourceMeasure
+     * @param multiExpression
+     * @param measureIndex
+     * @param staffIndex
      */
-    protected layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
-        (graphicalStaffEntry.parentMeasure as VexFlowMeasure).layoutStaffEntry(graphicalStaffEntry);
-    }
+  protected calculateSingleOctaveShift(sourceMeasure: SourceMeasure, multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+    return;
+  }
 
     /**
-     * calculates the y positions of the staff lines within a system and
-     * furthermore the y positions of the systems themselves.
+     * Calculate all the textual and symbolic [[RepetitionInstruction]]s (e.g. dal segno) for a single [[SourceMeasure]].
+     * @param repetitionInstruction
+     * @param measureIndex
      */
-    protected calculateSystemYLayout(): void {
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
-            if (!this.leadSheet) {
-                let globalY: number = this.rules.PageTopMargin + this.rules.TitleTopDistance + this.rules.SheetTitleHeight +
-                    this.rules.TitleBottomDistance;
-                for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
-                    let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
-                    // calculate y positions of stafflines within system
-                    let y: number = 0;
-                    for (let line of musicSystem.StaffLines) {
-                        line.PositionAndShape.RelativePosition.y = y;
-                        y += 10;
-                    }
-                    // set y positions of systems using the previous system and a fixed distance.
-                    musicSystem.PositionAndShape.BorderBottom = y + 0;
-                    musicSystem.PositionAndShape.RelativePosition.x = this.rules.PageLeftMargin + this.rules.SystemLeftMargin;
-                    musicSystem.PositionAndShape.RelativePosition.y = globalY;
-                    globalY += y + 5;
-                }
-            }
+  protected calculateWordRepetitionInstruction(repetitionInstruction: RepetitionInstruction, measureIndex: number): void {
+      // find first visible StaffLine
+      let uppermostMeasure: VexFlowMeasure = undefined;
+      const measures: VexFlowMeasure[]  = <VexFlowMeasure[]>this.graphicalMusicSheet.MeasureList[measureIndex];
+      for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
+        const graphicalMeasure: VexFlowMeasure = measures[idx];
+        if (graphicalMeasure.ParentStaffLine !== undefined && graphicalMeasure.ParentStaff.ParentInstrument.Visible) {
+            uppermostMeasure = <VexFlowMeasure>graphicalMeasure;
+            break;
         }
+      }
+      // ToDo: feature/Repetitions
+      // now create corresponding graphical symbol or Text in VexFlow:
+      // use top measure and staffline for positioning.
+      if (uppermostMeasure !== undefined) {
+        uppermostMeasure.addWordRepetition(repetitionInstruction.type);
+      }
     }
 
-    /**
-     * Is called at the begin of the method for creating the vertically aligned staff measures belonging to one source measure.
-     */
-    protected initStaffMeasuresCreation(): void {
-        return;
-    }
-
-    protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean): void {
-        let startNote: VexFlowGraphicalNote = (tie.StartNote as VexFlowGraphicalNote);
-        let vfStartNote: Vex.Flow.StaveNote = undefined;
-        if (startNote !== undefined) {
-            vfStartNote = startNote.vfnote[0];
-        }
-
-        let endNote: VexFlowGraphicalNote = (tie.EndNote as VexFlowGraphicalNote);
-        let vfEndNote: Vex.Flow.StaveNote = undefined;
-        if (endNote !== undefined) {
-            vfEndNote = endNote.vfnote[0];
-        }
-
-
-        if (tieIsAtSystemBreak) {
-            // split tie into two ties:
-            let vfTie1: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
-                first_note: vfStartNote,
-            });
-            let measure1: VexFlowMeasure = (startNote.parentStaffEntry.parentMeasure as VexFlowMeasure);
-            measure1.vfTies.push(vfTie1);
-
-            let vfTie2: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
-                last_note : vfEndNote,
-            });
-            let measure2: VexFlowMeasure = (endNote.parentStaffEntry.parentMeasure as VexFlowMeasure);
-            measure2.vfTies.push(vfTie2);
-        } else {
-            let vfTie: Vex.Flow.StaveTie = new Vex.Flow.StaveTie({
-                first_note: vfStartNote,
-                last_note : vfEndNote,
-            });
-            let measure: VexFlowMeasure = (endNote.parentStaffEntry.parentMeasure as VexFlowMeasure);
-            measure.vfTies.push(vfTie);
-        }
-    }
-
-    protected calculateSingleStaffLineLyricsPosition(staffLine: StaffLine, lyricVersesNumber: number[]): void {
-        return;
-    }
-
-    protected calculateSingleOctaveShift(sourceMeasure: SourceMeasure, multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
-        return;
-    }
-
-    protected calculateWordRepetitionInstruction(repetitionInstruction: RepetitionInstruction, measureIndex: number): void {
-        return;
-    }
-
-    protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
-        return;
-    }
-
-    protected handleTiedGraphicalNote(  tiedGraphicalNote: GraphicalNote, beams: Beam[], activeClef: ClefInstruction,
-                                        octaveShiftValue: OctaveEnum, graphicalStaffEntry: GraphicalStaffEntry, duration: Fraction,
-                                        openTie: Tie, isLastTieNote: boolean): void {
-        return;
-    }
+  protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+    return;
+  }
 
     /**
-     * Is called if a note is part of a beam.
-     * @param graphicalNote
-     * @param beam
-     * @param openBeams a list of all currently open beams
+     * Check if the tied graphical note belongs to any beams or tuplets and react accordingly.
+     * @param tiedGraphicalNote
+     * @param beams
+     * @param activeClef
+     * @param octaveShiftValue
+     * @param graphicalStaffEntry
+     * @param duration
+     * @param openTie
+     * @param isLastTieNote
      */
-    protected handleBeam(graphicalNote: GraphicalNote, beam: Beam, openBeams: Beam[]): void {
-        (graphicalNote.parentStaffEntry.parentMeasure as VexFlowMeasure).handleBeam(graphicalNote, beam);
-    }
-
-    protected handleVoiceEntryLyrics(lyricsEntries: Dictionary<number, LyricsEntry>, voiceEntry: VoiceEntry,
-                                     graphicalStaffEntry: GraphicalStaffEntry, openLyricWords: LyricWord[]): void {
-        return;
-    }
-
-    protected handleVoiceEntryOrnaments(ornamentContainer: OrnamentContainer, voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
-        return;
-    }
-
-    protected handleVoiceEntryArticulations(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
-        return;
+  protected handleTiedGraphicalNote(tiedGraphicalNote: GraphicalNote, beams: Beam[], activeClef: ClefInstruction,
+                                    octaveShiftValue: OctaveEnum, graphicalStaffEntry: GraphicalStaffEntry, duration: Fraction,
+                                    openTie: Tie, isLastTieNote: boolean): void {
+    return;
+  }
+
+  /**
+   * Is called if a note is part of a beam.
+   * @param graphicalNote
+   * @param beam
+   * @param openBeams a list of all currently open beams
+   */
+  protected handleBeam(graphicalNote: GraphicalNote, beam: Beam, openBeams: Beam[]): void {
+    (graphicalNote.parentStaffEntry.parentMeasure as VexFlowMeasure).handleBeam(graphicalNote, beam);
+  }
+
+    protected handleVoiceEntryLyrics(voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry, lyricWords: LyricWord[]): void {
+        voiceEntry.LyricsEntries.forEach((key: number, lyricsEntry: LyricsEntry) => {
+            const graphicalLyricEntry: GraphicalLyricEntry = new GraphicalLyricEntry(lyricsEntry,
+                                                                                     graphicalStaffEntry,
+                                                                                     this.rules.LyricsHeight,
+                                                                                     this.rules.StaffHeight);
+
+            graphicalStaffEntry.LyricsEntries.push(graphicalLyricEntry);
+
+            // create corresponding GraphicalLabel
+            const graphicalLabel: GraphicalLabel = graphicalLyricEntry.GraphicalLabel;
+            graphicalLabel.setLabelPositionAndShapeBorders();
+
+            if (lyricsEntry.Word !== undefined) {
+                const lyricsEntryIndex: number = lyricsEntry.Word.Syllables.indexOf(lyricsEntry);
+                let index: number = lyricWords.indexOf(lyricsEntry.Word);
+                if (index === -1) {
+                    lyricWords.push(lyricsEntry.Word);
+                    index = lyricWords.indexOf(lyricsEntry.Word);
+  }
+
+                if (this.graphicalLyricWords.length === 0 || index > this.graphicalLyricWords.length - 1) {
+                    const graphicalLyricWord: GraphicalLyricWord = new GraphicalLyricWord(lyricsEntry.Word);
+
+                    graphicalLyricEntry.ParentLyricWord = graphicalLyricWord;
+                    graphicalLyricWord.GraphicalLyricsEntries[lyricsEntryIndex] = graphicalLyricEntry;
+                    this.graphicalLyricWords.push(graphicalLyricWord);
+                } else {
+                    const graphicalLyricWord: GraphicalLyricWord = this.graphicalLyricWords[index];
+
+                    graphicalLyricEntry.ParentLyricWord = graphicalLyricWord;
+                    graphicalLyricWord.GraphicalLyricsEntries[lyricsEntryIndex] = graphicalLyricEntry;
+
+                    if (graphicalLyricWord.isFilled()) {
+                        lyricWords.splice(index, 1);
+                        this.graphicalLyricWords.splice(this.graphicalLyricWords.indexOf(graphicalLyricWord), 1);
+                    }
+                }
+            }
+        });
     }
 
-    /**
-     * Is called if a note is part of a tuplet.
-     * @param graphicalNote
-     * @param tuplet
-     * @param openTuplets a list of all currently open tuplets
-     */
-    protected handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet, openTuplets: Tuplet[]): void {
-        (graphicalNote.parentStaffEntry.parentMeasure as VexFlowMeasure).handleTuplet(graphicalNote, tuplet);
-    }
+  protected handleVoiceEntryOrnaments(ornamentContainer: OrnamentContainer, voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
+    return;
+  }
+
+  /**
+   * Add articulations to the given vexflow staff entry.
+   * @param articulations
+   * @param voiceEntry
+   * @param graphicalStaffEntry
+   */
+  protected handleVoiceEntryArticulations(articulations: ArticulationEnum[],
+                                          voiceEntry: VoiceEntry, staffEntry: GraphicalStaffEntry): void {
+    // uncomment this when implementing:
+    // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);
+
+    return;
+  }
+
+  /**
+   * Add technical instructions to the given vexflow staff entry.
+   * @param technicalInstructions
+   * @param voiceEntry
+   * @param staffEntry
+   */
+  protected handleVoiceEntryTechnicalInstructions(technicalInstructions: TechnicalInstruction[],
+                                                  voiceEntry: VoiceEntry, staffEntry: GraphicalStaffEntry): void {
+    // uncomment this when implementing:
+    // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);
+    return;
+  }
+
+  /**
+   * Is called if a note is part of a tuplet.
+   * @param graphicalNote
+   * @param tuplet
+   * @param openTuplets a list of all currently open tuplets
+   */
+  protected handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet, openTuplets: Tuplet[]): void {
+    (graphicalNote.parentStaffEntry.parentMeasure as VexFlowMeasure).handleTuplet(graphicalNote, tuplet);
+  }
 }

+ 68 - 34
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -1,34 +1,38 @@
-import Vex = require("vexflow");
 import {MusicSheetDrawer} from "../MusicSheetDrawer";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {VexFlowMeasure} from "./VexFlowMeasure";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
 import {GraphicalLabel} from "../GraphicalLabel";
-import {VexFlowConverter} from "./VexFlowConverter";
 import {VexFlowTextMeasurer} from "./VexFlowTextMeasurer";
 import {MusicSystem} from "../MusicSystem";
 import {GraphicalObject} from "../GraphicalObject";
+import {GraphicalLayers} from "../DrawingEnums";
+import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
+import {VexFlowBackend} from "./VexFlowBackend";
+import { VexFlowInstrumentBracket } from "./VexFlowInstrumentBracket";
+import { VexFlowInstrumentBrace } from "./VexFlowInstrumentBrace";
+import { GraphicalLyricEntry } from "../GraphicalLyricEntry";
 
 /**
- * This is a global contant which denotes the height in pixels of the space between two lines of the stave
+ * This is a global constant which denotes the height in pixels of the space between two lines of the stave
  * (when zoom = 1.0)
  * @type number
  */
 export const unitInPixels: number = 10;
 
 export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
-    private renderer: Vex.Flow.Renderer;
-    private vfctx: Vex.Flow.CanvasContext;
-    private ctx: CanvasRenderingContext2D;
+    private backend: VexFlowBackend;
     private zoom: number = 1.0;
 
-    constructor(canvas: HTMLCanvasElement, isPreviewImageDrawer: boolean = false) {
+    constructor(element: HTMLElement,
+                backend: VexFlowBackend,
+                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.backend = backend;
+    }
+
+    public clear(): void {
+        this.backend.clear();
     }
 
     /**
@@ -37,7 +41,7 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
      */
     public scale(k: number): void {
         this.zoom = k;
-        this.vfctx.scale(k, k);
+        this.backend.scale(this.zoom);
     }
 
     /**
@@ -46,12 +50,11 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
      * @param y
      */
     public resize(x: number, y: number): void {
-        this.renderer.resize(x, y);
+        this.backend.resize(x, y);
     }
 
     public translate(x: number, y: number): void {
-        // Translation seems not supported by VexFlow
-        this.ctx.translate(x, y);
+        this.backend.translate(x, y);
     }
 
     /**
@@ -68,15 +71,53 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
             measure.PositionAndShape.AbsolutePosition.x * unitInPixels,
             measure.PositionAndShape.AbsolutePosition.y * unitInPixels
         );
-        return measure.draw(this.vfctx);
+        measure.draw(this.backend.getContext());
+        for (const voiceID in measure.vfVoices) {
+            if (measure.vfVoices.hasOwnProperty(voiceID)) {
+                const tickables: Vex.Flow.Tickable[] = measure.vfVoices[voiceID].tickables;
+                for (const tick of tickables) {
+                    if ((<any>tick).getAttribute("type") === "StaveNote" && process.env.DEBUG) {
+                        tick.getBoundingBox().draw(this.backend.getContext());
+                    }
+                }
+            }
+        }
+
+        // Draw the StaffEntries
+        for (const staffEntry of measure.staffEntries) {
+            this.drawStaffEntry(staffEntry);
+        }
     }
 
-    protected drawInstrumentBrace(bracket: GraphicalObject, system: MusicSystem): void {
-        // empty
+    private drawStaffEntry(staffEntry: GraphicalStaffEntry): void {
+        // Draw ChordSymbol
+        if (staffEntry.graphicalChordContainer !== undefined) {
+            this.drawLabel(staffEntry.graphicalChordContainer.GetGraphicalLabel, <number>GraphicalLayers.Notes);
+        }
+        if (staffEntry.LyricsEntries.length > 0) {
+            this.drawLyrics(staffEntry.LyricsEntries, <number>GraphicalLayers.Notes);
+        }
+    }
+
+    /**
+     * Draw all lyrics to the canvas
+     * @param lyricEntries Array of lyric entries to be drawn
+     * @param layer Number of the layer that the lyrics should be drawn in
+     */
+    private drawLyrics(lyricEntries: GraphicalLyricEntry[], layer: number): void {
+        lyricEntries.forEach(lyricsEntry => this.drawLabel(lyricsEntry.GraphicalLabel, layer));
+    }
+
+    protected drawInstrumentBrace(brace: GraphicalObject, system: MusicSystem): void {
+        // Draw InstrumentBrackets at beginning of line
+        const vexBrace: VexFlowInstrumentBrace = (brace as VexFlowInstrumentBrace);
+        vexBrace.draw(this.backend.getContext());
     }
 
     protected drawGroupBracket(bracket: GraphicalObject, system: MusicSystem): void {
-        // empty
+        // Draw InstrumentBrackets at beginning of line
+        const vexBrace: VexFlowInstrumentBracket = (bracket as VexFlowInstrumentBracket);
+        vexBrace.draw(this.backend.getContext());
     }
 
     /**
@@ -90,15 +131,10 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
      */
     protected renderLabel(graphicalLabel: GraphicalLabel, layer: number, bitmapWidth: number,
                           bitmapHeight: number, heightInPixel: number, screenPosition: PointF2D): void {
-        let ctx: CanvasRenderingContext2D = (this.vfctx as any).vexFlowCanvasContext;
-        let old: string = ctx.font;
-        ctx.font = VexFlowConverter.font(
-            graphicalLabel.Label.fontHeight * unitInPixels,
-            graphicalLabel.Label.fontStyle,
-            graphicalLabel.Label.font
-        );
-        ctx.fillText(graphicalLabel.Label.text, screenPosition.x, screenPosition.y + heightInPixel);
-        ctx.font = old;
+        const height: number = graphicalLabel.Label.fontHeight * unitInPixels;
+        const { fontStyle, font, text } = graphicalLabel.Label;
+
+        this.backend.renderText(height, fontStyle, font, text, heightInPixel, screenPosition);
     }
 
     /**
@@ -107,12 +143,10 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
      * @param rectangle the rect in screen coordinates
      * @param layer is the current rendering layer. There are many layers on top of each other to which can be rendered. Not needed for now.
      * @param styleId the style id
+     * @param alpha alpha value between 0 and 1
      */
-    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number): void {
-        let old: string|CanvasGradient|CanvasPattern = this.ctx.fillStyle;
-        this.ctx.fillStyle = VexFlowConverter.style(styleId);
-        this.ctx.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
-        this.ctx.fillStyle = old;
+    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number, alpha: number): void {
+       this.backend.renderRectangle(rectangle, styleId, alpha);
     }
 
     /**

+ 28 - 8
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSystem.ts

@@ -4,10 +4,13 @@ import {SystemLinesEnum} from "../SystemLinesEnum";
 import {SystemLinePosition} from "../SystemLinePosition";
 import {StaffMeasure} from "../StaffMeasure";
 import {SystemLine} from "../SystemLine";
+import {VexFlowStaffLine} from "./VexFlowStaffLine";
 import {VexFlowMeasure} from "./VexFlowMeasure";
 import {VexFlowConverter} from "./VexFlowConverter";
 import {StaffLine} from "../StaffLine";
 import {EngravingRules} from "../EngravingRules";
+import { VexFlowInstrumentBracket } from "./VexFlowInstrumentBracket";
+import { VexFlowInstrumentBrace } from "./VexFlowInstrumentBrace";
 
 export class VexFlowMusicSystem extends MusicSystem {
     constructor(parent: GraphicalMusicPage, id: number) {
@@ -19,7 +22,7 @@ export class VexFlowMusicSystem extends MusicSystem {
         if (this.staffLines.length === 0) {
             return;
         }
-        let width: number = this.calcBracketsWidth();
+        const width: number = this.calcBracketsWidth();
         this.boundingBox.BorderLeft = -width;
         this.boundingBox.BorderMarginLeft = -width;
         this.boundingBox.XBordersHaveBeenSet = true;
@@ -37,20 +40,29 @@ export class VexFlowMusicSystem extends MusicSystem {
      */
     protected createSystemLine(xPosition: number, lineWidth: number, lineType: SystemLinesEnum, linePosition: SystemLinePosition,
                                musicSystem: MusicSystem, topMeasure: StaffMeasure, bottomMeasure: StaffMeasure = undefined): SystemLine {
-        // ToDo: create line in Vexflow
+        const vfMeasure: VexFlowMeasure = topMeasure as VexFlowMeasure;
+        vfMeasure.addMeasureLine(lineType, linePosition);
         if (bottomMeasure) {
-            (bottomMeasure as VexFlowMeasure).lineTo(topMeasure as VexFlowMeasure, VexFlowConverter.line(lineType));
+          // ToDo: feature/Repetitions
+          // create here the correct lines according to the given lineType.
+          (bottomMeasure as VexFlowMeasure).lineTo(topMeasure as VexFlowMeasure, VexFlowConverter.line(lineType, linePosition));
+          (bottomMeasure as VexFlowMeasure).addMeasureLine(lineType, linePosition);
         }
         return new SystemLine(lineType, linePosition, this, topMeasure, bottomMeasure);
     }
 
     /**
      * creates an instrument brace for the given dimension.
-     * The height and positioning can be inferred from the given points.
-     * @param firstStaffLine the upper staff line of the bracket to create
-     * @param lastStaffLine the lower staff line of the bracket to create
+     * The height and positioning can be inferred from the given staff lines.
+     * @param firstStaffLine the upper StaffLine (use a cast to get the VexFlowStaffLine) of the brace to create
+     * @param lastStaffLine the lower StaffLine (use a cast to get the VexFlowStaffLine) of the brace to create
      */
-    protected createInstrumentBrace(firstStaffLine: StaffLine, lastStaffLine: StaffLine): void {
+    protected createInstrumentBracket(firstStaffLine: StaffLine, lastStaffLine: StaffLine): void {
+        // You could write this in one line but the linter doesn't let me.
+        const firstVexStaff: VexFlowStaffLine = (firstStaffLine as VexFlowStaffLine);
+        const lastVexStaff: VexFlowStaffLine = (lastStaffLine as VexFlowStaffLine);
+        const vexFlowBracket: VexFlowInstrumentBrace = new VexFlowInstrumentBrace(firstVexStaff, lastVexStaff);
+        this.InstrumentBrackets.push(vexFlowBracket);
         return;
     }
 
@@ -60,10 +72,18 @@ export class VexFlowMusicSystem extends MusicSystem {
      * The recursion depth informs about the current depth level (needed for positioning)
      * @param firstStaffLine the upper staff line of the bracket to create
      * @param lastStaffLine the lower staff line of the bracket to create
-     * @param staffHeight
      * @param recursionDepth
      */
     protected createGroupBracket(firstStaffLine: StaffLine, lastStaffLine: StaffLine, recursionDepth: number): void {
+        const firstVexStaff: VexFlowStaffLine = (firstStaffLine as VexFlowStaffLine);
+        const lastVexStaff: VexFlowStaffLine = (lastStaffLine as VexFlowStaffLine);
+        if (recursionDepth === 0) {
+            const vexFlowBracket: VexFlowInstrumentBracket = new VexFlowInstrumentBracket(firstVexStaff, lastVexStaff, recursionDepth);
+            this.GroupBrackets.push(vexFlowBracket);
+        } else {
+            const vexFlowBrace: VexFlowInstrumentBrace = new VexFlowInstrumentBrace(firstVexStaff, lastVexStaff, recursionDepth);
+            this.GroupBrackets.push(vexFlowBrace);
+        }
         return;
     }
 }

+ 25 - 13
src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts

@@ -15,22 +15,34 @@ export class VexFlowStaffEntry extends GraphicalStaffEntry {
     public vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = {};
 
     /**
-     *
-     * @returns {number} the x-position (in units) of this StaffEntry
+     * Calculates the staff entry positions from the VexFlow stave information and the tickabels inside the staff.
+     * This is needed in order to set the OSMD staff entries (which are almost the same as tickables) to the correct positionts.
+     * It is also needed to be done after formatting!
      */
-    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) {
+    public calculateXPosition(): void {
+        const vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = this.vfNotes;
+        const stave: Vex.Flow.Stave = (this.parentMeasure as VexFlowMeasure).getVFStave();
+        let tickablePosition: number = 0;
+        let numberOfValidTickables: number = 0;
+        for (const voiceId in vfNotes) {
             if (vfNotes.hasOwnProperty(voiceId)) {
-                x += (vfNotes[voiceId].getNoteHeadBeginX() + vfNotes[voiceId].getNoteHeadEndX()) / 2;
-                n += 1;
+                const tickable: Vex.Flow.StaveNote = vfNotes[voiceId];
+                // This will let the tickable know how to calculate it's bounding box
+                tickable.setStave(stave);
+                // The middle of the tickable is also the OSMD BoundingBox center
+                const staveNote: Vex.Flow.StaveNote = (<Vex.Flow.StaveNote>tickable);
+                tickablePosition += staveNote.getNoteHeadEndX() - staveNote.getGlyphWidth() / 2;
+                numberOfValidTickables++;
             }
         }
-        if (n === 0) {
-            return 0;
-        }
-        return x / n / unitInPixels;
+        tickablePosition = tickablePosition / numberOfValidTickables;
+        // Calculate parent absolute position and reverse calculate the relative position
+        // All the modifiers signs, clefs, you name it have an offset in the measure. Therefore remove it.
+        // NOTE: Somehow vexflows shift is off by 25px.
+        const modifierOffset: number = stave.getModifierXShift() - (this.parentMeasure.MeasureNumber === 1 ? 25 : 0);
+        // const modifierOffset: number = 0;
+        // sets the vexflow x positions back into the bounding boxes of the staff entries in the osmd object model.
+        // The positions are needed for cursor placement and mouse/tap interactions
+        this.PositionAndShape.RelativePosition.x = (tickablePosition - stave.getNoteStartX() + modifierOffset) / unitInPixels;
     }
 }

+ 0 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowStaffLine.ts

@@ -5,6 +5,5 @@ import {Staff} from "../../VoiceData/Staff";
 export class VexFlowStaffLine extends StaffLine {
     constructor(parentSystem: MusicSystem, parentStaff: Staff) {
         super(parentSystem, parentStaff);
-
     }
 }

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

@@ -8,7 +8,7 @@ import {VexFlowConverter} from "./VexFlowConverter";
 
 export class VexFlowTextMeasurer implements ITextMeasurer {
     constructor() {
-        let canvas: HTMLCanvasElement = document.createElement("canvas");
+        const canvas: HTMLCanvasElement = document.createElement("canvas");
         this.context = canvas.getContext("2d");
     }
     // The context of a canvas used internally to compute font sizes

+ 24 - 22
src/MusicalScore/Instrument.ts

@@ -80,7 +80,7 @@ export class Instrument extends InstrumentalGroup {
     }
     public set Volume(value: number) {
         for (let idx: number = 0, len: number = this.subInstruments.length; idx < len; ++idx) {
-            let subInstrument: SubInstrument = this.subInstruments[idx];
+            const subInstrument: SubInstrument = this.subInstruments[idx];
             subInstrument.volume = value;
         }
     }
@@ -96,7 +96,7 @@ export class Instrument extends InstrumentalGroup {
     }
     public getSubInstrument(subInstrumentIdString: string): SubInstrument {
         for (let idx: number = 0, len: number = this.subInstruments.length; idx < len; ++idx) {
-            let subInstrument: SubInstrument = this.subInstruments[idx];
+            const subInstrument: SubInstrument = this.subInstruments[idx];
             if (subInstrument.idString === subInstrumentIdString) {
                 return subInstrument;
             }
@@ -112,49 +112,49 @@ export class Instrument extends InstrumentalGroup {
     }
     public set Visible(value: boolean) {
         for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
-            let v: Voice = this.Voices[idx];
+            const v: Voice = this.Voices[idx];
             v.Visible = value;
         }
     }
     public get Audible(): boolean {
         let result: boolean = false;
         for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
-            let v: Voice = this.Voices[idx];
+            const v: Voice = this.Voices[idx];
             result = result || v.Audible;
         }
         return result;
     }
     public set Audible(value: boolean) {
         for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
-            let v: Voice = this.Voices[idx];
+            const v: Voice = this.Voices[idx];
             v.Audible = value;
         }
         for (let idx: number = 0, len: number = this.staves.length; idx < len; ++idx) {
-            let staff: Staff = this.staves[idx];
+            const staff: Staff = this.staves[idx];
             staff.audible = value;
         }
     }
     public get Following(): boolean {
         let result: boolean = false;
         for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
-            let v: Voice = this.Voices[idx];
+            const v: Voice = this.Voices[idx];
             result = result || v.Following;
         }
         return result;
     }
     public set Following(value: boolean) {
         for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
-            let v: Voice = this.Voices[idx];
+            const v: Voice = this.Voices[idx];
             v.Following = value;
         }
         for (let idx: number = 0, len: number = this.staves.length; idx < len; ++idx) {
-            let staff: Staff = this.staves[idx];
+            const staff: Staff = this.staves[idx];
             staff.following = value;
         }
     }
     public SetVoiceAudible(voiceId: number, audible: boolean): void {
         for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
-            let v: Voice = this.Voices[idx];
+            const v: Voice = this.Voices[idx];
             if (v.VoiceId === voiceId) {
                 v.Audible = audible;
                 break;
@@ -163,7 +163,7 @@ export class Instrument extends InstrumentalGroup {
     }
     public SetVoiceFollowing(voiceId: number, following: boolean): void {
         for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
-            let v: Voice = this.Voices[idx];
+            const v: Voice = this.Voices[idx];
             if (v.VoiceId === voiceId) {
                 v.Following = following;
                 break;
@@ -171,22 +171,24 @@ export class Instrument extends InstrumentalGroup {
         }
     }
     public SetStaffAudible(staffId: number, audible: boolean): void {
-        let staff: Staff = this.staves[staffId - 1];
+        const staff: Staff = this.staves[staffId - 1];
         staff.audible = audible;
+        // hack for now:
+        // activate all voices needed so that the staff notes will be played
         if (audible) {
             for (let idx: number = 0, len: number = staff.Voices.length; idx < len; ++idx) {
-                let v: Voice = staff.Voices[idx];
+                const v: Voice = staff.Voices[idx];
                 v.Audible = true;
             }
         } else {
             for (let idx: number = 0, len: number = staff.Voices.length; idx < len; ++idx) {
-                let voice: Voice = staff.Voices[idx];
+                const voice: Voice = staff.Voices[idx];
                 let isAudibleInOtherStaves: boolean = false;
                 for (let idx2: number = 0, len2: number = this.Staves.length; idx2 < len2; ++idx2) {
-                    let st: Staff = this.Staves[idx2];
+                    const st: Staff = this.Staves[idx2];
                     if (st.Id === staffId || !st.audible) { continue; }
                     for (let idx3: number = 0, len3: number = st.Voices.length; idx3 < len3; ++idx3) {
-                        let v: Voice = st.Voices[idx3];
+                        const v: Voice = st.Voices[idx3];
                         if (v === voice) {
                             isAudibleInOtherStaves = true;
                         }
@@ -199,22 +201,22 @@ export class Instrument extends InstrumentalGroup {
         }
     }
     public SetStaffFollow(staffId: number, follow: boolean): void {
-        let staff: Staff = this.staves[staffId - 1];
+        const staff: Staff = this.staves[staffId - 1];
         staff.following = follow;
         if (follow) {
             for (let idx: number = 0, len: number = staff.Voices.length; idx < len; ++idx) {
-                let v: Voice = staff.Voices[idx];
+                const v: Voice = staff.Voices[idx];
                 v.Following = true;
             }
         } else {
             for (let idx: number = 0, len: number = staff.Voices.length; idx < len; ++idx) {
-                let voice: Voice = staff.Voices[idx];
+                const voice: Voice = staff.Voices[idx];
                 let isFollowingInOtherStaves: boolean = false;
                 for (let idx2: number = 0, len2: number = this.Staves.length; idx2 < len2; ++idx2) {
-                    let st: Staff = this.Staves[idx2];
+                    const st: Staff = this.Staves[idx2];
                     if (st.Id === staffId || !st.following) { continue; }
                     for (let idx3: number = 0, len3: number = st.Voices.length; idx3 < len3; ++idx3) {
-                        let v: Voice = st.Voices[idx3];
+                        const v: Voice = st.Voices[idx3];
                         if (v === voice) {
                             isFollowingInOtherStaves = true;
                         }
@@ -227,7 +229,7 @@ export class Instrument extends InstrumentalGroup {
         }
     }
     public areAllVoiceVisible(): boolean {
-        for (let voice of this.Voices) {
+        for (const voice of this.Voices) {
             if (!voice.Visible) {
                 return false;
             }

+ 8 - 0
src/MusicalScore/Interfaces/IAfterSheetReadingModule.ts

@@ -0,0 +1,8 @@
+import {MusicSheet} from "../MusicSheet";
+/**
+ * Created by Matthias on 22.02.2017.
+ */
+
+export interface IAfterSheetReadingModule {
+  calculate(musicSheet: MusicSheet): void;
+}

+ 2 - 1
src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts

@@ -12,7 +12,7 @@ import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
 import {Staff} from "../VoiceData/Staff";
 import {StaffLine} from "../Graphical/StaffLine";
 import {StaffMeasure} from "../Graphical/StaffMeasure";
-import {TechnicalInstruction} from "../VoiceData/Instructions/TechnicalInstruction";
+import { TechnicalInstruction } from "../VoiceData/Instructions/TechnicalInstruction";
 
 export interface IGraphicalSymbolFactory {
 
@@ -48,6 +48,7 @@ export interface IGraphicalSymbolFactory {
         technicalInstruction: TechnicalInstruction,
         graphicalStaffEntry: GraphicalStaffEntry): void;
 
+
     createInStaffClef(graphicalStaffEntry: GraphicalStaffEntry, clefInstruction: ClefInstruction): void;
 
     createChordSymbol(

+ 14 - 4
src/MusicalScore/MusicParts/MusicPartManager.ts

@@ -13,9 +13,17 @@ export class MusicPartManager /*implements ISelectionListener*/ {
     private musicSheet: MusicSheet;
     private sheetStart: Fraction;
     private sheetEnd: Fraction;
+
+    /**
+     * This method is called from CoreContainer when the user changes a Repetitions's userNumberOfRepetitions.
+     */
     public reInit(): void {
         this.init();
     }
+
+    /**
+     * Main initialize method for MusicPartManager.
+     */
     public init(): void {
         this.parts = this.musicSheet.Repetitions.slice();
         this.sheetStart = this.musicSheet.SelectionStart = new Fraction(0, 1);
@@ -36,7 +44,7 @@ export class MusicPartManager /*implements ISelectionListener*/ {
         if (this.timestamps.length === 0) {
             return timestamp;
         }
-        let transform: TimestampTransform = this.getCurrentRepetitionTimestampTransform(timestamp);
+        const transform: TimestampTransform = this.getCurrentRepetitionTimestampTransform(timestamp);
         return Fraction.plus(timestamp, Fraction.minus(transform.to, transform.$from)); // FIXME
     }
     public get Parts(): PartListEntry[] {
@@ -60,8 +68,8 @@ export class MusicPartManager /*implements ISelectionListener*/ {
         this.musicSheet.SelectionEnd = end === undefined ? this.sheetEnd : end;
     }
     private calcMapping(): void {
-        let timestamps: TimestampTransform[] = [];
-        let iterator: MusicPartManagerIterator = this.getIterator();
+        const timestamps: TimestampTransform[] = [];
+        const iterator: MusicPartManagerIterator = this.getIterator();
         let currentRepetition: Repetition = iterator.CurrentRepetition;
         let curTimestampTransform: TimestampTransform = new TimestampTransform(
             iterator.CurrentEnrolledTimestamp.clone(),
@@ -73,8 +81,10 @@ export class MusicPartManager /*implements ISelectionListener*/ {
         while (!iterator.EndReached) {
             if (iterator.JumpOccurred || currentRepetition !== iterator.CurrentRepetition) {
                 currentRepetition = iterator.CurrentRepetition;
+                // if we are still in the same repetition but in a different repetition run, we remember
+                // that we have to jump backwards at this position
                 if (iterator.backJumpOccurred) {
-                    let jumpRep: Repetition = iterator.JumpResponsibleRepetition;
+                    const jumpRep: Repetition = iterator.JumpResponsibleRepetition;
                     curTimestampTransform.nextBackJump = iterator.CurrentEnrolledTimestamp;
                     curTimestampTransform.curRepetition = jumpRep;
                     curTimestampTransform.curRepetitionIteration = iterator.CurrentJumpResponsibleRepetitionIterationBeforeJump;

+ 66 - 34
src/MusicalScore/MusicParts/MusicPartManagerIterator.ts

@@ -23,7 +23,7 @@ export class MusicPartManagerIterator {
             this.manager = manager;
             this.currentVoiceEntries = undefined;
             this.frontReached = false;
-            for (let rep of manager.MusicSheet.Repetitions) {
+            for (const rep of manager.MusicSheet.Repetitions) {
                 this.setRepetitionIterationCount(rep, 1);
             }
             this.activeDynamicExpressions = new Array(manager.MusicSheet.getCompleteNumberOfStaves());
@@ -35,11 +35,11 @@ export class MusicPartManagerIterator {
             for (let staffIndex: number = 0; staffIndex < this.activeDynamicExpressions.length; staffIndex++) {
                 if (this.activeDynamicExpressions[staffIndex] !== undefined) {
                     if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
-                        let continuousDynamic: ContinuousDynamicExpression =
+                        const continuousDynamic: ContinuousDynamicExpression =
                             <ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
                         this.currentDynamicChangingExpressions.push(new DynamicsContainer(continuousDynamic, staffIndex));
                     } else {
-                        let instantaniousDynamic: InstantaniousDynamicExpression =
+                        const instantaniousDynamic: InstantaniousDynamicExpression =
                             <InstantaniousDynamicExpression>this.activeDynamicExpressions[staffIndex];
                         this.currentDynamicChangingExpressions.push(new DynamicsContainer(instantaniousDynamic, staffIndex));
                     }
@@ -128,8 +128,12 @@ export class MusicPartManagerIterator {
     public get JumpResponsibleRepetition(): Repetition {
         return this.jumpResponsibleRepetition;
     }
+
+    /**
+     * Creates a clone of this iterator which has the same actual position.
+     */
     public clone(): MusicPartManagerIterator {
-        let ret: MusicPartManagerIterator = new MusicPartManagerIterator(this.manager);
+        const ret: MusicPartManagerIterator = new MusicPartManagerIterator(this.manager);
         ret.currentVoiceEntryIndex = this.currentVoiceEntryIndex;
         ret.currentMappingPart = this.currentMappingPart;
         ret.currentPartIndex = this.currentPartIndex;
@@ -139,64 +143,84 @@ export class MusicPartManagerIterator {
         return ret;
     }
 
+    /**
+     * Returns the visible voice entries for the provided instrument of the current iterator position.
+     * @param instrument
+     * Returns: A List of voiceEntries. If there are no entries the List has a Count of 0 (it does not return null).
+     */
     public CurrentVisibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
-        let voiceEntries: VoiceEntry[] = [];
+        const voiceEntries: VoiceEntry[] = [];
         if (this.currentVoiceEntries === undefined) {
             return voiceEntries;
         }
         if (instrument !== undefined) {
-            for (let entry of this.currentVoiceEntries) {
+            for (const entry of this.currentVoiceEntries) {
                 if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
                     this.getVisibleEntries(entry, voiceEntries);
                     return voiceEntries;
                 }
             }
         } else {
-            for (let entry of this.currentVoiceEntries) {
+            for (const entry of this.currentVoiceEntries) {
                 this.getVisibleEntries(entry, voiceEntries);
             }
         }
         return voiceEntries;
     }
 
+    /**
+     * Returns the visible voice entries for the provided instrument of the current iterator position.
+     * @param instrument
+     * Returns: A List of voiceEntries. If there are no entries the List has a Count of 0 (it does not return null).
+     */
     public CurrentAudibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
-        let voiceEntries: VoiceEntry[] = [];
+        const voiceEntries: VoiceEntry[] = [];
         if (this.currentVoiceEntries === undefined) {
             return voiceEntries;
         }
         if (instrument !== undefined) {
-            for (let entry of this.currentVoiceEntries) {
+            for (const entry of this.currentVoiceEntries) {
                 if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
                     this.getAudibleEntries(entry, voiceEntries);
                     return voiceEntries;
                 }
             }
         } else {
-            for (let entry of this.currentVoiceEntries) {
+            for (const entry of this.currentVoiceEntries) {
                 this.getAudibleEntries(entry, voiceEntries);
             }
         }
         return voiceEntries;
     }
 
+    /**
+     * Returns the audible dynamics of the current iterator position.
+     * Returns: A List of Dynamics. If there are no entries the List has a Count of 0 (it does not return null).
+     */
     public getCurrentDynamicChangingExpressions(): DynamicsContainer[] {
         return this.currentDynamicChangingExpressions;
     }
 
+    /**
+     * Returns the score following voice entries for the provided instrument of the current iterator position.
+     * @param instrument
+     * Returns: A List of voiceEntries. If there are no entries the List has a Count of 0
+     * (it does not return null).
+     */
     public CurrentScoreFollowingVoiceEntries(instrument?: Instrument): VoiceEntry[] {
-        let voiceEntries: VoiceEntry[] = [];
+        const voiceEntries: VoiceEntry[] = [];
         if (this.currentVoiceEntries === undefined) {
             return voiceEntries;
         }
         if (instrument !== undefined) {
-            for (let entry of this.currentVoiceEntries) {
+            for (const entry of this.currentVoiceEntries) {
                 if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
                     this.getScoreFollowingEntries(entry, voiceEntries);
                     return voiceEntries;
                 }
             }
         } else {
-            for (let entry of this.currentVoiceEntries) {
+            for (const entry of this.currentVoiceEntries) {
                 this.getScoreFollowingEntries(entry, voiceEntries);
             }
         }
@@ -235,7 +259,7 @@ export class MusicPartManagerIterator {
         }
     }
     private setRepetitionIterationCount(repetition: Repetition, iterationCount: number): number {
-        let i: number = this.repetitionIterationCountDictKeys.indexOf(repetition);
+        const i: number = this.repetitionIterationCountDictKeys.indexOf(repetition);
         if (i === -1) {
             this.repetitionIterationCountDictKeys.push(repetition);
             this.repetitionIterationCountDictValues.push(iterationCount);
@@ -245,7 +269,7 @@ export class MusicPartManagerIterator {
         return iterationCount;
     }
     private getRepetitionIterationCount(rep: Repetition): number {
-        let i: number = this.repetitionIterationCountDictKeys.indexOf(rep);
+        const i: number = this.repetitionIterationCountDictKeys.indexOf(rep);
         if (i !== -1) {
             return this.repetitionIterationCountDictValues[i];
         }
@@ -296,9 +320,9 @@ export class MusicPartManagerIterator {
     */
     private handleRepetitionsAtMeasureBegin(): void {
         for (let idx: number = 0, len: number = this.currentMeasure.FirstRepetitionInstructions.length; idx < len; ++idx) {
-            let repetitionInstruction: RepetitionInstruction = this.currentMeasure.FirstRepetitionInstructions[idx];
+            const repetitionInstruction: RepetitionInstruction = this.currentMeasure.FirstRepetitionInstructions[idx];
             if (repetitionInstruction.parentRepetition === undefined) { continue; }
-            let currentRepetition: Repetition = repetitionInstruction.parentRepetition;
+            const currentRepetition: Repetition = repetitionInstruction.parentRepetition;
             this.currentRepetition = currentRepetition;
             if (currentRepetition.StartIndex === this.currentMeasureIndex) {
                 if (
@@ -315,8 +339,8 @@ export class MusicPartManagerIterator {
 
     private handleRepetitionsAtMeasureEnd(): void {
         for (let idx: number = 0, len: number = this.currentMeasure.LastRepetitionInstructions.length; idx < len; ++idx) {
-            let repetitionInstruction: RepetitionInstruction = this.currentMeasure.LastRepetitionInstructions[idx];
-            let currentRepetition: Repetition = repetitionInstruction.parentRepetition;
+            const repetitionInstruction: RepetitionInstruction = this.currentMeasure.LastRepetitionInstructions[idx];
+            const currentRepetition: Repetition = repetitionInstruction.parentRepetition;
             if (currentRepetition === undefined) { continue; }
             if (currentRepetition.BackwardJumpInstructions.indexOf(repetitionInstruction) > -1) {
                 if (this.getRepetitionIterationCount(currentRepetition) < currentRepetition.UserNumberOfRepetitions) {
@@ -335,7 +359,7 @@ export class MusicPartManagerIterator {
                     this.resetRepetitionIterationCount(currentRepetition);
                 }
 
-                let forwardJumpTargetMeasureIndex: number = currentRepetition.getForwardJumpTargetForIteration(
+                const forwardJumpTargetMeasureIndex: number = currentRepetition.getForwardJumpTargetForIteration(
                   this.getRepetitionIterationCount(currentRepetition)
                 );
                 if (forwardJumpTargetMeasureIndex >= 0) {
@@ -369,9 +393,9 @@ export class MusicPartManagerIterator {
           this.currentMeasure.FirstInstructionsStaffEntries.length > 0 &&
           this.currentMeasure.FirstInstructionsStaffEntries[0] !== undefined
         ) {
-            let instructions: AbstractNotationInstruction[] = this.currentMeasure.FirstInstructionsStaffEntries[0].Instructions;
+            const instructions: AbstractNotationInstruction[] = this.currentMeasure.FirstInstructionsStaffEntries[0].Instructions;
             for (let idx: number = 0, len: number = instructions.length; idx < len; ++idx) {
-                let abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
+                const abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
                 if (abstractNotationInstruction instanceof RhythmInstruction) {
                     this.manager.MusicSheet.SheetPlaybackSetting.rhythm = (<RhythmInstruction>abstractNotationInstruction).Rhythm;
                 }
@@ -379,7 +403,7 @@ export class MusicPartManagerIterator {
         }
     }
     private activateCurrentDynamicOrTempoInstructions(): void {
-        let timeSortedDynamics: DynamicsContainer[] = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList;
+        const timeSortedDynamics: DynamicsContainer[] = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList;
         while (
           this.currentDynamicEntryIndex > 0 && (
             this.currentDynamicEntryIndex >= timeSortedDynamics.length ||
@@ -398,8 +422,8 @@ export class MusicPartManagerIterator {
           this.currentDynamicEntryIndex < timeSortedDynamics.length
           && timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp.Equals(this.CurrentSourceTimestamp)
         ) {
-            let dynamicsContainer: DynamicsContainer = timeSortedDynamics[this.currentDynamicEntryIndex];
-            let staffIndex: number = dynamicsContainer.staffNumber;
+            const dynamicsContainer: DynamicsContainer = timeSortedDynamics[this.currentDynamicEntryIndex];
+            const staffIndex: number = dynamicsContainer.staffNumber;
             if (this.CurrentSourceTimestamp.Equals(dynamicsContainer.parMultiExpression().AbsoluteTimestamp)) {
                 if (dynamicsContainer.continuousDynamicExpression !== undefined) {
                     this.activeDynamicExpressions[staffIndex] = dynamicsContainer.continuousDynamicExpression;
@@ -415,21 +439,21 @@ export class MusicPartManagerIterator {
                 let startTime: Fraction;
                 let endTime: Fraction;
                 if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
-                    let continuousDynamic: ContinuousDynamicExpression = <ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
+                    const continuousDynamic: ContinuousDynamicExpression = <ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
                     startTime = continuousDynamic.StartMultiExpression.AbsoluteTimestamp;
                     endTime = continuousDynamic.EndMultiExpression.AbsoluteTimestamp;
                     if (startTime.lte(this.CurrentSourceTimestamp) && this.CurrentSourceTimestamp.lte(endTime)) {
                         this.currentDynamicChangingExpressions.push(new DynamicsContainer(continuousDynamic, staffIndex));
                     }
                 } else {
-                    let instantaniousDynamic: InstantaniousDynamicExpression = <InstantaniousDynamicExpression>this.activeDynamicExpressions[staffIndex];
+                    const instantaniousDynamic: InstantaniousDynamicExpression = <InstantaniousDynamicExpression>this.activeDynamicExpressions[staffIndex];
                     if (this.CurrentSourceTimestamp.Equals(instantaniousDynamic.ParentMultiExpression.AbsoluteTimestamp)) {
                         this.currentDynamicChangingExpressions.push(new DynamicsContainer(instantaniousDynamic, staffIndex));
                     }
                 }
             }
         }
-        let timeSortedTempoExpressions: MultiTempoExpression[] = this.manager.MusicSheet.TimestampSortedTempoExpressionsList;
+        const timeSortedTempoExpressions: MultiTempoExpression[] = this.manager.MusicSheet.TimestampSortedTempoExpressionsList;
 
         while (this.currentTempoEntryIndex > 0 && (
           this.currentTempoEntryIndex >= timeSortedTempoExpressions.length
@@ -471,8 +495,9 @@ export class MusicPartManagerIterator {
             this.handleRepetitionsAtMeasureBegin();
             this.activateCurrentRhythmInstructions();
         }
+        // everything fine, no complications
         if (this.currentVoiceEntryIndex >= 0 && this.currentVoiceEntryIndex < this.currentMeasure.VerticalSourceStaffEntryContainers.length) {
-            let currentContainer: VerticalSourceStaffEntryContainer = this.currentMeasure.VerticalSourceStaffEntryContainers[this.currentVoiceEntryIndex];
+            const currentContainer: VerticalSourceStaffEntryContainer = this.currentMeasure.VerticalSourceStaffEntryContainers[this.currentVoiceEntryIndex];
             this.currentVoiceEntries = this.getVoiceEntries(currentContainer);
             this.currentVerticalContainerInMeasureTimestamp = currentContainer.Timestamp;
             this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
@@ -491,17 +516,24 @@ export class MusicPartManagerIterator {
             this.recursiveMove();
             return;
         }
+        // we reached the end
         this.currentVerticalContainerInMeasureTimestamp = new Fraction();
         this.currentMeasure = undefined;
         this.currentVoiceEntries = undefined;
         this.endReached = true;
     }
+
+    /**
+     * helper function for moveToNextVisibleVoiceEntry and moveToPreviousVisibleVoiceEntry
+     * Get all entries and check if there is at least one valid entry in the list
+     * @param notesOnly
+     */
     private checkEntries(notesOnly: boolean): boolean {
-        let tlist: VoiceEntry[] = this.CurrentVisibleVoiceEntries();
+        const tlist: VoiceEntry[] = this.CurrentVisibleVoiceEntries();
         if (tlist.length > 0) {
             if (!notesOnly) { return true; }
             for (let idx: number = 0, len: number = tlist.length; idx < len; ++idx) {
-                let entry: VoiceEntry = tlist[idx];
+                const entry: VoiceEntry = tlist[idx];
                 if (entry.Notes[0].Pitch !== undefined) { return true; }
             }
         }
@@ -523,10 +555,10 @@ export class MusicPartManagerIterator {
         }
     }
     private getVoiceEntries(container: VerticalSourceStaffEntryContainer): VoiceEntry[] {
-        let entries: VoiceEntry[] = [];
-        for (let sourceStaffEntry of container.StaffEntries) {
+        const entries: VoiceEntry[] = [];
+        for (const sourceStaffEntry of container.StaffEntries) {
             if (sourceStaffEntry === undefined) { continue; }
-            for (let voiceEntry of sourceStaffEntry.VoiceEntries) {
+            for (const voiceEntry of sourceStaffEntry.VoiceEntries) {
                 entries.push(voiceEntry);
             }
         }

+ 33 - 19
src/MusicalScore/MusicSheet.ts

@@ -27,15 +27,19 @@ export class PlaybackSettings {
 /**
  * This is the representation of a complete piece of sheet music.
  * It includes the contents of a MusicXML file after the reading.
+ * Notes: the musicsheet might not need the Rules, e.g. in the testframework. The EngravingRules Constructor
+ * fails when no FontInfo exists, which needs a TextMeasurer
  */
 export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet>*/ {
     constructor() {
         this.rules = EngravingRules.Rules;
         this.playbackSettings = new PlaybackSettings();
         // FIXME?
-        this.playbackSettings.rhythm = new Fraction(4, 4, false);
+        // initialize SheetPlaybackSetting with default values
+        this.playbackSettings.rhythm = new Fraction(4, 4, 0, false);
         this.userStartTempoInBPM = 100;
         this.pageWidth = 120;
+        // create MusicPartManager
         this.MusicPartManager = new MusicPartManager(this);
     }
     public static defaultTitle: string = "[kein Titel]";
@@ -235,9 +239,9 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     }
     public checkForInstrumentWithNoVoice(): void {
         for (let idx: number = 0, len: number = this.instruments.length; idx < len; ++idx) {
-            let instrument: Instrument = this.instruments[idx];
+            const instrument: Instrument = this.instruments[idx];
             if (instrument.Voices.length === 0) {
-                let voice: Voice = new Voice(instrument, 1);
+                const voice: Voice = new Voice(instrument, 1);
                 instrument.Voices.push(voice);
             }
         }
@@ -254,9 +258,9 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     public fillStaffList(): void {
         let i: number = 0;
         for (let idx: number = 0, len: number = this.instruments.length; idx < len; ++idx) {
-            let instrument: Instrument = this.instruments[idx];
+            const instrument: Instrument = this.instruments[idx];
             for (let idx2: number = 0, len2: number = instrument.Staves.length; idx2 < len2; ++idx2) {
-                let staff: Staff = instrument.Staves[idx2];
+                const staff: Staff = instrument.Staves[idx2];
                 staff.idInMusicSheet = i;
                 this.staves.push(staff);
                 i++;
@@ -272,7 +276,7 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     public getCompleteNumberOfStaves(): number {
         let num: number = 0;
         for (let idx: number = 0, len: number = this.instruments.length; idx < len; ++idx) {
-            let instrument: Instrument = this.instruments[idx];
+            const instrument: Instrument = this.instruments[idx];
             num += instrument.Staves.length;
         }
         return num;
@@ -285,32 +289,42 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
      * @returns {SourceMeasure[]}
      */
     public getListOfMeasuresFromIndeces(start: number, end: number): SourceMeasure[] {
-        let measures: SourceMeasure[] = [];
+        const measures: SourceMeasure[] = [];
         for (let i: number = start; i <= end; i++) {
             measures.push(this.sourceMeasures[i]);
         }
         return measures;
     }
+    /**
+     * Returns the next SourceMeasure from a given SourceMeasure.
+     * @param measure
+     */
     public getNextSourceMeasure(measure: SourceMeasure): SourceMeasure {
-        let index: number = this.sourceMeasures.indexOf(measure);
+        const index: number = this.sourceMeasures.indexOf(measure);
         if (index === this.sourceMeasures.length - 1) {
             return measure;
         }
         return this.sourceMeasures[index + 1];
     }
+    /**
+     * Returns the first SourceMeasure of MusicSheet.
+     */
     public getFirstSourceMeasure(): SourceMeasure {
         return this.sourceMeasures[0];
     }
+    /**
+     * Returns the last SourceMeasure of MusicSheet.
+     */
     public getLastSourceMeasure(): SourceMeasure {
         return this.sourceMeasures[this.sourceMeasures.length - 1];
     }
     public resetAllNoteStates(): void {
-       let iterator: MusicPartManagerIterator = this.MusicPartManager.getIterator();
+       const iterator: MusicPartManagerIterator = this.MusicPartManager.getIterator();
        while (!iterator.EndReached && iterator.CurrentVoiceEntries !== undefined) {
            for (let idx: number = 0, len: number = iterator.CurrentVoiceEntries.length; idx < len; ++idx) {
-               let voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[idx];
+               const voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[idx];
                for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
-                   let note: Note = voiceEntry.Notes[idx2];
+                   const note: Note = voiceEntry.Notes[idx2];
                    note.state = NoteState.Normal;
                }
            }
@@ -321,7 +335,7 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         return this.Instruments.indexOf(instrument);
     }
     public getGlobalStaffIndexOfFirstStaff(instrument: Instrument): number {
-        let instrumentIndex: number = this.getMusicSheetInstrumentIndex(instrument);
+        const instrumentIndex: number = this.getMusicSheetInstrumentIndex(instrument);
         let staffLineIndex: number = 0;
         for (let i: number = 0; i < instrumentIndex; i++) {
             staffLineIndex += this.Instruments[i].Staves.length;
@@ -481,11 +495,11 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     //    }
     // }
     public getEnrolledSelectionStartTimeStampWorkaround(): Fraction {
-        let iter: MusicPartManagerIterator = this.MusicPartManager.getIterator(this.SelectionStart);
+        const iter: MusicPartManagerIterator = this.MusicPartManager.getIterator(this.SelectionStart);
         return Fraction.createFromFraction(iter.CurrentEnrolledTimestamp);
     }
     public get SheetEndTimestamp(): Fraction {
-        let lastMeasure: SourceMeasure = this.getLastSourceMeasure();
+        const lastMeasure: SourceMeasure = this.getLastSourceMeasure();
         return Fraction.plus(lastMeasure.AbsoluteTimestamp, lastMeasure.Duration);
     }
 
@@ -496,9 +510,9 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
      */
     public getSourceMeasureFromTimeStamp(timeStamp: Fraction): SourceMeasure {
         for (let idx: number = 0, len: number = this.sourceMeasures.length; idx < len; ++idx) {
-            let sm: SourceMeasure = this.sourceMeasures[idx];
+            const sm: SourceMeasure = this.sourceMeasures[idx];
             for (let idx2: number = 0, len2: number = sm.VerticalSourceStaffEntryContainers.length; idx2 < len2; ++idx2) {
-                let vssec: VerticalSourceStaffEntryContainer = sm.VerticalSourceStaffEntryContainers[idx2];
+                const vssec: VerticalSourceStaffEntryContainer = sm.VerticalSourceStaffEntryContainers[idx2];
                 if (timeStamp.Equals(vssec.getAbsoluteTimestamp())) {
                     return sm;
                 }
@@ -507,7 +521,7 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         return this.findSourceMeasureFromTimeStamp(timeStamp);
     }
     public findSourceMeasureFromTimeStamp(timestamp: Fraction): SourceMeasure {
-        for (let sm of this.sourceMeasures) {
+        for (const sm of this.sourceMeasures) {
             if (sm.AbsoluteTimestamp.lte(timestamp) && timestamp.lt(Fraction.plus(sm.AbsoluteTimestamp, sm.Duration))) {
                 return sm;
             }
@@ -515,9 +529,9 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     }
 
     public getVisibleInstruments(): Instrument[] {
-        let visInstruments: Instrument[] = [];
+        const visInstruments: Instrument[] = [];
         for (let idx: number = 0, len: number = this.Instruments.length; idx < len; ++idx) {
-            let instrument: Instrument = this.Instruments[idx];
+            const instrument: Instrument = this.Instruments[idx];
             if (instrument.Voices.length > 0 && instrument.Voices[0].Visible) {
                 visInstruments.push(instrument);
             }

+ 9 - 9
src/MusicalScore/MusicSource/Repetition.ts

@@ -54,7 +54,7 @@ export class Repetition extends PartListEntry /*implements IRepetition*/ {
     public set UserNumberOfRepetitions(value: number) {
         this.userNumberOfRepetitions = value;
         this.repetitonIterationOrder = [];
-        let endingsDiff: number = this.userNumberOfRepetitions - this.NumberOfEndings;
+        const endingsDiff: number = this.userNumberOfRepetitions - this.NumberOfEndings;
         for (let i: number = 1; i <= this.userNumberOfRepetitions; i++) {
             if (i <= endingsDiff) {
                 this.repetitonIterationOrder.push(1);
@@ -64,7 +64,7 @@ export class Repetition extends PartListEntry /*implements IRepetition*/ {
         }
     }
     public getForwardJumpTargetForIteration(iteration: number): number {
-        let endingIndex: number = this.repetitonIterationOrder[iteration - 1];
+        const endingIndex: number = this.repetitonIterationOrder[iteration - 1];
         if (this.endingIndexDict[endingIndex] !== undefined) {
             return this.endingIndexDict[endingIndex].part.StartIndex;
         }
@@ -74,9 +74,9 @@ export class Repetition extends PartListEntry /*implements IRepetition*/ {
         return this.startMarker.measureIndex;
     }
     public SetEndingStartIndex(endingNumbers: number[], startIndex: number): void {
-        let part: RepetitionEndingPart = new RepetitionEndingPart(new SourceMusicPart(this.musicSheet2, startIndex, startIndex));
+        const part: RepetitionEndingPart = new RepetitionEndingPart(new SourceMusicPart(this.musicSheet2, startIndex, startIndex));
         this.endingParts.push(part);
-        for (let endingNumber of endingNumbers) {
+        for (const endingNumber of endingNumbers) {
             try {
                 this.endingIndexDict[endingNumber] = part;
                 part.endingIndices.push(endingNumber);
@@ -130,15 +130,15 @@ export class Repetition extends PartListEntry /*implements IRepetition*/ {
     }
     private checkRepetitionForMultipleLyricVerses(): number {
         let lyricVerses: number = 0;
-        let start: number = this.StartIndex;
-        let end: number = this.EndIndex;
+        const start: number = this.StartIndex;
+        const end: number = this.EndIndex;
         for (let measureIndex: number = start; measureIndex <= end; measureIndex++) {
-            let sourceMeasure: SourceMeasure = this.musicSheet2.SourceMeasures[measureIndex];
+            const sourceMeasure: SourceMeasure = this.musicSheet2.SourceMeasures[measureIndex];
             for (let i: number = 0; i < sourceMeasure.CompleteNumberOfStaves; i++) {
-                for (let sourceStaffEntry of sourceMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries) {
+                for (const sourceStaffEntry of sourceMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries) {
                     if (sourceStaffEntry !== undefined) {
                         let verses: number = 0;
-                        for (let voiceEntry of sourceStaffEntry.VoiceEntries) {
+                        for (const voiceEntry of sourceStaffEntry.VoiceEntries) {
                             verses += Object.keys(voiceEntry.LyricsEntries).length;
                         }
                         lyricVerses = Math.max(lyricVerses, verses);

+ 963 - 966
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -19,13 +19,13 @@ import {IXmlAttribute} from "../../Common/FileIO/Xml";
 import {ChordSymbolContainer} from "../VoiceData/ChordSymbolContainer";
 import {Logging} from "../../Common/Logging";
 import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
+import {ChordSymbolReader} from "./MusicSymbolModules/ChordSymbolReader";
+import { RepetitionInstructionReader } from "./MusicSymbolModules/RepetitionInstructionReader";
 //import Dictionary from "typescript-collections/dist/lib/Dictionary";
 
 // FIXME: The following classes are missing
-//type repetitionInstructionReader = any;
 //type ChordSymbolContainer = any;
 //type SlurReader = any;
-//type RepetitionInstructionReader = any;
 //type ExpressionReader = any;
 //declare class MusicSymbolModuleFactory {
 //  public static createSlurReader(x: any): any;
@@ -43,10 +43,6 @@ import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
 //  }
 //}
 
-/**
- * To be implemented
- */
-export type repetitionInstructionReader = any;
 
 /**
  * An InstrumentReader is used during the reading phase to keep parsing new measures from the MusicXML file
@@ -54,1033 +50,1034 @@ export type repetitionInstructionReader = any;
  */
 export class InstrumentReader {
 
-    constructor(repetitionInstructionReader: repetitionInstructionReader, xmlMeasureList: IXmlElement[], instrument: Instrument) {
-        // (*) this.repetitionInstructionReader = repetitionInstructionReader;
-        this.xmlMeasureList = xmlMeasureList;
-        this.musicSheet = instrument.GetMusicSheet;
-        this.instrument = instrument;
-        this.activeClefs = new Array(instrument.Staves.length);
-        this.activeClefsHaveBeenInitialized = new Array(instrument.Staves.length);
-        for (let i: number = 0; i < instrument.Staves.length; i++) {
-            this.activeClefsHaveBeenInitialized[i] = false;
-        }
-        // FIXME createExpressionGenerators(instrument.Staves.length);
-        // (*) this.slurReader = MusicSymbolModuleFactory.createSlurReader(this.musicSheet);
-    }
-
-    // (*) private repetitionInstructionReader: RepetitionInstructionReader;
-    private xmlMeasureList: IXmlElement[];
-    private musicSheet: MusicSheet;
-    private slurReader: any; // (*) SlurReader;
-    private instrument: Instrument;
-    private voiceGeneratorsDict: { [n: number]: VoiceGenerator; } = {};
-    private staffMainVoiceGeneratorDict: { [staffId: number]: VoiceGenerator } = {};
-    private inSourceMeasureInstrumentIndex: number;
-    private divisions: number = 0;
-    private currentMeasure: SourceMeasure;
-    private previousMeasure: SourceMeasure;
-    private currentXmlMeasureIndex: number = 0;
-    private currentStaff: Staff;
-    private currentStaffEntry: SourceStaffEntry;
-    private activeClefs: ClefInstruction[];
-    private activeKey: KeyInstruction;
-    private activeRhythm: RhythmInstruction;
-    private activeClefsHaveBeenInitialized: boolean[];
-    private activeKeyHasBeenInitialized: boolean = false;
-    private abstractInstructions: [number, AbstractNotationInstruction][] = [];
-    private openChordSymbolContainer: ChordSymbolContainer;
-    // (*) private expressionReaders: ExpressionReader[];
-    private currentVoiceGenerator: VoiceGenerator;
-    //private openSlurDict: { [n: number]: Slur; } = {};
-    private maxTieNoteFraction: Fraction;
-
-    public get ActiveKey(): KeyInstruction {
-        return this.activeKey;
-    }
+  constructor(repetitionInstructionReader: RepetitionInstructionReader, xmlMeasureList: IXmlElement[], instrument: Instrument) {
+      this.repetitionInstructionReader = repetitionInstructionReader;
+      this.xmlMeasureList = xmlMeasureList;
+      this.musicSheet = instrument.GetMusicSheet;
+      this.instrument = instrument;
+      this.activeClefs = new Array(instrument.Staves.length);
+      this.activeClefsHaveBeenInitialized = new Array(instrument.Staves.length);
+      for (let i: number = 0; i < instrument.Staves.length; i++) {
+        this.activeClefsHaveBeenInitialized[i] = false;
+      }
+      // FIXME createExpressionGenerators(instrument.Staves.length);
+      // (*) this.slurReader = MusicSymbolModuleFactory.createSlurReader(this.musicSheet);
+  }
 
-    public get MaxTieNoteFraction(): Fraction {
-        return this.maxTieNoteFraction;
-    }
+  private repetitionInstructionReader: RepetitionInstructionReader;
+  private xmlMeasureList: IXmlElement[];
+  private musicSheet: MusicSheet;
+  private slurReader: any; // (*) SlurReader;
+  private instrument: Instrument;
+  private voiceGeneratorsDict: { [n: number]: VoiceGenerator; } = {};
+  private staffMainVoiceGeneratorDict: { [staffId: number]: VoiceGenerator } = {};
+  private inSourceMeasureInstrumentIndex: number;
+  private divisions: number = 0;
+  private currentMeasure: SourceMeasure;
+  private previousMeasure: SourceMeasure;
+  private currentXmlMeasureIndex: number = 0;
+  private currentStaff: Staff;
+  private currentStaffEntry: SourceStaffEntry;
+  private activeClefs: ClefInstruction[];
+  private activeKey: KeyInstruction;
+  private activeRhythm: RhythmInstruction;
+  private activeClefsHaveBeenInitialized: boolean[];
+  private activeKeyHasBeenInitialized: boolean = false;
+  private abstractInstructions: [number, AbstractNotationInstruction][] = [];
+  private openChordSymbolContainer: ChordSymbolContainer;
+  // (*) private expressionReaders: ExpressionReader[];
+  private currentVoiceGenerator: VoiceGenerator;
+  //private openSlurDict: { [n: number]: Slur; } = {};
+  private maxTieNoteFraction: Fraction;
 
-    public get ActiveRhythm(): RhythmInstruction {
-        return this.activeRhythm;
-    }
-
-    public set ActiveRhythm(value: RhythmInstruction) {
-        this.activeRhythm = value;
-    }
-
-    /**
-     * Main CreateSheet: read the next XML Measure and save all data to the given [[SourceMeasure]].
-     * @param currentMeasure
-     * @param measureStartAbsoluteTimestamp - Using this instead of currentMeasure.AbsoluteTimestamp as it isn't set yet
-     * @param guitarPro
-     * @returns {boolean}
-     */
-    public readNextXmlMeasure(currentMeasure: SourceMeasure, measureStartAbsoluteTimestamp: Fraction, guitarPro: boolean): boolean {
-        if (this.currentXmlMeasureIndex >= this.xmlMeasureList.length) {
-            return false;
-        }
-        this.currentMeasure = currentMeasure;
-        this.inSourceMeasureInstrumentIndex = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.instrument);
-        // (*) if (this.repetitionInstructionReader !== undefined) {
-        //  this.repetitionInstructionReader.prepareReadingMeasure(currentMeasure, this.currentXmlMeasureIndex);
-        //}
-        let currentFraction: Fraction = new Fraction(0, 1);
-        let previousFraction: Fraction = new Fraction(0, 1);
-        let divisionsException: boolean = false;
-        this.maxTieNoteFraction = new Fraction(0, 1);
-        let lastNoteWasGrace: boolean = false;
-        try {
-            let xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[this.currentXmlMeasureIndex].elements();
-            for (let xmlNode of xmlMeasureListArr) {
-                if (xmlNode.name === "note") {
-                    if (xmlNode.hasAttributes && xmlNode.attribute("print-object") && xmlNode.attribute("print-spacing")) {
-                        continue;
-                    }
-                    let noteStaff: number = 1;
-                    if (this.instrument.Staves.length > 1) {
-                        if (xmlNode.element("staff") !== undefined) {
-                            noteStaff = parseInt(xmlNode.element("staff").value, 10);
-                            if (isNaN(noteStaff)) {
-                                Logging.debug("InstrumentReader.readNextXmlMeasure.get staff number");
-                                noteStaff = 1;
-                            }
-                        }
-                    }
+  public get ActiveKey(): KeyInstruction {
+    return this.activeKey;
+  }
 
-                    this.currentStaff = this.instrument.Staves[noteStaff - 1];
-                    let isChord: boolean = xmlNode.element("chord") !== undefined;
-                    if (xmlNode.element("voice") !== undefined) {
-                        let noteVoice: number = parseInt(xmlNode.element("voice").value, 10);
-                        this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(noteVoice, noteStaff - 1);
-                    } else {
-                        if (!isChord || this.currentVoiceGenerator === undefined) {
-                            this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(1, noteStaff - 1);
-                        }
-                    }
-                    let noteDivisions: number = 0;
-                    let noteDuration: Fraction = new Fraction(0, 1);
-                    let isTuplet: boolean = false;
-                    if (xmlNode.element("duration") !== undefined) {
-                        noteDivisions = parseInt(xmlNode.element("duration").value, 10);
-                        if (!isNaN(noteDivisions)) {
-                            noteDuration = new Fraction(noteDivisions, 4 * this.divisions);
-                            if (noteDivisions === 0) {
-                                noteDuration = this.getNoteDurationFromTypeNode(xmlNode);
-                            }
-                            if (xmlNode.element("time-modification") !== undefined) {
-                                noteDuration = this.getNoteDurationForTuplet(xmlNode);
-                                isTuplet = true;
-                            }
-                        } else {
-                            let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError", "Invalid Note Duration.");
-                            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                            Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
-                            continue;
-                        }
-                    }
+  public get MaxTieNoteFraction(): Fraction {
+    return this.maxTieNoteFraction;
+  }
 
-                    let restNote: boolean = xmlNode.element("rest") !== undefined;
-                    //Logging.log("New note found!", noteDivisions, noteDuration.toString(), restNote);
-                    let isGraceNote: boolean = xmlNode.element("grace") !== undefined || noteDivisions === 0 || isChord && lastNoteWasGrace;
-                    let musicTimestamp: Fraction = currentFraction.clone();
-                    if (isChord) {
-                        musicTimestamp = previousFraction.clone();
-                    }
-                    this.currentStaffEntry = this.currentMeasure.findOrCreateStaffEntry(
-                        musicTimestamp,
-                        this.inSourceMeasureInstrumentIndex + noteStaff - 1,
-                        this.currentStaff
-                    ).staffEntry;
-                    //Logging.log("currentStaffEntry", this.currentStaffEntry, this.currentMeasure.VerticalSourceStaffEntryContainers.length);
+  public get ActiveRhythm(): RhythmInstruction {
+    return this.activeRhythm;
+  }
 
-                    if (!this.currentVoiceGenerator.hasVoiceEntry() || (!isChord && !isGraceNote && !lastNoteWasGrace) || (!lastNoteWasGrace && isGraceNote)) {
-                        this.currentVoiceGenerator.createVoiceEntry(musicTimestamp, this.currentStaffEntry, !restNote);
-                    }
-                    if (!isGraceNote && !isChord) {
-                        previousFraction = currentFraction.clone();
-                        currentFraction.Add(noteDuration);
-                    }
-                    if (
-                        isChord &&
-                        this.currentStaffEntry !== undefined &&
-                        this.currentStaffEntry.ParentStaff !== this.currentStaff
-                    ) {
-                        this.currentStaffEntry = this.currentVoiceGenerator.checkForStaffEntryLink(
-                            this.inSourceMeasureInstrumentIndex + noteStaff - 1, this.currentStaff, this.currentStaffEntry, this.currentMeasure
-                        );
-                    }
-                    let beginOfMeasure: boolean = (
-                        this.currentStaffEntry !== undefined &&
-                        this.currentStaffEntry.Timestamp !== undefined &&
-                        this.currentStaffEntry.Timestamp.Equals(new Fraction(0, 1)) && !this.currentStaffEntry.hasNotes()
-                    );
-                    this.saveAbstractInstructionList(this.instrument.Staves.length, beginOfMeasure);
-                    if (this.openChordSymbolContainer !== undefined) {
-                        this.currentStaffEntry.ChordContainer = this.openChordSymbolContainer;
-                        this.openChordSymbolContainer = undefined;
-                    }
-                    if (this.activeRhythm !== undefined) {
-                        // (*) this.musicSheet.SheetPlaybackSetting.Rhythm = this.activeRhythm.Rhythm;
-                    }
-                    if (isTuplet) {
-                        this.currentVoiceGenerator.read(
-                            xmlNode, noteDuration.Numerator,
-                            noteDuration.Denominator, restNote, isGraceNote,
-                            this.currentStaffEntry, this.currentMeasure,
-                            measureStartAbsoluteTimestamp,
-                            this.maxTieNoteFraction, isChord, guitarPro
-                        );
-                    } else {
-                        this.currentVoiceGenerator.read(
-                            xmlNode, noteDivisions, 4 * this.divisions,
-                            restNote, isGraceNote, this.currentStaffEntry,
-                            this.currentMeasure, measureStartAbsoluteTimestamp,
-                            this.maxTieNoteFraction, isChord, guitarPro
-                        );
-                    }
-                    let notationsNode: IXmlElement = xmlNode.element("notations");
-                    if (notationsNode !== undefined && notationsNode.element("dynamics") !== undefined) {
-                        // (*) let expressionReader: ExpressionReader = this.expressionReaders[this.readExpressionStaffNumber(xmlNode) - 1];
-                        //if (expressionReader !== undefined) {
-                        //  expressionReader.readExpressionParameters(
-                        //    xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, false
-                        //  );
-                        //  expressionReader.read(
-                        //    xmlNode, this.currentMeasure, previousFraction
-                        //  );
-                        //}
-                    }
-                    lastNoteWasGrace = isGraceNote;
-                } else if (xmlNode.name === "attributes") {
-                    let divisionsNode: IXmlElement = xmlNode.element("divisions");
-                    if (divisionsNode !== undefined) {
-                        this.divisions = parseInt(divisionsNode.value, 10);
-                        if (isNaN(this.divisions)) {
-                            let errorMsg: string = ITextTranslation.translateText(  "ReaderErrorMessages/DivisionError",
-                                                                                    "Invalid divisions value at Instrument: ");
-                            Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
-                            this.divisions = this.readDivisionsFromNotes();
-                            if (this.divisions > 0) {
-                                this.musicSheet.SheetErrors.push(errorMsg + this.instrument.Name);
-                            } else {
-                                divisionsException = true;
-                                throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
-                            }
-                        }
+  public set ActiveRhythm(value: RhythmInstruction) {
+    this.activeRhythm = value;
+  }
 
-                    }
-                    if (
-                        xmlNode.element("divisions") === undefined &&
-                        this.divisions === 0 &&
-                        this.currentXmlMeasureIndex === 0
-                    ) {
-                        let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DivisionError", "Invalid divisions value at Instrument: ");
-                        this.divisions = this.readDivisionsFromNotes();
-                        if (this.divisions > 0) {
-                            this.musicSheet.SheetErrors.push(errorMsg + this.instrument.Name);
-                        } else {
-                            divisionsException = true;
-                            throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
-                        }
-                    }
-                    this.addAbstractInstruction(xmlNode, guitarPro);
-                    if (currentFraction.Equals(new Fraction(0, 1)) &&
-                        this.isAttributesNodeAtBeginOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
-                        this.saveAbstractInstructionList(this.instrument.Staves.length, true);
-                    }
-                    if (this.isAttributesNodeAtEndOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
-                        this.saveClefInstructionAtEndOfMeasure();
-                    }
-                } else if (xmlNode.name === "forward") {
-                    let forFraction: number = parseInt(xmlNode.element("duration").value, 10);
-                    currentFraction.Add(new Fraction(forFraction, 4 * this.divisions));
-                } else if (xmlNode.name === "backup") {
-                    let backFraction: number = parseInt(xmlNode.element("duration").value, 10);
-                    currentFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
-                    if (currentFraction.Numerator < 0) {
-                        currentFraction = new Fraction(0, 1);
-                    }
-                    previousFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
-                    if (previousFraction.Numerator < 0) {
-                        previousFraction = new Fraction(0, 1);
-                    }
-                } else if (xmlNode.name === "direction") {
-                    // unused let directionTypeNode: IXmlElement = xmlNode.element("direction-type");
-                    // (*) MetronomeReader.readMetronomeInstructions(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
-                    let relativePositionInMeasure: number = Math.min(1, currentFraction.RealValue);
-                    if (this.activeRhythm !== undefined && this.activeRhythm.Rhythm !== undefined) {
-                        relativePositionInMeasure /= this.activeRhythm.Rhythm.RealValue;
-                    }
-                    // unused: let handeled: boolean = false;
-                    // (*) if (this.repetitionInstructionReader !== undefined) {
-                    //  handeled = this.repetitionInstructionReader.handleRepetitionInstructionsFromWordsOrSymbols(directionTypeNode,
-                    //                                                                                              relativePositionInMeasure);
-                    //}
-                    //if (!handeled) {
-                    //  let expressionReader: ExpressionReader = this.expressionReaders[0];
-                    //  let staffIndex: number = this.readExpressionStaffNumber(xmlNode) - 1;
-                    //  if (staffIndex < this.expressionReaders.length) {
-                    //    expressionReader = this.expressionReaders[staffIndex];
-                    //  }
-                    //  if (expressionReader !== undefined) {
-                    //    if (directionTypeNode.element("octave-shift") !== undefined) {
-                    //      expressionReader.readExpressionParameters(
-                    //        xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, true
-                    //      );
-                    //      expressionReader.addOctaveShift(xmlNode, this.currentMeasure, previousFraction.clone());
-                    //    }
-                    //    expressionReader.readExpressionParameters(
-                    //      xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, false
-                    //    );
-                    //    expressionReader.read(xmlNode, this.currentMeasure, currentFraction);
-                    //  }
-                    //}
-                } else if (xmlNode.name === "barline") {
-                    // (*)
-                    //if (this.repetitionInstructionReader !== undefined) {
-                    //  let measureEndsSystem: boolean = false;
-                    //  this.repetitionInstructionReader.handleLineRepetitionInstructions(xmlNode, measureEndsSystem);
-                    //  if (measureEndsSystem) {
-                    //    this.currentMeasure.BreakSystemAfter = true;
-                    //    this.currentMeasure.endsPiece = true;
-                    //  }
-                    //}
-                } else if (xmlNode.name === "sound") {
-                    // (*) MetronomeReader.readTempoInstruction(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
-                } else if (xmlNode.name === "harmony") {
-                    // (*) this.openChordSymbolContainer = ChordSymbolReader.readChordSymbol(xmlNode, this.musicSheet, this.activeKey);
-                }
-            }
-            for (let j in this.voiceGeneratorsDict) {
-                if (this.voiceGeneratorsDict.hasOwnProperty(j)) {
-                    let voiceGenerator: VoiceGenerator = this.voiceGeneratorsDict[j];
-                    voiceGenerator.checkForOpenBeam();
-                    voiceGenerator.checkForOpenGraceNotes();
-                }
+  /**
+   * Main CreateSheet: read the next XML Measure and save all data to the given [[SourceMeasure]].
+   * @param currentMeasure
+   * @param measureStartAbsoluteTimestamp - Using this instead of currentMeasure.AbsoluteTimestamp as it isn't set yet
+   * @param guitarPro
+   * @returns {boolean}
+   */
+  public readNextXmlMeasure(currentMeasure: SourceMeasure, measureStartAbsoluteTimestamp: Fraction, guitarPro: boolean): boolean {
+    if (this.currentXmlMeasureIndex >= this.xmlMeasureList.length) {
+      return false;
+    }
+    this.currentMeasure = currentMeasure;
+    this.inSourceMeasureInstrumentIndex = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.instrument);
+    if (this.repetitionInstructionReader !== undefined) {
+     this.repetitionInstructionReader.prepareReadingMeasure(currentMeasure, this.currentXmlMeasureIndex);
+    }
+    let currentFraction: Fraction = new Fraction(0, 1);
+    let previousFraction: Fraction = new Fraction(0, 1);
+    let divisionsException: boolean = false;
+    this.maxTieNoteFraction = new Fraction(0, 1);
+    let lastNoteWasGrace: boolean = false;
+    try {
+      const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[this.currentXmlMeasureIndex].elements();
+      for (const xmlNode of xmlMeasureListArr) {
+        if (xmlNode.name === "note") {
+          if (xmlNode.hasAttributes && xmlNode.attribute("print-object") && xmlNode.attribute("print-spacing")) {
+            continue;
+          }
+          let noteStaff: number = 1;
+          if (this.instrument.Staves.length > 1) {
+            if (xmlNode.element("staff") !== undefined) {
+              noteStaff = parseInt(xmlNode.element("staff").value, 10);
+              if (isNaN(noteStaff)) {
+                Logging.debug("InstrumentReader.readNextXmlMeasure.get staff number");
+                noteStaff = 1;
+              }
             }
-            if (this.currentXmlMeasureIndex === this.xmlMeasureList.length - 1) {
-                for (let i: number = 0; i < this.instrument.Staves.length; i++) {
-                    if (!this.activeClefsHaveBeenInitialized[i]) {
-                        this.createDefaultClefInstruction(this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.instrument) + i);
-                    }
-                }
-                if (!this.activeKeyHasBeenInitialized) {
-                    this.createDefaultKeyInstruction();
-                }
-                // (*)
-                //for (let i: number = 0; i < this.expressionReaders.length; i++) {
-                //  let reader: ExpressionReader = this.expressionReaders[i];
-                //  if (reader !== undefined) {
-                //    reader.checkForOpenExpressions(this.currentMeasure, currentFraction);
-                //  }
-                //}
+          }
+
+          this.currentStaff = this.instrument.Staves[noteStaff - 1];
+          const isChord: boolean = xmlNode.element("chord") !== undefined;
+          if (xmlNode.element("voice") !== undefined) {
+            const noteVoice: number = parseInt(xmlNode.element("voice").value, 10);
+            this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(noteVoice, noteStaff - 1);
+          } else {
+            if (!isChord || this.currentVoiceGenerator === undefined) {
+              this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(1, noteStaff - 1);
             }
-        } catch (e) {
-            if (divisionsException) {
-                throw new MusicSheetReadingException(e.Message);
+          }
+          let noteDivisions: number = 0;
+          let noteDuration: Fraction = new Fraction(0, 1);
+          let isTuplet: boolean = false;
+          if (xmlNode.element("duration") !== undefined) {
+            noteDivisions = parseInt(xmlNode.element("duration").value, 10);
+            if (!isNaN(noteDivisions)) {
+              noteDuration = new Fraction(noteDivisions, 4 * this.divisions);
+              if (noteDivisions === 0) {
+                noteDuration = this.getNoteDurationFromTypeNode(xmlNode);
+              }
+              if (xmlNode.element("time-modification") !== undefined) {
+                noteDuration = this.getNoteDurationForTuplet(xmlNode);
+                isTuplet = true;
+              }
+            } else {
+              const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError", "Invalid Note Duration.");
+              this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+              Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
+              continue;
             }
-            let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/MeasureError", "Error while reading Measure.");
-            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-            Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg, e);
-        }
+          }
 
-        this.previousMeasure = this.currentMeasure;
-        this.currentXmlMeasureIndex += 1;
-        return true;
-    }
+          const restNote: boolean = xmlNode.element("rest") !== undefined;
+          //Logging.log("New note found!", noteDivisions, noteDuration.toString(), restNote);
+          const isGraceNote: boolean = xmlNode.element("grace") !== undefined || noteDivisions === 0 || isChord && lastNoteWasGrace;
+          let musicTimestamp: Fraction = currentFraction.clone();
+          if (isChord) {
+            musicTimestamp = previousFraction.clone();
+          }
+          this.currentStaffEntry = this.currentMeasure.findOrCreateStaffEntry(
+            musicTimestamp,
+            this.inSourceMeasureInstrumentIndex + noteStaff - 1,
+            this.currentStaff
+          ).staffEntry;
+          //Logging.log("currentStaffEntry", this.currentStaffEntry, this.currentMeasure.VerticalSourceStaffEntryContainers.length);
 
-    public doCalculationsAfterDurationHasBeenSet(): void {
-        for (let j in this.voiceGeneratorsDict) {
-            if (this.voiceGeneratorsDict.hasOwnProperty(j)) {
-                this.voiceGeneratorsDict[j].checkOpenTies();
+          if (!this.currentVoiceGenerator.hasVoiceEntry() || (!isChord && !isGraceNote && !lastNoteWasGrace) || (!lastNoteWasGrace && isGraceNote)) {
+            this.currentVoiceGenerator.createVoiceEntry(musicTimestamp, this.currentStaffEntry, !restNote);
+          }
+          if (!isGraceNote && !isChord) {
+            previousFraction = currentFraction.clone();
+            currentFraction.Add(noteDuration);
+          }
+          if (
+            isChord &&
+            this.currentStaffEntry !== undefined &&
+            this.currentStaffEntry.ParentStaff !== this.currentStaff
+          ) {
+            this.currentStaffEntry = this.currentVoiceGenerator.checkForStaffEntryLink(
+              this.inSourceMeasureInstrumentIndex + noteStaff - 1, this.currentStaff, this.currentStaffEntry, this.currentMeasure
+            );
+          }
+          const beginOfMeasure: boolean = (
+            this.currentStaffEntry !== undefined &&
+            this.currentStaffEntry.Timestamp !== undefined &&
+            this.currentStaffEntry.Timestamp.Equals(new Fraction(0, 1)) && !this.currentStaffEntry.hasNotes()
+          );
+          this.saveAbstractInstructionList(this.instrument.Staves.length, beginOfMeasure);
+          if (this.openChordSymbolContainer !== undefined) {
+            this.currentStaffEntry.ChordContainer = this.openChordSymbolContainer;
+            this.openChordSymbolContainer = undefined;
+          }
+          if (this.activeRhythm !== undefined) {
+            // (*) this.musicSheet.SheetPlaybackSetting.Rhythm = this.activeRhythm.Rhythm;
+          }
+          if (isTuplet) {
+            this.currentVoiceGenerator.read(
+              xmlNode, noteDuration, restNote, isGraceNote,
+              this.currentStaffEntry, this.currentMeasure,
+              measureStartAbsoluteTimestamp,
+              this.maxTieNoteFraction, isChord, guitarPro
+            );
+          } else {
+            this.currentVoiceGenerator.read(
+              xmlNode, new Fraction(noteDivisions, 4 * this.divisions),
+              restNote, isGraceNote, this.currentStaffEntry,
+              this.currentMeasure, measureStartAbsoluteTimestamp,
+              this.maxTieNoteFraction, isChord, guitarPro
+            );
+          }
+          const notationsNode: IXmlElement = xmlNode.element("notations");
+          if (notationsNode !== undefined && notationsNode.element("dynamics") !== undefined) {
+            // (*) let expressionReader: ExpressionReader = this.expressionReaders[this.readExpressionStaffNumber(xmlNode) - 1];
+            //if (expressionReader !== undefined) {
+            //  expressionReader.readExpressionParameters(
+            //    xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, false
+            //  );
+            //  expressionReader.read(
+            //    xmlNode, this.currentMeasure, previousFraction
+            //  );
+            //}
+          }
+          lastNoteWasGrace = isGraceNote;
+        } else if (xmlNode.name === "attributes") {
+          const divisionsNode: IXmlElement = xmlNode.element("divisions");
+          if (divisionsNode !== undefined) {
+            this.divisions = parseInt(divisionsNode.value, 10);
+            if (isNaN(this.divisions)) {
+              const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DivisionError",
+                                                                      "Invalid divisions value at Instrument: ");
+              Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
+              this.divisions = this.readDivisionsFromNotes();
+              if (this.divisions > 0) {
+                this.musicSheet.SheetErrors.push(errorMsg + this.instrument.Name);
+              } else {
+                divisionsException = true;
+                throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
+              }
             }
-        }
-    }
 
-    /**
-     * Get or create the passing [[VoiceGenerator]].
-     * @param voiceId
-     * @param staffId
-     * @returns {VoiceGenerator}
-     */
-    private getOrCreateVoiceGenerator(voiceId: number, staffId: number): VoiceGenerator {
-        let staff: Staff = this.instrument.Staves[staffId];
-        let voiceGenerator: VoiceGenerator = this.voiceGeneratorsDict[voiceId];
-        if (voiceGenerator !== undefined) {
-            if (staff.Voices.indexOf(voiceGenerator.GetVoice) === -1) {
-                staff.Voices.push(voiceGenerator.GetVoice);
-            }
-        } else {
-            let mainVoiceGenerator: VoiceGenerator = this.staffMainVoiceGeneratorDict[staffId];
-            if (mainVoiceGenerator !== undefined) {
-                voiceGenerator = new VoiceGenerator(this.instrument, voiceId, this.slurReader, mainVoiceGenerator.GetVoice);
-                staff.Voices.push(voiceGenerator.GetVoice);
-                this.voiceGeneratorsDict[voiceId] = voiceGenerator;
+          }
+          if (
+            xmlNode.element("divisions") === undefined &&
+            this.divisions === 0 &&
+            this.currentXmlMeasureIndex === 0
+          ) {
+            const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DivisionError", "Invalid divisions value at Instrument: ");
+            this.divisions = this.readDivisionsFromNotes();
+            if (this.divisions > 0) {
+              this.musicSheet.SheetErrors.push(errorMsg + this.instrument.Name);
             } else {
-                voiceGenerator = new VoiceGenerator(this.instrument, voiceId, this.slurReader);
-                staff.Voices.push(voiceGenerator.GetVoice);
-                this.voiceGeneratorsDict[voiceId] = voiceGenerator;
-                this.staffMainVoiceGeneratorDict[staffId] = voiceGenerator;
+              divisionsException = true;
+              throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
             }
+          }
+          this.addAbstractInstruction(xmlNode, guitarPro);
+          if (currentFraction.Equals(new Fraction(0, 1)) &&
+            this.isAttributesNodeAtBeginOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
+            this.saveAbstractInstructionList(this.instrument.Staves.length, true);
+          }
+          if (this.isAttributesNodeAtEndOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
+            this.saveClefInstructionAtEndOfMeasure();
+          }
+        } else if (xmlNode.name === "forward") {
+          const forFraction: number = parseInt(xmlNode.element("duration").value, 10);
+          currentFraction.Add(new Fraction(forFraction, 4 * this.divisions));
+        } else if (xmlNode.name === "backup") {
+          const backFraction: number = parseInt(xmlNode.element("duration").value, 10);
+          currentFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
+          if (currentFraction.IsNegative()) {
+            currentFraction = new Fraction(0, 1);
+          }
+          previousFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
+          if (previousFraction.IsNegative()) {
+            previousFraction = new Fraction(0, 1);
+          }
+        } else if (xmlNode.name === "direction") {
+          // unused let directionTypeNode: IXmlElement = xmlNode.element("direction-type");
+          // (*) MetronomeReader.readMetronomeInstructions(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
+          let relativePositionInMeasure: number = Math.min(1, currentFraction.RealValue);
+          if (this.activeRhythm !== undefined && this.activeRhythm.Rhythm !== undefined) {
+            relativePositionInMeasure /= this.activeRhythm.Rhythm.RealValue;
+          }
+          const directionTypeNode: IXmlElement = xmlNode.element("direction-type");
+          //let handeled: boolean = false;
+          if (this.repetitionInstructionReader !== undefined) {
+            //handeled =
+            this.repetitionInstructionReader.handleRepetitionInstructionsFromWordsOrSymbols(directionTypeNode,
+                                                                                            relativePositionInMeasure);
+          }
+          //}
+          //if (!handeled) {
+          //  let expressionReader: ExpressionReader = this.expressionReaders[0];
+          //  let staffIndex: number = this.readExpressionStaffNumber(xmlNode) - 1;
+          //  if (staffIndex < this.expressionReaders.length) {
+          //    expressionReader = this.expressionReaders[staffIndex];
+          //  }
+          //  if (expressionReader !== undefined) {
+          //    if (directionTypeNode.element("octave-shift") !== undefined) {
+          //      expressionReader.readExpressionParameters(
+          //        xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, true
+          //      );
+          //      expressionReader.addOctaveShift(xmlNode, this.currentMeasure, previousFraction.clone());
+          //    }
+          //    expressionReader.readExpressionParameters(
+          //      xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, false
+          //    );
+          //    expressionReader.read(xmlNode, this.currentMeasure, currentFraction);
+          //  }
+          //}
+        } else if (xmlNode.name === "barline") {
+          if (this.repetitionInstructionReader !== undefined) {
+           const measureEndsSystem: boolean = false;
+           this.repetitionInstructionReader.handleLineRepetitionInstructions(xmlNode, measureEndsSystem);
+           if (measureEndsSystem) {
+             this.currentMeasure.BreakSystemAfter = true;
+             this.currentMeasure.endsPiece = true;
+           }
+          }
+        } else if (xmlNode.name === "sound") {
+          // (*) MetronomeReader.readTempoInstruction(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
+        } else if (xmlNode.name === "harmony") {
+                    this.openChordSymbolContainer = ChordSymbolReader.readChordSymbol(xmlNode, this.musicSheet, this.activeKey);
+        }
+      }
+      for (const j in this.voiceGeneratorsDict) {
+        if (this.voiceGeneratorsDict.hasOwnProperty(j)) {
+          const voiceGenerator: VoiceGenerator = this.voiceGeneratorsDict[j];
+          voiceGenerator.checkForOpenBeam();
+          voiceGenerator.checkForOpenGraceNotes();
+        }
+      }
+      if (this.currentXmlMeasureIndex === this.xmlMeasureList.length - 1) {
+        for (let i: number = 0; i < this.instrument.Staves.length; i++) {
+          if (!this.activeClefsHaveBeenInitialized[i]) {
+            this.createDefaultClefInstruction(this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.instrument) + i);
+          }
+        }
+        if (!this.activeKeyHasBeenInitialized) {
+          this.createDefaultKeyInstruction();
         }
-        return voiceGenerator;
+        // (*)
+        //for (let i: number = 0; i < this.expressionReaders.length; i++) {
+        //  let reader: ExpressionReader = this.expressionReaders[i];
+        //  if (reader !== undefined) {
+        //    reader.checkForOpenExpressions(this.currentMeasure, currentFraction);
+        //  }
+        //}
+      }
+    } catch (e) {
+      if (divisionsException) {
+        throw new MusicSheetReadingException(e.Message);
+      }
+      const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/MeasureError", "Error while reading Measure.");
+      this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+      Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg, e);
     }
 
+    this.previousMeasure = this.currentMeasure;
+    this.currentXmlMeasureIndex += 1;
+    return true;
+  }
 
-    //private createExpressionGenerators(numberOfStaves: number): void {
-    //  // (*)
-    //  //this.expressionReaders = new Array(numberOfStaves);
-    //  //for (let i: number = 0; i < numberOfStaves; i++) {
-    //  //  this.expressionReaders[i] = MusicSymbolModuleFactory.createExpressionGenerator(this.musicSheet, this.instrument, i + 1);
-    //  //}
-    //}
+  public doCalculationsAfterDurationHasBeenSet(): void {
+    for (const j in this.voiceGeneratorsDict) {
+      if (this.voiceGeneratorsDict.hasOwnProperty(j)) {
+        this.voiceGeneratorsDict[j].checkOpenTies();
+      }
+    }
+  }
 
-    /**
-     * Create the default [[ClefInstruction]] for the given staff index.
-     * @param staffIndex
-     */
-    private createDefaultClefInstruction(staffIndex: number): void {
-        let first: SourceMeasure;
-        if (this.musicSheet.SourceMeasures.length > 0) {
-            first = this.musicSheet.SourceMeasures[0];
-        } else {
-            first = this.currentMeasure;
-        }
-        let clefInstruction: ClefInstruction = new ClefInstruction(ClefEnum.G, 0, 2);
-        let firstStaffEntry: SourceStaffEntry;
-        if (first.FirstInstructionsStaffEntries[staffIndex] === undefined) {
-            firstStaffEntry = new SourceStaffEntry(undefined, undefined);
-            first.FirstInstructionsStaffEntries[staffIndex] = firstStaffEntry;
-        } else {
-            firstStaffEntry = first.FirstInstructionsStaffEntries[staffIndex];
-            firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
-        }
-        clefInstruction.Parent = firstStaffEntry;
-        firstStaffEntry.Instructions.splice(0, 0, clefInstruction);
+  /**
+   * Get or create the passing [[VoiceGenerator]].
+   * @param voiceId
+   * @param staffId
+   * @returns {VoiceGenerator}
+   */
+  private getOrCreateVoiceGenerator(voiceId: number, staffId: number): VoiceGenerator {
+    const staff: Staff = this.instrument.Staves[staffId];
+    let voiceGenerator: VoiceGenerator = this.voiceGeneratorsDict[voiceId];
+    if (voiceGenerator !== undefined) {
+      if (staff.Voices.indexOf(voiceGenerator.GetVoice) === -1) {
+        staff.Voices.push(voiceGenerator.GetVoice);
+      }
+    } else {
+      const mainVoiceGenerator: VoiceGenerator = this.staffMainVoiceGeneratorDict[staffId];
+      if (mainVoiceGenerator !== undefined) {
+        voiceGenerator = new VoiceGenerator(this.instrument, voiceId, this.slurReader, mainVoiceGenerator.GetVoice);
+        staff.Voices.push(voiceGenerator.GetVoice);
+        this.voiceGeneratorsDict[voiceId] = voiceGenerator;
+      } else {
+        voiceGenerator = new VoiceGenerator(this.instrument, voiceId, this.slurReader);
+        staff.Voices.push(voiceGenerator.GetVoice);
+        this.voiceGeneratorsDict[voiceId] = voiceGenerator;
+        this.staffMainVoiceGeneratorDict[staffId] = voiceGenerator;
+      }
+    }
+    return voiceGenerator;
+  }
+
+
+  //private createExpressionGenerators(numberOfStaves: number): void {
+  //  // (*)
+  //  //this.expressionReaders = new Array(numberOfStaves);
+  //  //for (let i: number = 0; i < numberOfStaves; i++) {
+  //  //  this.expressionReaders[i] = MusicSymbolModuleFactory.createExpressionGenerator(this.musicSheet, this.instrument, i + 1);
+  //  //}
+  //}
+
+  /**
+   * Create the default [[ClefInstruction]] for the given staff index.
+   * @param staffIndex
+   */
+  private createDefaultClefInstruction(staffIndex: number): void {
+    let first: SourceMeasure;
+    if (this.musicSheet.SourceMeasures.length > 0) {
+      first = this.musicSheet.SourceMeasures[0];
+    } else {
+      first = this.currentMeasure;
+    }
+    const clefInstruction: ClefInstruction = new ClefInstruction(ClefEnum.G, 0, 2);
+    let firstStaffEntry: SourceStaffEntry;
+    if (first.FirstInstructionsStaffEntries[staffIndex] === undefined) {
+      firstStaffEntry = new SourceStaffEntry(undefined, undefined);
+      first.FirstInstructionsStaffEntries[staffIndex] = firstStaffEntry;
+    } else {
+      firstStaffEntry = first.FirstInstructionsStaffEntries[staffIndex];
+      firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
     }
+    clefInstruction.Parent = firstStaffEntry;
+    firstStaffEntry.Instructions.splice(0, 0, clefInstruction);
+  }
 
-    /**
-     * Create the default [[KeyInstruction]] in case no [[KeyInstruction]] is given in the whole [[Instrument]].
-     */
-    private createDefaultKeyInstruction(): void {
-        let first: SourceMeasure;
-        if (this.musicSheet.SourceMeasures.length > 0) {
-            first = this.musicSheet.SourceMeasures[0];
+  /**
+   * Create the default [[KeyInstruction]] in case no [[KeyInstruction]] is given in the whole [[Instrument]].
+   */
+  private createDefaultKeyInstruction(): void {
+    let first: SourceMeasure;
+    if (this.musicSheet.SourceMeasures.length > 0) {
+      first = this.musicSheet.SourceMeasures[0];
+    } else {
+      first = this.currentMeasure;
+    }
+    const keyInstruction: KeyInstruction = new KeyInstruction(undefined, 0, KeyEnum.major);
+    for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + this.instrument.Staves.length; j++) {
+      if (first.FirstInstructionsStaffEntries[j] === undefined) {
+        const firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+        first.FirstInstructionsStaffEntries[j] = firstStaffEntry;
+        keyInstruction.Parent = firstStaffEntry;
+        firstStaffEntry.Instructions.push(keyInstruction);
+      } else {
+        const firstStaffEntry: SourceStaffEntry = first.FirstInstructionsStaffEntries[j];
+        keyInstruction.Parent = firstStaffEntry;
+        firstStaffEntry.removeFirstInstructionOfTypeKeyInstruction();
+        if (firstStaffEntry.Instructions[0] instanceof ClefInstruction) {
+          firstStaffEntry.Instructions.splice(1, 0, keyInstruction);
         } else {
-            first = this.currentMeasure;
-        }
-        let keyInstruction: KeyInstruction = new KeyInstruction(undefined, 0, KeyEnum.major);
-        for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + this.instrument.Staves.length; j++) {
-            if (first.FirstInstructionsStaffEntries[j] === undefined) {
-                let firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
-                first.FirstInstructionsStaffEntries[j] = firstStaffEntry;
-                keyInstruction.Parent = firstStaffEntry;
-                firstStaffEntry.Instructions.push(keyInstruction);
-            } else {
-                let firstStaffEntry: SourceStaffEntry = first.FirstInstructionsStaffEntries[j];
-                keyInstruction.Parent = firstStaffEntry;
-                firstStaffEntry.removeFirstInstructionOfTypeKeyInstruction();
-                if (firstStaffEntry.Instructions[0] instanceof ClefInstruction) {
-                    firstStaffEntry.Instructions.splice(1, 0, keyInstruction);
-                } else {
-                    firstStaffEntry.Instructions.splice(0, 0, keyInstruction);
-                }
-            }
+          firstStaffEntry.Instructions.splice(0, 0, keyInstruction);
         }
+      }
     }
+  }
 
-    /**
-     * Check if the given attributesNode is at the begin of a XmlMeasure.
-     * @param parentNode
-     * @param attributesNode
-     * @returns {boolean}
-     */
-    private isAttributesNodeAtBeginOfMeasure(parentNode: IXmlElement, attributesNode: IXmlElement): boolean {
-        let children: IXmlElement[] = parentNode.elements();
-        let attributesNodeIndex: number = children.indexOf(attributesNode); // FIXME | 0
-        if (attributesNodeIndex > 0 && children[attributesNodeIndex - 1].name === "backup") {
-            return true;
-        }
-        let firstNoteNodeIndex: number = -1;
-        for (let i: number = 0; i < children.length; i++) {
-            if (children[i].name === "note") {
-                firstNoteNodeIndex = i;
-                break;
-            }
-        }
-        return (attributesNodeIndex < firstNoteNodeIndex && firstNoteNodeIndex > 0) || (firstNoteNodeIndex < 0);
+  /**
+   * Check if the given attributesNode is at the begin of a XmlMeasure.
+   * @param parentNode
+   * @param attributesNode
+   * @returns {boolean}
+   */
+  private isAttributesNodeAtBeginOfMeasure(parentNode: IXmlElement, attributesNode: IXmlElement): boolean {
+    const children: IXmlElement[] = parentNode.elements();
+    const attributesNodeIndex: number = children.indexOf(attributesNode); // FIXME | 0
+    if (attributesNodeIndex > 0 && children[attributesNodeIndex - 1].name === "backup") {
+      return true;
+    }
+    let firstNoteNodeIndex: number = -1;
+    for (let i: number = 0; i < children.length; i++) {
+      if (children[i].name === "note") {
+        firstNoteNodeIndex = i;
+        break;
+      }
     }
+    return (attributesNodeIndex < firstNoteNodeIndex && firstNoteNodeIndex > 0) || (firstNoteNodeIndex < 0);
+  }
 
-    /**
-     * Check if the given attributesNode is at the end of a XmlMeasure.
-     * @param parentNode
-     * @param attributesNode
-     * @returns {boolean}
-     */
-    private isAttributesNodeAtEndOfMeasure(parentNode: IXmlElement, attributesNode: IXmlElement): boolean {
-        let childs: IXmlElement[] = parentNode.elements().slice();
-        let attributesNodeIndex: number = 0;
-        for (let i: number = 0; i < childs.length; i++) {
-            if (childs[i] === attributesNode) {
-                attributesNodeIndex = i;
-                break;
-            }
-        }
-        let nextNoteNodeIndex: number = 0;
-        for (let i: number = attributesNodeIndex; i < childs.length; i++) {
-            if (childs[i].name === "note") {
-                nextNoteNodeIndex = i;
-                break;
-            }
-        }
-        return attributesNodeIndex > nextNoteNodeIndex;
+  /**
+   * Check if the given attributesNode is at the end of a XmlMeasure.
+   * @param parentNode
+   * @param attributesNode
+   * @returns {boolean}
+   */
+  private isAttributesNodeAtEndOfMeasure(parentNode: IXmlElement, attributesNode: IXmlElement): boolean {
+    const childs: IXmlElement[] = parentNode.elements().slice();
+    let attributesNodeIndex: number = 0;
+    for (let i: number = 0; i < childs.length; i++) {
+      if (childs[i] === attributesNode) {
+        attributesNodeIndex = i;
+        break;
+      }
+    }
+    let nextNoteNodeIndex: number = 0;
+    for (let i: number = attributesNodeIndex; i < childs.length; i++) {
+      if (childs[i].name === "note") {
+        nextNoteNodeIndex = i;
+        break;
+      }
     }
+    return attributesNodeIndex > nextNoteNodeIndex;
+  }
 
-    /**
-     * Called only when no noteDuration is given in XML.
-     * @param xmlNode
-     * @returns {Fraction}
-     */
-    private getNoteDurationFromTypeNode(xmlNode: IXmlElement): Fraction {
-        if (xmlNode.element("type") !== undefined) {
-            let typeNode: IXmlElement = xmlNode.element("type");
-            if (typeNode !== undefined) {
-                let type: string = typeNode.value;
-                return this.currentVoiceGenerator.getNoteDurationFromType(type);
-            }
-        }
-        return new Fraction(0, 4 * this.divisions);
+  /**
+   * Called only when no noteDuration is given in XML.
+   * @param xmlNode
+   * @returns {Fraction}
+   */
+  private getNoteDurationFromTypeNode(xmlNode: IXmlElement): Fraction {
+    if (xmlNode.element("type") !== undefined) {
+      const typeNode: IXmlElement = xmlNode.element("type");
+      if (typeNode !== undefined) {
+        const type: string = typeNode.value;
+        return this.currentVoiceGenerator.getNoteDurationFromType(type);
+      }
     }
+    return new Fraction(0, 4 * this.divisions);
+  }
+
+  /**
+   * Add (the three basic) Notation Instructions to a list
+   * @param node
+   * @param guitarPro
+   */
+  private addAbstractInstruction(node: IXmlElement, guitarPro: boolean): void {
+    if (node.element("divisions") !== undefined) {
+      if (node.elements().length === 1) {
+        return;
+      }
+    }
+    const transposeNode: IXmlElement = node.element("transpose");
+    if (transposeNode !== undefined) {
+      const chromaticNode: IXmlElement = transposeNode.element("chromatic");
+      if (chromaticNode !== undefined) {
+        this.instrument.PlaybackTranspose = parseInt(chromaticNode.value, 10);
+      }
+    }
+    const clefList: IXmlElement[] = node.elements("clef");
+    let errorMsg: string;
+    if (clefList.length > 0) {
+      for (let idx: number = 0, len: number = clefList.length; idx < len; ++idx) {
+        const nodeList: IXmlElement = clefList[idx];
+        let clefEnum: ClefEnum = ClefEnum.G;
+        let line: number = 2;
+        let staffNumber: number = 1;
+        let clefOctaveOffset: number = 0;
+        const lineNode: IXmlElement = nodeList.element("line");
+        if (lineNode !== undefined) {
+          try {
+            line = parseInt(lineNode.value, 10);
+          } catch (ex) {
+            errorMsg = ITextTranslation.translateText(
+              "ReaderErrorMessages/ClefLineError",
+              "Invalid clef line given -> using default clef line."
+            );
+            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+            line = 2;
+            Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+          }
 
-    /**
-     * Add (the three basic) Notation Instructions to a list
-     * @param node
-     * @param guitarPro
-     */
-    private addAbstractInstruction(node: IXmlElement, guitarPro: boolean): void {
-        if (node.element("divisions") !== undefined) {
-            if (node.elements().length === 1) {
-                return;
-            }
         }
-        let transposeNode: IXmlElement = node.element("transpose");
-        if (transposeNode !== undefined) {
-            let chromaticNode: IXmlElement = transposeNode.element("chromatic");
-            if (chromaticNode !== undefined) {
-                this.instrument.PlaybackTranspose = parseInt(chromaticNode.value, 10);
+        const signNode: IXmlElement = nodeList.element("sign");
+        if (signNode !== undefined) {
+          try {
+            clefEnum = ClefEnum[signNode.value];
+            if (!ClefInstruction.isSupportedClef(clefEnum)) {
+              if (clefEnum === ClefEnum.TAB && guitarPro) {
+                clefOctaveOffset = -1;
+              }
+              errorMsg = ITextTranslation.translateText(
+                "ReaderErrorMessages/ClefError",
+                "Unsupported clef found -> using default clef."
+              );
+              this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+              clefEnum = ClefEnum.G;
+              line = 2;
             }
-        }
-        let clefList: IXmlElement[] = node.elements("clef");
-        let errorMsg: string;
-        if (clefList.length > 0) {
-            for (let idx: number = 0, len: number = clefList.length; idx < len; ++idx) {
-                let nodeList: IXmlElement = clefList[idx];
-                let clefEnum: ClefEnum = ClefEnum.G;
-                let line: number = 2;
-                let staffNumber: number = 1;
-                let clefOctaveOffset: number = 0;
-                let lineNode: IXmlElement = nodeList.element("line");
-                if (lineNode !== undefined) {
-                    try {
-                        line = parseInt(lineNode.value, 10);
-                    } catch (ex) {
-                        errorMsg = ITextTranslation.translateText(
-                            "ReaderErrorMessages/ClefLineError",
-                            "Invalid clef line given -> using default clef line."
-                        );
-                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                        line = 2;
-                        Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
-                    }
-
-                }
-                let signNode: IXmlElement = nodeList.element("sign");
-                if (signNode !== undefined) {
-                    try {
-                        clefEnum = ClefEnum[signNode.value];
-                        if (!ClefInstruction.isSupportedClef(clefEnum)) {
-                            if (clefEnum === ClefEnum.TAB && guitarPro) {
-                                clefOctaveOffset = -1;
-                            }
-                            errorMsg = ITextTranslation.translateText(
-                                "ReaderErrorMessages/ClefError",
-                                "Unsupported clef found -> using default clef."
-                            );
-                            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                            clefEnum = ClefEnum.G;
-                            line = 2;
-                        }
-                    } catch (e) {
-                        errorMsg = ITextTranslation.translateText(
-                            "ReaderErrorMessages/ClefError",
-                            "Invalid clef found -> using default clef."
-                        );
-                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                        clefEnum = ClefEnum.G;
-                        line = 2;
-                        Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, e);
-                    }
+          } catch (e) {
+            errorMsg = ITextTranslation.translateText(
+              "ReaderErrorMessages/ClefError",
+              "Invalid clef found -> using default clef."
+            );
+            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+            clefEnum = ClefEnum.G;
+            line = 2;
+            Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, e);
+          }
 
-                }
-                let clefOctaveNode: IXmlElement = nodeList.element("clef-octave-change");
-                if (clefOctaveNode !== undefined) {
-                    try {
-                        clefOctaveOffset = parseInt(clefOctaveNode.value, 10);
-                    } catch (e) {
-                        errorMsg = ITextTranslation.translateText(
-                            "ReaderErrorMessages/ClefOctaveError",
-                            "Invalid clef octave found -> using default clef octave."
-                        );
-                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                        clefOctaveOffset = 0;
-                    }
+        }
+        const clefOctaveNode: IXmlElement = nodeList.element("clef-octave-change");
+        if (clefOctaveNode !== undefined) {
+          try {
+            clefOctaveOffset = parseInt(clefOctaveNode.value, 10);
+          } catch (e) {
+            errorMsg = ITextTranslation.translateText(
+              "ReaderErrorMessages/ClefOctaveError",
+              "Invalid clef octave found -> using default clef octave."
+            );
+            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+            clefOctaveOffset = 0;
+          }
 
-                }
-                if (nodeList.hasAttributes && nodeList.attributes()[0].name === "number") {
-                    try {
-                        staffNumber = parseInt(nodeList.attributes()[0].value, 10);
-                    } catch (err) {
-                        errorMsg = ITextTranslation.translateText(
-                            "ReaderErrorMessages/ClefError",
-                            "Invalid clef found -> using default clef."
-                        );
-                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                        staffNumber = 1;
-                    }
-                }
+        }
+        if (nodeList.hasAttributes && nodeList.attributes()[0].name === "number") {
+          try {
+            staffNumber = parseInt(nodeList.attributes()[0].value, 10);
+          } catch (err) {
+            errorMsg = ITextTranslation.translateText(
+              "ReaderErrorMessages/ClefError",
+              "Invalid clef found -> using default clef."
+            );
+            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+            staffNumber = 1;
+          }
+        }
 
-                let clefInstruction: ClefInstruction = new ClefInstruction(clefEnum, clefOctaveOffset, line);
-                this.abstractInstructions.push([staffNumber, clefInstruction]);
-            }
+        const clefInstruction: ClefInstruction = new ClefInstruction(clefEnum, clefOctaveOffset, line);
+        this.abstractInstructions.push([staffNumber, clefInstruction]);
+      }
+    }
+    if (node.element("key") !== undefined && this.instrument.MidiInstrumentId !== MidiInstrument.Percussion) {
+      let key: number = 0;
+      const keyNode: IXmlElement = node.element("key").element("fifths");
+      if (keyNode !== undefined) {
+        try {
+          key = parseInt(keyNode.value, 10);
+        } catch (ex) {
+          errorMsg = ITextTranslation.translateText(
+            "ReaderErrorMessages/KeyError",
+            "Invalid key found -> set to default."
+          );
+          this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+          key = 0;
+          Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
         }
-        if (node.element("key") !== undefined && this.instrument.MidiInstrumentId !== MidiInstrument.Percussion) {
-            let key: number = 0;
-            let keyNode: IXmlElement = node.element("key").element("fifths");
-            if (keyNode !== undefined) {
-                try {
-                    key = parseInt(keyNode.value, 10);
-                } catch (ex) {
-                    errorMsg = ITextTranslation.translateText(
-                        "ReaderErrorMessages/KeyError",
-                        "Invalid key found -> set to default."
-                    );
-                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                    key = 0;
-                    Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
-                }
 
-            }
-            let keyEnum: KeyEnum = KeyEnum.none;
-            let modeNode: IXmlElement = node.element("key");
-            if (modeNode !== undefined) {
-                modeNode = modeNode.element("mode");
-            }
-            if (modeNode !== undefined) {
-                try {
-                    keyEnum = KeyEnum[modeNode.value];
-                } catch (ex) {
-                    errorMsg = ITextTranslation.translateText(
-                        "ReaderErrorMessages/KeyError",
-                        "Invalid key found -> set to default."
-                    );
-                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                    keyEnum = KeyEnum.major;
-                    Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
-                }
+      }
+      let keyEnum: KeyEnum = KeyEnum.none;
+      let modeNode: IXmlElement = node.element("key");
+      if (modeNode !== undefined) {
+        modeNode = modeNode.element("mode");
+      }
+      if (modeNode !== undefined) {
+        try {
+          keyEnum = KeyEnum[modeNode.value];
+        } catch (ex) {
+          errorMsg = ITextTranslation.translateText(
+            "ReaderErrorMessages/KeyError",
+            "Invalid key found -> set to default."
+          );
+          this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+          keyEnum = KeyEnum.major;
+          Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+        }
 
-            }
-            let keyInstruction: KeyInstruction = new KeyInstruction(undefined, key, keyEnum);
-            this.abstractInstructions.push([1, keyInstruction]);
+      }
+      const keyInstruction: KeyInstruction = new KeyInstruction(undefined, key, keyEnum);
+      this.abstractInstructions.push([1, keyInstruction]);
+    }
+    if (node.element("time") !== undefined) {
+      let symbolEnum: RhythmSymbolEnum = RhythmSymbolEnum.NONE;
+      const timeNode: IXmlElement = node.element("time");
+      if (timeNode !== undefined && timeNode.hasAttributes) {
+        const firstAttr: IXmlAttribute = timeNode.firstAttribute;
+        if (firstAttr.name === "symbol") {
+          if (firstAttr.value === "common") {
+            symbolEnum = RhythmSymbolEnum.COMMON;
+          } else if (firstAttr.value === "cut") {
+            symbolEnum = RhythmSymbolEnum.CUT;
+          }
         }
-        if (node.element("time") !== undefined) {
-            let symbolEnum: RhythmSymbolEnum = RhythmSymbolEnum.NONE;
-            let timeNode: IXmlElement = node.element("time");
-            if (timeNode !== undefined && timeNode.hasAttributes) {
-                let firstAttr: IXmlAttribute = timeNode.firstAttribute;
-                if (firstAttr.name === "symbol") {
-                    if (firstAttr.value === "common") {
-                        symbolEnum = RhythmSymbolEnum.COMMON;
-                    } else if (firstAttr.value === "cut") {
-                        symbolEnum = RhythmSymbolEnum.CUT;
-                    }
+      }
+      let num: number = 0;
+      let denom: number = 0;
+      const senzaMisura: boolean = (timeNode !== undefined && timeNode.element("senza-misura") !== undefined);
+      const timeList: IXmlElement[] = node.elements("time");
+      const beatsList: IXmlElement[] = [];
+      const typeList: IXmlElement[] = [];
+      for (let idx: number = 0, len: number = timeList.length; idx < len; ++idx) {
+        const xmlNode: IXmlElement = timeList[idx];
+        beatsList.push.apply(beatsList, xmlNode.elements("beats"));
+        typeList.push.apply(typeList, xmlNode.elements("beat-type"));
+      }
+      if (!senzaMisura) {
+        try {
+          if (beatsList !== undefined && beatsList.length > 0 && typeList !== undefined && beatsList.length === typeList.length) {
+            const length: number = beatsList.length;
+            const fractions: Fraction[] = new Array(length);
+            let maxDenom: number = 0;
+            for (let i: number = 0; i < length; i++) {
+              const s: string = beatsList[i].value;
+              let n: number = 0;
+              let d: number = 0;
+              if (s.indexOf("+") !== -1) {
+                const numbers: string[] = s.split("+");
+                for (let idx: number = 0, len: number = numbers.length; idx < len; ++idx) {
+                  n += parseInt(numbers[idx], 10);
                 }
+              } else {
+                n = parseInt(s, 10);
+              }
+              d = parseInt(typeList[i].value, 10);
+              maxDenom = Math.max(maxDenom, d);
+              fractions[i] = new Fraction(n, d, 0, false);
             }
-            let num: number = 0;
-            let denom: number = 0;
-            let senzaMisura: boolean = (timeNode !== undefined && timeNode.element("senza-misura") !== undefined);
-            let timeList: IXmlElement[] = node.elements("time");
-            let beatsList: IXmlElement[] = [];
-            let typeList: IXmlElement[] = [];
-            for (let idx: number = 0, len: number = timeList.length; idx < len; ++idx) {
-                let xmlNode: IXmlElement = timeList[idx];
-                beatsList.push.apply(beatsList, xmlNode.elements("beats"));
-                typeList.push.apply(typeList, xmlNode.elements("beat-type"));
+            for (let i: number = 0; i < length; i++) {
+              if (fractions[i].Denominator === maxDenom) {
+                num += fractions[i].Numerator;
+              } else {
+                num += (maxDenom / fractions[i].Denominator) * fractions[i].Numerator;
+              }
             }
-            if (!senzaMisura) {
-                try {
-                    if (beatsList !== undefined && beatsList.length > 0 && typeList !== undefined && beatsList.length === typeList.length) {
-                        let length: number = beatsList.length;
-                        let fractions: Fraction[] = new Array(length);
-                        let maxDenom: number = 0;
-                        for (let i: number = 0; i < length; i++) {
-                            let s: string = beatsList[i].value;
-                            let n: number = 0;
-                            let d: number = 0;
-                            if (s.indexOf("+") !== -1) {
-                                let numbers: string[] = s.split("+");
-                                for (let idx: number = 0, len: number = numbers.length; idx < len; ++idx) {
-                                    n += parseInt(numbers[idx], 10);
-                                }
-                            } else {
-                                n = parseInt(s, 10);
-                            }
-                            d = parseInt(typeList[i].value, 10);
-                            maxDenom = Math.max(maxDenom, d);
-                            fractions[i] = new Fraction(n, d, false);
-                        }
-                        for (let i: number = 0; i < length; i++) {
-                            if (fractions[i].Denominator === maxDenom) {
-                                num += fractions[i].Numerator;
-                            } else {
-                                num += (maxDenom / fractions[i].Denominator) * fractions[i].Numerator;
-                            }
-                        }
-                        denom = maxDenom;
-                    } else {
-                        num = parseInt(node.element("time").element("beats").value, 10);
-                        denom = parseInt(node.element("time").element("beat-type").value, 10);
-                    }
-                } catch (ex) {
-                    errorMsg = ITextTranslation.translateText("ReaderErrorMessages/RhythmError", "Invalid rhythm found -> set to default.");
-                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                    num = 4;
-                    denom = 4;
-                    Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
-                }
+            denom = maxDenom;
+          } else {
+            num = parseInt(node.element("time").element("beats").value, 10);
+            denom = parseInt(node.element("time").element("beat-type").value, 10);
+          }
+        } catch (ex) {
+          errorMsg = ITextTranslation.translateText("ReaderErrorMessages/RhythmError", "Invalid rhythm found -> set to default.");
+          this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+          num = 4;
+          denom = 4;
+          Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+        }
 
-                if ((num === 4 && denom === 4) || (num === 2 && denom === 2)) {
-                    symbolEnum = RhythmSymbolEnum.NONE;
-                }
-                this.abstractInstructions.push([1, new RhythmInstruction(
-                    new Fraction(num, denom, false), num, denom, symbolEnum
-                )]);
-            } else {
-                this.abstractInstructions.push([1, new RhythmInstruction(new Fraction(4, 4, false), 4, 4, RhythmSymbolEnum.NONE)]);
-            }
+        if ((num === 4 && denom === 4) || (num === 2 && denom === 2)) {
+          symbolEnum = RhythmSymbolEnum.NONE;
         }
+        this.abstractInstructions.push([1, new RhythmInstruction(
+          new Fraction(num, denom, 0, false), symbolEnum
+        )]);
+      } else {
+        this.abstractInstructions.push([1, new RhythmInstruction(new Fraction(4, 4, 0, false), RhythmSymbolEnum.NONE)]);
+      }
     }
+  }
 
-    /**
-     * Save the current AbstractInstructions to the corresponding [[StaffEntry]]s.
-     * @param numberOfStaves
-     * @param beginOfMeasure
-     */
-    private saveAbstractInstructionList(numberOfStaves: number, beginOfMeasure: boolean): void {
-        for (let i: number = this.abstractInstructions.length - 1; i >= 0; i--) {
-            let pair: [number, AbstractNotationInstruction] = this.abstractInstructions[i];
-            let key: number = pair[0];
-            let value: AbstractNotationInstruction = pair[1];
-            if (value instanceof ClefInstruction) {
-                let clefInstruction: ClefInstruction = <ClefInstruction>value;
-                if (this.currentXmlMeasureIndex === 0 || (key <= this.activeClefs.length && clefInstruction !== this.activeClefs[key - 1])) {
-                    if (!beginOfMeasure && this.currentStaffEntry !== undefined && !this.currentStaffEntry.hasNotes() && key - 1
-                        === this.instrument.Staves.indexOf(this.currentStaffEntry.ParentStaff)) {
-                        let newClefInstruction: ClefInstruction = clefInstruction;
-                        newClefInstruction.Parent = this.currentStaffEntry;
-                        this.currentStaffEntry.removeFirstInstructionOfTypeClefInstruction();
-                        this.currentStaffEntry.Instructions.push(newClefInstruction);
-                        this.activeClefs[key - 1] = clefInstruction;
-                        this.abstractInstructions.splice(i, 1);
-                    } else if (beginOfMeasure) {
-                        let firstStaffEntry: SourceStaffEntry;
-                        if (this.currentMeasure !== undefined) {
-                            let newClefInstruction: ClefInstruction = clefInstruction;
-                            let sseIndex: number = this.inSourceMeasureInstrumentIndex + key - 1;
-                            let firstSse: SourceStaffEntry = this.currentMeasure.FirstInstructionsStaffEntries[sseIndex];
-                            if (this.currentXmlMeasureIndex === 0) {
-                                if (firstSse === undefined) {
-                                    firstStaffEntry = new SourceStaffEntry(undefined, undefined);
-                                    this.currentMeasure.FirstInstructionsStaffEntries[sseIndex] = firstStaffEntry;
-                                    newClefInstruction.Parent = firstStaffEntry;
-                                    firstStaffEntry.Instructions.push(newClefInstruction);
-                                    this.activeClefsHaveBeenInitialized[key - 1] = true;
-                                } else if (this.currentMeasure.FirstInstructionsStaffEntries[sseIndex]
-                                    !==
-                                    undefined && !(firstSse.Instructions[0] instanceof ClefInstruction)) {
-                                    firstStaffEntry = firstSse;
-                                    newClefInstruction.Parent = firstStaffEntry;
-                                    firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
-                                    firstStaffEntry.Instructions.splice(0, 0, newClefInstruction);
-                                    this.activeClefsHaveBeenInitialized[key - 1] = true;
-                                } else {
-                                    let lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
-                                    this.currentMeasure.LastInstructionsStaffEntries[sseIndex] = lastStaffEntry;
-                                    newClefInstruction.Parent = lastStaffEntry;
-                                    lastStaffEntry.Instructions.push(newClefInstruction);
-                                }
-                            } else if (!this.activeClefsHaveBeenInitialized[key - 1]) {
-                                let first: SourceMeasure = this.musicSheet.SourceMeasures[0];
-                                if (first.FirstInstructionsStaffEntries[sseIndex] === undefined) {
-                                    firstStaffEntry = new SourceStaffEntry(undefined, undefined);
-                                } else {
-                                    firstStaffEntry = first.FirstInstructionsStaffEntries[sseIndex];
-                                    firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
-                                }
-                                newClefInstruction.Parent = firstStaffEntry;
-                                firstStaffEntry.Instructions.splice(0, 0, newClefInstruction);
-                                this.activeClefsHaveBeenInitialized[key - 1] = true;
-                            } else {
-                                let lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
-                                this.previousMeasure.LastInstructionsStaffEntries[sseIndex] = lastStaffEntry;
-                                newClefInstruction.Parent = lastStaffEntry;
-                                lastStaffEntry.Instructions.push(newClefInstruction);
-                            }
-                            this.activeClefs[key - 1] = clefInstruction;
-                            this.abstractInstructions.splice(i, 1);
-                        }
-                    }
-                } else if (key <= this.activeClefs.length && clefInstruction === this.activeClefs[key - 1]) {
-                    this.abstractInstructions.splice(i, 1);
+  /**
+   * Save the current AbstractInstructions to the corresponding [[StaffEntry]]s.
+   * @param numberOfStaves
+   * @param beginOfMeasure
+   */
+  private saveAbstractInstructionList(numberOfStaves: number, beginOfMeasure: boolean): void {
+    for (let i: number = this.abstractInstructions.length - 1; i >= 0; i--) {
+      const pair: [number, AbstractNotationInstruction] = this.abstractInstructions[i];
+      const key: number = pair[0];
+      const value: AbstractNotationInstruction = pair[1];
+      if (value instanceof ClefInstruction) {
+        const clefInstruction: ClefInstruction = <ClefInstruction>value;
+        if (this.currentXmlMeasureIndex === 0 || (key <= this.activeClefs.length && clefInstruction !== this.activeClefs[key - 1])) {
+          if (!beginOfMeasure && this.currentStaffEntry !== undefined && !this.currentStaffEntry.hasNotes() && key - 1
+            === this.instrument.Staves.indexOf(this.currentStaffEntry.ParentStaff)) {
+            const newClefInstruction: ClefInstruction = clefInstruction;
+            newClefInstruction.Parent = this.currentStaffEntry;
+            this.currentStaffEntry.removeFirstInstructionOfTypeClefInstruction();
+            this.currentStaffEntry.Instructions.push(newClefInstruction);
+            this.activeClefs[key - 1] = clefInstruction;
+            this.abstractInstructions.splice(i, 1);
+          } else if (beginOfMeasure) {
+            let firstStaffEntry: SourceStaffEntry;
+            if (this.currentMeasure !== undefined) {
+              const newClefInstruction: ClefInstruction = clefInstruction;
+              const sseIndex: number = this.inSourceMeasureInstrumentIndex + key - 1;
+              const firstSse: SourceStaffEntry = this.currentMeasure.FirstInstructionsStaffEntries[sseIndex];
+              if (this.currentXmlMeasureIndex === 0) {
+                if (firstSse === undefined) {
+                  firstStaffEntry = new SourceStaffEntry(undefined, undefined);
+                  this.currentMeasure.FirstInstructionsStaffEntries[sseIndex] = firstStaffEntry;
+                  newClefInstruction.Parent = firstStaffEntry;
+                  firstStaffEntry.Instructions.push(newClefInstruction);
+                  this.activeClefsHaveBeenInitialized[key - 1] = true;
+                } else if (this.currentMeasure.FirstInstructionsStaffEntries[sseIndex]
+                  !==
+                  undefined && !(firstSse.Instructions[0] instanceof ClefInstruction)) {
+                  firstStaffEntry = firstSse;
+                  newClefInstruction.Parent = firstStaffEntry;
+                  firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
+                  firstStaffEntry.Instructions.splice(0, 0, newClefInstruction);
+                  this.activeClefsHaveBeenInitialized[key - 1] = true;
+                } else {
+                  const lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+                  this.currentMeasure.LastInstructionsStaffEntries[sseIndex] = lastStaffEntry;
+                  newClefInstruction.Parent = lastStaffEntry;
+                  lastStaffEntry.Instructions.push(newClefInstruction);
                 }
-            }
-            if (value instanceof KeyInstruction) {
-                let keyInstruction: KeyInstruction = <KeyInstruction>value;
-                if (this.activeKey === undefined || this.activeKey.Key !== keyInstruction.Key) {
-                    this.activeKey = keyInstruction;
-                    this.abstractInstructions.splice(i, 1);
-                    let sourceMeasure: SourceMeasure;
-                    if (!this.activeKeyHasBeenInitialized) {
-                        this.activeKeyHasBeenInitialized = true;
-                        if (this.currentXmlMeasureIndex > 0) {
-                            sourceMeasure = this.musicSheet.SourceMeasures[0];
-                        } else {
-                            sourceMeasure = this.currentMeasure;
-                        }
-                    } else {
-                        sourceMeasure = this.currentMeasure;
-                    }
-                    if (sourceMeasure !== undefined) {
-                        for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
-                            let newKeyInstruction: KeyInstruction = keyInstruction;
-                            if (sourceMeasure.FirstInstructionsStaffEntries[j] === undefined) {
-                                let firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
-                                sourceMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
-                                newKeyInstruction.Parent = firstStaffEntry;
-                                firstStaffEntry.Instructions.push(newKeyInstruction);
-                            } else {
-                                let firstStaffEntry: SourceStaffEntry = sourceMeasure.FirstInstructionsStaffEntries[j];
-                                newKeyInstruction.Parent = firstStaffEntry;
-                                firstStaffEntry.removeFirstInstructionOfTypeKeyInstruction();
-                                if (firstStaffEntry.Instructions.length === 0) {
-                                    firstStaffEntry.Instructions.push(newKeyInstruction);
-                                } else {
-                                    if (firstStaffEntry.Instructions[0] instanceof ClefInstruction) {
-                                        firstStaffEntry.Instructions.splice(1, 0, newKeyInstruction);
-                                    } else {
-                                        firstStaffEntry.Instructions.splice(0, 0, newKeyInstruction);
-                                    }
-                                }
-                            }
-                        }
-                    }
+              } else if (!this.activeClefsHaveBeenInitialized[key - 1]) {
+                const first: SourceMeasure = this.musicSheet.SourceMeasures[0];
+                if (first.FirstInstructionsStaffEntries[sseIndex] === undefined) {
+                  firstStaffEntry = new SourceStaffEntry(undefined, undefined);
                 } else {
-                    this.abstractInstructions.splice(i, 1);
+                  firstStaffEntry = first.FirstInstructionsStaffEntries[sseIndex];
+                  firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
                 }
+                newClefInstruction.Parent = firstStaffEntry;
+                firstStaffEntry.Instructions.splice(0, 0, newClefInstruction);
+                this.activeClefsHaveBeenInitialized[key - 1] = true;
+              } else {
+                const lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+                this.previousMeasure.LastInstructionsStaffEntries[sseIndex] = lastStaffEntry;
+                newClefInstruction.Parent = lastStaffEntry;
+                lastStaffEntry.Instructions.push(newClefInstruction);
+              }
+              this.activeClefs[key - 1] = clefInstruction;
+              this.abstractInstructions.splice(i, 1);
             }
-            if (value instanceof RhythmInstruction) {
-                let rhythmInstruction: RhythmInstruction = <RhythmInstruction>value;
-                if (this.activeRhythm === undefined || this.activeRhythm !== rhythmInstruction) {
-                    this.activeRhythm = rhythmInstruction;
-                    this.abstractInstructions.splice(i, 1);
-                    if (this.currentMeasure !== undefined) {
-                        for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
-                            let newRhythmInstruction: RhythmInstruction = rhythmInstruction;
-                            let firstStaffEntry: SourceStaffEntry;
-                            if (this.currentMeasure.FirstInstructionsStaffEntries[j] === undefined) {
-                                firstStaffEntry = new SourceStaffEntry(undefined, undefined);
-                                this.currentMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
-                            } else {
-                                firstStaffEntry = this.currentMeasure.FirstInstructionsStaffEntries[j];
-                                firstStaffEntry.removeFirstInstructionOfTypeRhythmInstruction();
-                            }
-                            newRhythmInstruction.Parent = firstStaffEntry;
-                            firstStaffEntry.Instructions.push(newRhythmInstruction);
-                        }
-                    }
+          }
+        } else if (key <= this.activeClefs.length && clefInstruction === this.activeClefs[key - 1]) {
+          this.abstractInstructions.splice(i, 1);
+        }
+      }
+      if (value instanceof KeyInstruction) {
+        const keyInstruction: KeyInstruction = <KeyInstruction>value;
+        if (this.activeKey === undefined || this.activeKey.Key !== keyInstruction.Key) {
+          this.activeKey = keyInstruction;
+          this.abstractInstructions.splice(i, 1);
+          let sourceMeasure: SourceMeasure;
+          if (!this.activeKeyHasBeenInitialized) {
+            this.activeKeyHasBeenInitialized = true;
+            if (this.currentXmlMeasureIndex > 0) {
+              sourceMeasure = this.musicSheet.SourceMeasures[0];
+            } else {
+              sourceMeasure = this.currentMeasure;
+            }
+          } else {
+            sourceMeasure = this.currentMeasure;
+          }
+          if (sourceMeasure !== undefined) {
+            for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
+              const newKeyInstruction: KeyInstruction = keyInstruction;
+              if (sourceMeasure.FirstInstructionsStaffEntries[j] === undefined) {
+                const firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+                sourceMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
+                newKeyInstruction.Parent = firstStaffEntry;
+                firstStaffEntry.Instructions.push(newKeyInstruction);
+              } else {
+                const firstStaffEntry: SourceStaffEntry = sourceMeasure.FirstInstructionsStaffEntries[j];
+                newKeyInstruction.Parent = firstStaffEntry;
+                firstStaffEntry.removeFirstInstructionOfTypeKeyInstruction();
+                if (firstStaffEntry.Instructions.length === 0) {
+                  firstStaffEntry.Instructions.push(newKeyInstruction);
                 } else {
-                    this.abstractInstructions.splice(i, 1);
+                  if (firstStaffEntry.Instructions[0] instanceof ClefInstruction) {
+                    firstStaffEntry.Instructions.splice(1, 0, newKeyInstruction);
+                  } else {
+                    firstStaffEntry.Instructions.splice(0, 0, newKeyInstruction);
+                  }
                 }
+              }
             }
+          }
+        } else {
+          this.abstractInstructions.splice(i, 1);
+        }
+      }
+      if (value instanceof RhythmInstruction) {
+        const rhythmInstruction: RhythmInstruction = <RhythmInstruction>value;
+        if (this.activeRhythm === undefined || this.activeRhythm !== rhythmInstruction) {
+          this.activeRhythm = rhythmInstruction;
+          this.abstractInstructions.splice(i, 1);
+          if (this.currentMeasure !== undefined) {
+            for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
+              const newRhythmInstruction: RhythmInstruction = rhythmInstruction;
+              let firstStaffEntry: SourceStaffEntry;
+              if (this.currentMeasure.FirstInstructionsStaffEntries[j] === undefined) {
+                firstStaffEntry = new SourceStaffEntry(undefined, undefined);
+                this.currentMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
+              } else {
+                firstStaffEntry = this.currentMeasure.FirstInstructionsStaffEntries[j];
+                firstStaffEntry.removeFirstInstructionOfTypeRhythmInstruction();
+              }
+              newRhythmInstruction.Parent = firstStaffEntry;
+              firstStaffEntry.Instructions.push(newRhythmInstruction);
+            }
+          }
+        } else {
+          this.abstractInstructions.splice(i, 1);
         }
+      }
     }
+  }
 
-    /**
-     * Save any ClefInstruction given - exceptionally - at the end of the currentMeasure.
-     */
-    private saveClefInstructionAtEndOfMeasure(): void {
-        for (let i: number = this.abstractInstructions.length - 1; i >= 0; i--) {
-            let key: number = this.abstractInstructions[i][0];
-            let value: AbstractNotationInstruction = this.abstractInstructions[i][1];
-            if (value instanceof ClefInstruction) {
-                let clefInstruction: ClefInstruction = <ClefInstruction>value;
-                if (
-                    (this.activeClefs[key - 1] === undefined) ||
-                    (clefInstruction.ClefType !== this.activeClefs[key - 1].ClefType || (
-                        clefInstruction.ClefType === this.activeClefs[key - 1].ClefType &&
-                        clefInstruction.Line !== this.activeClefs[key - 1].Line
-                    ))) {
-                    let lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
-                    this.currentMeasure.LastInstructionsStaffEntries[this.inSourceMeasureInstrumentIndex + key - 1] = lastStaffEntry;
-                    let newClefInstruction: ClefInstruction = clefInstruction;
-                    newClefInstruction.Parent = lastStaffEntry;
-                    lastStaffEntry.Instructions.push(newClefInstruction);
-                    this.activeClefs[key - 1] = clefInstruction;
-                    this.abstractInstructions.splice(i, 1);
-                }
-            }
+  /**
+   * Save any ClefInstruction given - exceptionally - at the end of the currentMeasure.
+   */
+  private saveClefInstructionAtEndOfMeasure(): void {
+    for (let i: number = this.abstractInstructions.length - 1; i >= 0; i--) {
+      const key: number = this.abstractInstructions[i][0];
+      const value: AbstractNotationInstruction = this.abstractInstructions[i][1];
+      if (value instanceof ClefInstruction) {
+        const clefInstruction: ClefInstruction = <ClefInstruction>value;
+        if (
+          (this.activeClefs[key - 1] === undefined) ||
+          (clefInstruction.ClefType !== this.activeClefs[key - 1].ClefType || (
+            clefInstruction.ClefType === this.activeClefs[key - 1].ClefType &&
+            clefInstruction.Line !== this.activeClefs[key - 1].Line
+          ))) {
+          const lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+          this.currentMeasure.LastInstructionsStaffEntries[this.inSourceMeasureInstrumentIndex + key - 1] = lastStaffEntry;
+          const newClefInstruction: ClefInstruction = clefInstruction;
+          newClefInstruction.Parent = lastStaffEntry;
+          lastStaffEntry.Instructions.push(newClefInstruction);
+          this.activeClefs[key - 1] = clefInstruction;
+          this.abstractInstructions.splice(i, 1);
         }
+      }
     }
+  }
 
-    /**
-     * In case of a [[Tuplet]], read NoteDuration from type.
-     * @param xmlNode
-     * @returns {Fraction}
-     */
-    private getNoteDurationForTuplet(xmlNode: IXmlElement): Fraction {
-        let duration: Fraction = new Fraction(0, 1);
-        let typeDuration: Fraction = this.getNoteDurationFromTypeNode(xmlNode);
-        if (xmlNode.element("time-modification") !== undefined) {
-            let time: IXmlElement = xmlNode.element("time-modification");
-            if (time !== undefined) {
-                if (time.element("actual-notes") !== undefined && time.element("normal-notes") !== undefined) {
-                    let actualNotes: IXmlElement = time.element("actual-notes");
-                    let normalNotes: IXmlElement = time.element("normal-notes");
-                    if (actualNotes !== undefined && normalNotes !== undefined) {
-                        let actual: number = parseInt(actualNotes.value, 10);
-                        let normal: number = parseInt(normalNotes.value, 10);
-                        duration = new Fraction(normal * typeDuration.Numerator, actual * typeDuration.Denominator);
-                    }
-                }
-            }
+  /**
+   * In case of a [[Tuplet]], read NoteDuration from type.
+   * @param xmlNode
+   * @returns {Fraction}
+   */
+  private getNoteDurationForTuplet(xmlNode: IXmlElement): Fraction {
+    let duration: Fraction = new Fraction(0, 1);
+    const typeDuration: Fraction = this.getNoteDurationFromTypeNode(xmlNode);
+    if (xmlNode.element("time-modification") !== undefined) {
+      const time: IXmlElement = xmlNode.element("time-modification");
+      if (time !== undefined) {
+        if (time.element("actual-notes") !== undefined && time.element("normal-notes") !== undefined) {
+          const actualNotes: IXmlElement = time.element("actual-notes");
+          const normalNotes: IXmlElement = time.element("normal-notes");
+          if (actualNotes !== undefined && normalNotes !== undefined) {
+            const actual: number = parseInt(actualNotes.value, 10);
+            const normal: number = parseInt(normalNotes.value, 10);
+            duration = new Fraction(normal * typeDuration.Numerator, actual * typeDuration.Denominator);
+          }
         }
-        return duration;
+      }
     }
+    return duration;
+  }
 
-    //private readExpressionStaffNumber(xmlNode: IXmlElement): number {
-    //  let directionStaffNumber: number = 1;
-    //  if (xmlNode.element("staff") !== undefined) {
-    //    let staffNode: IXmlElement = xmlNode.element("staff");
-    //    if (staffNode !== undefined) {
-    //      try {
-    //        directionStaffNumber = parseInt(staffNode.value, 10);
-    //      } catch (ex) {
-    //        let errorMsg: string = ITextTranslation.translateText(
-    //          "ReaderErrorMessages/ExpressionStaffError", "Invalid Expression staff number -> set to default."
-    //        );
-    //        this.musicSheet.SheetErrors.pushTemp(errorMsg);
-    //        directionStaffNumber = 1;
-    //        logging.debug("InstrumentReader.readExpressionStaffNumber", errorMsg, ex);
-    //      }
-    //
-    //    }
-    //  }
-    //  return directionStaffNumber;
-    //}
+  //private readExpressionStaffNumber(xmlNode: IXmlElement): number {
+  //  let directionStaffNumber: number = 1;
+  //  if (xmlNode.element("staff") !== undefined) {
+  //    let staffNode: IXmlElement = xmlNode.element("staff");
+  //    if (staffNode !== undefined) {
+  //      try {
+  //        directionStaffNumber = parseInt(staffNode.value, 10);
+  //      } catch (ex) {
+  //        let errorMsg: string = ITextTranslation.translateText(
+  //          "ReaderErrorMessages/ExpressionStaffError", "Invalid Expression staff number -> set to default."
+  //        );
+  //        this.musicSheet.SheetErrors.pushTemp(errorMsg);
+  //        directionStaffNumber = 1;
+  //        logging.debug("InstrumentReader.readExpressionStaffNumber", errorMsg, ex);
+  //      }
+  //
+  //    }
+  //  }
+  //  return directionStaffNumber;
+  //}
 
-    /**
-     * Calculate the divisions value from the type and duration of the first MeasureNote that makes sense
-     * (meaning itself hasn't any errors and it doesn't belong to a [[Tuplet]]).
-     *
-     * If all the MeasureNotes belong to a [[Tuplet]], then we read the next XmlMeasure (and so on...).
-     * If we have reached the end of the [[Instrument]] and still the divisions aren't set, we throw an exception
-     * @returns {number}
-     */
-    private readDivisionsFromNotes(): number {
-        let divisionsFromNote: number = 0;
-        let xmlMeasureIndex: number = this.currentXmlMeasureIndex;
-        let read: boolean = false;
-        while (!read) {
-            let xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[xmlMeasureIndex].elements();
-            for (let idx: number = 0, len: number = xmlMeasureListArr.length; idx < len; ++idx) {
-                let xmlNode: IXmlElement = xmlMeasureListArr[idx];
-                if (xmlNode.name === "note" && xmlNode.element("time-modification") === undefined) {
-                    if (xmlNode.element("duration") !== undefined && xmlNode.element("type") !== undefined) {
-                        let durationNode: IXmlElement = xmlNode.element("duration");
-                        let typeNode: IXmlElement = xmlNode.element("type");
-                        if (durationNode !== undefined && typeNode !== undefined) {
-                            let type: string = typeNode.value;
-                            let noteDuration: number = 0;
-                            try {
-                                noteDuration = parseInt(durationNode.value, 10);
-                            } catch (ex) {
-                                Logging.debug("InstrumentReader.readDivisionsFromNotes", ex);
-                                continue;
-                            }
+  /**
+   * Calculate the divisions value from the type and duration of the first MeasureNote that makes sense
+   * (meaning itself hasn't any errors and it doesn't belong to a [[Tuplet]]).
+   *
+   * If all the MeasureNotes belong to a [[Tuplet]], then we read the next XmlMeasure (and so on...).
+   * If we have reached the end of the [[Instrument]] and still the divisions aren't set, we throw an exception
+   * @returns {number}
+   */
+  private readDivisionsFromNotes(): number {
+    let divisionsFromNote: number = 0;
+    let xmlMeasureIndex: number = this.currentXmlMeasureIndex;
+    let read: boolean = false;
+    while (!read) {
+      const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[xmlMeasureIndex].elements();
+      for (let idx: number = 0, len: number = xmlMeasureListArr.length; idx < len; ++idx) {
+        const xmlNode: IXmlElement = xmlMeasureListArr[idx];
+        if (xmlNode.name === "note" && xmlNode.element("time-modification") === undefined) {
+          if (xmlNode.element("duration") !== undefined && xmlNode.element("type") !== undefined) {
+            const durationNode: IXmlElement = xmlNode.element("duration");
+            const typeNode: IXmlElement = xmlNode.element("type");
+            if (durationNode !== undefined && typeNode !== undefined) {
+              const type: string = typeNode.value;
+              let noteDuration: number = 0;
+              try {
+                noteDuration = parseInt(durationNode.value, 10);
+              } catch (ex) {
+                Logging.debug("InstrumentReader.readDivisionsFromNotes", ex);
+                continue;
+              }
 
-                            switch (type) {
-                                case "1024th":
-                                    divisionsFromNote = (noteDuration / 4) * 1024;
-                                    break;
-                                case "512th":
-                                    divisionsFromNote = (noteDuration / 4) * 512;
-                                    break;
-                                case "256th":
-                                    divisionsFromNote = (noteDuration / 4) * 256;
-                                    break;
-                                case "128th":
-                                    divisionsFromNote = (noteDuration / 4) * 128;
-                                    break;
-                                case "64th":
-                                    divisionsFromNote = (noteDuration / 4) * 64;
-                                    break;
-                                case "32nd":
-                                    divisionsFromNote = (noteDuration / 4) * 32;
-                                    break;
-                                case "16th":
-                                    divisionsFromNote = (noteDuration / 4) * 16;
-                                    break;
-                                case "eighth":
-                                    divisionsFromNote = (noteDuration / 4) * 8;
-                                    break;
-                                case "quarter":
-                                    divisionsFromNote = (noteDuration / 4) * 4;
-                                    break;
-                                case "half":
-                                    divisionsFromNote = (noteDuration / 4) * 2;
-                                    break;
-                                case "whole":
-                                    divisionsFromNote = (noteDuration / 4);
-                                    break;
-                                case "breve":
-                                    divisionsFromNote = (noteDuration / 4) / 2;
-                                    break;
-                                case "long":
-                                    divisionsFromNote = (noteDuration / 4) / 4;
-                                    break;
-                                case "maxima":
-                                    divisionsFromNote = (noteDuration / 4) / 8;
-                                    break;
-                                default:
-                                    break;
-                            }
-                        }
-                    }
-                }
-                if (divisionsFromNote > 0) {
-                    read = true;
-                    break;
-                }
-            }
-            if (divisionsFromNote === 0) {
-                xmlMeasureIndex++;
-                if (xmlMeasureIndex === this.xmlMeasureList.length) {
-                    let errorMsg: string = ITextTranslation.translateText("ReaderErrorMEssages/DivisionsError", "Invalid divisions value at Instrument: ");
-                    throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
-                }
+              switch (type) {
+                case "1024th":
+                  divisionsFromNote = (noteDuration / 4) * 1024;
+                  break;
+                case "512th":
+                  divisionsFromNote = (noteDuration / 4) * 512;
+                  break;
+                case "256th":
+                  divisionsFromNote = (noteDuration / 4) * 256;
+                  break;
+                case "128th":
+                  divisionsFromNote = (noteDuration / 4) * 128;
+                  break;
+                case "64th":
+                  divisionsFromNote = (noteDuration / 4) * 64;
+                  break;
+                case "32nd":
+                  divisionsFromNote = (noteDuration / 4) * 32;
+                  break;
+                case "16th":
+                  divisionsFromNote = (noteDuration / 4) * 16;
+                  break;
+                case "eighth":
+                  divisionsFromNote = (noteDuration / 4) * 8;
+                  break;
+                case "quarter":
+                  divisionsFromNote = (noteDuration / 4) * 4;
+                  break;
+                case "half":
+                  divisionsFromNote = (noteDuration / 4) * 2;
+                  break;
+                case "whole":
+                  divisionsFromNote = (noteDuration / 4);
+                  break;
+                case "breve":
+                  divisionsFromNote = (noteDuration / 4) / 2;
+                  break;
+                case "long":
+                  divisionsFromNote = (noteDuration / 4) / 4;
+                  break;
+                case "maxima":
+                  divisionsFromNote = (noteDuration / 4) / 8;
+                  break;
+                default:
+                  break;
+              }
             }
+          }
+        }
+        if (divisionsFromNote > 0) {
+          read = true;
+          break;
+        }
+      }
+      if (divisionsFromNote === 0) {
+        xmlMeasureIndex++;
+        if (xmlMeasureIndex === this.xmlMeasureList.length) {
+          const errorMsg: string = ITextTranslation.translateText("ReaderErrorMEssages/DivisionsError", "Invalid divisions value at Instrument: ");
+          throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
         }
-        return divisionsFromNote;
+      }
     }
+    return divisionsFromNote;
+  }
 }

+ 114 - 119
src/MusicalScore/ScoreIO/MusicSheetReader.ts

@@ -17,31 +17,26 @@ import {SubInstrument} from "../SubInstrument";
 import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
 import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
 import {Label} from "../Label";
-
-/**
- * To be implemented
- */
-type RepetitionInstructionReader = any;
-/**
- * To be implemented
- */
-type RepetitionCalculator = any;
+import {MusicSymbolModuleFactory} from "./MusicSymbolModuleFactory";
+import {IAfterSheetReadingModule} from "../Interfaces/IAfterSheetReadingModule";
+import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
+import {RepetitionCalculator} from "./MusicSymbolModules/RepetitionCalculator";
 
 export class MusicSheetReader /*implements IMusicSheetReader*/ {
 
-    //constructor(afterSheetReadingModules: IAfterSheetReadingModule[]) {
-    //  if (afterSheetReadingModules === undefined) {
-    //    this.afterSheetReadingModules = [];
-    //  } else {
-    //    this.afterSheetReadingModules = afterSheetReadingModules;
-    //  }
-    //  this.repetitionInstructionReader = MusicSymbolModuleFactory.createRepetitionInstructionReader();
-    //  this.repetitionCalculator = MusicSymbolModuleFactory.createRepetitionCalculator();
-    //}
+    constructor(afterSheetReadingModules: IAfterSheetReadingModule[] = undefined) {
+     if (afterSheetReadingModules === undefined) {
+       this.afterSheetReadingModules = [];
+     } else {
+       this.afterSheetReadingModules = afterSheetReadingModules;
+     }
+     this.repetitionInstructionReader = MusicSymbolModuleFactory.createRepetitionInstructionReader();
+     this.repetitionCalculator = MusicSymbolModuleFactory.createRepetitionCalculator();
+    }
 
     private repetitionInstructionReader: RepetitionInstructionReader;
     private repetitionCalculator: RepetitionCalculator;
-    // private afterSheetReadingModules: IAfterSheetReadingModule[];
+    private afterSheetReadingModules: IAfterSheetReadingModule[];
     private musicSheet: MusicSheet;
     private completeNumberOfStaves: number = 0;
     private currentMeasure: SourceMeasure;
@@ -53,7 +48,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     }
 
     private static doCalculationsAfterDurationHasBeenSet(instrumentReaders: InstrumentReader[]): void {
-        for (let instrumentReader of instrumentReaders) {
+        for (const instrumentReader of instrumentReaders) {
             instrumentReader.doCalculationsAfterDurationHasBeenSet();
         }
     }
@@ -73,7 +68,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     }
 
     private _removeFromArray(list: any[], elem: any): void {
-        let i: number = list.indexOf(elem);
+        const i: number = list.indexOf(elem);
         if (i !== -1) {
             list.splice(i, 1);
         }
@@ -116,7 +111,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     //}
 
     private _createMusicSheet(root: IXmlElement, path: string): MusicSheet {
-        let instrumentReaders: InstrumentReader[] = [];
+        const instrumentReaders: InstrumentReader[] = [];
         let sourceMeasureCounter: number = 0;
         this.musicSheet = new MusicSheet();
         this.musicSheet.Path = path;
@@ -124,13 +119,13 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             throw new MusicSheetReadingException("Undefined root element");
         }
         this.pushSheetLabels(root, path);
-        let partlistNode: IXmlElement = root.element("part-list");
+        const partlistNode: IXmlElement = root.element("part-list");
         if (partlistNode === undefined) {
             throw new MusicSheetReadingException("Undefined partListNode");
         }
 
-        let partInst: IXmlElement[] = root.elements("part");
-        let partList: IXmlElement[] = partlistNode.elements();
+        const partInst: IXmlElement[] = root.elements("part");
+        const partList: IXmlElement[] = partlistNode.elements();
         this.initializeReading(partList, partInst, instrumentReaders);
         let couldReadMeasure: boolean = true;
         this.currentFraction = new Fraction(0, 1);
@@ -151,11 +146,11 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                 sourceMeasureCounter = 0;
             }
             this.currentMeasure = new SourceMeasure(this.completeNumberOfStaves);
-            for (let instrumentReader of instrumentReaders) {
+            for (const instrumentReader of instrumentReaders) {
                 try {
                     couldReadMeasure = couldReadMeasure && instrumentReader.readNextXmlMeasure(this.currentMeasure, this.currentFraction, guitarPro);
                 } catch (e) {
-                    let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/InstrumentError", "Error while reading instruments.");
+                    const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/InstrumentError", "Error while reading instruments.");
                     throw new MusicSheetReadingException(errorMsg, e);
                 }
 
@@ -176,40 +171,40 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         if (this.repetitionInstructionReader !== undefined) {
             this.repetitionInstructionReader.removeRedundantInstructions();
             if (this.repetitionCalculator !== undefined) {
-                this.repetitionCalculator.calculateRepetitions(this.musicSheet, this.repetitionInstructionReader.RepetitionInstructions);
+                this.repetitionCalculator.calculateRepetitions(this.musicSheet, this.repetitionInstructionReader.repetitionInstructions);
             }
         }
         this.musicSheet.checkForInstrumentWithNoVoice();
         this.musicSheet.fillStaffList();
         //this.musicSheet.DefaultStartTempoInBpm = this.musicSheet.SheetPlaybackSetting.BeatsPerMinute;
-        //for (let idx: number = 0, len: number = this.afterSheetReadingModules.length; idx < len; ++idx) {
-        //  let afterSheetReadingModule: IAfterSheetReadingModule = this.afterSheetReadingModules[idx];
-        //  afterSheetReadingModule.calculate(this.musicSheet);
-        //}
+        for (let idx: number = 0, len: number = this.afterSheetReadingModules.length; idx < len; ++idx) {
+         const afterSheetReadingModule: IAfterSheetReadingModule = this.afterSheetReadingModules[idx];
+         afterSheetReadingModule.calculate(this.musicSheet);
+        }
 
         return this.musicSheet;
     }
 
     private initializeReading(partList: IXmlElement[], partInst: IXmlElement[], instrumentReaders: InstrumentReader[]): void {
-        let instrumentDict: { [_: string]: Instrument; } = this.createInstrumentGroups(partList);
+        const instrumentDict: { [_: string]: Instrument; } = this.createInstrumentGroups(partList);
         this.completeNumberOfStaves = this.getCompleteNumberOfStavesFromXml(partInst);
         if (partInst.length !== 0) {
-            // (*) this.repetitionInstructionReader.MusicSheet = this.musicSheet;
+            this.repetitionInstructionReader.MusicSheet = this.musicSheet;
             this.currentFraction = new Fraction(0, 1);
             this.currentMeasure = undefined;
             this.previousMeasure = undefined;
         }
         let counter: number = 0;
-        for (let node of partInst) {
-            let idNode: IXmlAttribute = node.attribute("id");
+        for (const node of partInst) {
+            const idNode: IXmlAttribute = node.attribute("id");
             if (idNode) {
-                let currentInstrument: Instrument = instrumentDict[idNode.value];
-                let xmlMeasureList: IXmlElement[] = node.elements("measure");
+                const currentInstrument: Instrument = instrumentDict[idNode.value];
+                const xmlMeasureList: IXmlElement[] = node.elements("measure");
                 let instrumentNumberOfStaves: number = 1;
                 try {
                     instrumentNumberOfStaves = this.getInstrumentNumberOfStavesFromXml(node);
                 } catch (err) {
-                    let errorMsg: string = ITextTranslation.translateText(
+                    const errorMsg: string = ITextTranslation.translateText(
                         "ReaderErrorMessages/InstrumentStavesNumberError",
                         "Invalid number of staves at instrument: "
                     );
@@ -220,7 +215,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                 currentInstrument.createStaves(instrumentNumberOfStaves);
                 instrumentReaders.push(new InstrumentReader(this.repetitionInstructionReader, xmlMeasureList, currentInstrument));
                 if (this.repetitionInstructionReader !== undefined) {
-                    this.repetitionInstructionReader.XmlMeasureList[counter] = xmlMeasureList;
+                    this.repetitionInstructionReader.xmlMeasureList[counter] = xmlMeasureList;
                 }
                 counter++;
             }
@@ -235,10 +230,10 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
      * @param instrumentReaders
      */
     private checkIfRhythmInstructionsAreSetAndEqual(instrumentReaders: InstrumentReader[]): void {
-        let rhythmInstructions: RhythmInstruction[] = [];
+        const rhythmInstructions: RhythmInstruction[] = [];
         for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
             if (this.currentMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
-                let last: AbstractNotationInstruction = this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions[
+                const last: AbstractNotationInstruction = this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions[
                 this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.length - 1
                     ];
                 if (last instanceof RhythmInstruction) {
@@ -249,7 +244,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         let maxRhythmValue: number = 0.0;
         let index: number = -1;
         for (let idx: number = 0, len: number = rhythmInstructions.length; idx < len; ++idx) {
-            let rhythmInstruction: RhythmInstruction = rhythmInstructions[idx];
+            const rhythmInstruction: RhythmInstruction = rhythmInstructions[idx];
             if (rhythmInstruction.Rhythm.RealValue > maxRhythmValue) {
                 if (this.areRhythmInstructionsMixed(rhythmInstructions) && rhythmInstruction.SymbolEnum !== RhythmSymbolEnum.NONE) {
                     continue;
@@ -259,7 +254,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             }
         }
         if (rhythmInstructions.length > 0 && rhythmInstructions.length < this.completeNumberOfStaves) {
-            let rhythmInstruction: RhythmInstruction = rhythmInstructions[index].clone();
+            const rhythmInstruction: RhythmInstruction = rhythmInstructions[index].clone();
             for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
                 if (
                     this.currentMeasure.FirstInstructionsStaffEntries[i] !== undefined &&
@@ -274,12 +269,12 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                 }
             }
             for (let idx: number = 0, len: number = instrumentReaders.length; idx < len; ++idx) {
-                let instrumentReader: InstrumentReader = instrumentReaders[idx];
+                const instrumentReader: InstrumentReader = instrumentReaders[idx];
                 instrumentReader.ActiveRhythm = rhythmInstruction;
             }
         }
         if (rhythmInstructions.length === 0 && this.currentMeasure === this.musicSheet.SourceMeasures[0]) {
-            let rhythmInstruction: RhythmInstruction = new RhythmInstruction(new Fraction(4, 4, false), 4, 4, RhythmSymbolEnum.NONE);
+            const rhythmInstruction: RhythmInstruction = new RhythmInstruction(new Fraction(4, 4, 0, false), RhythmSymbolEnum.NONE);
             for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
                 if (this.currentMeasure.FirstInstructionsStaffEntries[i] === undefined) {
                     this.currentMeasure.FirstInstructionsStaffEntries[i] = new SourceStaffEntry(undefined, undefined);
@@ -289,18 +284,18 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                 this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.push(rhythmInstruction);
             }
             for (let idx: number = 0, len: number = instrumentReaders.length; idx < len; ++idx) {
-                let instrumentReader: InstrumentReader = instrumentReaders[idx];
+                const instrumentReader: InstrumentReader = instrumentReaders[idx];
                 instrumentReader.ActiveRhythm = rhythmInstruction;
             }
         }
         for (let idx: number = 0, len: number = rhythmInstructions.length; idx < len; ++idx) {
-            let rhythmInstruction: RhythmInstruction = rhythmInstructions[idx];
+            const rhythmInstruction: RhythmInstruction = rhythmInstructions[idx];
             if (rhythmInstruction.Rhythm.RealValue < maxRhythmValue) {
                 if (this._lastElement(
                         this.currentMeasure.FirstInstructionsStaffEntries[rhythmInstructions.indexOf(rhythmInstruction)].Instructions
                     ) instanceof RhythmInstruction) {
                     // TODO Test correctness
-                    let instrs: AbstractNotationInstruction[] =
+                    const instrs: AbstractNotationInstruction[] =
                         this.currentMeasure.FirstInstructionsStaffEntries[rhythmInstructions.indexOf(rhythmInstruction)].Instructions;
                     instrs[instrs.length - 1] = rhythmInstructions[index].clone();
                 }
@@ -340,17 +335,17 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
      */
     private setSourceMeasureDuration(instrumentReaders: InstrumentReader[], sourceMeasureCounter: number): number {
         let activeRhythm: Fraction = new Fraction(0, 1);
-        let instrumentsMaxTieNoteFractions: Fraction[] = [];
-        for (let instrumentReader of instrumentReaders) {
+        const instrumentsMaxTieNoteFractions: Fraction[] = [];
+        for (const instrumentReader of instrumentReaders) {
             instrumentsMaxTieNoteFractions.push(instrumentReader.MaxTieNoteFraction);
-            let activeRythmMeasure: Fraction = instrumentReader.ActiveRhythm.Rhythm;
+            const activeRythmMeasure: Fraction = instrumentReader.ActiveRhythm.Rhythm;
             if (activeRhythm.lt(activeRythmMeasure)) {
-                activeRhythm = new Fraction(activeRythmMeasure.Numerator, activeRythmMeasure.Denominator, false);
+                activeRhythm = new Fraction(activeRythmMeasure.Numerator, activeRythmMeasure.Denominator, 0, false);
             }
         }
-        let instrumentsDurations: Fraction[] = this.currentMeasure.calculateInstrumentsDuration(this.musicSheet, instrumentsMaxTieNoteFractions);
+        const instrumentsDurations: Fraction[] = this.currentMeasure.calculateInstrumentsDuration(this.musicSheet, instrumentsMaxTieNoteFractions);
         let maxInstrumentDuration: Fraction = new Fraction(0, 1);
-        for (let instrumentsDuration of instrumentsDurations) {
+        for (const instrumentsDuration of instrumentsDurations) {
             if (maxInstrumentDuration.lt(instrumentsDuration)) {
                 maxInstrumentDuration = instrumentsDuration;
             }
@@ -370,18 +365,18 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         this.currentMeasure.Duration = maxInstrumentDuration;
         this.currentMeasure.MeasureNumber = sourceMeasureCounter;
         for (let i: number = 0; i < instrumentsDurations.length; i++) {
-            let instrumentsDuration: Fraction = instrumentsDurations[i];
+            const instrumentsDuration: Fraction = instrumentsDurations[i];
             if (
                 (this.currentMeasure.ImplicitMeasure && instrumentsDuration !== maxInstrumentDuration) ||
                 !Fraction.Equal(instrumentsDuration, activeRhythm) &&
                 !this.allInstrumentsHaveSameDuration(instrumentsDurations, maxInstrumentDuration)
             ) {
-                let firstStaffIndexOfInstrument: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.musicSheet.Instruments[i]);
+                const firstStaffIndexOfInstrument: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.musicSheet.Instruments[i]);
                 for (let staffIndex: number = 0; staffIndex < this.musicSheet.Instruments[i].Staves.length; staffIndex++) {
                     if (!this.staffMeasureIsEmpty(firstStaffIndexOfInstrument + staffIndex)) {
                         this.currentMeasure.setErrorInStaffMeasure(firstStaffIndexOfInstrument + staffIndex, true);
-                        let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/MissingNotesError",
-                                                                              "Given Notes don't correspond to measure duration.");
+                        const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/MissingNotesError",
+                                                                                "Given Notes don't correspond to measure duration.");
                         this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                     }
                 }
@@ -399,8 +394,8 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
      */
     private checkFractionsForEquivalence(maxInstrumentDuration: Fraction, activeRhythm: Fraction): void {
         if (activeRhythm.Denominator > maxInstrumentDuration.Denominator) {
-            let factor: number = activeRhythm.Denominator / maxInstrumentDuration.Denominator;
-            maxInstrumentDuration.multiplyWithFactor(factor);
+            const factor: number = activeRhythm.Denominator / maxInstrumentDuration.Denominator;
+            maxInstrumentDuration.expand(factor);
         }
     }
 
@@ -429,7 +424,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     private allInstrumentsHaveSameDuration(instrumentsDurations: Fraction[], maxInstrumentDuration: Fraction): boolean {
         let counter: number = 0;
         for (let idx: number = 0, len: number = instrumentsDurations.length; idx < len; ++idx) {
-            let instrumentsDuration: Fraction = instrumentsDurations[idx];
+            const instrumentsDuration: Fraction = instrumentsDurations[idx];
             if (instrumentsDuration.Equals(maxInstrumentDuration)) {
                 counter++;
             }
@@ -454,10 +449,10 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     private checkSourceMeasureForNullEntries(): void {
         for (let i: number = this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1; i >= 0; i--) {
             for (let j: number = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length - 1; j >= 0; j--) {
-                let sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[j];
+                const sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[j];
                 if (sourceStaffEntry !== undefined) {
                     for (let k: number = sourceStaffEntry.VoiceEntries.length - 1; k >= 0; k--) {
-                        let voiceEntry: VoiceEntry = sourceStaffEntry.VoiceEntries[k];
+                        const voiceEntry: VoiceEntry = sourceStaffEntry.VoiceEntries[k];
                         if (voiceEntry.Notes.length === 0) {
                             this._removeFromArray(voiceEntry.ParentVoice.VoiceEntries, voiceEntry);
                             this._removeFromArray(sourceStaffEntry.VoiceEntries, voiceEntry);
@@ -472,7 +467,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         for (let i: number = this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1; i >= 0; i--) {
             let counter: number = 0;
             for (let idx: number = 0, len: number = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length; idx < len; ++idx) {
-                let sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[idx];
+                const sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[idx];
                 if (sourceStaffEntry === undefined) {
                     counter++;
                 }
@@ -496,11 +491,11 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         }
         if (this.musicSheet.Title === undefined) {
             try {
-                let barI: number = Math.max(
+                const barI: number = Math.max(
                     0, filePath.lastIndexOf("/"), filePath.lastIndexOf("\\")
                 );
-                let filename: string = filePath.substr(barI);
-                let filenameSplits: string[] = filename.split(".", 1);
+                const filename: string = filePath.substr(barI);
+                const filenameSplits: string[] = filename.split(".", 1);
                 this.musicSheet.Title = new Label(filenameSplits[0]);
             } catch (ex) {
                 Logging.log("MusicSheetReader.pushSheetLabels: ", ex);
@@ -511,7 +506,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
 
     // Checks whether _elem_ has an attribute with value _val_.
     private presentAttrsWithValue(elem: IXmlElement, val: string): boolean {
-        for (let attr of elem.attributes()) {
+        for (const attr of elem.attributes()) {
             if (attr.value === val) {
                 return true;
             }
@@ -520,11 +515,11 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     }
 
     private readComposer(root: IXmlElement): void {
-        let identificationNode: IXmlElement = root.element("identification");
+        const identificationNode: IXmlElement = root.element("identification");
         if (identificationNode !== undefined) {
-            let creators: IXmlElement[] = identificationNode.elements("creator");
+            const creators: IXmlElement[] = identificationNode.elements("creator");
             for (let idx: number = 0, len: number = creators.length; idx < len; ++idx) {
-                let creator: IXmlElement = creators[idx];
+                const creator: IXmlElement = creators[idx];
                 if (creator.hasAttributes) {
                     if (this.presentAttrsWithValue(creator, "composer")) {
                         this.musicSheet.Composer = new Label(this.trimString(creator.value));
@@ -539,7 +534,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     }
 
     private readTitleAndComposerFromCredits(root: IXmlElement): void {
-        let systemYCoordinates: number = this.computeSystemYCoordinates(root);
+        const systemYCoordinates: number = this.computeSystemYCoordinates(root);
         if (systemYCoordinates === 0) {
             return;
         }
@@ -548,9 +543,9 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         let largestCreditYInfo: number = 0;
         let finalSubtitle: string = undefined;
         let possibleTitle: string = undefined;
-        let creditElements: IXmlElement[] = root.elements("credit");
+        const creditElements: IXmlElement[] = root.elements("credit");
         for (let idx: number = 0, len: number = creditElements.length; idx < len; ++idx) {
-            let credit: IXmlElement = creditElements[idx];
+            const credit: IXmlElement = creditElements[idx];
             if (!credit.attribute("page")) {
                 return;
             }
@@ -561,13 +556,13 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                     if (!creditChild.attribute("justify")) {
                         break;
                     }
-                    let creditJustify: string = creditChild.attribute("justify").value;
-                    let creditY: string = creditChild.attribute("default-y").value;
-                    let creditYInfo: number = parseFloat(creditY);
+                    const creditJustify: string = creditChild.attribute("justify").value;
+                    const creditY: string = creditChild.attribute("default-y").value;
+                    const creditYInfo: number = parseFloat(creditY);
                     if (creditYInfo > systemYCoordinates) {
                         if (this.musicSheet.Title === undefined) {
-                            let creditSize: string = creditChild.attribute("font-size").value;
-                            let titleCreditSizeInt: number = parseFloat(creditSize);
+                            const creditSize: string = creditChild.attribute("font-size").value;
+                            const titleCreditSizeInt: number = parseFloat(creditSize);
                             if (largestTitleCreditSize < titleCreditSizeInt) {
                                 largestTitleCreditSize = titleCreditSizeInt;
                                 finalTitle = creditChild.value;
@@ -616,20 +611,20 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
         }
         let paperHeight: number = 0;
         let topSystemDistance: number = 0;
-        let defi: string = root.element("defaults").element("page-layout").element("page-height").value;
+        const defi: string = root.element("defaults").element("page-layout").element("page-height").value;
         paperHeight = parseFloat(defi);
         let found: boolean = false;
-        let parts: IXmlElement[] = root.elements("part");
+        const parts: IXmlElement[] = root.elements("part");
         for (let idx: number = 0, len: number = parts.length; idx < len; ++idx) {
-            let measures: IXmlElement[] = parts[idx].elements("measure");
+            const measures: IXmlElement[] = parts[idx].elements("measure");
             for (let idx2: number = 0, len2: number = measures.length; idx2 < len2; ++idx2) {
-                let measure: IXmlElement = measures[idx2];
+                const measure: IXmlElement = measures[idx2];
                 if (measure.element("print") !== undefined) {
-                    let systemLayouts: IXmlElement[] = measure.element("print").elements("system-layout");
+                    const systemLayouts: IXmlElement[] = measure.element("print").elements("system-layout");
                     for (let idx3: number = 0, len3: number = systemLayouts.length; idx3 < len3; ++idx3) {
-                        let syslab: IXmlElement = systemLayouts[idx3];
+                        const syslab: IXmlElement = systemLayouts[idx3];
                         if (syslab.element("top-system-distance") !== undefined) {
-                            let topSystemDistanceString: string = syslab.element("top-system-distance").value;
+                            const topSystemDistanceString: string = syslab.element("top-system-distance").value;
                             topSystemDistance = parseFloat(topSystemDistanceString);
                             found = true;
                             break;
@@ -643,9 +638,9 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             }
         }
         if (root.element("defaults").element("system-layout") !== undefined) {
-            let syslay: IXmlElement = root.element("defaults").element("system-layout");
+            const syslay: IXmlElement = root.element("defaults").element("system-layout");
             if (syslay.element("top-system-distance") !== undefined) {
-                let topSystemDistanceString: string = root.element("defaults").element("system-layout").element("top-system-distance").value;
+                const topSystemDistanceString: string = root.element("defaults").element("system-layout").element("top-system-distance").value;
                 topSystemDistance = parseFloat(topSystemDistanceString);
             }
         }
@@ -656,7 +651,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     }
 
     private readTitle(root: IXmlElement): void {
-        let titleNode: IXmlElement = root.element("work");
+        const titleNode: IXmlElement = root.element("work");
         let titleNodeChild: IXmlElement = undefined;
         if (titleNode !== undefined) {
             titleNodeChild = titleNode.element("work-title");
@@ -664,7 +659,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                 this.musicSheet.Title = new Label(this.trimString(titleNodeChild.value));
             }
         }
-        let movementNode: IXmlElement = root.element("movement-title");
+        const movementNode: IXmlElement = root.element("movement-title");
         let finalSubTitle: string = "";
         if (movementNode !== undefined) {
             if (this.musicSheet.Title === undefined) {
@@ -674,9 +669,9 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             }
         }
         if (titleNode !== undefined) {
-            let subtitleNodeChild: IXmlElement = titleNode.element("work-number");
+            const subtitleNodeChild: IXmlElement = titleNode.element("work-number");
             if (subtitleNodeChild !== undefined) {
-                let workNumber: string = subtitleNodeChild.value;
+                const workNumber: string = subtitleNodeChild.value;
                 if (workNumber) {
                     if (finalSubTitle) {
                         finalSubTitle = workNumber;
@@ -699,27 +694,27 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
      */
     private createInstrumentGroups(entryList: IXmlElement[]): { [_: string]: Instrument; } {
         let instrumentId: number = 0;
-        let instrumentDict: { [_: string]: Instrument; } = {};
+        const instrumentDict: { [_: string]: Instrument; } = {};
         let currentGroup: InstrumentalGroup;
         try {
-            let entryArray: IXmlElement[] = entryList;
+            const entryArray: IXmlElement[] = entryList;
             for (let idx: number = 0, len: number = entryArray.length; idx < len; ++idx) {
-                let node: IXmlElement = entryArray[idx];
+                const node: IXmlElement = entryArray[idx];
                 if (node.name === "score-part") {
-                    let instrIdString: string = node.attribute("id").value;
-                    let instrument: Instrument = new Instrument(instrumentId, instrIdString, this.musicSheet, currentGroup);
+                    const instrIdString: string = node.attribute("id").value;
+                    const instrument: Instrument = new Instrument(instrumentId, instrIdString, this.musicSheet, currentGroup);
                     instrumentId++;
-                    let partElements: IXmlElement[] = node.elements();
+                    const partElements: IXmlElement[] = node.elements();
                     for (let idx2: number = 0, len2: number = partElements.length; idx2 < len2; ++idx2) {
-                        let partElement: IXmlElement = partElements[idx2];
+                        const partElement: IXmlElement = partElements[idx2];
                         try {
                             if (partElement.name === "part-name") {
                                 instrument.Name = partElement.value;
                             } else if (partElement.name === "score-instrument") {
-                                let subInstrument: SubInstrument = new SubInstrument(instrument);
+                                const subInstrument: SubInstrument = new SubInstrument(instrument);
                                 subInstrument.idString = partElement.firstAttribute.value;
                                 instrument.SubInstruments.push(subInstrument);
-                                let subElement: IXmlElement = partElement.element("instrument-name");
+                                const subElement: IXmlElement = partElement.element("instrument-name");
                                 if (subElement !== undefined) {
                                     subInstrument.name = subElement.value;
                                     subInstrument.setMidiInstrument(subElement.value);
@@ -727,15 +722,15 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                             } else if (partElement.name === "midi-instrument") {
                                 let subInstrument: SubInstrument = instrument.getSubInstrument(partElement.firstAttribute.value);
                                 for (let idx3: number = 0, len3: number = instrument.SubInstruments.length; idx3 < len3; ++idx3) {
-                                    let subInstr: SubInstrument = instrument.SubInstruments[idx3];
+                                    const subInstr: SubInstrument = instrument.SubInstruments[idx3];
                                     if (subInstr.idString === partElement.value) {
                                         subInstrument = subInstr;
                                         break;
                                     }
                                 }
-                                let instrumentElements: IXmlElement[] = partElement.elements();
+                                const instrumentElements: IXmlElement[] = partElement.elements();
                                 for (let idx3: number = 0, len3: number = instrumentElements.length; idx3 < len3; ++idx3) {
-                                    let instrumentElement: IXmlElement = instrumentElements[idx3];
+                                    const instrumentElement: IXmlElement = instrumentElements[idx3];
                                     try {
                                         if (instrumentElement.name === "midi-channel") {
                                             if (parseInt(instrumentElement.value, 10) === 10) {
@@ -749,7 +744,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                                             subInstrument.fixedKey = Math.max(0, parseInt(instrumentElement.value, 10));
                                         } else if (instrumentElement.name === "volume") {
                                             try {
-                                                let result: number = <number>parseFloat(instrumentElement.value);
+                                                const result: number = <number>parseFloat(instrumentElement.value);
                                                 subInstrument.volume = result / 127.0;
                                             } catch (ex) {
                                                 Logging.debug("ExpressionReader.readExpressionParameters", "read volume", ex);
@@ -757,7 +752,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
 
                                         } else if (instrumentElement.name === "pan") {
                                             try {
-                                                let result: number = <number>parseFloat(instrumentElement.value);
+                                                const result: number = <number>parseFloat(instrumentElement.value);
                                                 subInstrument.pan = result / 64.0;
                                             } catch (ex) {
                                                 Logging.debug("ExpressionReader.readExpressionParameters", "read pan", ex);
@@ -776,7 +771,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
 
                     }
                     if (instrument.SubInstruments.length === 0) {
-                        let subInstrument: SubInstrument = new SubInstrument(instrument);
+                        const subInstrument: SubInstrument = new SubInstrument(instrument);
                         instrument.SubInstruments.push(subInstrument);
                     }
                     instrumentDict[instrIdString] = instrument;
@@ -789,7 +784,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                     }
                 } else {
                     if ((node.name === "part-group") && (node.attribute("type").value === "start")) {
-                        let iG: InstrumentalGroup = new InstrumentalGroup("group", this.musicSheet, currentGroup);
+                        const iG: InstrumentalGroup = new InstrumentalGroup("group", this.musicSheet, currentGroup);
                         if (currentGroup !== undefined) {
                             currentGroup.InstrumentalGroups.push(iG);
                         } else {
@@ -800,7 +795,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                         if ((node.name === "part-group") && (node.attribute("type").value === "stop")) {
                             if (currentGroup !== undefined) {
                                 if (currentGroup.InstrumentalGroups.length === 1) {
-                                    let instr: InstrumentalGroup = currentGroup.InstrumentalGroups[0];
+                                    const instr: InstrumentalGroup = currentGroup.InstrumentalGroups[0];
                                     if (currentGroup.Parent !== undefined) {
                                         currentGroup.Parent.InstrumentalGroups.push(instr);
                                         this._removeFromArray(currentGroup.Parent.InstrumentalGroups, currentGroup);
@@ -816,14 +811,14 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
                 }
             }
         } catch (e) {
-            let errorMsg: string = ITextTranslation.translateText(
+            const errorMsg: string = ITextTranslation.translateText(
                 "ReaderErrorMessages/InstrumentError", "Error while reading Instruments"
             );
             throw new MusicSheetReadingException(errorMsg, e);
         }
 
         for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
-            let instrument: Instrument = this.musicSheet.Instruments[idx];
+            const instrument: Instrument = this.musicSheet.Instruments[idx];
             if (!instrument.Name) {
                 instrument.Name = "Instr. " + instrument.IdString;
             }
@@ -838,10 +833,10 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
      */
     private getCompleteNumberOfStavesFromXml(partInst: IXmlElement[]): number {
         let num: number = 0;
-        for (let partNode of partInst) {
-            let xmlMeasureList: IXmlElement[] = partNode.elements("measure");
+        for (const partNode of partInst) {
+            const xmlMeasureList: IXmlElement[] = partNode.elements("measure");
             if (xmlMeasureList.length > 0) {
-                let xmlMeasure: IXmlElement = xmlMeasureList[0];
+                const xmlMeasure: IXmlElement = xmlMeasureList[0];
                 if (xmlMeasure !== undefined) {
                     let stavesNode: IXmlElement = xmlMeasure.element("attributes");
                     if (stavesNode !== undefined) {
@@ -856,7 +851,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             }
         }
         if (isNaN(num) || num <= 0) {
-            let errorMsg: string = ITextTranslation.translateText(
+            const errorMsg: string = ITextTranslation.translateText(
                 "ReaderErrorMessages/StaffError", "Invalid number of staves."
             );
             throw new MusicSheetReadingException(errorMsg);
@@ -871,9 +866,9 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
      */
     private getInstrumentNumberOfStavesFromXml(partNode: IXmlElement): number {
         let num: number = 0;
-        let xmlMeasure: IXmlElement = partNode.element("measure");
+        const xmlMeasure: IXmlElement = partNode.element("measure");
         if (xmlMeasure !== undefined) {
-            let attributes: IXmlElement = xmlMeasure.element("attributes");
+            const attributes: IXmlElement = xmlMeasure.element("attributes");
             let staves: IXmlElement = undefined;
             if (attributes !== undefined) {
                 staves = attributes.element("staves");
@@ -885,7 +880,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             }
         }
         if (isNaN(num) || num <= 0) {
-            let errorMsg: string = ITextTranslation.translateText(
+            const errorMsg: string = ITextTranslation.translateText(
                 "ReaderErrorMessages/StaffError", "Invalid number of Staves."
             );
             throw new MusicSheetReadingException(errorMsg);

+ 31 - 0
src/MusicalScore/ScoreIO/MusicSymbolModuleFactory.ts

@@ -0,0 +1,31 @@
+import {RepetitionInstructionReader} from "./MusicSymbolModules/RepetitionInstructionReader";
+import {RepetitionCalculator} from "./MusicSymbolModules/RepetitionCalculator";
+
+export class MusicSymbolModuleFactory {
+  public static createRepetitionInstructionReader(): RepetitionInstructionReader {
+    return new RepetitionInstructionReader();
+  }
+
+  public static createRepetitionCalculator(): RepetitionCalculator {
+    return new RepetitionCalculator();
+  }
+
+  /*
+   public static createExpressionGenerator(musicSheet: MusicSheet,
+   instrument: Instrument, staffNumber: number): ExpressionReader {
+   return new ExpressionReader(musicSheet, instrument, staffNumber);
+   }
+
+   public static createSlurReader(musicSheet: MusicSheet): SlurReader {
+   return new SlurReader(musicSheet);
+   }
+
+   public static createLyricsReader(musicSheet: MusicSheet): LyricsReader {
+   return new LyricsReader(musicSheet);
+   }
+
+   public static createArticulationReader(): ArticulationReader {
+   return new ArticulationReader();
+   }
+   */
+}

+ 195 - 0
src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts

@@ -0,0 +1,195 @@
+import {ArticulationEnum, VoiceEntry} from "../../VoiceData/VoiceEntry";
+import {IXmlAttribute, IXmlElement} from "../../../Common/FileIO/Xml";
+import * as log from "loglevel";
+import {TechnicalInstruction, TechnicalInstructionType} from "../../VoiceData/Instructions/TechnicalInstruction";
+import {OrnamentContainer, OrnamentEnum} from "../../VoiceData/OrnamentContainer";
+import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
+import {AccidentalEnum} from "../../../Common/DataObjects/Pitch";
+export class ArticulationReader {
+
+  private getAccEnumFromString(input: string): AccidentalEnum {
+    switch (input) {
+      case "natural":
+        return AccidentalEnum.NATURAL;
+      case "sharp":
+        return AccidentalEnum.SHARP;
+      case "sharp-sharp":
+      case "double-sharp":
+        return AccidentalEnum.DOUBLESHARP;
+      case "flat":
+        return AccidentalEnum.FLAT;
+      case "flat-flat":
+        return AccidentalEnum.DOUBLEFLAT;
+      default:
+        return AccidentalEnum.NONE;
+    }
+  }
+
+  /**
+   * This method adds an Articulation Expression to the currentVoiceEntry.
+   * @param node
+   * @param currentVoiceEntry
+   */
+  public addArticulationExpression(node: IXmlElement, currentVoiceEntry: VoiceEntry): void {
+    if (node !== undefined && node.elements().length > 0) {
+      const childNotes: IXmlElement[] = node.elements();
+      for (let idx: number = 0, len: number = childNotes.length; idx < len; ++idx) {
+        const childNote: IXmlElement = childNotes[idx];
+        const name: string = childNote.name;
+        try {
+          // some Articulations appear in Xml separated with a "-" (eg strong-accent), we remove it for enum parsing
+          name.replace("-", "");
+          const articulationEnum: ArticulationEnum = ArticulationEnum[name];
+          if (VoiceEntry.isSupportedArticulation(articulationEnum)) {
+            // staccato should be first
+            if (name === "staccato") {
+              if (currentVoiceEntry.Articulations.length > 0 &&
+                currentVoiceEntry.Articulations[0] !== ArticulationEnum.staccato) {
+                currentVoiceEntry.Articulations.splice(0, 0, articulationEnum);
+              }
+            }
+
+            // don't add the same articulation twice
+            if (currentVoiceEntry.Articulations.indexOf(articulationEnum) === -1) {
+              currentVoiceEntry.Articulations.push(articulationEnum);
+            }
+          }
+        } catch (ex) {
+          const errorMsg: string = "Invalid note articulation.";
+          log.debug("addArticulationExpression", errorMsg, ex);
+          return;
+        }
+      }
+    }
+  }
+
+  /**
+   * This method add a Fermata to the currentVoiceEntry.
+   * @param xmlNode
+   * @param currentVoiceEntry
+   */
+  public addFermata(xmlNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
+    // fermata appears as separate tag in XML
+    let articulationEnum: ArticulationEnum = ArticulationEnum.fermata;
+    if (xmlNode.attributes().length > 0 && xmlNode.attribute("type") !== undefined) {
+      if (xmlNode.attribute("type").value === "inverted") {
+        articulationEnum = ArticulationEnum.invertedfermata;
+      }
+    }
+    // add to VoiceEntry
+    currentVoiceEntry.Articulations.push(articulationEnum);
+  }
+
+  /**
+   * This method add a technical Articulation to the currentVoiceEntry.
+   * @param xmlNode
+   * @param currentVoiceEntry
+   */
+  public addTechnicalArticulations(xmlNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
+    let node: IXmlElement = xmlNode.element("up-bow");
+    if (node !== undefined) {
+      if (currentVoiceEntry.Articulations.indexOf(ArticulationEnum.upbow) === -1) {
+        currentVoiceEntry.Articulations.push(ArticulationEnum.upbow);
+      }
+    }
+    node = xmlNode.element("down-bow");
+    if (node !== undefined) {
+      if (currentVoiceEntry.Articulations.indexOf(ArticulationEnum.downbow) === -1) {
+        currentVoiceEntry.Articulations.push(ArticulationEnum.downbow);
+      }
+    }
+    node = xmlNode.element("open-string");
+    if (node !== undefined) {
+      if (currentVoiceEntry.Articulations.indexOf(ArticulationEnum.naturalharmonic) === -1) {
+        currentVoiceEntry.Articulations.push(ArticulationEnum.naturalharmonic);
+      }
+    }
+    node = xmlNode.element("stopped");
+    if (node !== undefined) {
+      if (currentVoiceEntry.Articulations.indexOf(ArticulationEnum.lefthandpizzicato) === -1) {
+        currentVoiceEntry.Articulations.push(ArticulationEnum.lefthandpizzicato);
+      }
+    }
+    node = xmlNode.element("snap-pizzicato");
+    if (node !== undefined) {
+      if (currentVoiceEntry.Articulations.indexOf(ArticulationEnum.snappizzicato) === -1) {
+        currentVoiceEntry.Articulations.push(ArticulationEnum.snappizzicato);
+      }
+    }
+    node = xmlNode.element("fingering");
+    if (node !== undefined) {
+      const currentTechnicalInstruction: TechnicalInstruction = new TechnicalInstruction();
+      currentTechnicalInstruction.type = TechnicalInstructionType.Fingering;
+      currentTechnicalInstruction.value = node.value;
+      currentVoiceEntry.TechnicalInstructions.push(currentTechnicalInstruction);
+    }
+  }
+
+  /**
+   * This method adds an Ornament to the currentVoiceEntry.
+   * @param ornamentsNode
+   * @param currentVoiceEntry
+   */
+  public addOrnament(ornamentsNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
+    if (ornamentsNode !== undefined) {
+      let ornament: OrnamentContainer = undefined;
+      let node: IXmlElement = ornamentsNode.element("trill-mark");
+      if (node !== undefined) {
+        ornament = new OrnamentContainer(OrnamentEnum.Trill);
+      }
+      node = ornamentsNode.element("turn");
+      if (node !== undefined) {
+        ornament = new OrnamentContainer(OrnamentEnum.Turn);
+      }
+      node = ornamentsNode.element("inverted-turn");
+      if (node !== undefined) {
+        ornament = new OrnamentContainer(OrnamentEnum.InvertedTurn);
+      }
+      node = ornamentsNode.element("delayed-turn");
+      if (node !== undefined) {
+        ornament = new OrnamentContainer(OrnamentEnum.DelayedTurn);
+      }
+      node = ornamentsNode.element("delayed-inverted-turn");
+      if (node !== undefined) {
+        ornament = new OrnamentContainer(OrnamentEnum.DelayedInvertedTurn);
+      }
+      node = ornamentsNode.element("mordent");
+      if (node !== undefined) {
+        ornament = new OrnamentContainer(OrnamentEnum.Mordent);
+      }
+      node = ornamentsNode.element("inverted-mordent");
+      if (node !== undefined) {
+        ornament = new OrnamentContainer(OrnamentEnum.InvertedMordent);
+      }
+      if (ornament !== undefined) {
+        const accidentalsList: IXmlElement[] = ornamentsNode.elements("accidental-mark");
+        if (accidentalsList !== undefined) {
+          let placement: PlacementEnum = PlacementEnum.Below;
+          let accidental: AccidentalEnum = AccidentalEnum.NONE;
+          const accidentalsListArr: IXmlElement[] = accidentalsList;
+          for (let idx: number = 0, len: number = accidentalsListArr.length; idx < len; ++idx) {
+            const accidentalNode: IXmlElement = accidentalsListArr[idx];
+            let text: string = accidentalNode.value;
+            accidental = this.getAccEnumFromString(text);
+            const placementAttr: IXmlAttribute = accidentalNode.attribute("placement");
+            if (accidentalNode.hasAttributes && placementAttr !== undefined) {
+              text = placementAttr.value;
+              if (text === "above") {
+                placement = PlacementEnum.Above;
+              } else if (text === "below") {
+                placement = PlacementEnum.Below;
+              }
+            }
+            if (placement === PlacementEnum.Above) {
+              ornament.AccidentalAbove = accidental;
+            } else if (placement === PlacementEnum.Below) {
+              ornament.AccidentalBelow = accidental;
+            }
+          }
+        }
+        // add this to currentVoiceEntry
+        currentVoiceEntry.OrnamentContainer = ornament;
+      }
+    }
+  }
+}

+ 144 - 0
src/MusicalScore/ScoreIO/MusicSymbolModules/ChordSymbolReader.ts

@@ -0,0 +1,144 @@
+import {IXmlElement} from "../../../Common/FileIO/Xml";
+import {MusicSheet} from "../../MusicSheet";
+import {ChordDegreeText, ChordSymbolContainer, ChordSymbolEnum, Degree} from "../../VoiceData/ChordSymbolContainer";
+import {AccidentalEnum, NoteEnum, Pitch} from "../../../Common/DataObjects/Pitch";
+import {KeyInstruction} from "../../VoiceData/Instructions/KeyInstruction";
+import {ITextTranslation} from "../../Interfaces/ITextTranslation";
+import * as log from "loglevel";
+
+export class ChordSymbolReader {
+    public static readChordSymbol(xmlNode: IXmlElement, musicSheet: MusicSheet, activeKey: KeyInstruction): ChordSymbolContainer {
+        const root: IXmlElement = xmlNode.element("root");
+        const kind: IXmlElement = xmlNode.element("kind");
+
+        // must be always present
+        if (root === undefined || kind === undefined) {
+          return undefined;
+        }
+
+        const rootStep: IXmlElement = root.element("root-step");
+        const rootAlter: IXmlElement = root.element("root-alter");
+
+        // a valid NoteEnum value should be present
+        if (rootStep === undefined) {
+            return undefined;
+        }
+        let rootNote: NoteEnum;
+        try {
+            rootNote = NoteEnum[rootStep.value.trim()];
+        } catch (ex) {
+            const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
+                                                                    "Invalid chord symbol");
+            musicSheet.SheetErrors.pushMeasureError(errorMsg);
+            log.debug("InstrumentReader.readChordSymbol", errorMsg, ex);
+            return undefined;
+        }
+
+        // an alteration value isn't necessary
+        let rootAlteration: AccidentalEnum = AccidentalEnum.NONE;
+        if (rootAlter !== undefined) {
+            try {
+                rootAlteration = <AccidentalEnum>parseInt(rootAlter.value, undefined);
+            } catch (ex) {
+                const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
+                                                                        "Invalid chord symbol");
+                musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                log.debug("InstrumentReader.readChordSymbol", errorMsg, ex);
+            }
+
+        }
+        // using default octave value, to be changed later
+        const rootPitch: Pitch = new Pitch(rootNote, 1, rootAlteration);
+        const kindValue: string = kind.value.trim().replace("-", "");
+        let chordKind: ChordSymbolEnum;
+        try {
+            chordKind = ChordSymbolEnum[kindValue];
+        } catch (ex) {
+            const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
+                                                                    "Invalid chord symbol");
+            musicSheet.SheetErrors.pushMeasureError(errorMsg);
+            log.debug("InstrumentReader.readChordSymbol", errorMsg, ex);
+            return undefined;
+        }
+
+        // bass is optional
+        let bassPitch: Pitch = undefined;
+        const bass: IXmlElement = xmlNode.element("bass");
+        if (bass !== undefined) {
+            const bassStep: IXmlElement = bass.element("bass-step");
+            const bassAlter: IXmlElement = bass.element("bass-alter");
+            let bassNote: NoteEnum = NoteEnum.C;
+            if (bassStep !== undefined) {
+                try {
+                    bassNote = NoteEnum[bassStep.value.trim()];
+                } catch (ex) {
+                    const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
+                                                                            "Invalid chord symbol");
+                    musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    log.debug("InstrumentReader.readChordSymbol", errorMsg, ex);
+                    return undefined;
+                }
+            }
+            let bassAlteration: AccidentalEnum = AccidentalEnum.NONE;
+            if (bassAlter !== undefined) {
+                try {
+                    bassAlteration = <AccidentalEnum>parseInt(bassAlter.value, undefined);
+                } catch (ex) {
+                    const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
+                                                                            "Invalid chord symbol");
+                    musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    log.debug("InstrumentReader.readChordSymbol", errorMsg, ex);
+                }
+            }
+            bassPitch = new Pitch(bassNote, 1, bassAlteration);
+        }
+
+        // degree is optional
+        let degree: Degree = undefined;
+        const degreeNode: IXmlElement = xmlNode.element("degree");
+        if (degreeNode !== undefined) {
+            const degreeValue: IXmlElement = degreeNode.element("degree-value");
+            const degreeAlter: IXmlElement = degreeNode.element("degree-alter");
+            const degreeType: IXmlElement = degreeNode.element("degree-type");
+            if (degreeValue === undefined || degreeAlter === undefined || degreeType === undefined) {
+              return undefined;
+            }
+
+            let value: number;
+            try {
+                value = parseInt(degreeValue.value.trim(), undefined);
+            } catch (ex) {
+                const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
+                                                                        "Invalid chord symbol");
+                musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                log.debug("InstrumentReader.readChordSymbol", errorMsg, ex);
+                return undefined;
+            }
+
+            let alter: AccidentalEnum;
+            try {
+                alter = <AccidentalEnum>parseInt(degreeAlter.value, undefined);
+            } catch (ex) {
+                const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
+                                                                        "Invalid chord symbol");
+                musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                log.debug("InstrumentReader.readChordSymbol", errorMsg, ex);
+                return undefined;
+            }
+
+            let text: ChordDegreeText;
+            try {
+                text = ChordDegreeText[degreeType.value.trim().toLowerCase()];
+            } catch (ex) {
+                const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/ChordSymbolError",
+                                                                        "Invalid chord symbol");
+                musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                log.debug("InstrumentReader.readChordSymbol", errorMsg, ex);
+                return undefined;
+            }
+
+            degree = new Degree(value, alter, text);
+        }
+        return new ChordSymbolContainer(rootPitch, chordKind, bassPitch, degree, activeKey);
+    }
+}

+ 140 - 0
src/MusicalScore/ScoreIO/MusicSymbolModules/LyricsReader.ts

@@ -0,0 +1,140 @@
+import {LyricWord} from "../../VoiceData/Lyrics/LyricsWord";
+import {VoiceEntry} from "../../VoiceData/VoiceEntry";
+import {IXmlElement} from "../../../Common/FileIO/Xml";
+import {LyricsEntry} from "../../VoiceData/Lyrics/LyricsEntry";
+import {ITextTranslation} from "../../Interfaces/ITextTranslation";
+import {MusicSheet} from "../../MusicSheet";
+
+export class LyricsReader {
+    private openLyricWords: { [_: number]: LyricWord; } = {};
+    private currentLyricWord: LyricWord;
+    private musicSheet: MusicSheet;
+
+    constructor(musicSheet: MusicSheet) {
+        this.musicSheet = musicSheet;
+    }
+    /**
+     * This method adds a single LyricEntry to a VoiceEntry
+     * @param {IXmlElement[]} lyricNodeList
+     * @param {VoiceEntry} currentVoiceEntry
+     */
+    public addLyricEntry(lyricNodeList: IXmlElement[], currentVoiceEntry: VoiceEntry): void {
+        if (lyricNodeList !== undefined) {
+            const lyricNodeListArr: IXmlElement[] = lyricNodeList;
+            for (let idx: number = 0, len: number = lyricNodeListArr.length; idx < len; ++idx) {
+                const lyricNode: IXmlElement = lyricNodeListArr[idx];
+                try {
+                    let syllabic: string = "single"; // Single as default
+                    if (lyricNode.element("text") !== undefined) {
+                        let textNode: IXmlElement = lyricNode.element("text");
+                        if (lyricNode.element("syllabic") !== undefined) {
+                            syllabic = lyricNode.element("syllabic").value;
+                        }
+                        if (textNode !== undefined) {
+                            const text: string = textNode.value;
+                            // <elision> separates Multiple syllabels on a single LyricNote
+                            // "-" text indicating separated syllabel should be ignored
+                            // we calculate the Dash element much later
+                            if (lyricNode.element("elision") !== undefined && text === "-") {
+                                const lyricNodeChildren: IXmlElement[] = lyricNode.elements();
+                                let elisionIndex: number = 0;
+                                for (let i: number = 0; i < lyricNodeChildren.length; i++) {
+                                    const child: IXmlElement = lyricNodeChildren[i];
+                                    if (child.name === "elision") {
+                                        elisionIndex = i;
+                                        break;
+                                    }
+                                }
+                                let nextText: IXmlElement = undefined;
+                                let nextSyllabic: IXmlElement = undefined;
+                                // read the next nodes
+                                if (elisionIndex > 0) {
+                                    for (let i: number = elisionIndex; i < lyricNodeChildren.length; i++) {
+                                        const child: IXmlElement = lyricNodeChildren[i];
+                                        if (child.name === "text") {
+                                            nextText = child;
+                                        }
+                                        if (child.name === "syllabic") {
+                                            nextSyllabic = child;
+                                        }
+                                    }
+                                }
+                                if (nextText !== undefined && nextSyllabic !== undefined) {
+                                    textNode = nextText;
+                                    syllabic = "middle";
+                                }
+                            }
+                            let currentLyricVerseNumber: number = 1;
+                            if (lyricNode.attributes() !== undefined && lyricNode.attribute("number") !== undefined) {
+                                try {
+                                    currentLyricVerseNumber = parseInt(lyricNode.attribute("number").value, 10);
+                                } catch (err) {
+                                    try {
+                                        const result: string[] = lyricNode.attribute("number").value.toLowerCase().split("verse");
+                                        if (result.length > 1) {
+                                            currentLyricVerseNumber = parseInt(result[1], 10);
+                                        }
+                                    } catch (err) {
+                                        const errorMsg: string =
+                                        ITextTranslation.translateText("ReaderErrorMessages/LyricVerseNumberError", "Invalid lyric verse number");
+                                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                                        continue;
+                                    }
+                                }
+                            }
+                            let lyricsEntry: LyricsEntry = undefined;
+                            if (syllabic === "single" || syllabic === "end") {
+                                if (this.openLyricWords[currentLyricVerseNumber] !== undefined) { // word end given or some word still open
+                                    this.currentLyricWord = this.openLyricWords[currentLyricVerseNumber];
+                                    lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, this.currentLyricWord, currentVoiceEntry);
+                                    this.currentLyricWord.Syllables.push(lyricsEntry);
+                                    delete this.openLyricWords[currentLyricVerseNumber];
+                                    this.currentLyricWord = undefined;
+                                } else { // single syllable given or end given while no word has been started
+                                    lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, undefined, currentVoiceEntry);
+                                }
+                                lyricsEntry.extend = lyricNode.element("extend") !== undefined;
+                            } else if (syllabic === "begin") { // first finishing, if a word already is open (can only happen, when wrongly given)
+                                if (this.openLyricWords[currentLyricVerseNumber] !== undefined) {
+                                    delete this.openLyricWords[currentLyricVerseNumber];
+                                    this.currentLyricWord = undefined;
+                                }
+                                this.currentLyricWord = new LyricWord();
+                                this.openLyricWords[currentLyricVerseNumber] = this.currentLyricWord;
+                                lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, this.currentLyricWord, currentVoiceEntry);
+                                this.currentLyricWord.Syllables.push(lyricsEntry);
+                            } else if (syllabic === "middle") {
+                                if (this.openLyricWords[currentLyricVerseNumber] !== undefined) {
+                                    this.currentLyricWord = this.openLyricWords[currentLyricVerseNumber];
+                                    lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, this.currentLyricWord, currentVoiceEntry);
+                                    this.currentLyricWord.Syllables.push(lyricsEntry);
+                                } else {
+                                    // in case the wrong syllabel information is given, create a single Entry and add it to currentVoiceEntry
+                                    lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, undefined, currentVoiceEntry);
+                                }
+                            }
+                            // add each LyricEntry to currentVoiceEntry
+                            if (lyricsEntry !== undefined) {
+                                // only add the lyric entry if not another entry has already been given:
+                                if (!currentVoiceEntry.LyricsEntries[currentLyricVerseNumber] !== undefined) {
+                                    currentVoiceEntry.LyricsEntries.setValue(currentLyricVerseNumber, lyricsEntry);
+                                }
+                                // save in currentInstrument the verseNumber (only once)
+                                if (!currentVoiceEntry.ParentVoice.Parent.LyricVersesNumbers[currentLyricVerseNumber] !== undefined) {
+                                    currentVoiceEntry.ParentVoice.Parent.LyricVersesNumbers.push(currentLyricVerseNumber);
+                                }
+                            }
+                        }
+                    }
+                } catch (err) {
+                    const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/LyricError", "Error while reading lyric entry.");
+                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    continue;
+                }
+            }
+            // Squash to unique numbers
+            currentVoiceEntry.ParentVoice.Parent.LyricVersesNumbers =
+            currentVoiceEntry.ParentVoice.Parent.LyricVersesNumbers.filter((lvn, index, self) => self.indexOf(lvn) === index);
+        }
+    }
+}

+ 99 - 0
src/MusicalScore/ScoreIO/MusicSymbolModules/RepetitionCalculator.ts

@@ -0,0 +1,99 @@
+import {SourceMeasure} from "../../VoiceData/SourceMeasure";
+import {RepetitionInstruction, RepetitionInstructionEnum, AlignmentType} from "../../VoiceData/Instructions/RepetitionInstruction";
+import {RepetitionInstructionComparer} from "../../VoiceData/Instructions/RepetitionInstruction";
+import {ArgumentOutOfRangeException} from "../../Exceptions";
+import {MusicSheet} from "../../MusicSheet";
+
+export class RepetitionCalculator {
+  private musicSheet: MusicSheet;
+  private repetitionInstructions: RepetitionInstruction[] = [];
+  private currentMeasure: SourceMeasure;
+  private currentMeasureIndex: number;
+
+  /**
+   * Is called when all repetition symbols have been read from xml.
+   * Creates the repetition instructions and adds them to the corresponding measure.
+   * Creates the logical repetition objects for iteration and playback.
+   * @param musicSheet
+   * @param repetitionInstructions
+   */
+  public calculateRepetitions(musicSheet: MusicSheet, repetitionInstructions: RepetitionInstruction[]): void {
+    this.musicSheet = <MusicSheet>musicSheet;
+    this.repetitionInstructions = repetitionInstructions;
+    const sourceMeasures: SourceMeasure[] = this.musicSheet.SourceMeasures;
+    for (let idx: number = 0, len: number = this.repetitionInstructions.length; idx < len; ++idx) {
+      const instruction: RepetitionInstruction = this.repetitionInstructions[idx];
+      this.currentMeasureIndex = instruction.measureIndex;
+      this.currentMeasure = sourceMeasures[this.currentMeasureIndex];
+      this.handleRepetitionInstructions(instruction);
+    }
+
+    // if there are more than one instruction at measure begin or end,
+    // sort them according to the nesting of the repetitions:
+    for (let idx: number = 0, len: number = this.musicSheet.SourceMeasures.length; idx < len; ++idx) {
+      const measure: SourceMeasure = this.musicSheet.SourceMeasures[idx];
+      if (measure.FirstRepetitionInstructions.length > 1) {
+        measure.FirstRepetitionInstructions.sort(RepetitionInstructionComparer.Compare);
+      }
+      if (measure.LastRepetitionInstructions.length > 1) {
+        measure.LastRepetitionInstructions.sort(RepetitionInstructionComparer.Compare);
+      }
+    }
+  }
+
+  private handleRepetitionInstructions(currentRepetitionInstruction: RepetitionInstruction): boolean {
+    switch (currentRepetitionInstruction.type) {
+      case RepetitionInstructionEnum.StartLine:
+        this.currentMeasure.FirstRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.BackJumpLine:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.Ending:
+        // set ending start or end
+        if (currentRepetitionInstruction.alignment === AlignmentType.Begin) {  // ending start
+          this.currentMeasure.FirstRepetitionInstructions.push(currentRepetitionInstruction);
+        } else { // ending end
+          for (let idx: number = 0, len: number = currentRepetitionInstruction.endingIndices.length; idx < len; ++idx) {
+            this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+          }
+        }
+        break;
+      case RepetitionInstructionEnum.Segno:
+        this.currentMeasure.FirstRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.Fine:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.ToCoda:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.Coda:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.DaCapo:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.DalSegno:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.DalSegnoAlFine:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.DaCapoAlFine:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.DalSegnoAlCoda:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.DaCapoAlCoda:
+        this.currentMeasure.LastRepetitionInstructions.push(currentRepetitionInstruction);
+        break;
+      case RepetitionInstructionEnum.None:
+        break;
+      default:
+        throw new ArgumentOutOfRangeException("currentRepetitionInstruction");
+    }
+    return true;
+  }
+}

+ 380 - 0
src/MusicalScore/ScoreIO/MusicSymbolModules/RepetitionInstructionReader.ts

@@ -0,0 +1,380 @@
+import {MusicSheet} from "../../MusicSheet";
+import {IXmlElement} from "../../../Common/FileIO/Xml";
+import {SourceMeasure} from "../../VoiceData/SourceMeasure";
+import {RepetitionInstruction, RepetitionInstructionEnum, AlignmentType} from "../../VoiceData/Instructions/RepetitionInstruction";
+import {RepetitionInstructionComparer} from "../../VoiceData/Instructions/RepetitionInstruction";
+import {StringUtil} from "../../../Common/Strings/StringUtil";
+export class RepetitionInstructionReader {
+  /**
+   * A global list of all repetition instructions in the musicsheet.
+   */
+  public repetitionInstructions: RepetitionInstruction[];
+  public xmlMeasureList: IXmlElement[][];
+  private musicSheet: MusicSheet;
+  private currentMeasureIndex: number;
+
+  public set MusicSheet(value: MusicSheet) {
+    this.musicSheet = value;
+    this.xmlMeasureList = new Array(this.musicSheet.Instruments.length);
+    this.repetitionInstructions = [];
+  }
+
+  /**
+   * is called when starting reading an xml measure
+   * @param measure
+   * @param currentMeasureIndex
+   */
+  public prepareReadingMeasure(measure: SourceMeasure, currentMeasureIndex: number): void {
+    this.currentMeasureIndex = currentMeasureIndex;
+  }
+
+  public handleLineRepetitionInstructions(barlineNode: IXmlElement, pieceEndingDetected: boolean): void {
+    pieceEndingDetected = false;
+    if (barlineNode.elements().length > 0) {
+      let location: string = "";
+      let hasRepeat: boolean = false;
+      let direction: string = "";
+      let type: string = "";
+      let style: string = "";
+      const endingIndices: number[] = [];
+
+      // read barline style
+      const styleNode: IXmlElement = barlineNode.element("bar-style");
+
+      // if location is ommited in Xml, right is implied (from documentation)
+      if (styleNode !== undefined) {
+        style = styleNode.value;
+      }
+      if (barlineNode.attributes().length > 0 && barlineNode.attribute("location") !== undefined) {
+        location = barlineNode.attribute("location").value;
+      } else {
+        location = "right";
+      }
+      const barlineNodeElements: IXmlElement[] = barlineNode.elements();
+
+      // read repeat- or ending line information
+      for (let idx: number = 0, len: number = barlineNodeElements.length; idx < len; ++idx) {
+        const childNode: IXmlElement = barlineNodeElements[idx];
+        if ("repeat" === childNode.name && childNode.hasAttributes) {
+          hasRepeat = true;
+          direction = childNode.attribute("direction").value;
+        } else if ( "ending" === childNode.name && childNode.hasAttributes &&
+                    childNode.attribute("type") !== undefined && childNode.attribute("number") !== undefined) {
+          type = childNode.attribute("type").value;
+          const num: string = childNode.attribute("number").value;
+
+          // Parse the given ending indices:
+          // handle cases like: "1, 2" or "1 + 2" or even "1 - 3, 6"
+          const separatedEndingIndices: string[] = num.split("[,+]");
+          for (let idx2: number = 0, len2: number = separatedEndingIndices.length; idx2 < len2; ++idx2) {
+            const separatedEndingIndex: string = separatedEndingIndices[idx2];
+            const indices: string[] = separatedEndingIndex.match("[0-9]");
+
+            // check if possibly something like "1-3" is given..
+            if (separatedEndingIndex.search("-") !== -1 && indices.length === 2) {
+              const startIndex: number = parseInt(indices[0], 10);
+              const endIndex: number = parseInt(indices[1], 10);
+              for (let index: number = startIndex; index <= endIndex; index++) {
+                endingIndices.push(index);
+              }
+            } else {
+              for (let idx3: number = 0, len3: number = indices.length; idx3 < len3; ++idx3) {
+                const index: string = indices[idx3];
+                endingIndices.push(parseInt(index, 10));
+              }
+            }
+          }
+        }
+      }
+
+      // reset measure counter if not lastMeasure
+      if (style === "light-heavy" && endingIndices.length === 0 && !hasRepeat) {
+        pieceEndingDetected = true;
+      }
+      if (hasRepeat || endingIndices.length > 0) {
+        if (location === "left") {
+          if (type === "start") {
+            const newInstruction: RepetitionInstruction = new RepetitionInstruction(this.currentMeasureIndex, RepetitionInstructionEnum.Ending,
+                                                                                    AlignmentType.Begin, undefined, endingIndices);
+            this.addInstruction(this.repetitionInstructions, newInstruction);
+          }
+          if (direction === "forward") {
+            // start new Repetition
+            const newInstruction: RepetitionInstruction = new RepetitionInstruction(this.currentMeasureIndex, RepetitionInstructionEnum.StartLine);
+            this.addInstruction(this.repetitionInstructions, newInstruction);
+          }
+        } else { // location right
+          if (type === "stop" || type === "discontinue") {
+            const newInstruction: RepetitionInstruction = new RepetitionInstruction(this.currentMeasureIndex, RepetitionInstructionEnum.Ending,
+                                                                                    AlignmentType.End, undefined, endingIndices);
+            this.addInstruction(this.repetitionInstructions, newInstruction);
+          }
+          if (direction === "backward") {
+            const newInstruction: RepetitionInstruction = new RepetitionInstruction(this.currentMeasureIndex, RepetitionInstructionEnum.BackJumpLine);
+            this.addInstruction(this.repetitionInstructions, newInstruction);
+          }
+        }
+      }
+    }
+  }
+
+  public handleRepetitionInstructionsFromWordsOrSymbols(directionTypeNode: IXmlElement, relativeMeasurePosition: number): boolean {
+    const wordsNode: IXmlElement = directionTypeNode.element("words");
+    if (wordsNode !== undefined) {
+      // must Trim string and ToLower before compare
+      const innerText: string = wordsNode.value.trim().toLowerCase();
+      if (StringUtil.StringContainsSeparatedWord(innerText, "d.s. al fine") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "d. s. al fine")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition < 0.5 && this.currentMeasureIndex < this.xmlMeasureList[0].length - 1) { // not in last measure
+          measureIndex--;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DalSegnoAlFine);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+      if (StringUtil.StringContainsSeparatedWord(innerText, "d.s. al coda") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "d. s. al coda")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition < 0.5) {
+          measureIndex--;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DalSegnoAlCoda);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+      if (StringUtil.StringContainsSeparatedWord(innerText, "d.c. al fine") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "d. c. al fine")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition < 0.5 && this.currentMeasureIndex < this.xmlMeasureList[0].length - 1) { // not in last measure
+          measureIndex--;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DaCapoAlFine);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+      if (StringUtil.StringContainsSeparatedWord(innerText, "d.c. al coda") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "d. c. al coda")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition < 0.5) {
+          measureIndex--;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DaCapoAlCoda);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+      if (StringUtil.StringContainsSeparatedWord(innerText, "d.c.") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "d. c.") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "dacapo") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "da capo")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition < 0.5 && this.currentMeasureIndex < this.xmlMeasureList[0].length - 1) { // not in last measure
+          measureIndex--;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DaCapo);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+      if (StringUtil.StringContainsSeparatedWord(innerText, "d.s.") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "d. s.") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "dalsegno") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "dal segno")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition < 0.5 && this.currentMeasureIndex < this.xmlMeasureList[0].length - 1) { // not in last measure
+          measureIndex--;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.DalSegno);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+      if (StringUtil.StringContainsSeparatedWord(innerText, "tocoda") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "to coda") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "a coda") ||
+        StringUtil.StringContainsSeparatedWord(innerText, "a la coda")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition < 0.5) {
+          measureIndex--;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.ToCoda);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+      if (StringUtil.StringContainsSeparatedWord(innerText, "fine")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition < 0.5) {
+          measureIndex--;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Fine);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+      if (StringUtil.StringContainsSeparatedWord(innerText, "coda")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition > 0.5) {
+          measureIndex++;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Coda);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+      if (StringUtil.StringContainsSeparatedWord(innerText, "segno")) {
+        let measureIndex: number = this.currentMeasureIndex;
+        if (relativeMeasurePosition > 0.5) {
+          measureIndex++;
+        }
+        const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Segno);
+        this.addInstruction(this.repetitionInstructions, newInstruction);
+        return true;
+      }
+    } else if (directionTypeNode.element("segno") !== undefined) {
+      let measureIndex: number = this.currentMeasureIndex;
+      if (relativeMeasurePosition > 0.5) {
+        measureIndex++;
+      }
+      const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Segno);
+      this.addInstruction(this.repetitionInstructions, newInstruction);
+      return true;
+    } else if (directionTypeNode.element("coda") !== undefined) {
+      let measureIndex: number = this.currentMeasureIndex;
+      if (relativeMeasurePosition > 0.5) {
+        measureIndex++;
+      }
+      const newInstruction: RepetitionInstruction = new RepetitionInstruction(measureIndex, RepetitionInstructionEnum.Coda);
+      this.addInstruction(this.repetitionInstructions, newInstruction);
+      return true;
+    }
+    return false;
+  }
+
+  public removeRedundantInstructions(): void {
+    let segnoCount: number = 0;
+    let codaCount: number = 0;
+    //const fineCount: number = 0;
+    let toCodaCount: number = 0;
+    let dalSegnaCount: number = 0;
+    for (let index: number = 0; index < this.repetitionInstructions.length; index++) {
+      const instruction: RepetitionInstruction = this.repetitionInstructions[index];
+      switch (instruction.type) {
+        case RepetitionInstructionEnum.Coda:
+          if (toCodaCount > 0) {
+            if (this.findInstructionInPreviousMeasure(index, instruction.measureIndex, RepetitionInstructionEnum.ToCoda)) {
+              instruction.type = RepetitionInstructionEnum.None;
+            }
+          }
+          if (codaCount === 0 && toCodaCount === 0) {
+            instruction.type = RepetitionInstructionEnum.ToCoda;
+            instruction.alignment = AlignmentType.End;
+            instruction.measureIndex--;
+          }
+          break;
+        case RepetitionInstructionEnum.Segno:
+          if (segnoCount - dalSegnaCount > 0) { // two segnos in a row
+            let foundInstruction: boolean = false;
+            for (let idx: number = 0, len: number = this.repetitionInstructions.length; idx < len; ++idx) {
+              const instr: RepetitionInstruction = this.repetitionInstructions[idx];
+              if (instruction.measureIndex - instr.measureIndex === 1) {
+                switch (instr.type) {
+                  case RepetitionInstructionEnum.BackJumpLine:
+                    if (toCodaCount - codaCount > 0) { // open toCoda existing
+                      instr.type = RepetitionInstructionEnum.DalSegnoAlCoda;
+                    } else {
+                      instr.type = RepetitionInstructionEnum.DalSegno;
+                    }
+                    instruction.type = RepetitionInstructionEnum.None;
+                    foundInstruction = true;
+                    break;
+                  case RepetitionInstructionEnum.DalSegno:
+                  case RepetitionInstructionEnum.DalSegnoAlFine:
+                  case RepetitionInstructionEnum.DalSegnoAlCoda:
+                    instruction.type = RepetitionInstructionEnum.None;
+                    foundInstruction = true;
+                    break;
+                  default:
+                    break;
+                }
+              }
+              if (foundInstruction) {
+                break;
+              }
+            }
+            if (foundInstruction) {
+              break;
+            }
+            // convert to dal segno instruction:
+            if (toCodaCount - codaCount > 0) { // open toCoda existing
+              instruction.type = RepetitionInstructionEnum.DalSegnoAlCoda;
+            } else {
+              instruction.type = RepetitionInstructionEnum.DalSegno;
+            }
+            instruction.alignment = AlignmentType.End;
+            instruction.measureIndex--;
+          }
+          break;
+        default:
+          break;
+      }
+
+      // check if this  instruction already exists or is otherwise redundant:
+      if (this.backwardSearchForPreviousIdenticalInstruction(index, instruction) || instruction.type === RepetitionInstructionEnum.None) {
+        this.repetitionInstructions.splice(index, 1);
+        index--;
+      } else {
+        switch (instruction.type) {
+          case RepetitionInstructionEnum.Fine:
+            //fineCount++;
+            break;
+          case RepetitionInstructionEnum.ToCoda:
+            toCodaCount++;
+            break;
+          case RepetitionInstructionEnum.Coda:
+            codaCount++;
+            break;
+          case RepetitionInstructionEnum.Segno:
+            segnoCount++;
+            break;
+          case RepetitionInstructionEnum.DalSegnoAlFine:
+          case RepetitionInstructionEnum.DalSegnoAlCoda:
+            dalSegnaCount++;
+            break;
+          default:
+            break;
+        }
+      }
+    }
+    this.repetitionInstructions.sort(RepetitionInstructionComparer.Compare);
+  }
+
+  private findInstructionInPreviousMeasure(currentInstructionIndex: number, currentMeasureIndex: number, searchedType: RepetitionInstructionEnum): boolean {
+    for (let index: number = currentInstructionIndex - 1; index >= 0; index--) {
+      const instruction: RepetitionInstruction = this.repetitionInstructions[index];
+      if (currentMeasureIndex - instruction.measureIndex === 1 && instruction.type === searchedType) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private backwardSearchForPreviousIdenticalInstruction(currentInstructionIndex: number, currentInstruction: RepetitionInstruction): boolean {
+    for (let index: number = currentInstructionIndex - 1; index >= 0; index--) {
+      const instruction: RepetitionInstruction = this.repetitionInstructions[index];
+      if (instruction.equals(currentInstruction)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private addInstruction(currentRepetitionInstructions: RepetitionInstruction[], newInstruction: RepetitionInstruction): void {
+    let addInstruction: boolean = true;
+    for (let idx: number = 0, len: number = currentRepetitionInstructions.length; idx < len; ++idx) {
+      const repetitionInstruction: RepetitionInstruction = currentRepetitionInstructions[idx];
+      if (newInstruction.equals(repetitionInstruction)) {
+        addInstruction = false;
+        break;
+      }
+    }
+    if (addInstruction) {
+      currentRepetitionInstructions.push(newInstruction);
+    }
+  }
+}

+ 825 - 797
src/MusicalScore/ScoreIO/VoiceGenerator.ts

@@ -16,6 +16,7 @@ import {ITextTranslation} from "../Interfaces/ITextTranslation";
 import {ArticulationEnum} from "../VoiceData/VoiceEntry";
 import {Slur} from "../VoiceData/Expressions/ContinuousExpressions/Slur";
 import {LyricsEntry} from "../VoiceData/Lyrics/LyricsEntry";
+import {LyricsReader} from "../ScoreIO/MusicSymbolModules/LyricsReader";
 import {MusicSheetReadingException} from "../Exceptions";
 import {AccidentalEnum} from "../../Common/DataObjects/Pitch";
 import {NoteEnum} from "../../Common/DataObjects/Pitch";
@@ -27,6 +28,7 @@ import {Pitch} from "../../Common/DataObjects/Pitch";
 import {IXmlAttribute} from "../../Common/FileIO/Xml";
 import {CollectionUtil} from "../../Util/CollectionUtil";
 import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import {ArticulationReader} from "./MusicSymbolModules/ArticulationReader";
 
 /**
  * To be implemented
@@ -34,936 +36,962 @@ import Dictionary from "typescript-collections/dist/lib/Dictionary";
 export type SlurReader = any;
 
 export class VoiceGenerator {
-    constructor(instrument: Instrument, voiceId: number, slurReader: SlurReader, mainVoice: Voice = undefined) {
-        this.musicSheet = instrument.GetMusicSheet;
-        this.slurReader = slurReader;
-        if (mainVoice !== undefined) {
-            this.voice = new LinkedVoice(instrument, voiceId, mainVoice);
-        } else {
-            this.voice = new Voice(instrument, voiceId);
+  constructor(instrument: Instrument, voiceId: number, slurReader: SlurReader, mainVoice: Voice = undefined) {
+    this.musicSheet = instrument.GetMusicSheet;
+    // this.slurReader = slurReader;
+    if (mainVoice !== undefined) {
+      this.voice = new LinkedVoice(instrument, voiceId, mainVoice);
+    } else {
+      this.voice = new Voice(instrument, voiceId);
+    }
+    instrument.Voices.push(this.voice);
+    this.lyricsReader = new LyricsReader(this.musicSheet);
+    this.articulationReader = new ArticulationReader();
+  }
+
+  // private slurReader: SlurReader;
+    private lyricsReader: LyricsReader;
+  private articulationReader: ArticulationReader;
+  private musicSheet: MusicSheet;
+  private voice: Voice;
+  private currentVoiceEntry: VoiceEntry;
+  private currentNote: Note;
+  private currentMeasure: SourceMeasure;
+  private currentStaffEntry: SourceStaffEntry;
+  private lastBeamTag: string = "";
+  private openBeam: Beam;
+  private openGraceBeam: Beam;
+  private openTieDict: { [_: number]: Tie; } = {};
+  private currentOctaveShift: number = 0;
+  private tupletDict: { [_: number]: Tuplet; } = {};
+  private openTupletNumber: number = 0;
+
+  public get GetVoice(): Voice {
+    return this.voice;
+  }
+
+  public get OctaveShift(): number {
+    return this.currentOctaveShift;
+  }
+
+  public set OctaveShift(value: number) {
+    this.currentOctaveShift = value;
+  }
+
+  /**
+   * Create new [[VoiceEntry]], add it to given [[SourceStaffEntry]] and if given so, to [[Voice]].
+   * @param musicTimestamp
+   * @param parentStaffEntry
+   * @param addToVoice
+   */
+  public createVoiceEntry(musicTimestamp: Fraction, parentStaffEntry: SourceStaffEntry, addToVoice: boolean): void {
+    this.currentVoiceEntry = new VoiceEntry(musicTimestamp.clone(), this.voice, parentStaffEntry);
+    if (addToVoice) {
+      this.voice.VoiceEntries.push(this.currentVoiceEntry);
+    }
+    if (parentStaffEntry.VoiceEntries.indexOf(this.currentVoiceEntry) === -1) {
+      parentStaffEntry.VoiceEntries.push(this.currentVoiceEntry);
+    }
+  }
+
+  /**
+   * Create [[Note]]s and handle Lyrics, Articulations, Beams, Ties, Slurs, Tuplets.
+   * @param noteNode
+   * @param noteDuration
+   * @param divisions
+   * @param restNote
+   * @param graceNote
+   * @param parentStaffEntry
+   * @param parentMeasure
+   * @param measureStartAbsoluteTimestamp
+   * @param maxTieNoteFraction
+   * @param chord
+   * @param guitarPro
+   * @returns {Note}
+   */
+  public read(noteNode: IXmlElement, noteDuration: Fraction, restNote: boolean, graceNote: boolean,
+              parentStaffEntry: SourceStaffEntry, parentMeasure: SourceMeasure,
+              measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction, chord: boolean, guitarPro: boolean): Note {
+    this.currentStaffEntry = parentStaffEntry;
+    this.currentMeasure = parentMeasure;
+    //Logging.debug("read called:", restNote);
+    try {
+      this.currentNote = restNote
+        ? this.addRestNote(noteDuration)
+        : this.addSingleNote(noteNode, noteDuration, graceNote, chord, guitarPro);
+
+      if (this.lyricsReader !== undefined && noteNode.elements("lyric") !== undefined) {
+          this.lyricsReader.addLyricEntry(noteNode.elements("lyric"), this.currentVoiceEntry);
+          this.voice.Parent.HasLyrics = true;
+      }
+      let hasTupletCommand: boolean = false;
+      const notationNode: IXmlElement = noteNode.element("notations");
+      if (notationNode !== undefined) {
+        // let articNode: IXmlElement = undefined;
+        // (*)
+        if (this.articulationReader !== undefined) {
+          this.readArticulations(notationNode, this.currentVoiceEntry);
         }
-        instrument.Voices.push(this.voice);
-        //this.lyricsReader = MusicSymbolModuleFactory.createLyricsReader(this.musicSheet);
-        //this.articulationReader = MusicSymbolModuleFactory.createArticulationReader();
-    }
-
-    private slurReader: SlurReader;
-    //private lyricsReader: LyricsReader;
-    //private articulationReader: ArticulationReader;
-    private musicSheet: MusicSheet;
-    private voice: Voice;
-    private currentVoiceEntry: VoiceEntry;
-    private currentNote: Note;
-    private currentMeasure: SourceMeasure;
-    private currentStaffEntry: SourceStaffEntry;
-    private lastBeamTag: string = "";
-    private openBeam: Beam;
-    private openGraceBeam: Beam;
-    private openTieDict: { [_: number]: Tie; } = {};
-    private currentOctaveShift: number = 0;
-    private tupletDict: { [_: number]: Tuplet; } = {};
-    private openTupletNumber: number = 0;
-
-    public get GetVoice(): Voice {
-        return this.voice;
-    }
-    public get OctaveShift(): number {
-        return this.currentOctaveShift;
-    }
-    public set OctaveShift(value: number) {
-        this.currentOctaveShift = value;
-    }
-
-    /**
-     * Create new [[VoiceEntry]], add it to given [[SourceStaffEntry]] and if given so, to [[Voice]].
-     * @param musicTimestamp
-     * @param parentStaffEntry
-     * @param addToVoice
-     */
-    public createVoiceEntry(musicTimestamp: Fraction, parentStaffEntry: SourceStaffEntry, addToVoice: boolean): void {
-        this.currentVoiceEntry = new VoiceEntry(musicTimestamp.clone(), this.voice, parentStaffEntry);
-        if (addToVoice) {
-            this.voice.VoiceEntries.push(this.currentVoiceEntry);
+        //let slurNodes: IXmlElement[] = undefined;
+        // (*)
+        //if (this.slurReader !== undefined && (slurNodes = notationNode.elements("slur")))
+        //    this.slurReader.addSlur(slurNodes, this.currentNote);
+        // check for Tuplets
+        const tupletNodeList: IXmlElement[] = notationNode.elements("tuplet");
+        if (tupletNodeList.length > 0) {
+          this.openTupletNumber = this.addTuplet(noteNode, tupletNodeList);
+          hasTupletCommand = true;
         }
-        if (parentStaffEntry.VoiceEntries.indexOf(this.currentVoiceEntry) === -1) {
-            parentStaffEntry.VoiceEntries.push(this.currentVoiceEntry);
+        // check for Arpeggios
+        if (notationNode.element("arpeggiate") !== undefined && !graceNote) {
+          this.currentVoiceEntry.ArpeggiosNotesIndices.push(this.currentVoiceEntry.Notes.indexOf(this.currentNote));
+        }
+        // check for Ties - must be the last check
+        const tiedNodeList: IXmlElement[] = notationNode.elements("tied");
+        if (tiedNodeList.length > 0) {
+          this.addTie(tiedNodeList, measureStartAbsoluteTimestamp, maxTieNoteFraction);
         }
-    }
-
-    /**
-     * Create [[Note]]s and handle Lyrics, Articulations, Beams, Ties, Slurs, Tuplets.
-     * @param noteNode
-     * @param noteDuration
-     * @param divisions
-     * @param restNote
-     * @param graceNote
-     * @param parentStaffEntry
-     * @param parentMeasure
-     * @param measureStartAbsoluteTimestamp
-     * @param maxTieNoteFraction
-     * @param chord
-     * @param guitarPro
-     * @returns {Note}
-     */
-    public read(
-        noteNode: IXmlElement, noteDuration: number, divisions: number, restNote: boolean, graceNote: boolean,
-        parentStaffEntry: SourceStaffEntry, parentMeasure: SourceMeasure,
-        measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction, chord: boolean, guitarPro: boolean
-    ): Note {
-        this.currentStaffEntry = parentStaffEntry;
-        this.currentMeasure = parentMeasure;
-        //Logging.debug("read called:", restNote);
-        try {
-            this.currentNote = restNote
-                ? this.addRestNote(noteDuration, divisions)
-                : this.addSingleNote(noteNode, noteDuration, divisions, graceNote, chord, guitarPro);
-            // (*)
-            //if (this.lyricsReader !== undefined && noteNode.element("lyric") !== undefined) {
-            //    this.lyricsReader.addLyricEntry(noteNode, this.currentVoiceEntry);
-            //    this.voice.Parent.HasLyrics = true;
-            //}
-            let notationNode: IXmlElement = noteNode.element("notations");
-            if (notationNode !== undefined) {
-                // let articNode: IXmlElement = undefined;
-                // (*)
-                //if (this.articulationReader !== undefined) {
-                //    this.readArticulations(notationNode, this.currentVoiceEntry);
-                //}
-                //let slurNodes: IXmlElement[] = undefined;
-                // (*)
-                //if (this.slurReader !== undefined && (slurNodes = notationNode.elements("slur")))
-                //    this.slurReader.addSlur(slurNodes, this.currentNote);
-                let tupletNodeList: IXmlElement[] = notationNode.elements("tuplet");
-                if (tupletNodeList) {
-                    this.openTupletNumber = this.addTuplet(noteNode, tupletNodeList);
-                }
-                if (notationNode.element("arpeggiate") !== undefined && !graceNote) {
-                    this.currentVoiceEntry.ArpeggiosNotesIndices.push(this.currentVoiceEntry.Notes.indexOf(this.currentNote));
-                }
-                let tiedNodeList: IXmlElement[] = notationNode.elements("tied");
-                if (tiedNodeList) {
-                    this.addTie(tiedNodeList, measureStartAbsoluteTimestamp, maxTieNoteFraction);
-                }
 
-                let openTieDict: { [_: number]: Tie; } = this.openTieDict;
-                for (let key in openTieDict) {
-                    if (openTieDict.hasOwnProperty(key)) {
-                        let tie: Tie = openTieDict[key];
+        // remove open ties, if there is already a gap between the last tie note and now.
+        const openTieDict: { [_: number]: Tie; } = this.openTieDict;
+        for (const key in openTieDict) {
+          if (openTieDict.hasOwnProperty(key)) {
+                        const tie: Tie = openTieDict[key];
                         if (Fraction.plus(tie.Start.ParentStaffEntry.Timestamp, tie.Start.Length).lt(this.currentStaffEntry.Timestamp)) {
-                            delete openTieDict[key];
-                        }
-                    }
-                }
-            }
-            if (noteNode.element("time-modification") !== undefined && notationNode === undefined) {
-                this.handleTimeModificationNode(noteNode);
+              delete openTieDict[key];
             }
-        } catch (err) {
-            let errorMsg: string = ITextTranslation.translateText(
-                "ReaderErrorMessages/NoteError", "Ignored erroneous Note."
-            );
-            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+          }
         }
+      }
+      // time-modification yields tuplet in currentNote
+      // mustn't execute method, if this is the Note where the Tuplet has been created
+      if (noteNode.element("time-modification") !== undefined && !hasTupletCommand) {
+        this.handleTimeModificationNode(noteNode);
+      }
+    } catch (err) {
+            const errorMsg: string = ITextTranslation.translateText(
+        "ReaderErrorMessages/NoteError", "Ignored erroneous Note."
+      );
+            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+    }
 
-        return this.currentNote;
-    }
-
-    /**
-     * Handle the GraceNotes that appear before the Measure's End
-     * and aren't assigned to any normal (with [[VoiceEntries]]) [[SourceStaffEntry]]s yet.
-     */
-    public checkForOpenGraceNotes(): void {
-        if (
-            this.currentStaffEntry !== undefined
-            && this.currentStaffEntry.VoiceEntries.length === 0
-            && this.currentVoiceEntry.graceVoiceEntriesBefore !== undefined
-            && this.currentVoiceEntry.graceVoiceEntriesBefore.length > 0
-        ) {
-            let voice: Voice = this.currentVoiceEntry.ParentVoice;
-            let horizontalIndex: number = this.currentMeasure.VerticalSourceStaffEntryContainers.indexOf(this.currentStaffEntry.VerticalContainerParent);
-            let verticalIndex: number = this.currentStaffEntry.VerticalContainerParent.StaffEntries.indexOf(this.currentStaffEntry);
-            let previousStaffEntry: SourceStaffEntry = this.currentMeasure.getPreviousSourceStaffEntryFromIndex(verticalIndex, horizontalIndex);
-            if (previousStaffEntry !== undefined) {
-                let previousVoiceEntry: VoiceEntry = undefined;
-                for (let idx: number = 0, len: number = previousStaffEntry.VoiceEntries.length; idx < len; ++idx) {
-                    let voiceEntry: VoiceEntry = previousStaffEntry.VoiceEntries[idx];
-                    if (voiceEntry.ParentVoice === voice) {
-                        previousVoiceEntry = voiceEntry;
-                        previousVoiceEntry.graceVoiceEntriesAfter = [];
-                        for (let idx2: number = 0, len2: number = this.currentVoiceEntry.graceVoiceEntriesBefore.length; idx2 < len2; ++idx2) {
-                            let graceVoiceEntry: VoiceEntry = this.currentVoiceEntry.graceVoiceEntriesBefore[idx2];
-                            previousVoiceEntry.graceVoiceEntriesAfter.push(graceVoiceEntry);
-                        }
-                        this.currentVoiceEntry.graceVoiceEntriesBefore = [];
-                        this.currentStaffEntry = undefined;
-                        break;
-                    }
-                }
+    return this.currentNote;
+  }
+
+  /**
+   * Handle the GraceNotes that appear before the Measure's End
+   * and aren't assigned to any normal (with [[VoiceEntries]]) [[SourceStaffEntry]]s yet.
+   */
+  public checkForOpenGraceNotes(): void {
+    if (
+      this.currentStaffEntry !== undefined
+      && this.currentStaffEntry.VoiceEntries.length === 0
+      && this.currentVoiceEntry.graceVoiceEntriesBefore !== undefined
+      && this.currentVoiceEntry.graceVoiceEntriesBefore.length > 0
+    ) {
+        const voice: Voice = this.currentVoiceEntry.ParentVoice;
+        const horizontalIndex: number = this.currentMeasure.VerticalSourceStaffEntryContainers.indexOf(this.currentStaffEntry.VerticalContainerParent);
+        const verticalIndex: number = this.currentStaffEntry.VerticalContainerParent.StaffEntries.indexOf(this.currentStaffEntry);
+        const previousStaffEntry: SourceStaffEntry = this.currentMeasure.getPreviousSourceStaffEntryFromIndex(verticalIndex, horizontalIndex);
+        if (previousStaffEntry !== undefined) {
+        let previousVoiceEntry: VoiceEntry = undefined;
+        for (let idx: number = 0, len: number = previousStaffEntry.VoiceEntries.length; idx < len; ++idx) {
+          const voiceEntry: VoiceEntry = previousStaffEntry.VoiceEntries[idx];
+          if (voiceEntry.ParentVoice === voice) {
+            previousVoiceEntry = voiceEntry;
+            previousVoiceEntry.graceVoiceEntriesAfter = [];
+            for (let idx2: number = 0, len2: number = this.currentVoiceEntry.graceVoiceEntriesBefore.length; idx2 < len2; ++idx2) {
+              const graceVoiceEntry: VoiceEntry = this.currentVoiceEntry.graceVoiceEntriesBefore[idx2];
+              previousVoiceEntry.graceVoiceEntriesAfter.push(graceVoiceEntry);
             }
+            this.currentVoiceEntry.graceVoiceEntriesBefore = [];
+            this.currentStaffEntry = undefined;
+            break;
+          }
         }
+      }
     }
-
-    /**
-     * Create a new [[StaffEntryLink]] and sets the currenstStaffEntry accordingly.
-     * @param index
-     * @param currentStaff
-     * @param currentStaffEntry
-     * @param currentMeasure
-     * @returns {SourceStaffEntry}
-     */
-    public checkForStaffEntryLink(
-        index: number, currentStaff: Staff, currentStaffEntry: SourceStaffEntry, currentMeasure: SourceMeasure
-    ): SourceStaffEntry {
-        let staffEntryLink: StaffEntryLink = new StaffEntryLink(this.currentVoiceEntry);
-        staffEntryLink.LinkStaffEntries.push(currentStaffEntry);
-        currentStaffEntry.Link = staffEntryLink;
-        let linkMusicTimestamp: Fraction = this.currentVoiceEntry.Timestamp.clone();
-        let verticalSourceStaffEntryContainer: VerticalSourceStaffEntryContainer = currentMeasure.getVerticalContainerByTimestamp(linkMusicTimestamp);
-        currentStaffEntry = verticalSourceStaffEntryContainer.StaffEntries[index];
-        if (currentStaffEntry === undefined) {
-            currentStaffEntry = new SourceStaffEntry(verticalSourceStaffEntryContainer, currentStaff);
-            verticalSourceStaffEntryContainer.StaffEntries[index] = currentStaffEntry;
-        }
-        currentStaffEntry.VoiceEntries.push(this.currentVoiceEntry);
-        staffEntryLink.LinkStaffEntries.push(currentStaffEntry);
-        currentStaffEntry.Link = staffEntryLink;
-        return currentStaffEntry;
-    }
-    public checkForOpenBeam(): void {
-        if (this.openBeam !== undefined && this.currentNote !== undefined) {
-            this.handleOpenBeam();
-        }
+  }
+
+  /**
+   * Create a new [[StaffEntryLink]] and sets the currenstStaffEntry accordingly.
+   * @param index
+   * @param currentStaff
+   * @param currentStaffEntry
+   * @param currentMeasure
+   * @returns {SourceStaffEntry}
+   */
+  public checkForStaffEntryLink(index: number, currentStaff: Staff, currentStaffEntry: SourceStaffEntry, currentMeasure: SourceMeasure): SourceStaffEntry {
+    const staffEntryLink: StaffEntryLink = new StaffEntryLink(this.currentVoiceEntry);
+    staffEntryLink.LinkStaffEntries.push(currentStaffEntry);
+    currentStaffEntry.Link = staffEntryLink;
+    const linkMusicTimestamp: Fraction = this.currentVoiceEntry.Timestamp.clone();
+    const verticalSourceStaffEntryContainer: VerticalSourceStaffEntryContainer = currentMeasure.getVerticalContainerByTimestamp(linkMusicTimestamp);
+    currentStaffEntry = verticalSourceStaffEntryContainer.StaffEntries[index];
+    if (currentStaffEntry === undefined) {
+      currentStaffEntry = new SourceStaffEntry(verticalSourceStaffEntryContainer, currentStaff);
+      verticalSourceStaffEntryContainer.StaffEntries[index] = currentStaffEntry;
+    }
+    currentStaffEntry.VoiceEntries.push(this.currentVoiceEntry);
+    staffEntryLink.LinkStaffEntries.push(currentStaffEntry);
+    currentStaffEntry.Link = staffEntryLink;
+    return currentStaffEntry;
+  }
+
+  public checkForOpenBeam(): void {
+    if (this.openBeam !== undefined && this.currentNote !== undefined) {
+      this.handleOpenBeam();
     }
-    public checkOpenTies(): void {
-        let openTieDict: {[key: number]: Tie} = this.openTieDict;
-        for (let key in openTieDict) {
-            if (openTieDict.hasOwnProperty(key)) {
-                let tie: Tie = openTieDict[key];
+  }
+
+  public checkOpenTies(): void {
+    const openTieDict: {[key: number]: Tie} = this.openTieDict;
+    for (const key in openTieDict) {
+      if (openTieDict.hasOwnProperty(key)) {
+                const tie: Tie = openTieDict[key];
                 if (Fraction.plus(tie.Start.ParentStaffEntry.Timestamp, tie.Start.Length)
-                        .lt(tie.Start.ParentStaffEntry.VerticalContainerParent.ParentMeasure.Duration)) {
-                    delete openTieDict[key];
-                }
-            }
+            .lt(tie.Start.ParentStaffEntry.VerticalContainerParent.ParentMeasure.Duration)) {
+          delete openTieDict[key];
         }
+      }
     }
-    public hasVoiceEntry(): boolean {
-        return this.currentVoiceEntry !== undefined;
-    }
-
-    /**
-     *
-     * @param type
-     * @returns {Fraction} - a Note's Duration from a given type (type must be valid).
-     */
-    public getNoteDurationFromType(type: string): Fraction {
-        switch (type) {
-            case "1024th":
-                return new Fraction(1, 1024);
-            case "512th":
-                return new Fraction(1, 512);
-            case "256th":
-                return new Fraction(1, 256);
-            case "128th":
-                return new Fraction(1, 128);
-            case "64th":
-                return new Fraction(1, 64);
-            case "32th":
-            case "32nd":
-                return new Fraction(1, 32);
-            case "16th":
-                return new Fraction(1, 16);
-            case "eighth":
-                return new Fraction(1, 8);
-            case "quarter":
-                return new Fraction(1, 4);
-            case "half":
-                return new Fraction(1, 2);
-            case "whole":
-                return new Fraction(1, 1);
-            case "breve":
-                return new Fraction(2, 1);
-            case "long":
-                return new Fraction(4, 1);
-            case "maxima":
-                return new Fraction(8, 1);
-            default: {
-                let errorMsg: string = ITextTranslation.translateText(
-                    "ReaderErrorMessages/NoteDurationError", "Invalid note duration."
-                );
+  }
+
+  public hasVoiceEntry(): boolean {
+    return this.currentVoiceEntry !== undefined;
+  }
+
+  /**
+   *
+   * @param type
+   * @returns {Fraction} - a Note's Duration from a given type (type must be valid).
+   */
+  public getNoteDurationFromType(type: string): Fraction {
+    switch (type) {
+      case "1024th":
+        return new Fraction(1, 1024);
+      case "512th":
+        return new Fraction(1, 512);
+      case "256th":
+        return new Fraction(1, 256);
+      case "128th":
+        return new Fraction(1, 128);
+      case "64th":
+        return new Fraction(1, 64);
+      case "32th":
+      case "32nd":
+        return new Fraction(1, 32);
+      case "16th":
+        return new Fraction(1, 16);
+      case "eighth":
+        return new Fraction(1, 8);
+      case "quarter":
+        return new Fraction(1, 4);
+      case "half":
+        return new Fraction(1, 2);
+      case "whole":
+        return new Fraction(1, 1);
+      case "breve":
+        return new Fraction(2, 1);
+      case "long":
+        return new Fraction(4, 1);
+      case "maxima":
+        return new Fraction(8, 1);
+      default: {
+                const errorMsg: string = ITextTranslation.translateText(
+          "ReaderErrorMessages/NoteDurationError", "Invalid note duration."
+        );
                 throw new MusicSheetReadingException(errorMsg);
-            }
-        }
+      }
     }
-    // (*)
-    //private readArticulations(notationNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
-    //    let articNode: IXmlElement;
-    //    if ((articNode = notationNode.element("articulations")) !== undefined)
-    //        this.articulationReader.addArticulationExpression(articNode, currentVoiceEntry);
-    //    let fermaNode: IXmlElement = undefined;
-    //    if ((fermaNode = notationNode.element("fermata")) !== undefined)
-    //        this.articulationReader.addFermata(fermaNode, currentVoiceEntry);
-    //    let tecNode: IXmlElement = undefined;
-    //    if ((tecNode = notationNode.element("technical")) !== undefined)
-    //        this.articulationReader.addTechnicalArticulations(tecNode, currentVoiceEntry);
-    //    let ornaNode: IXmlElement = undefined;
-    //    if ((ornaNode = notationNode.element("ornaments")) !== undefined)
-    //        this.articulationReader.addOrnament(ornaNode, currentVoiceEntry);
-    //}
-
-    /**
-     * Create a new [[Note]] and adds it to the currentVoiceEntry
-     * @param node
-     * @param noteDuration
-     * @param divisions
-     * @param graceNote
-     * @param chord
-     * @param guitarPro
-     * @returns {Note}
-     */
-    private addSingleNote(
-        node: IXmlElement, noteDuration: number, divisions: number, graceNote: boolean, chord: boolean, guitarPro: boolean
-    ): Note {
-        //Logging.debug("addSingleNote called");
-        let noteAlter: AccidentalEnum = AccidentalEnum.NONE;
-        let noteStep: NoteEnum = NoteEnum.C;
-        let noteOctave: number = 0;
-        let playbackInstrumentId: string = undefined;
-        let xmlnodeElementsArr: IXmlElement[] = node.elements();
-        for (let idx: number = 0, len: number = xmlnodeElementsArr.length; idx < len; ++idx) {
-            let noteElement: IXmlElement = xmlnodeElementsArr[idx];
+  }
+
+  private readArticulations(notationNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
+    const articNode: IXmlElement = notationNode.element("articulations");
+    if (articNode !== undefined) {
+      this.articulationReader.addArticulationExpression(articNode, currentVoiceEntry);
+    }
+    const fermaNode: IXmlElement = notationNode.element("fermata");
+    if (fermaNode !== undefined) {
+      this.articulationReader.addFermata(fermaNode, currentVoiceEntry);
+    }
+    const tecNode: IXmlElement = notationNode.element("technical");
+    if (tecNode !== undefined) {
+      this.articulationReader.addTechnicalArticulations(tecNode, currentVoiceEntry);
+    }
+    const ornaNode: IXmlElement = notationNode.element("ornaments");
+    if (ornaNode !== undefined) {
+      this.articulationReader.addOrnament(ornaNode, currentVoiceEntry);
+    }
+  }
+
+  /**
+   * Create a new [[Note]] and adds it to the currentVoiceEntry
+   * @param node
+   * @param noteDuration
+   * @param divisions
+   * @param graceNote
+   * @param chord
+   * @param guitarPro
+   * @returns {Note}
+   */
+  private addSingleNote(node: IXmlElement, noteDuration: Fraction, graceNote: boolean, chord: boolean, guitarPro: boolean): Note {
+    //Logging.debug("addSingleNote called");
+    let noteAlter: AccidentalEnum = AccidentalEnum.NONE;
+    let noteStep: NoteEnum = NoteEnum.C;
+    let noteOctave: number = 0;
+    let playbackInstrumentId: string = undefined;
+    const xmlnodeElementsArr: IXmlElement[] = node.elements();
+    for (let idx: number = 0, len: number = xmlnodeElementsArr.length; idx < len; ++idx) {
+      const noteElement: IXmlElement = xmlnodeElementsArr[idx];
+      try {
+        if (noteElement.name === "pitch") {
+          const noteElementsArr: IXmlElement[] = noteElement.elements();
+          for (let idx2: number = 0, len2: number = noteElementsArr.length; idx2 < len2; ++idx2) {
+            const pitchElement: IXmlElement = noteElementsArr[idx2];
             try {
-                if (noteElement.name === "pitch") {
-                    let noteElementsArr: IXmlElement[] = noteElement.elements();
-                    for (let idx2: number = 0, len2: number = noteElementsArr.length; idx2 < len2; ++idx2) {
-                        let pitchElement: IXmlElement = noteElementsArr[idx2];
-                        try {
-                            if (pitchElement.name === "step") {
-                                noteStep = NoteEnum[pitchElement.value];
-                                if (noteStep === undefined) {
-                                    let errorMsg: string = ITextTranslation.translateText(
-                                        "ReaderErrorMessages/NotePitchError",
-                                        "Invalid pitch while reading note."
-                                    );
-                                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                                    throw new MusicSheetReadingException(errorMsg, undefined);
-                                }
-                            } else if (pitchElement.name === "alter") {
-                                noteAlter = parseInt(pitchElement.value, 10);
-                                if (isNaN(noteAlter)) {
-                                    let errorMsg: string = ITextTranslation.translateText(
-                                        "ReaderErrorMessages/NoteAlterationError", "Invalid alteration while reading note."
-                                    );
-                                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                                    throw new MusicSheetReadingException(errorMsg, undefined);
-                                }
-
-                            } else if (pitchElement.name === "octave") {
-                                noteOctave = parseInt(pitchElement.value, 10);
-                                if (isNaN(noteOctave)) {
-                                    let errorMsg: string = ITextTranslation.translateText(
-                                        "ReaderErrorMessages/NoteOctaveError", "Invalid octave value while reading note."
-                                    );
+              if (pitchElement.name === "step") {
+                noteStep = NoteEnum[pitchElement.value];
+                if (noteStep === undefined) {
+                    const errorMsg: string = ITextTranslation.translateText(
+                    "ReaderErrorMessages/NotePitchError",
+                    "Invalid pitch while reading note."
+                  );
+                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    throw new MusicSheetReadingException(errorMsg, undefined);
+                }
+              } else if (pitchElement.name === "alter") {
+                noteAlter = parseInt(pitchElement.value, 10);
+                if (isNaN(noteAlter)) {
+                                    const errorMsg: string = ITextTranslation.translateText(
+                    "ReaderErrorMessages/NoteAlterationError", "Invalid alteration while reading note."
+                  );
                                     this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                                     throw new MusicSheetReadingException(errorMsg, undefined);
-                                }
-                            }
-                        } catch (ex) {
-                            Logging.log("VoiceGenerator.addSingleNote read Step: ", ex.message);
-                        }
+                }
 
-                    }
-                } else if (noteElement.name === "unpitched") {
-                    let displayStep: IXmlElement = noteElement.element("display-step");
-                    if (displayStep !== undefined) {
-                        noteStep = NoteEnum[displayStep.value.toUpperCase()];
-                    }
-                    let octave: IXmlElement = noteElement.element("display-octave");
-                    if (octave !== undefined) {
-                        noteOctave = parseInt(octave.value, 10);
-                        if (guitarPro) {
-                            noteOctave += 1;
-                        }
-                    }
-                } else if (noteElement.name === "instrument") {
-                    if (noteElement.firstAttribute !== undefined) {
-                        playbackInstrumentId = noteElement.firstAttribute.value;
-                    }
+              } else if (pitchElement.name === "octave") {
+                noteOctave = parseInt(pitchElement.value, 10);
+                if (isNaN(noteOctave)) {
+                  const errorMsg: string = ITextTranslation.translateText(
+                    "ReaderErrorMessages/NoteOctaveError", "Invalid octave value while reading note."
+                  );
+                  this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                  throw new MusicSheetReadingException(errorMsg, undefined);
                 }
+              }
             } catch (ex) {
-                Logging.log("VoiceGenerator.addSingleNote: ", ex);
+              Logging.log("VoiceGenerator.addSingleNote read Step: ", ex.message);
             }
-        }
 
-        noteOctave -= Pitch.OctaveXmlDifference;
-        let pitch: Pitch = new Pitch(noteStep, noteOctave, noteAlter);
-        let noteLength: Fraction = new Fraction(noteDuration, divisions);
-        let note: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch);
-        note.PlaybackInstrumentId = playbackInstrumentId;
-        if (!graceNote) {
-            this.currentVoiceEntry.Notes.push(note);
-        } else {
-            this.handleGraceNote(node, note);
-        }
-        if (node.elements("beam") && !chord) {
-            this.createBeam(node, note, graceNote);
-        }
-        return note;
-    }
-
-    /**
-     * Create a new rest note and add it to the currentVoiceEntry.
-     * @param noteDuration
-     * @param divisions
-     * @returns {Note}
-     */
-    private addRestNote(noteDuration: number, divisions: number): Note {
-        let restFraction: Fraction = new Fraction(noteDuration, divisions);
-        let restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, undefined);
-        this.currentVoiceEntry.Notes.push(restNote);
-        if (this.openBeam !== undefined) {
-            this.openBeam.ExtendedNoteList.push(restNote);
+          }
+        } else if (noteElement.name === "unpitched") {
+          const displayStep: IXmlElement = noteElement.element("display-step");
+          if (displayStep !== undefined) {
+            noteStep = NoteEnum[displayStep.value.toUpperCase()];
+          }
+          const octave: IXmlElement = noteElement.element("display-octave");
+          if (octave !== undefined) {
+            noteOctave = parseInt(octave.value, 10);
+            if (guitarPro) {
+              noteOctave += 1;
+            }
+          }
+        } else if (noteElement.name === "instrument") {
+          if (noteElement.firstAttribute !== undefined) {
+            playbackInstrumentId = noteElement.firstAttribute.value;
+          }
         }
-        return restNote;
+      } catch (ex) {
+        Logging.log("VoiceGenerator.addSingleNote: ", ex);
+      }
     }
 
-    /**
-     * Handle the currentVoiceBeam.
-     * @param node
-     * @param note
-     * @param grace
-     */
-    private createBeam(node: IXmlElement, note: Note, grace: boolean): void {
-        try {
-            let beamNode: IXmlElement = node.element("beam");
-            let beamAttr: IXmlAttribute = undefined;
-            if (beamNode !== undefined && beamNode.hasAttributes) {
-                beamAttr = beamNode.attribute("number");
+    noteOctave -= Pitch.OctaveXmlDifference;
+    const pitch: Pitch = new Pitch(noteStep, noteOctave, noteAlter);
+    const noteLength: Fraction = Fraction.createFromFraction(noteDuration);
+    const note: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch);
+    note.PlaybackInstrumentId = playbackInstrumentId;
+    if (!graceNote) {
+      this.currentVoiceEntry.Notes.push(note);
+    } else {
+      this.handleGraceNote(node, note);
+    }
+    if (node.elements("beam") && !chord) {
+      this.createBeam(node, note, graceNote);
+    }
+    return note;
+  }
+
+  /**
+   * Create a new rest note and add it to the currentVoiceEntry.
+   * @param noteDuration
+   * @param divisions
+   * @returns {Note}
+   */
+  private addRestNote(noteDuration: Fraction): Note {
+    const restFraction: Fraction = Fraction.createFromFraction(noteDuration);
+    const restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, undefined);
+    this.currentVoiceEntry.Notes.push(restNote);
+    if (this.openBeam !== undefined) {
+      this.openBeam.ExtendedNoteList.push(restNote);
+    }
+    return restNote;
+  }
+
+  /**
+   * Handle the currentVoiceBeam.
+   * @param node
+   * @param note
+   * @param grace
+   */
+  private createBeam(node: IXmlElement, note: Note, grace: boolean): void {
+    try {
+      const beamNode: IXmlElement = node.element("beam");
+      let beamAttr: IXmlAttribute = undefined;
+      if (beamNode !== undefined && beamNode.hasAttributes) {
+        beamAttr = beamNode.attribute("number");
+      }
+      if (beamAttr !== undefined) {
+        const beamNumber: number = parseInt(beamAttr.value, 10);
+        const mainBeamNode: IXmlElement[] = node.elements("beam");
+        const currentBeamTag: string = mainBeamNode[0].value;
+        if (beamNumber === 1 && mainBeamNode !== undefined) {
+          if (currentBeamTag === "begin" && this.lastBeamTag !== currentBeamTag) {
+            if (grace) {
+              if (this.openGraceBeam !== undefined) {
+                this.handleOpenBeam();
+              }
+              this.openGraceBeam = new Beam();
+            } else {
+              if (this.openBeam !== undefined) {
+                this.handleOpenBeam();
+              }
+              this.openBeam = new Beam();
             }
-            if (beamAttr !== undefined) {
-                let beamNumber: number = parseInt(beamAttr.value, 10);
-                let mainBeamNode: IXmlElement[] = node.elements("beam");
-                let currentBeamTag: string = mainBeamNode[0].value;
-                if (beamNumber === 1 && mainBeamNode !== undefined) {
-                    if (currentBeamTag === "begin" && this.lastBeamTag !== currentBeamTag) {
-                        if (grace) {
-                            if (this.openGraceBeam !== undefined) {
-                                this.handleOpenBeam();
-                            }
-                            this.openGraceBeam = new Beam();
-                        } else {
-                            if (this.openBeam !== undefined) {
-                                this.handleOpenBeam();
-                            }
-                            this.openBeam = new Beam();
-                        }
-                    }
-                    this.lastBeamTag = currentBeamTag;
-                }
-                let sameVoiceEntry: boolean = false;
-                if (grace) {
-                    if (this.openGraceBeam === undefined) { return; }
-                    for (let idx: number = 0, len: number = this.openGraceBeam.Notes.length; idx < len; ++idx) {
-                        let beamNote: Note = this.openGraceBeam.Notes[idx];
-                        if (this.currentVoiceEntry === beamNote.ParentVoiceEntry) {
-                            sameVoiceEntry = true;
-                        }
-                    }
-                    if (!sameVoiceEntry) {
-                        this.openGraceBeam.addNoteToBeam(note);
-                        if (currentBeamTag === "end" && beamNumber === 1) {
-                            this.openGraceBeam = undefined;
-                        }
-                    }
-                } else {
-                    if (this.openBeam === undefined) { return; }
-                    for (let idx: number = 0, len: number = this.openBeam.Notes.length; idx < len; ++idx) {
-                        let beamNote: Note = this.openBeam.Notes[idx];
-                        if (this.currentVoiceEntry === beamNote.ParentVoiceEntry) {
-                            sameVoiceEntry = true;
-                        }
-                    }
-                    if (!sameVoiceEntry) {
-                        this.openBeam.addNoteToBeam(note);
-                        if (currentBeamTag === "end" && beamNumber === 1) {
-                            this.openBeam = undefined;
-                        }
-                    }
-                }
+          }
+          this.lastBeamTag = currentBeamTag;
+        }
+        let sameVoiceEntry: boolean = false;
+        if (grace) {
+          if (this.openGraceBeam === undefined) {
+            return;
+          }
+          for (let idx: number = 0, len: number = this.openGraceBeam.Notes.length; idx < len; ++idx) {
+            const beamNote: Note = this.openGraceBeam.Notes[idx];
+            if (this.currentVoiceEntry === beamNote.ParentVoiceEntry) {
+              sameVoiceEntry = true;
             }
-        } catch (e) {
-            let errorMsg: string = ITextTranslation.translateText(
-                "ReaderErrorMessages/BeamError", "Error while reading beam."
-            );
-            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-            throw new MusicSheetReadingException("", e);
+          }
+          if (!sameVoiceEntry) {
+            this.openGraceBeam.addNoteToBeam(note);
+            if (currentBeamTag === "end" && beamNumber === 1) {
+              this.openGraceBeam = undefined;
+            }
+          }
+        } else {
+          if (this.openBeam === undefined) {
+            return;
+          }
+          for (let idx: number = 0, len: number = this.openBeam.Notes.length; idx < len; ++idx) {
+            const beamNote: Note = this.openBeam.Notes[idx];
+            if (this.currentVoiceEntry === beamNote.ParentVoiceEntry) {
+              sameVoiceEntry = true;
+            }
+          }
+          if (!sameVoiceEntry) {
+            this.openBeam.addNoteToBeam(note);
+            if (currentBeamTag === "end" && beamNumber === 1) {
+              this.openBeam = undefined;
+            }
+          }
         }
-
+      }
+    } catch (e) {
+      const errorMsg: string = ITextTranslation.translateText(
+        "ReaderErrorMessages/BeamError", "Error while reading beam."
+      );
+      this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+      throw new MusicSheetReadingException("", e);
     }
 
-    /**
-     * Check for open [[Beam]]s at end of [[SourceMeasure]] and closes them explicity.
-     */
-    private handleOpenBeam(): void {
-        if (this.openBeam.Notes.length === 1) {
-            let beamNote: Note = this.openBeam.Notes[0];
-            beamNote.NoteBeam = undefined;
-            this.openBeam = undefined;
-            return;
-        }
-        if (this.currentNote === CollectionUtil.last(this.openBeam.Notes)) {
-            this.openBeam = undefined;
-        } else {
-            let beamLastNote: Note = CollectionUtil.last(this.openBeam.Notes);
-            let beamLastNoteStaffEntry: SourceStaffEntry = beamLastNote.ParentStaffEntry;
-            let horizontalIndex: number = this.currentMeasure.getVerticalContainerIndexByTimestamp(beamLastNoteStaffEntry.Timestamp);
-            let verticalIndex: number = beamLastNoteStaffEntry.VerticalContainerParent.StaffEntries.indexOf(beamLastNoteStaffEntry);
-            if (horizontalIndex < this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1) {
-                let nextStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[horizontalIndex + 1].StaffEntries[verticalIndex];
-                if (nextStaffEntry !== undefined) {
-                    for (let idx: number = 0, len: number = nextStaffEntry.VoiceEntries.length; idx < len; ++idx) {
-                        let voiceEntry: VoiceEntry = nextStaffEntry.VoiceEntries[idx];
-                        if (voiceEntry.ParentVoice === this.voice) {
-                            let candidateNote: Note = voiceEntry.Notes[0];
-                            if (candidateNote.Length.lte(new Fraction(1, 8))) {
-                                this.openBeam.addNoteToBeam(candidateNote);
-                                this.openBeam = undefined;
-                            } else {
-                                this.openBeam = undefined;
-                            }
-                        }
-                    }
-                }
-            } else {
+  }
+
+  /**
+   * Check for open [[Beam]]s at end of [[SourceMeasure]] and closes them explicity.
+   */
+  private handleOpenBeam(): void {
+    if (this.openBeam.Notes.length === 1) {
+      const beamNote: Note = this.openBeam.Notes[0];
+      beamNote.NoteBeam = undefined;
+      this.openBeam = undefined;
+      return;
+    }
+    if (this.currentNote === CollectionUtil.last(this.openBeam.Notes)) {
+      this.openBeam = undefined;
+    } else {
+      const beamLastNote: Note = CollectionUtil.last(this.openBeam.Notes);
+      const beamLastNoteStaffEntry: SourceStaffEntry = beamLastNote.ParentStaffEntry;
+      const horizontalIndex: number = this.currentMeasure.getVerticalContainerIndexByTimestamp(beamLastNoteStaffEntry.Timestamp);
+      const verticalIndex: number = beamLastNoteStaffEntry.VerticalContainerParent.StaffEntries.indexOf(beamLastNoteStaffEntry);
+      if (horizontalIndex < this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1) {
+        const nextStaffEntry: SourceStaffEntry = this.currentMeasure
+                                                             .VerticalSourceStaffEntryContainers[horizontalIndex + 1]
+                                                             .StaffEntries[verticalIndex];
+        if (nextStaffEntry !== undefined) {
+          for (let idx: number = 0, len: number = nextStaffEntry.VoiceEntries.length; idx < len; ++idx) {
+            const voiceEntry: VoiceEntry = nextStaffEntry.VoiceEntries[idx];
+            if (voiceEntry.ParentVoice === this.voice) {
+              const candidateNote: Note = voiceEntry.Notes[0];
+              if (candidateNote.Length.lte(new Fraction(1, 8))) {
+                this.openBeam.addNoteToBeam(candidateNote);
+                this.openBeam = undefined;
+              } else {
                 this.openBeam = undefined;
+              }
             }
+          }
         }
+      } else {
+        this.openBeam = undefined;
+      }
     }
-    private handleGraceNote(node: IXmlElement, note: Note): void {
-        let graceChord: boolean = false;
-        let type: string = "";
-        if (node.elements("type")) {
-            let typeNode: IXmlElement[] = node.elements("type");
-            if (typeNode) {
-                type = typeNode[0].value;
-                try {
-                    note.Length = this.getNoteDurationFromType(type);
-                    note.Length.Numerator = 1;
-                } catch (e) {
-                    let errorMsg: string = ITextTranslation.translateText(
-                        "ReaderErrorMessages/NoteDurationError", "Invalid note duration."
-                    );
+  }
+
+  private handleGraceNote(node: IXmlElement, note: Note): void {
+    let graceChord: boolean = false;
+    let type: string = "";
+    if (node.elements("type")) {
+      const typeNode: IXmlElement[] = node.elements("type");
+      if (typeNode) {
+        type = typeNode[0].value;
+        try {
+          note.Length = this.getNoteDurationFromType(type);
+          note.Length.Numerator = 1;
+        } catch (e) {
+                    const errorMsg: string = ITextTranslation.translateText(
+            "ReaderErrorMessages/NoteDurationError", "Invalid note duration."
+          );
                     this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                     throw new MusicSheetReadingException(errorMsg, e);
-                }
-
-            }
         }
-        let graceNode: IXmlElement = node.element("grace");
-        if (graceNode !== undefined && graceNode.attributes()) {
-            if (graceNode.attribute("slash")) {
-                let slash: string = graceNode.attribute("slash").value;
+
+      }
+    }
+    const graceNode: IXmlElement = node.element("grace");
+    if (graceNode !== undefined && graceNode.attributes()) {
+      if (graceNode.attribute("slash")) {
+                const slash: string = graceNode.attribute("slash").value;
                 if (slash === "yes") {
-                    note.GraceNoteSlash = true;
-                }
-            }
-        }
-        if (node.element("chord") !== undefined) {
-            graceChord = true;
-        }
-        let graceVoiceEntry: VoiceEntry = undefined;
-        if (!graceChord) {
-            graceVoiceEntry = new VoiceEntry(
-                new Fraction(0, 1), this.currentVoiceEntry.ParentVoice, this.currentStaffEntry
-            );
-            if (this.currentVoiceEntry.graceVoiceEntriesBefore === undefined) {
-                this.currentVoiceEntry.graceVoiceEntriesBefore = [];
-            }
-            this.currentVoiceEntry.graceVoiceEntriesBefore.push(graceVoiceEntry);
-        } else {
-            if (
-                this.currentVoiceEntry.graceVoiceEntriesBefore !== undefined
-                && this.currentVoiceEntry.graceVoiceEntriesBefore.length > 0
-            ) {
-                graceVoiceEntry = CollectionUtil.last(this.currentVoiceEntry.graceVoiceEntriesBefore);
-            }
-        }
-        if (graceVoiceEntry !== undefined) {
-            graceVoiceEntry.Notes.push(note);
-            note.ParentVoiceEntry = graceVoiceEntry;
+          note.GraceNoteSlash = true;
         }
+      }
     }
-
-    /**
-     * Create a [[Tuplet]].
-     * @param node
-     * @param tupletNodeList
-     * @returns {number}
-     */
-    private addTuplet(node: IXmlElement, tupletNodeList: IXmlElement[]): number {
-        if (tupletNodeList !== undefined && tupletNodeList.length > 1) {
-            let timeModNode: IXmlElement = node.element("time-modification");
-            if (timeModNode !== undefined) {
-                timeModNode = timeModNode.element("actual-notes");
-            }
-            let tupletNodeListArr: IXmlElement[] = tupletNodeList;
-            for (let idx: number = 0, len: number = tupletNodeListArr.length; idx < len; ++idx) {
-                let tupletNode: IXmlElement = tupletNodeListArr[idx];
+    if (node.element("chord") !== undefined) {
+      graceChord = true;
+    }
+    let graceVoiceEntry: VoiceEntry = undefined;
+    if (!graceChord) {
+      graceVoiceEntry = new VoiceEntry(
+        new Fraction(0, 1), this.currentVoiceEntry.ParentVoice, this.currentStaffEntry
+      );
+      if (this.currentVoiceEntry.graceVoiceEntriesBefore === undefined) {
+        this.currentVoiceEntry.graceVoiceEntriesBefore = [];
+      }
+      this.currentVoiceEntry.graceVoiceEntriesBefore.push(graceVoiceEntry);
+    } else {
+      if (
+        this.currentVoiceEntry.graceVoiceEntriesBefore !== undefined
+        && this.currentVoiceEntry.graceVoiceEntriesBefore.length > 0
+      ) {
+        graceVoiceEntry = CollectionUtil.last(this.currentVoiceEntry.graceVoiceEntriesBefore);
+      }
+    }
+    if (graceVoiceEntry !== undefined) {
+      graceVoiceEntry.Notes.push(note);
+      note.ParentVoiceEntry = graceVoiceEntry;
+    }
+  }
+
+  /**
+   * Create a [[Tuplet]].
+   * @param node
+   * @param tupletNodeList
+   * @returns {number}
+   */
+  private addTuplet(node: IXmlElement, tupletNodeList: IXmlElement[]): number {
+    if (tupletNodeList !== undefined && tupletNodeList.length > 1) {
+      let timeModNode: IXmlElement = node.element("time-modification");
+      if (timeModNode !== undefined) {
+        timeModNode = timeModNode.element("actual-notes");
+      }
+      const tupletNodeListArr: IXmlElement[] = tupletNodeList;
+      for (let idx: number = 0, len: number = tupletNodeListArr.length; idx < len; ++idx) {
+                const tupletNode: IXmlElement = tupletNodeListArr[idx];
                 if (tupletNode !== undefined && tupletNode.attributes()) {
-                    let type: string = tupletNode.attribute("type").value;
+                    const type: string = tupletNode.attribute("type").value;
                     if (type === "start") {
-                        let tupletNumber: number = 1;
-                        if (tupletNode.attribute("number")) {
-                            tupletNumber = parseInt(tupletNode.attribute("number").value, 10);
-                        }
-                        let tupletLabelNumber: number = 0;
-                        if (timeModNode !== undefined) {
-                            tupletLabelNumber = parseInt(timeModNode.value, 10);
-                            if (isNaN(tupletLabelNumber)) {
-                                let errorMsg: string = ITextTranslation.translateText(
-                                    "ReaderErrorMessages/TupletNoteDurationError", "Invalid tuplet note duration."
-                                );
+            let tupletNumber: number = 1;
+            if (tupletNode.attribute("number")) {
+              tupletNumber = parseInt(tupletNode.attribute("number").value, 10);
+            }
+            let tupletLabelNumber: number = 0;
+            if (timeModNode !== undefined) {
+              tupletLabelNumber = parseInt(timeModNode.value, 10);
+              if (isNaN(tupletLabelNumber)) {
+                                const errorMsg: string = ITextTranslation.translateText(
+                  "ReaderErrorMessages/TupletNoteDurationError", "Invalid tuplet note duration."
+                );
                                 this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                                 throw new MusicSheetReadingException(errorMsg, undefined);
-                            }
+              }
 
-                        }
-                        let tuplet: Tuplet = new Tuplet(tupletLabelNumber);
-                        if (this.tupletDict[tupletNumber] !== undefined) {
-                            delete this.tupletDict[tupletNumber];
-                            if (Object.keys(this.tupletDict).length === 0) {
-                                this.openTupletNumber = 0;
-                            } else if (Object.keys(this.tupletDict).length > 1) {
-                                this.openTupletNumber--;
-                            }
-                        }
-                        this.tupletDict[tupletNumber] = tuplet;
-                        let subnotelist: Note[] = [];
-                        subnotelist.push(this.currentNote);
-                        tuplet.Notes.push(subnotelist);
-                        tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
-                        this.currentNote.NoteTuplet = tuplet;
-                        this.openTupletNumber = tupletNumber;
-                    } else if (type === "stop") {
-                        let tupletNumber: number = 1;
-                        if (tupletNode.attribute("number")) {
-                            tupletNumber = parseInt(tupletNode.attribute("number").value, 10);
-                        }
-                        let tuplet: Tuplet = this.tupletDict[tupletNumber];
-                        if (tuplet !== undefined) {
-                            let subnotelist: Note[] = [];
+            }
+            const tuplet: Tuplet = new Tuplet(tupletLabelNumber);
+            if (this.tupletDict[tupletNumber] !== undefined) {
+              delete this.tupletDict[tupletNumber];
+              if (Object.keys(this.tupletDict).length === 0) {
+                this.openTupletNumber = 0;
+              } else if (Object.keys(this.tupletDict).length > 1) {
+                this.openTupletNumber--;
+              }
+            }
+            this.tupletDict[tupletNumber] = tuplet;
+            const subnotelist: Note[] = [];
+            subnotelist.push(this.currentNote);
+            tuplet.Notes.push(subnotelist);
+            tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
+            this.currentNote.NoteTuplet = tuplet;
+            this.openTupletNumber = tupletNumber;
+          } else if (type === "stop") {
+            let tupletNumber: number = 1;
+            if (tupletNode.attribute("number")) {
+              tupletNumber = parseInt(tupletNode.attribute("number").value, 10);
+            }
+            const tuplet: Tuplet = this.tupletDict[tupletNumber];
+            if (tuplet !== undefined) {
+                            const subnotelist: Note[] = [];
                             subnotelist.push(this.currentNote);
                             tuplet.Notes.push(subnotelist);
                             tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
                             this.currentNote.NoteTuplet = tuplet;
                             delete this.tupletDict[tupletNumber];
                             if (Object.keys(this.tupletDict).length === 0) {
-                                this.openTupletNumber = 0;
-                            } else if (Object.keys(this.tupletDict).length > 1) {
-                                this.openTupletNumber--;
-                            }
-                        }
-                    }
-                }
+                this.openTupletNumber = 0;
+              } else if (Object.keys(this.tupletDict).length > 1) {
+                this.openTupletNumber--;
+              }
             }
-        } else if (tupletNodeList[0] !== undefined) {
-            let n: IXmlElement = tupletNodeList[0];
+          }
+        }
+      }
+    } else if (tupletNodeList[0] !== undefined) {
+            const n: IXmlElement = tupletNodeList[0];
             if (n.hasAttributes) {
-                let type: string = n.attribute("type").value;
+                const type: string = n.attribute("type").value;
                 let tupletnumber: number = 1;
                 if (n.attribute("number")) {
-                    tupletnumber = parseInt(n.attribute("number").value, 10);
-                }
-                let noTupletNumbering: boolean = isNaN(tupletnumber);
+          tupletnumber = parseInt(n.attribute("number").value, 10);
+        }
+                const noTupletNumbering: boolean = isNaN(tupletnumber);
 
                 if (type === "start") {
-                    let tupletLabelNumber: number = 0;
-                    let timeModNode: IXmlElement = node.element("time-modification");
-                    if (timeModNode !== undefined) {
-                        timeModNode = timeModNode.element("actual-notes");
-                    }
-                    if (timeModNode !== undefined) {
-                        tupletLabelNumber = parseInt(timeModNode.value, 10);
-                        if (isNaN(tupletLabelNumber)) {
-                            let errorMsg: string = ITextTranslation.translateText(
-                                "ReaderErrorMessages/TupletNoteDurationError", "Invalid tuplet note duration."
-                            );
+          let tupletLabelNumber: number = 0;
+          let timeModNode: IXmlElement = node.element("time-modification");
+          if (timeModNode !== undefined) {
+            timeModNode = timeModNode.element("actual-notes");
+          }
+          if (timeModNode !== undefined) {
+            tupletLabelNumber = parseInt(timeModNode.value, 10);
+            if (isNaN(tupletLabelNumber)) {
+                            const errorMsg: string = ITextTranslation.translateText(
+                "ReaderErrorMessages/TupletNoteDurationError", "Invalid tuplet note duration."
+              );
                             this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                             throw new MusicSheetReadingException(errorMsg);
-                        }
+            }
 
-                    }
-                    if (noTupletNumbering) {
-                        this.openTupletNumber++;
-                        tupletnumber = this.openTupletNumber;
-                    }
-                    let tuplet: Tuplet = this.tupletDict[tupletnumber];
-                    if (tuplet === undefined) {
-                        tuplet = this.tupletDict[tupletnumber] = new Tuplet(tupletLabelNumber);
-                    }
-                    let subnotelist: Note[] = [];
-                    subnotelist.push(this.currentNote);
-                    tuplet.Notes.push(subnotelist);
-                    tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
-                    this.currentNote.NoteTuplet = tuplet;
-                    this.openTupletNumber = tupletnumber;
-                } else if (type === "stop") {
-                    if (noTupletNumbering) {
-                        tupletnumber = this.openTupletNumber;
-                    }
-                    let tuplet: Tuplet = this.tupletDict[this.openTupletNumber];
-                    if (tuplet !== undefined) {
-                        let subnotelist: Note[] = [];
+          }
+          if (noTupletNumbering) {
+            this.openTupletNumber++;
+            tupletnumber = this.openTupletNumber;
+          }
+          let tuplet: Tuplet = this.tupletDict[tupletnumber];
+          if (tuplet === undefined) {
+            tuplet = this.tupletDict[tupletnumber] = new Tuplet(tupletLabelNumber);
+          }
+          const subnotelist: Note[] = [];
+          subnotelist.push(this.currentNote);
+          tuplet.Notes.push(subnotelist);
+          tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
+          this.currentNote.NoteTuplet = tuplet;
+          this.openTupletNumber = tupletnumber;
+        } else if (type === "stop") {
+          if (noTupletNumbering) {
+            tupletnumber = this.openTupletNumber;
+          }
+          const tuplet: Tuplet = this.tupletDict[this.openTupletNumber];
+          if (tuplet !== undefined) {
+                        const subnotelist: Note[] = [];
                         subnotelist.push(this.currentNote);
                         tuplet.Notes.push(subnotelist);
                         tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
                         this.currentNote.NoteTuplet = tuplet;
                         if (Object.keys(this.tupletDict).length === 0) {
-                            this.openTupletNumber = 0;
-                        } else if (Object.keys(this.tupletDict).length > 1) {
-                            this.openTupletNumber--;
-                        }
-                        delete this.tupletDict[tupletnumber];
-                    }
-                }
+              this.openTupletNumber = 0;
+            } else if (Object.keys(this.tupletDict).length > 1) {
+              this.openTupletNumber--;
             }
+                        delete this.tupletDict[tupletnumber];
+          }
         }
-        return this.openTupletNumber;
+      }
     }
-
-    /**
-     * Handle the time-modification [[IXmlElement]] for the [[Tuplet]] case (tupletNotes not at begin/end of [[Tuplet]]).
-     * @param noteNode
-     */
-    private handleTimeModificationNode(noteNode: IXmlElement): void {
-        if (this.openTupletNumber in this.tupletDict) {
-            try {
-                let tuplet: Tuplet = this.tupletDict[this.openTupletNumber];
-                let notes: Note[] = CollectionUtil.last(tuplet.Notes);
-                let lastTupletVoiceEntry: VoiceEntry = notes[0].ParentVoiceEntry;
+    return this.openTupletNumber;
+  }
+
+  /**
+   * This method handles the time-modification IXmlElement for the Tuplet case (tupletNotes not at begin/end of Tuplet).
+   * @param noteNode
+   */
+  private handleTimeModificationNode(noteNode: IXmlElement): void {
+    if (this.tupletDict[this.openTupletNumber] !== undefined) {
+      try {
+        // Tuplet should already be created
+                const tuplet: Tuplet = this.tupletDict[this.openTupletNumber];
+                const notes: Note[] = CollectionUtil.last(tuplet.Notes);
+                const lastTupletVoiceEntry: VoiceEntry = notes[0].ParentVoiceEntry;
                 let noteList: Note[];
                 if (lastTupletVoiceEntry.Timestamp.Equals(this.currentVoiceEntry.Timestamp)) {
-                    noteList = notes;
-                } else {
-                    noteList = [];
-                    tuplet.Notes.push(noteList);
-                    tuplet.Fractions.push(this.getTupletNoteDurationFromType(noteNode));
-                }
+          noteList = notes;
+        } else {
+          noteList = [];
+          tuplet.Notes.push(noteList);
+          tuplet.Fractions.push(this.getTupletNoteDurationFromType(noteNode));
+        }
                 noteList.push(this.currentNote);
                 this.currentNote.NoteTuplet = tuplet;
-            } catch (ex) {
-                let errorMsg: string = ITextTranslation.translateText(
-                    "ReaderErrorMessages/TupletNumberError", "Invalid tuplet number."
-                );
+      } catch (ex) {
+                const errorMsg: string = ITextTranslation.translateText(
+          "ReaderErrorMessages/TupletNumberError", "Invalid tuplet number."
+        );
                 this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                 throw ex;
-            }
+      }
 
-        } else if (this.currentVoiceEntry.Notes.length > 0) {
-            let firstNote: Note = this.currentVoiceEntry.Notes[0];
+    } else if (this.currentVoiceEntry.Notes.length > 0) {
+            const firstNote: Note = this.currentVoiceEntry.Notes[0];
             if (firstNote.NoteTuplet !== undefined) {
-                let tuplet: Tuplet = firstNote.NoteTuplet;
-                let notes: Note[] = CollectionUtil.last(tuplet.Notes);
+                const tuplet: Tuplet = firstNote.NoteTuplet;
+                const notes: Note[] = CollectionUtil.last(tuplet.Notes);
                 notes.push(this.currentNote);
                 this.currentNote.NoteTuplet = tuplet;
-            }
-        }
+      }
     }
-    private addTie(tieNodeList: IXmlElement[], measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction): void {
-        if (tieNodeList !== undefined) {
-            if (tieNodeList.length === 1) {
-                let tieNode: IXmlElement = tieNodeList[0];
+  }
+
+  private addTie(tieNodeList: IXmlElement[], measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction): void {
+    if (tieNodeList !== undefined) {
+      if (tieNodeList.length === 1) {
+                const tieNode: IXmlElement = tieNodeList[0];
                 if (tieNode !== undefined && tieNode.attributes()) {
-                    let type: string = tieNode.attribute("type").value;
+                    const type: string = tieNode.attribute("type").value;
                     try {
-                        if (type === "start") {
-                            let num: number = this.findCurrentNoteInTieDict(this.currentNote);
+            if (type === "start") {
+                            const num: number = this.findCurrentNoteInTieDict(this.currentNote);
                             if (num < 0) {
-                                delete this.openTieDict[num];
-                            }
-                            let newTieNumber: number = this.getNextAvailableNumberForTie();
-                            let tie: Tie = new Tie(this.currentNote);
+                delete this.openTieDict[num];
+              }
+                            const newTieNumber: number = this.getNextAvailableNumberForTie();
+                            const tie: Tie = new Tie(this.currentNote);
                             this.openTieDict[newTieNumber] = tie;
                             if (this.currentNote.NoteBeam !== undefined) {
-                                if (this.currentNote.NoteBeam.Notes[0] === this.currentNote) {
-                                    tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
-                                } else {
-                                    for (let idx: number = 0, len: number = this.currentNote.NoteBeam.Notes.length; idx < len; ++idx) {
-                                        let note: Note = this.currentNote.NoteBeam.Notes[idx];
+                if (this.currentNote.NoteBeam.Notes[0] === this.currentNote) {
+                  tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
+                } else {
+                  for (let idx: number = 0, len: number = this.currentNote.NoteBeam.Notes.length; idx < len; ++idx) {
+                                        const note: Note = this.currentNote.NoteBeam.Notes[idx];
                                         if (note.NoteTie !== undefined && note.NoteTie !== tie && note.NoteTie.BeamStartTimestamp !== undefined) {
-                                            tie.BeamStartTimestamp = note.NoteTie.BeamStartTimestamp;
-                                            break;
-                                        }
-                                    }
-                                    if (this.currentNote === CollectionUtil.last(this.currentNote.NoteBeam.Notes)) {
-                                        tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
-                                    }
-                                }
-                            }
-                        } else if (type === "stop") {
-                            let tieNumber: number = this.findCurrentNoteInTieDict(this.currentNote);
-                            let tie: Tie = this.openTieDict[tieNumber];
+                      tie.BeamStartTimestamp = note.NoteTie.BeamStartTimestamp;
+                      break;
+                    }
+                  }
+                  if (this.currentNote === CollectionUtil.last(this.currentNote.NoteBeam.Notes)) {
+                    tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
+                  }
+                }
+              }
+            } else if (type === "stop") {
+                            const tieNumber: number = this.findCurrentNoteInTieDict(this.currentNote);
+                            const tie: Tie = this.openTieDict[tieNumber];
                             if (tie !== undefined) {
-                                let tieStartNote: Note = tie.Start;
+                                const tieStartNote: Note = tie.Start;
                                 tieStartNote.NoteTie = tie;
                                 tieStartNote.Length.Add(this.currentNote.Length);
                                 tie.Fractions.push(this.currentNote.Length);
                                 if (maxTieNoteFraction.lt(Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length))) {
-                                    maxTieNoteFraction = Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length);
-                                }
-                                let i: number = this.currentVoiceEntry.Notes.indexOf(this.currentNote);
-                                if (i !== -1) { this.currentVoiceEntry.Notes.splice(i, 1); }
+                  maxTieNoteFraction = Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length);
+                }
+                                const i: number = this.currentVoiceEntry.Notes.indexOf(this.currentNote);
+                                if (i !== -1) {
+                  this.currentVoiceEntry.Notes.splice(i, 1);
+                }
                                 if (
-                                    this.currentVoiceEntry.Articulations.length === 1
-                                    && this.currentVoiceEntry.Articulations[0] === ArticulationEnum.fermata
-                                    && tieStartNote.ParentVoiceEntry.Articulations[ArticulationEnum.fermata] === undefined
-                                ) {
-                                    tieStartNote.ParentVoiceEntry.Articulations.push(ArticulationEnum.fermata);
-                                }
+                  this.currentVoiceEntry.Articulations.length === 1
+                  && this.currentVoiceEntry.Articulations[0] === ArticulationEnum.fermata
+                  && tieStartNote.ParentVoiceEntry.Articulations[ArticulationEnum.fermata] === undefined
+                ) {
+                  tieStartNote.ParentVoiceEntry.Articulations.push(ArticulationEnum.fermata);
+                }
                                 if (this.currentNote.NoteBeam !== undefined) {
-                                    let noteBeamIndex: number = this.currentNote.NoteBeam.Notes.indexOf(this.currentNote);
+                                    const noteBeamIndex: number = this.currentNote.NoteBeam.Notes.indexOf(this.currentNote);
                                     if (noteBeamIndex === 0 && tie.BeamStartTimestamp === undefined) {
-                                        tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
-                                    }
-                                    let noteBeam: Beam = this.currentNote.NoteBeam;
+                    tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
+                  }
+                                    const noteBeam: Beam = this.currentNote.NoteBeam;
                                     noteBeam.Notes[noteBeamIndex] = tieStartNote;
                                     tie.TieBeam = noteBeam;
-                                }
+                }
                                 if (this.currentNote.NoteTuplet !== undefined) {
-                                    let noteTupletIndex: number = this.currentNote.NoteTuplet.getNoteIndex(this.currentNote);
-                                    let index: number = this.currentNote.NoteTuplet.Notes[noteTupletIndex].indexOf(this.currentNote);
-                                    let noteTuplet: Tuplet = this.currentNote.NoteTuplet;
+                                    const noteTupletIndex: number = this.currentNote.NoteTuplet.getNoteIndex(this.currentNote);
+                                    const index: number = this.currentNote.NoteTuplet.Notes[noteTupletIndex].indexOf(this.currentNote);
+                                    const noteTuplet: Tuplet = this.currentNote.NoteTuplet;
                                     noteTuplet.Notes[noteTupletIndex][index] = tieStartNote;
                                     tie.TieTuplet = noteTuplet;
-                                }
+                }
                                 for (let idx: number = 0, len: number = this.currentNote.NoteSlurs.length; idx < len; ++idx) {
-                                    let slur: Slur = this.currentNote.NoteSlurs[idx];
+                                    const slur: Slur = this.currentNote.NoteSlurs[idx];
                                     if (slur.StartNote === this.currentNote) {
-                                        slur.StartNote = tie.Start;
-                                        slur.StartNote.NoteSlurs.push(slur);
-                                    }
+                    slur.StartNote = tie.Start;
+                    slur.StartNote.NoteSlurs.push(slur);
+                  }
                                     if (slur.EndNote === this.currentNote) {
-                                        slur.EndNote = tie.Start;
-                                        slur.EndNote.NoteSlurs.push(slur);
-                                    }
-                                }
-                                let lyricsEntries: Dictionary<number, LyricsEntry> = this.currentVoiceEntry.LyricsEntries;
-                                for (let lyricsEntry in lyricsEntries) {
-                                    if (lyricsEntries.hasOwnProperty(lyricsEntry)) {
-                                        let val: LyricsEntry = this.currentVoiceEntry.LyricsEntries[lyricsEntry];
+                    slur.EndNote = tie.Start;
+                    slur.EndNote.NoteSlurs.push(slur);
+                  }
+                }
+                                const lyricsEntries: Dictionary<number, LyricsEntry> = this.currentVoiceEntry.LyricsEntries;
+                                for (const lyricsEntry in lyricsEntries) {
+                  if (lyricsEntries.hasOwnProperty(lyricsEntry)) {
+                                        const val: LyricsEntry = this.currentVoiceEntry.LyricsEntries[lyricsEntry];
                                         if (!tieStartNote.ParentVoiceEntry.LyricsEntries.hasOwnProperty(lyricsEntry)) {
-                                            tieStartNote.ParentVoiceEntry.LyricsEntries[lyricsEntry] = val;
-                                            val.Parent = tieStartNote.ParentVoiceEntry;
-                                        }
-                                    }
-                                }
+                      tieStartNote.ParentVoiceEntry.LyricsEntries[lyricsEntry] = val;
+                      val.Parent = tieStartNote.ParentVoiceEntry;
+                    }
+                  }
+                }
                                 delete this.openTieDict[tieNumber];
-                            }
-                        }
-                    } catch (err) {
-                        let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/TieError", "Error while reading tie.");
+              }
+            }
+          } catch (err) {
+                        const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/TieError", "Error while reading tie.");
                         this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
-                    }
+          }
 
-                }
-            } else if (tieNodeList.length === 2) {
-                let tieNumber: number = this.findCurrentNoteInTieDict(this.currentNote);
+        }
+      } else if (tieNodeList.length === 2) {
+                const tieNumber: number = this.findCurrentNoteInTieDict(this.currentNote);
                 if (tieNumber >= 0) {
-                    let tie: Tie = this.openTieDict[tieNumber];
-                    let tieStartNote: Note = tie.Start;
+                    const tie: Tie = this.openTieDict[tieNumber];
+                    const tieStartNote: Note = tie.Start;
                     tieStartNote.Length.Add(this.currentNote.Length);
                     tie.Fractions.push(this.currentNote.Length);
                     if (this.currentNote.NoteBeam !== undefined) {
-                        let noteBeamIndex: number = this.currentNote.NoteBeam.Notes.indexOf(this.currentNote);
+                        const noteBeamIndex: number = this.currentNote.NoteBeam.Notes.indexOf(this.currentNote);
                         if (noteBeamIndex === 0 && tie.BeamStartTimestamp === undefined) {
-                            tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
-                        }
-                        let noteBeam: Beam = this.currentNote.NoteBeam;
+              tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
+            }
+                        const noteBeam: Beam = this.currentNote.NoteBeam;
                         noteBeam.Notes[noteBeamIndex] = tieStartNote;
                         tie.TieBeam = noteBeam;
-                    }
+          }
                     for (let idx: number = 0, len: number = this.currentNote.NoteSlurs.length; idx < len; ++idx) {
-                        let slur: Slur = this.currentNote.NoteSlurs[idx];
+                        const slur: Slur = this.currentNote.NoteSlurs[idx];
                         if (slur.StartNote === this.currentNote) {
-                            slur.StartNote = tie.Start;
-                            slur.StartNote.NoteSlurs.push(slur);
-                        }
+              slur.StartNote = tie.Start;
+              slur.StartNote.NoteSlurs.push(slur);
+            }
                         if (slur.EndNote === this.currentNote) {
-                            slur.EndNote = tie.Start;
-                            slur.EndNote.NoteSlurs.push(slur);
-                        }
-                    }
+              slur.EndNote = tie.Start;
+              slur.EndNote.NoteSlurs.push(slur);
+            }
+          }
 
                     this.currentVoiceEntry.LyricsEntries.forEach((key: number, value: LyricsEntry): void => {
-                        if (!tieStartNote.ParentVoiceEntry.LyricsEntries.containsKey(key)) {
-                            tieStartNote.ParentVoiceEntry.LyricsEntries.setValue(key, value);
-                            value.Parent = tieStartNote.ParentVoiceEntry;
-                        }
-                    });
-                    if (maxTieNoteFraction.lt(Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length))) {
-                        maxTieNoteFraction = Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length);
-                    }
-                    // delete currentNote from Notes:
-                    let i: number = this.currentVoiceEntry.Notes.indexOf(this.currentNote);
-                    if (i !== -1) { this.currentVoiceEntry.Notes.splice(i, 1); }
-                }
+            if (!tieStartNote.ParentVoiceEntry.LyricsEntries.containsKey(key)) {
+              tieStartNote.ParentVoiceEntry.LyricsEntries.setValue(key, value);
+              value.Parent = tieStartNote.ParentVoiceEntry;
             }
+          });
+                    if (maxTieNoteFraction.lt(Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length))) {
+            maxTieNoteFraction = Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length);
+          }
+          // delete currentNote from Notes:
+                    const i: number = this.currentVoiceEntry.Notes.indexOf(this.currentNote);
+                    if (i !== -1) {
+            this.currentVoiceEntry.Notes.splice(i, 1);
+          }
         }
+      }
+    }
+  }
+
+  /**
+   * Find the next free int (starting from 0) to use as key in TieDict.
+   * @returns {number}
+   */
+  private getNextAvailableNumberForTie(): number {
+        const keys: string[] = Object.keys(this.openTieDict);
+        if (keys.length === 0) {
+      return 1;
     }
-
-    /**
-     * Find the next free int (starting from 0) to use as key in TieDict.
-     * @returns {number}
-     */
-    private getNextAvailableNumberForTie(): number {
-        let keys: string[] = Object.keys(this.openTieDict);
-        if (keys.length === 0) { return 1; }
         keys.sort((a, b) => (+a - +b)); // FIXME Andrea: test
         for (let i: number = 0; i < keys.length; i++) {
-            if ("" + (i + 1) !== keys[i]) {
-                return i + 1;
-            }
-        }
-        return +(keys[keys.length - 1]) + 1;
+      if ("" + (i + 1) !== keys[i]) {
+        return i + 1;
+      }
     }
-
-    /**
-     * Search the tieDictionary for the corresponding candidateNote to the currentNote (same FundamentalNote && Octave).
-     * @param candidateNote
-     * @returns {number}
-     */
-    private findCurrentNoteInTieDict(candidateNote: Note): number {
-        let openTieDict: { [_: number]: Tie; } = this.openTieDict;
-        for (let key in openTieDict) {
-            if (openTieDict.hasOwnProperty(key)) {
-                let tie: Tie = openTieDict[key];
+        return +(keys[keys.length - 1]) + 1;
+  }
+
+  /**
+   * Search the tieDictionary for the corresponding candidateNote to the currentNote (same FundamentalNote && Octave).
+   * @param candidateNote
+   * @returns {number}
+   */
+  private findCurrentNoteInTieDict(candidateNote: Note): number {
+        const openTieDict: { [_: number]: Tie; } = this.openTieDict;
+        for (const key in openTieDict) {
+      if (openTieDict.hasOwnProperty(key)) {
+                const tie: Tie = openTieDict[key];
                 if (tie.Start.Pitch.FundamentalNote === candidateNote.Pitch.FundamentalNote && tie.Start.Pitch.Octave === candidateNote.Pitch.Octave) {
-                    return +key;
-                }
-            }
+          return +key;
         }
-        return -1;
+      }
     }
-
-    /**
-     * Calculate the normal duration of a [[Tuplet]] note.
-     * @param xmlNode
-     * @returns {any}
-     */
-    private getTupletNoteDurationFromType(xmlNode: IXmlElement): Fraction {
-        if (xmlNode.element("type") !== undefined) {
-            let typeNode: IXmlElement = xmlNode.element("type");
+        return -1;
+  }
+
+  /**
+   * Calculate the normal duration of a [[Tuplet]] note.
+   * @param xmlNode
+   * @returns {any}
+   */
+  private getTupletNoteDurationFromType(xmlNode: IXmlElement): Fraction {
+    if (xmlNode.element("type") !== undefined) {
+            const typeNode: IXmlElement = xmlNode.element("type");
             if (typeNode !== undefined) {
-                let type: string = typeNode.value;
+                const type: string = typeNode.value;
                 try {
-                    return this.getNoteDurationFromType(type);
-                } catch (e) {
-                    let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError", "Invalid note duration.");
+          return this.getNoteDurationFromType(type);
+        } catch (e) {
+                    const errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError", "Invalid note duration.");
                     this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                     throw new MusicSheetReadingException("", e);
-                }
-
-            }
         }
-        return undefined;
+
+      }
     }
+    return undefined;
+  }
 }

+ 6 - 4
src/MusicalScore/SubInstrument.ts

@@ -93,17 +93,19 @@ export class SubInstrument {
     private parseMidiInstrument(instrumentType: string): string {
         // FIXME: test this function
         try {
+            // find the best match for the given instrumentType:
             if (instrumentType) {
-                let tmpName: string = instrumentType.toLowerCase().trim();
-                for (let key in SubInstrument.midiInstrument) {
+                const tmpName: string = instrumentType.toLowerCase().trim();
+                for (const key in SubInstrument.midiInstrument) {
                     if (tmpName.indexOf(key) !== -1) {
                         return key;
                     }
                 }
             }
+            // if the instrumentType didn't work, use the name:
             if (this.parentInstrument.Name) {
-                let tmpName: string = this.parentInstrument.Name.toLowerCase().trim();
-                for (let key in SubInstrument.midiInstrument) {
+                const tmpName: string = this.parentInstrument.Name.toLowerCase().trim();
+                for (const key in SubInstrument.midiInstrument) {
                     if (tmpName.indexOf(key) !== -1) {
                         return key;
                     }

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

@@ -4,7 +4,7 @@ export class AbstractExpression {
     //}
     protected static isStringInStringList(stringList: Array<string>, inputString: string): boolean {
         for (let idx: number = 0, len: number = stringList.length; idx < len; ++idx) {
-            let s: string = stringList[idx];
+            const s: string = stringList[idx];
             if (inputString.toLowerCase() === s.toLowerCase().trim()) {
                 return true;
             }

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

@@ -38,7 +38,7 @@ export abstract class AbstractTempoExpression {
     }
 
     protected static isStringInStringList(wordsToFind: string[], inputString: string): boolean {
-        for (let wordToFind of wordsToFind) {
+        for (const wordToFind of wordsToFind) {
             if (AbstractTempoExpression.stringContainsSeparatedWord(inputString.toLowerCase().trim(), wordToFind.toLowerCase().trim())) {
                 return true;
             }

+ 3 - 3
src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression.ts

@@ -91,7 +91,7 @@ export class ContinuousDynamicExpression extends AbstractExpression {
         );
     }
     public getInterpolatedDynamic(currentAbsoluteTimestamp: Fraction): number {
-        let continuousAbsoluteStartTimestamp: Fraction = this.StartMultiExpression.AbsoluteTimestamp;
+        const continuousAbsoluteStartTimestamp: Fraction = this.StartMultiExpression.AbsoluteTimestamp;
         let continuousAbsoluteEndTimestamp: Fraction;
         if (this.EndMultiExpression !== undefined) {
             continuousAbsoluteEndTimestamp = this.EndMultiExpression.AbsoluteTimestamp;
@@ -102,10 +102,10 @@ export class ContinuousDynamicExpression extends AbstractExpression {
         }
         if (currentAbsoluteTimestamp.lt(continuousAbsoluteStartTimestamp)) { return -1; }
         if (continuousAbsoluteEndTimestamp.lt(currentAbsoluteTimestamp)) { return -2; }
-        let interpolationRatio: number =
+        const interpolationRatio: number =
             Fraction.minus(currentAbsoluteTimestamp, continuousAbsoluteStartTimestamp).RealValue
             / Fraction.minus(continuousAbsoluteEndTimestamp, continuousAbsoluteStartTimestamp).RealValue;
-        let interpolatedVolume: number = Math.max(0.0, Math.min(99.9, this.startVolume + (this.endVolume - this.startVolume) * interpolationRatio));
+        const interpolatedVolume: number = Math.max(0.0, Math.min(99.9, this.startVolume + (this.endVolume - this.startVolume) * interpolationRatio));
         return interpolatedVolume;
     }
     public isWedge(): boolean {

+ 3 - 3
src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/ContinuousTempoExpression.ts

@@ -68,15 +68,15 @@ export class ContinuousTempoExpression extends AbstractTempoExpression {
         return this.ParentMultiTempoExpression.AbsoluteTimestamp.RealValue;
     }
     public getInterpolatedTempo(currentAbsoluteTimestamp: Fraction): number {
-        let continuousAbsoluteStartTimestamp: Fraction = Fraction.plus(
+        const continuousAbsoluteStartTimestamp: Fraction = Fraction.plus(
             this.parentMultiTempoExpression.SourceMeasureParent.AbsoluteTimestamp, this.parentMultiTempoExpression.Timestamp
         );
         if (currentAbsoluteTimestamp.lt(continuousAbsoluteStartTimestamp)) { return -1; }
         if (this.absoluteEndTimestamp.lt(currentAbsoluteTimestamp)) { return -2; }
-        let interpolationRatio: number =
+        const interpolationRatio: number =
             Fraction.minus(currentAbsoluteTimestamp, continuousAbsoluteStartTimestamp).RealValue
             / Fraction.minus(this.absoluteEndTimestamp, continuousAbsoluteStartTimestamp).RealValue;
-        let interpolatedTempo: number = Math.max(0.0, Math.min(250.0, this.startTempo + (this.endTempo - this.startTempo) * interpolationRatio));
+        const interpolatedTempo: number = Math.max(0.0, Math.min(250.0, this.startTempo + (this.endTempo - this.startTempo) * interpolationRatio));
         return interpolatedTempo;
     }
 

+ 5 - 5
src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/Slur.ts

@@ -24,7 +24,7 @@ export class Slur {
     public startNoteHasMoreStartingSlurs(): boolean {
         if (this.startNote === undefined) { return false; }
         for (let idx: number = 0, len: number = this.startNote.NoteSlurs.length; idx < len; ++idx) {
-            let slur: Slur = this.startNote.NoteSlurs[idx];
+            const slur: Slur = this.startNote.NoteSlurs[idx];
             if (slur !== this && slur.StartNote === this.startNote) {
                 return true;
             }
@@ -34,7 +34,7 @@ export class Slur {
     public endNoteHasMoreEndingSlurs(): boolean {
         if (this.endNote === undefined) { return false; }
         for (let idx: number = 0, len: number = this.endNote.NoteSlurs.length; idx < len; ++idx) {
-            let slur: Slur = this.endNote.NoteSlurs[idx];
+            const slur: Slur = this.endNote.NoteSlurs[idx];
             if (slur !== this && slur.EndNote === this.endNote) {
                 return true;
             }
@@ -48,9 +48,9 @@ export class Slur {
         if (this.endNote === undefined || this.startNote === undefined) {
             return false;
         }
-        let length: Fraction = Fraction.minus(this.endNote.getAbsoluteTimestamp(), this.startNote.getAbsoluteTimestamp());
+        const length: Fraction = Fraction.minus(this.endNote.getAbsoluteTimestamp(), this.startNote.getAbsoluteTimestamp());
         for (let idx: number = 0, len: number = this.startNote.NoteSlurs.length; idx < len; ++idx) {
-            let slur: Slur = this.startNote.NoteSlurs[idx];
+            const slur: Slur = this.startNote.NoteSlurs[idx];
             if (
                 slur !== this
                 && slur.EndNote !== undefined
@@ -61,7 +61,7 @@ export class Slur {
             }
         }
         for (let idx: number = 0, len: number = this.endNote.NoteSlurs.length; idx < len; ++idx) {
-            let slur: Slur = this.endNote.NoteSlurs[idx];
+            const slur: Slur = this.endNote.NoteSlurs[idx];
             if (
                 slur !== this
                 && slur.EndNote !== undefined

+ 2 - 2
src/MusicalScore/VoiceData/Expressions/MultiExpression.ts

@@ -170,7 +170,7 @@ export class MultiExpression /*implements IComparable<MultiExpression>*/ {
         }
     }
     private addExpressionToEntryList(expression: AbstractExpression, prefix: string): void {
-        let multiExpressionEntry: MultiExpressionEntry = new MultiExpressionEntry();
+        const multiExpressionEntry: MultiExpressionEntry = new MultiExpressionEntry();
         multiExpressionEntry.prefix = prefix;
         multiExpressionEntry.expression = expression;
         if (expression instanceof ContinuousDynamicExpression) {
@@ -186,7 +186,7 @@ export class MultiExpression /*implements IComparable<MultiExpression>*/ {
     }
     private removeExpressionFromEntryList(expression: AbstractExpression): void {
         for (let idx: number = 0, len: number = this.expressions.length; idx < len; ++idx) {
-            let entry: MultiExpressionEntry = this.expressions[idx];
+            const entry: MultiExpressionEntry = this.expressions[idx];
             if (entry.expression === expression) {
                 this.expressions.splice(idx, 1);
                 break;

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

@@ -86,7 +86,7 @@ export class MultiTempoExpression /*implements IComparable<MultiTempoExpression>
         } else if (abstractTempoExpression instanceof ContinuousTempoExpression) {
             this.continuousTempo = <ContinuousTempoExpression>abstractTempoExpression;
         }
-        let tempoExpressionEntry: TempoExpressionEntry = new TempoExpressionEntry();
+        const tempoExpressionEntry: TempoExpressionEntry = new TempoExpressionEntry();
         tempoExpressionEntry.prefix = prefix;
         tempoExpressionEntry.expression = abstractTempoExpression;
         tempoExpressionEntry.label = abstractTempoExpression.Label;

+ 0 - 4
src/MusicalScore/VoiceData/Expressions/UnknownExpression.ts

@@ -2,10 +2,6 @@ import {PlacementEnum, AbstractExpression} from "./AbstractExpression";
 import {TextAlignment} from "../../../Common/Enums/TextAlignment";
 
 export class UnknownExpression extends AbstractExpression {
-    //constructor(label: string, placementEnum: PlacementEnum, staffNumber: number) {
-    //    this(label, placementEnum, OSMDTextAlignment.LeftBottom, staffNumber);
-    //
-    //}
     constructor(label: string, placementEnum: PlacementEnum, textAlignment: TextAlignment, staffNumber: number) {
         super();
         this.label = label;

+ 3 - 3
src/MusicalScore/VoiceData/Instructions/ClefInstruction.ts

@@ -48,13 +48,13 @@ export class ClefInstruction extends AbstractNotationInstruction {
     }
 
     public static getAllPossibleClefs(): ClefInstruction[] {
-        let clefList: ClefInstruction[] = [];
+        const clefList: ClefInstruction[] = [];
         for (let i: number = 0; i <= 2; i++) {
-            let clefInstructionG: ClefInstruction = new ClefInstruction(ClefEnum.G, i, 2);
+            const clefInstructionG: ClefInstruction = new ClefInstruction(ClefEnum.G, i, 2);
             clefList.push(clefInstructionG);
         }
         for (let j: number = -2; j <= 0; j++) {
-            let clefInstructionF: ClefInstruction = new ClefInstruction(ClefEnum.F, j, 4);
+            const clefInstructionF: ClefInstruction = new ClefInstruction(ClefEnum.F, j, 4);
             clefList.push(clefInstructionF);
         }
         return clefList;

+ 13 - 6
src/MusicalScore/VoiceData/Instructions/KeyInstruction.ts

@@ -21,12 +21,12 @@ export class KeyInstruction extends AbstractNotationInstruction {
     private mode: KeyEnum;
 
     public static copy(keyInstruction: KeyInstruction): KeyInstruction {
-        let newKeyInstruction: KeyInstruction = new KeyInstruction(keyInstruction.parent, keyInstruction.Key, keyInstruction.Mode);
+        const newKeyInstruction: KeyInstruction = new KeyInstruction(keyInstruction.parent, keyInstruction.Key, keyInstruction.Mode);
         return newKeyInstruction;
     }
 
     public static getNoteEnumList(instruction: KeyInstruction): NoteEnum[] {
-        let enums: NoteEnum[] = [];
+        const enums: NoteEnum[] = [];
         if (instruction.keyType > 0) {
             for (let i: number = 0; i < instruction.keyType; i++) {
                 enums.push(KeyInstruction.sharpPositionList[i]);
@@ -41,9 +41,9 @@ export class KeyInstruction extends AbstractNotationInstruction {
     }
 
     public static getAllPossibleMajorKeyInstructions(): KeyInstruction[] {
-        let keyInstructionList: KeyInstruction[] = [];
+        const keyInstructionList: KeyInstruction[] = [];
         for (let keyType: number = -7; keyType < 7; keyType++) {
-            let currentKeyInstruction: KeyInstruction = new KeyInstruction(undefined, keyType, KeyEnum.major);
+            const currentKeyInstruction: KeyInstruction = new KeyInstruction(undefined, keyType, KeyEnum.major);
             keyInstructionList.push(currentKeyInstruction);
         }
         return keyInstructionList;
@@ -66,7 +66,7 @@ export class KeyInstruction extends AbstractNotationInstruction {
     }
 
     public getFundamentalNotesOfAccidentals(): NoteEnum[] {
-        let noteList: NoteEnum[] = [];
+        const noteList: NoteEnum[] = [];
         if (this.keyType > 0) {
             for (let i: number = 0; i < this.keyType; i++) {
                 noteList.push(KeyInstruction.sharpPositionList[i]);
@@ -93,7 +93,7 @@ export class KeyInstruction extends AbstractNotationInstruction {
     }
 
     public OperatorEquals(key2: KeyInstruction): boolean {
-        let key1: KeyInstruction = this;
+        const key1: KeyInstruction = this;
         if (key1 === key2) {
             return true;
         }
@@ -122,4 +122,11 @@ export enum KeyEnum {
     major = 0,
     minor = 1,
     none = 2,
+    dorian = 3,
+    phrygian = 4,
+    lydian = 5,
+    mixolydian = 6,
+    aeolian = 7,
+    ionian = 8,
+    locrian = 9,
 }

+ 7 - 4
src/MusicalScore/VoiceData/Instructions/RepetitionInstruction.ts

@@ -41,22 +41,25 @@ export class RepetitionInstruction /*implements IComparable*/ {
 
      }
      */
-    constructor(measureIndex: number, endingIndices: number[], type: RepetitionInstructionEnum, alignment: AlignmentType, parentRepetition: Repetition) {
+    constructor(measureIndex: number, type: RepetitionInstructionEnum, alignment: AlignmentType = AlignmentType.End,
+                parentRepetition: Repetition = undefined, endingIndices: number[] = undefined) {
         this.measureIndex = measureIndex;
-        this.endingIndices = endingIndices.slice();
+        if (endingIndices !== undefined) {
+            this.endingIndices = endingIndices.slice();
+        }
         this.type = type;
         this.alignment = alignment;
         this.parentRepetition = parentRepetition;
     }
 
     public measureIndex: number;
-    public endingIndices: number[];
+    public endingIndices: number[] = undefined;
     public type: RepetitionInstructionEnum;
     public alignment: AlignmentType;
     public parentRepetition: Repetition;
 
     public CompareTo(obj: Object): number {
-        let other: RepetitionInstruction = <RepetitionInstruction>obj;
+        const other: RepetitionInstruction = <RepetitionInstruction>obj;
         if (this.measureIndex > other.measureIndex) {
             return 1;
         } else if (this.measureIndex < other.measureIndex) {

+ 6 - 6
src/MusicalScore/VoiceData/Instructions/RhythmInstruction.ts

@@ -5,11 +5,11 @@ import {Fraction} from "../../../Common/DataObjects/Fraction";
  * A [[RhythmInstruction]] is the time signature which specifies the number of beats in each bar, and the value of one beat.
  */
 export class RhythmInstruction extends AbstractNotationInstruction {
-    constructor(rhythm: Fraction, numerator: number, denominator: number, rhythmSymbolEnum: RhythmSymbolEnum) {
+    constructor(rhythm: Fraction, rhythmSymbolEnum: RhythmSymbolEnum) {
         super(undefined); // FIXME no parent SourceStaffEntry
         this.rhythm = rhythm;
-        this.numerator = numerator;
-        this.denominator = denominator;
+        this.numerator = rhythm.Numerator;
+        this.denominator = rhythm.Denominator;
         this.symbolEnum = rhythmSymbolEnum;
     }
 
@@ -35,11 +35,11 @@ export class RhythmInstruction extends AbstractNotationInstruction {
     }
 
     public clone(): RhythmInstruction {
-        return new RhythmInstruction(this.rhythm.clone(), this.numerator, this.denominator, this.symbolEnum);
+        return new RhythmInstruction(this.rhythm.clone(), this.symbolEnum);
     }
 
     public OperatorEquals(rhythm2: RhythmInstruction): boolean {
-        let rhythm1: RhythmInstruction = this;
+        const rhythm1: RhythmInstruction = this;
         if (rhythm1 === rhythm2) {
             return true;
         }
@@ -50,7 +50,7 @@ export class RhythmInstruction extends AbstractNotationInstruction {
     }
 
     public OperatorNotEqual(rhythm2: RhythmInstruction): boolean {
-        let rhythm1: RhythmInstruction = this;
+        const rhythm1: RhythmInstruction = this;
         return !(rhythm1 === rhythm2);
     }
 

+ 8 - 1
src/MusicalScore/VoiceData/Lyrics/LyricsEntry.ts

@@ -2,14 +2,17 @@ import {LyricWord} from "./LyricsWord";
 import {VoiceEntry} from "../VoiceEntry";
 
 export class LyricsEntry {
-    constructor(text: string, word: LyricWord, parent: VoiceEntry) {
+    constructor(text: string, verseNumber: number, word: LyricWord, parent: VoiceEntry) {
         this.text = text;
         this.word = word;
         this.parent = parent;
+        this.verseNumber = verseNumber;
     }
     private text: string;
     private word: LyricWord;
     private parent: VoiceEntry;
+    private verseNumber: number;
+    public extend: boolean;
 
     public get Text(): string {
         return this.text;
@@ -26,4 +29,8 @@ export class LyricsEntry {
     public set Parent(value: VoiceEntry) {
         this.parent = value;
     }
+
+    public get VerseNumber(): number {
+        return this.verseNumber;
+    }
 }

+ 2 - 2
src/MusicalScore/VoiceData/Lyrics/LyricsWord.ts

@@ -9,7 +9,7 @@ export class LyricWord {
     }
     public containsVoiceEntry(voiceEntry: VoiceEntry): boolean {
         for (let idx: number = 0, len: number = this.Syllables.length; idx < len; ++idx) {
-            let lyricsEntry: LyricsEntry = this.Syllables[idx];
+            const lyricsEntry: LyricsEntry = this.Syllables[idx];
             if (lyricsEntry.Parent === voiceEntry) {
                 return true;
             }
@@ -18,7 +18,7 @@ export class LyricWord {
     }
     public findLyricEntryInVoiceEntry(voiceEntry: VoiceEntry): LyricsEntry {
         for (let idx: number = 0, len: number = this.Syllables.length; idx < len; ++idx) {
-            let lyricsEntry: LyricsEntry = this.Syllables[idx];
+            const lyricsEntry: LyricsEntry = this.Syllables[idx];
             if (lyricsEntry.Parent === voiceEntry) {
                 return lyricsEntry;
             }

+ 8 - 4
src/MusicalScore/VoiceData/Note.ts

@@ -103,10 +103,14 @@ export class Note {
         this.playbackInstrumentId = value;
     }
 
+    public isRest(): boolean {
+        return this.Pitch === undefined;
+    }
+
     public calculateNoteLengthWithoutTie(): Fraction {
-        let withoutTieLength: Fraction = this.length.clone();
+        const withoutTieLength: Fraction = this.length.clone();
         if (this.tie !== undefined) {
-            for (let fraction of this.tie.Fractions) {
+            for (const fraction of this.tie.Fractions) {
                 withoutTieLength.Sub(fraction);
             }
         }
@@ -120,7 +124,7 @@ export class Note {
             return this.length;
         }
         if (originalLength.Numerator > 1) {
-            let exp: number = Math.floor(Math.log(originalLength.Denominator) / Math.LN2) - this.calculateNumberOfNeededDots(originalLength);
+            const exp: number = Math.floor(Math.log(originalLength.Denominator) / Math.LN2) - this.calculateNumberOfNeededDots(originalLength);
             originalLength.Denominator = Math.pow(2, exp);
             originalLength.Numerator = 1;
         }
@@ -141,7 +145,7 @@ export class Note {
     }
     public checkForDoubleSlur(slur: Slur): boolean {
         for (let idx: number = 0, len: number = this.slurs.length; idx < len; ++idx) {
-            let noteSlur: Slur = this.slurs[idx];
+            const noteSlur: Slur = this.slurs[idx];
             if (
               noteSlur.StartNote !== undefined &&
               noteSlur.EndNote !== undefined &&

+ 38 - 31
src/MusicalScore/VoiceData/SourceMeasure.ts

@@ -1,7 +1,7 @@
 import {Fraction} from "../../Common/DataObjects/Fraction";
 import {VerticalSourceStaffEntryContainer} from "./VerticalSourceStaffEntryContainer";
 import {SourceStaffEntry} from "./SourceStaffEntry";
-import {RepetitionInstruction} from "./Instructions/RepetitionInstruction";
+import {RepetitionInstruction, RepetitionInstructionEnum} from "./Instructions/RepetitionInstruction";
 import {Staff} from "./Staff";
 import {VoiceEntry} from "./VoiceEntry";
 import {Voice} from "./Voice";
@@ -166,7 +166,7 @@ export class SourceMeasure extends BaseIdClass {
         let staffEntry: SourceStaffEntry = undefined;
         // Find:
         let existingVerticalSourceStaffEntryContainer: VerticalSourceStaffEntryContainer;
-        for (let container of this.verticalSourceStaffEntryContainers) {
+        for (const container of this.verticalSourceStaffEntryContainers) {
             if (container.Timestamp.Equals(inMeasureTimestamp)) {
                 existingVerticalSourceStaffEntryContainer = container;
                 break;
@@ -181,9 +181,9 @@ export class SourceMeasure extends BaseIdClass {
             }
             return {createdNewContainer: false, staffEntry: staffEntry};
         }
-        let last: VerticalSourceStaffEntryContainer = this.verticalSourceStaffEntryContainers[this.verticalSourceStaffEntryContainers.length - 1];
+        const last: VerticalSourceStaffEntryContainer = this.verticalSourceStaffEntryContainers[this.verticalSourceStaffEntryContainers.length - 1];
         if (this.verticalSourceStaffEntryContainers.length === 0 || last.Timestamp.lt(inMeasureTimestamp)) {
-            let container: VerticalSourceStaffEntryContainer = new VerticalSourceStaffEntryContainer(
+            const container: VerticalSourceStaffEntryContainer = new VerticalSourceStaffEntryContainer(
                 this, inMeasureTimestamp.clone(), this.completeNumberOfStaves
             );
             this.verticalSourceStaffEntryContainers.push(container);
@@ -195,7 +195,7 @@ export class SourceMeasure extends BaseIdClass {
                 i >= 0; i--
             ) {
                 if (this.verticalSourceStaffEntryContainers[i].Timestamp.lt(inMeasureTimestamp)) {
-                    let container: VerticalSourceStaffEntryContainer = new VerticalSourceStaffEntryContainer(
+                    const container: VerticalSourceStaffEntryContainer = new VerticalSourceStaffEntryContainer(
                         this, inMeasureTimestamp.clone(), this.completeNumberOfStaves
                     );
                     this.verticalSourceStaffEntryContainers.splice(i + 1, 0, container);
@@ -204,7 +204,7 @@ export class SourceMeasure extends BaseIdClass {
                     break;
                 }
                 if (i === 0) {
-                    let container: VerticalSourceStaffEntryContainer = new VerticalSourceStaffEntryContainer(
+                    const container: VerticalSourceStaffEntryContainer = new VerticalSourceStaffEntryContainer(
                         this, inMeasureTimestamp.clone(), this.completeNumberOfStaves
                     );
                     this.verticalSourceStaffEntryContainers.splice(i, 0, container);
@@ -228,7 +228,7 @@ export class SourceMeasure extends BaseIdClass {
     public findOrCreateVoiceEntry(sse: SourceStaffEntry, voice: Voice): { createdVoiceEntry: boolean, voiceEntry: VoiceEntry } {
         let ve: VoiceEntry = undefined;
         let createdNewVoiceEntry: boolean = false;
-        for (let voiceEntry of sse.VoiceEntries) {
+        for (const voiceEntry of sse.VoiceEntries) {
             if (voiceEntry.ParentVoice === voice) {
                 ve = voiceEntry;
                 break;
@@ -279,7 +279,7 @@ export class SourceMeasure extends BaseIdClass {
      */
     public getVerticalContainerByTimestamp(musicTimestamp: Fraction): VerticalSourceStaffEntryContainer {
         for (let idx: number = 0, len: number = this.VerticalSourceStaffEntryContainers.length; idx < len; ++idx) {
-            let verticalSourceStaffEntryContainer: VerticalSourceStaffEntryContainer = this.VerticalSourceStaffEntryContainers[idx];
+            const verticalSourceStaffEntryContainer: VerticalSourceStaffEntryContainer = this.VerticalSourceStaffEntryContainers[idx];
             if (verticalSourceStaffEntryContainer.Timestamp.Equals(musicTimestamp)) {
                 return verticalSourceStaffEntryContainer;
             }
@@ -314,16 +314,16 @@ export class SourceMeasure extends BaseIdClass {
      */
     public reverseCheck(musicSheet: MusicSheet, maxInstDuration: Fraction): Fraction {
         let maxDuration: Fraction = new Fraction(0, 1);
-        let instrumentsDurations: Fraction[] = [];
+        const instrumentsDurations: Fraction[] = [];
         for (let i: number = 0; i < musicSheet.Instruments.length; i++) {
             let instrumentDuration: Fraction = new Fraction(0, 1);
-            let inSourceMeasureInstrumentIndex: number = musicSheet.getGlobalStaffIndexOfFirstStaff(musicSheet.Instruments[i]);
+            const inSourceMeasureInstrumentIndex: number = musicSheet.getGlobalStaffIndexOfFirstStaff(musicSheet.Instruments[i]);
             for (let j: number = 0; j < musicSheet.Instruments[i].Staves.length; j++) {
-                let lastStaffEntry: SourceStaffEntry = this.getLastSourceStaffEntryForInstrument(inSourceMeasureInstrumentIndex + j);
+                const lastStaffEntry: SourceStaffEntry = this.getLastSourceStaffEntryForInstrument(inSourceMeasureInstrumentIndex + j);
                 if (lastStaffEntry !== undefined && !lastStaffEntry.hasTie()) {
-                    let verticalContainerIndex: number = this.verticalSourceStaffEntryContainers.indexOf(lastStaffEntry.VerticalContainerParent);
+                    const verticalContainerIndex: number = this.verticalSourceStaffEntryContainers.indexOf(lastStaffEntry.VerticalContainerParent);
                     for (let m: number = verticalContainerIndex - 1; m >= 0; m--) {
-                        let previousStaffEntry: SourceStaffEntry = this.verticalSourceStaffEntryContainers[m][inSourceMeasureInstrumentIndex + j];
+                        const previousStaffEntry: SourceStaffEntry = this.verticalSourceStaffEntryContainers[m][inSourceMeasureInstrumentIndex + j];
                         if (previousStaffEntry !== undefined && previousStaffEntry.hasTie()) {
                             if (instrumentDuration.lt(Fraction.plus(previousStaffEntry.Timestamp, previousStaffEntry.calculateMaxNoteLength()))) {
                                 instrumentDuration = Fraction.plus(previousStaffEntry.Timestamp, previousStaffEntry.calculateMaxNoteLength());
@@ -336,7 +336,7 @@ export class SourceMeasure extends BaseIdClass {
             instrumentsDurations.push(instrumentDuration);
         }
         for (let idx: number = 0, len: number = instrumentsDurations.length; idx < len; ++idx) {
-            let instrumentsDuration: Fraction = instrumentsDurations[idx];
+            const instrumentsDuration: Fraction = instrumentsDurations[idx];
             if (maxDuration.lt(instrumentsDuration)) {
                 maxDuration = instrumentsDuration;
             }
@@ -352,12 +352,12 @@ export class SourceMeasure extends BaseIdClass {
      * @returns {Fraction[]}
      */
     public calculateInstrumentsDuration(musicSheet: MusicSheet, instrumentMaxTieNoteFractions: Fraction[]): Fraction[] {
-        let instrumentsDurations: Fraction[] = [];
+        const instrumentsDurations: Fraction[] = [];
         for (let i: number = 0; i < musicSheet.Instruments.length; i++) {
             let instrumentDuration: Fraction = new Fraction(0, 1);
-            let inSourceMeasureInstrumentIndex: number = musicSheet.getGlobalStaffIndexOfFirstStaff(musicSheet.Instruments[i]);
+            const inSourceMeasureInstrumentIndex: number = musicSheet.getGlobalStaffIndexOfFirstStaff(musicSheet.Instruments[i]);
             for (let j: number = 0; j < musicSheet.Instruments[i].Staves.length; j++) {
-                let lastStaffEntry: SourceStaffEntry = this.getLastSourceStaffEntryForInstrument(inSourceMeasureInstrumentIndex + j);
+                const lastStaffEntry: SourceStaffEntry = this.getLastSourceStaffEntryForInstrument(inSourceMeasureInstrumentIndex + j);
                 if (lastStaffEntry !== undefined && lastStaffEntry.Timestamp !== undefined) {
                     if (instrumentDuration.lt(Fraction.plus(lastStaffEntry.Timestamp, lastStaffEntry.calculateMaxNoteLength()))) {
                         instrumentDuration = Fraction.plus(lastStaffEntry.Timestamp, lastStaffEntry.calculateMaxNoteLength());
@@ -373,9 +373,9 @@ export class SourceMeasure extends BaseIdClass {
     }
 
     public getEntriesPerStaff(staffIndex: number): SourceStaffEntry[] {
-        let sourceStaffEntries: SourceStaffEntry[] = [];
-        for (let container of this.VerticalSourceStaffEntryContainers) {
-            let sse: SourceStaffEntry = container.StaffEntries[staffIndex];
+        const sourceStaffEntries: SourceStaffEntry[] = [];
+        for (const container of this.VerticalSourceStaffEntryContainers) {
+            const sse: SourceStaffEntry = container.StaffEntries[staffIndex];
             if (sse !== undefined) {
                 sourceStaffEntries.push(sse);
             }
@@ -389,7 +389,7 @@ export class SourceMeasure extends BaseIdClass {
      */
     public hasBeginInstructions(): boolean {
         for (let staffIndex: number = 0, len: number = this.FirstInstructionsStaffEntries.length; staffIndex < len; staffIndex++) {
-            let beginInstructionsStaffEntry: SourceStaffEntry = this.FirstInstructionsStaffEntries[staffIndex];
+            const beginInstructionsStaffEntry: SourceStaffEntry = this.FirstInstructionsStaffEntries[staffIndex];
             if (beginInstructionsStaffEntry !== undefined && beginInstructionsStaffEntry.Instructions.length > 0) {
                 return true;
             }
@@ -399,7 +399,10 @@ export class SourceMeasure extends BaseIdClass {
 
     public beginsWithLineRepetition(): boolean {
         for (let idx: number = 0, len: number = this.FirstRepetitionInstructions.length; idx < len; ++idx) {
-            let instr: RepetitionInstruction = this.FirstRepetitionInstructions[idx];
+            const instr: RepetitionInstruction = this.FirstRepetitionInstructions[idx];
+            if (instr.type === RepetitionInstructionEnum.StartLine) {
+                return true;
+            }
             if (instr.parentRepetition !== undefined && instr === instr.parentRepetition.startMarker && !instr.parentRepetition.FromWords) {
                 return true;
             }
@@ -413,8 +416,12 @@ export class SourceMeasure extends BaseIdClass {
      */
     public endsWithLineRepetition(): boolean {
         for (let idx: number = 0, len: number = this.LastRepetitionInstructions.length; idx < len; ++idx) {
-            let instruction: RepetitionInstruction = this.LastRepetitionInstructions[idx];
-            let rep: Repetition = instruction.parentRepetition;
+            const instruction: RepetitionInstruction = this.LastRepetitionInstructions[idx];
+            if (instruction.type === RepetitionInstructionEnum.BackJumpLine) {
+                return true;
+            }
+
+            const rep: Repetition = instruction.parentRepetition;
             if (rep === undefined) {
                 continue;
             }
@@ -422,7 +429,7 @@ export class SourceMeasure extends BaseIdClass {
                 continue;
             }
             for (let idx2: number = 0, len2: number = rep.BackwardJumpInstructions.length; idx2 < len2; ++idx2) {
-                let backJumpInstruction: RepetitionInstruction = rep.BackwardJumpInstructions[idx2];
+                const backJumpInstruction: RepetitionInstruction = rep.BackwardJumpInstructions[idx2];
                 if (instruction === backJumpInstruction) {
                     return true;
                 }
@@ -437,7 +444,7 @@ export class SourceMeasure extends BaseIdClass {
      */
     public beginsWithWordRepetition(): boolean {
         for (let idx: number = 0, len: number = this.FirstRepetitionInstructions.length; idx < len; ++idx) {
-            let instruction: RepetitionInstruction = this.FirstRepetitionInstructions[idx];
+            const instruction: RepetitionInstruction = this.FirstRepetitionInstructions[idx];
             if (instruction.parentRepetition !== undefined &&
                 instruction === instruction.parentRepetition.startMarker && instruction.parentRepetition.FromWords) {
                 return true;
@@ -452,8 +459,8 @@ export class SourceMeasure extends BaseIdClass {
      */
     public endsWithWordRepetition(): boolean {
         for (let idx: number = 0, len: number = this.LastRepetitionInstructions.length; idx < len; ++idx) {
-            let instruction: RepetitionInstruction = this.LastRepetitionInstructions[idx];
-            let rep: Repetition = instruction.parentRepetition;
+            const instruction: RepetitionInstruction = this.LastRepetitionInstructions[idx];
+            const rep: Repetition = instruction.parentRepetition;
             if (rep === undefined) {
                 continue;
             }
@@ -461,7 +468,7 @@ export class SourceMeasure extends BaseIdClass {
                 continue;
             }
             for (let idx2: number = 0, len2: number = rep.BackwardJumpInstructions.length; idx2 < len2; ++idx2) {
-                let backJumpInstruction: RepetitionInstruction = rep.BackwardJumpInstructions[idx2];
+                const backJumpInstruction: RepetitionInstruction = rep.BackwardJumpInstructions[idx2];
                 if (instruction === backJumpInstruction) {
                     return true;
                 }
@@ -475,9 +482,9 @@ export class SourceMeasure extends BaseIdClass {
 
     public getKeyInstruction(staffIndex: number): KeyInstruction {
         if (this.FirstInstructionsStaffEntries[staffIndex] !== undefined) {
-            let sourceStaffEntry: SourceStaffEntry = this.FirstInstructionsStaffEntries[staffIndex];
+            const sourceStaffEntry: SourceStaffEntry = this.FirstInstructionsStaffEntries[staffIndex];
             for (let idx: number = 0, len: number = sourceStaffEntry.Instructions.length; idx < len; ++idx) {
-                let abstractNotationInstruction: AbstractNotationInstruction = sourceStaffEntry.Instructions[idx];
+                const abstractNotationInstruction: AbstractNotationInstruction = sourceStaffEntry.Instructions[idx];
                 if (abstractNotationInstruction instanceof KeyInstruction) {
                     return <KeyInstruction>abstractNotationInstruction;
                 }

+ 9 - 9
src/MusicalScore/VoiceData/SourceStaffEntry.ts

@@ -194,9 +194,9 @@ export class SourceStaffEntry {
     public calculateMinNoteLength(): Fraction {
         let duration: Fraction = new Fraction(Number.MAX_VALUE, 1);
         for (let idx: number = 0, len: number = this.VoiceEntries.length; idx < len; ++idx) {
-            let voiceEntry: VoiceEntry = this.VoiceEntries[idx];
+            const voiceEntry: VoiceEntry = this.VoiceEntries[idx];
             for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
-                let note: Note = voiceEntry.Notes[idx2];
+                const note: Note = voiceEntry.Notes[idx2];
                 if (note.NoteTie !== undefined) {
                     if (note.calculateNoteLengthWithoutTie().lt(duration)) {
                         duration = note.calculateNoteLengthWithoutTie();
@@ -212,14 +212,14 @@ export class SourceStaffEntry {
     public calculateMaxNoteLength(): Fraction {
         let duration: Fraction = new Fraction(0, 1);
         for (let idx: number = 0, len: number = this.VoiceEntries.length; idx < len; ++idx) {
-            let voiceEntry: VoiceEntry = this.VoiceEntries[idx];
+            const voiceEntry: VoiceEntry = this.VoiceEntries[idx];
             for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
-                let note: Note = voiceEntry.Notes[idx2];
+                const note: Note = voiceEntry.Notes[idx2];
                 if (note.NoteTie !== undefined) {
                     if (duration < note.calculateNoteLengthWithoutTie()) {
                         duration = note.calculateNoteLengthWithoutTie();
                         for (let idx3: number = 0, len3: number = note.NoteTie.Fractions.length; idx3 < len3; ++idx3) {
-                            let fraction: Fraction = note.NoteTie.Fractions[idx3];
+                            const fraction: Fraction = note.NoteTie.Fractions[idx3];
                             duration.Add(fraction);
                         }
                     }
@@ -233,7 +233,7 @@ export class SourceStaffEntry {
 
     public hasNotes(): boolean {
         for (let idx: number = 0, len: number = this.VoiceEntries.length; idx < len; ++idx) {
-            let voiceEntry: VoiceEntry = this.VoiceEntries[idx];
+            const voiceEntry: VoiceEntry = this.VoiceEntries[idx];
             if (voiceEntry.Notes.length > 0) {
                 return true;
             }
@@ -243,7 +243,7 @@ export class SourceStaffEntry {
 
     public hasTie(): boolean {
         for (let idx: number = 0, len: number = this.VoiceEntries.length; idx < len; ++idx) {
-            let voiceEntry: VoiceEntry = this.VoiceEntries[idx];
+            const voiceEntry: VoiceEntry = this.VoiceEntries[idx];
             if (voiceEntry.hasTie()) {
                 return true;
             }
@@ -253,9 +253,9 @@ export class SourceStaffEntry {
 
     public findLinkedNotes(linkedNotes: Note[]): void {
         for (let idx: number = 0, len: number = this.voiceEntries.length; idx < len; ++idx) {
-            let voiceEntry: VoiceEntry = this.voiceEntries[idx];
+            const voiceEntry: VoiceEntry = this.voiceEntries[idx];
             for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
-                let note: Note = voiceEntry.Notes[idx2];
+                const note: Note = voiceEntry.Notes[idx2];
                 if (note.ParentStaffEntry === this) {
                     linkedNotes.push(note);
                 }

+ 1 - 1
src/MusicalScore/VoiceData/Tie.ts

@@ -68,7 +68,7 @@ export class Tie extends BaseIdClass {
         this.noteHasBeenCreated = new Array(this.fractions.length);
     }
     public allGraphicalNotesHaveBeenCreated(): boolean {
-        for (let b of this.noteHasBeenCreated) {
+        for (const b of this.noteHasBeenCreated) {
             if (!b) {
                 return false;
             }

+ 1 - 1
src/MusicalScore/VoiceData/Tuplet.ts

@@ -39,7 +39,7 @@ export class Tuplet {
     }
 
     /**
-     * Return the index of the first List (notes[0], notes[1],...).
+     * Returns the index of the given Note in the Tuplet List (notes[0], notes[1],...).
      * @param note
      * @returns {number}
      */

+ 0 - 2
src/MusicalScore/VoiceData/VerticalSourceStaffEntryContainer.ts

@@ -9,13 +9,11 @@ export class VerticalSourceStaffEntryContainer {
 
     constructor(parentMeasure: SourceMeasure, timestamp: Fraction, size: number) {
         this.timestamp = timestamp;
-        this.size = size;
         this.staffEntries = new Array(size);
         this.parentMeasure = parentMeasure;
     }
 
     private timestamp: Fraction;
-    private size: number;
     private staffEntries: SourceStaffEntry[] = [];
     private comments: Comment[] = [];
     private parentMeasure: SourceMeasure;

+ 58 - 43
src/MusicalScore/VoiceData/VoiceEntry.ts

@@ -39,6 +39,7 @@ export class VoiceEntry {
     private lyricsEntries: Dictionary<number, LyricsEntry> = new Dictionary<number, LyricsEntry>();
     private arpeggiosNotesIndices: number[] = [];
     private ornamentContainer: OrnamentContainer;
+    private stemDirection: StemDirectionType = StemDirectionType.Undefined;
 
     public get ParentSourceStaffEntry(): SourceStaffEntry {
         return this.parentSourceStaffEntry;
@@ -77,6 +78,13 @@ export class VoiceEntry {
         this.ornamentContainer = value;
     }
 
+    public get StemDirection(): StemDirectionType {
+        return this.stemDirection;
+    }
+    public set StemDirection(value: StemDirectionType) {
+        this.stemDirection = value;
+    }
+
     public static isSupportedArticulation(articulation: ArticulationEnum): boolean {
         switch (articulation) {
             case ArticulationEnum.accent:
@@ -102,28 +110,28 @@ export class VoiceEntry {
     }
     public hasTie(): boolean {
         for (let idx: number = 0, len: number = this.Notes.length; idx < len; ++idx) {
-            let note: Note = this.Notes[idx];
+            const note: Note = this.Notes[idx];
             if (note.NoteTie !== undefined) { return true; }
         }
         return false;
     }
     public hasSlur(): boolean {
         for (let idx: number = 0, len: number = this.Notes.length; idx < len; ++idx) {
-            let note: Note = this.Notes[idx];
+            const note: Note = this.Notes[idx];
             if (note.NoteSlurs.length > 0) { return true; }
         }
         return false;
     }
     public isStaccato(): boolean {
         for (let idx: number = 0, len: number = this.Articulations.length; idx < len; ++idx) {
-            let articulation: ArticulationEnum = this.Articulations[idx];
+            const articulation: ArticulationEnum = this.Articulations[idx];
             if (articulation === ArticulationEnum.staccato) { return true; }
         }
         return false;
     }
     public isAccent(): boolean {
         for (let idx: number = 0, len: number = this.Articulations.length; idx < len; ++idx) {
-            let articulation: ArticulationEnum = this.Articulations[idx];
+            const articulation: ArticulationEnum = this.Articulations[idx];
             if (articulation === ArticulationEnum.accent || articulation === ArticulationEnum.strongaccent) {
                 return true;
             }
@@ -146,20 +154,20 @@ export class VoiceEntry {
         if (voiceEntryWithOrnament === undefined) {
             voiceEntryWithOrnament = this;
         }
-        let voiceEntries: VoiceEntry[] = [];
+        const voiceEntries: VoiceEntry[] = [];
         if (voiceEntryWithOrnament.ornamentContainer === undefined) {
             return;
         }
-        let baseNote: Note = this.notes[0];
-        let baselength: Fraction = baseNote.calculateNoteLengthWithoutTie();
-        let baseVoice: Voice = voiceEntryWithOrnament.ParentVoice;
-        let baseTimestamp: Fraction = voiceEntryWithOrnament.Timestamp;
+        const baseNote: Note = this.notes[0];
+        const baselength: Fraction = baseNote.calculateNoteLengthWithoutTie();
+        const baseVoice: Voice = voiceEntryWithOrnament.ParentVoice;
+        const baseTimestamp: Fraction = voiceEntryWithOrnament.Timestamp;
         let currentTimestamp: Fraction = Fraction.createFromFraction(baseTimestamp);
         //let length: Fraction;
         switch (voiceEntryWithOrnament.ornamentContainer.GetOrnament) {
             case OrnamentEnum.Trill: {
-                let length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 8);
-                let higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
+                const length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 8);
+                const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
                 let alteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 if (voiceEntryWithOrnament.OrnamentContainer.AccidentalAbove !== AccidentalEnum.NONE) {
                     alteration = <AccidentalEnum><number>voiceEntryWithOrnament.ornamentContainer.AccidentalAbove;
@@ -175,11 +183,11 @@ export class VoiceEntry {
                 break;
             }
             case OrnamentEnum.Turn: {
-                let length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 4);
-                let lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
-                let lowerAlteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
-                let higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
-                let higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
+                const length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 4);
+                const lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
+                const lowerAlteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
+                const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
+                const higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 this.createAlteratedVoiceEntry(
                     currentTimestamp, length, baseVoice, higherPitch, higherAlteration, voiceEntries
                 );
@@ -194,11 +202,11 @@ export class VoiceEntry {
                 break;
             }
             case OrnamentEnum.InvertedTurn: {
-                let length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 4);
-                let lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
-                let lowerAlteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
-                let higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
-                let higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
+                const length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 4);
+                const lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
+                const lowerAlteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
+                const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
+                const higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 this.createAlteratedVoiceEntry(
                     currentTimestamp, length, baseVoice, lowerPitch, lowerAlteration, voiceEntries
                 );
@@ -213,11 +221,11 @@ export class VoiceEntry {
                 break;
             }
             case OrnamentEnum.DelayedTurn: {
-                let length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 2);
-                let lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
-                let lowerAlteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
-                let higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
-                let higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
+                const length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 2);
+                const lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
+                const lowerAlteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
+                const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
+                const higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 length.Denominator = baselength.Denominator * 8;
@@ -231,11 +239,11 @@ export class VoiceEntry {
                 break;
             }
             case OrnamentEnum.DelayedInvertedTurn: {
-                let length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 2);
-                let lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
-                let lowerAlteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
-                let higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
-                let higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
+                const length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 2);
+                const lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
+                const lowerAlteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
+                const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
+                const higherAlteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp = Fraction.plus(baseTimestamp, length);
                 length.Denominator = baselength.Denominator * 8;
@@ -249,9 +257,9 @@ export class VoiceEntry {
                 break;
             }
             case OrnamentEnum.Mordent: {
-                let length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 4);
-                let higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
-                let alteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
+                const length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 4);
+                const higherPitch: Pitch = baseNote.Pitch.getTransposedPitch(1);
+                const alteration: AccidentalEnum = activeKey.getAlterationForPitch(higherPitch);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp.Add(length);
                 this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, higherPitch, alteration, voiceEntries);
@@ -261,9 +269,9 @@ export class VoiceEntry {
                 break;
             }
             case OrnamentEnum.InvertedMordent: {
-                let length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 4);
-                let lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
-                let alteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
+                const length: Fraction = new Fraction(baselength.Numerator, baselength.Denominator * 4);
+                const lowerPitch: Pitch = baseNote.Pitch.getTransposedPitch(-1);
+                const alteration: AccidentalEnum = activeKey.getAlterationForPitch(lowerPitch);
                 this.createBaseVoiceEntry(currentTimestamp, length, baseVoice, baseNote, voiceEntries);
                 currentTimestamp.Add(length);
                 this.createAlteratedVoiceEntry(currentTimestamp, length, baseVoice, lowerPitch, alteration, voiceEntries);
@@ -280,9 +288,9 @@ export class VoiceEntry {
     private createBaseVoiceEntry(
         currentTimestamp: Fraction, length: Fraction, baseVoice: Voice, baseNote: Note, voiceEntries: VoiceEntry[]
     ): void {
-        let voiceEntry: VoiceEntry = new VoiceEntry(currentTimestamp, baseVoice, baseNote.ParentStaffEntry);
-        let pitch: Pitch = new Pitch(baseNote.Pitch.FundamentalNote, baseNote.Pitch.Octave, baseNote.Pitch.Accidental);
-        let note: Note = new Note(voiceEntry, undefined, length, pitch);
+        const voiceEntry: VoiceEntry = new VoiceEntry(currentTimestamp, baseVoice, baseNote.ParentStaffEntry);
+        const pitch: Pitch = new Pitch(baseNote.Pitch.FundamentalNote, baseNote.Pitch.Octave, baseNote.Pitch.Accidental);
+        const note: Note = new Note(voiceEntry, undefined, length, pitch);
         voiceEntry.Notes.push(note);
         voiceEntries.push(voiceEntry);
     }
@@ -290,9 +298,9 @@ export class VoiceEntry {
         currentTimestamp: Fraction, length: Fraction, baseVoice: Voice, higherPitch: Pitch,
         alteration: AccidentalEnum, voiceEntries: VoiceEntry[]
     ): void {
-        let voiceEntry: VoiceEntry = new VoiceEntry(currentTimestamp, baseVoice, undefined);
-        let pitch: Pitch = new Pitch(higherPitch.FundamentalNote, higherPitch.Octave, alteration);
-        let note: Note = new Note(voiceEntry, undefined, length, pitch);
+        const voiceEntry: VoiceEntry = new VoiceEntry(currentTimestamp, baseVoice, undefined);
+        const pitch: Pitch = new Pitch(higherPitch.FundamentalNote, higherPitch.Octave, alteration);
+        const note: Note = new Note(voiceEntry, undefined, length, pitch);
         voiceEntry.Notes.push(note);
         voiceEntries.push(voiceEntry);
     }
@@ -325,3 +333,10 @@ export enum ArticulationEnum {
     detachedlegato,
     otherarticulation
 }
+
+export enum StemDirectionType {
+    Undefined = -1,
+    Up = 0,
+    Down = 1,
+    None = 2
+}

+ 0 - 159
src/OSMD/Cursor.ts

@@ -1,159 +0,0 @@
-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";
-
-/**
- * A cursor which can iterate through the music sheet.
- */
-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 manager: MusicPartManager;
-    private iterator: MusicPartManagerIterator;
-    private graphic: GraphicalMusicSheet;
-    private hidden: boolean = true;
-    private cursorElement: HTMLImageElement;
-
-    public init(manager: MusicPartManager, graphic: GraphicalMusicSheet): void {
-        this.manager = manager;
-        this.reset();
-        this.graphic = graphic;
-        this.hidden = true;
-        this.hide();
-    }
-
-    /**
-     * Make the cursor visible
-     */
-    public show(): void {
-        this.hidden = false;
-        this.update();
-        // Forcing the sheet to re-render is not necessary anymore,
-        // since the cursor is an HTML element.
-        // this.osmd.render();
-    }
-
-    public update(): void {
-        // Warning! This 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.measureListIndex;
-            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, but it could come useful later):
-            // it highlights the notes under the cursor.
-            //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 cursor
-        // // 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 next entry
-     */
-    public reset(): void {
-        this.iterator = this.manager.getIterator();
-        this.iterator.moveToNext();
-        this.update();
-    }
-
-    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");
-    }
-}

+ 0 - 0
src/OSMD/AJAX.ts → src/OpenSheetMusicDisplay/AJAX.ts


+ 159 - 0
src/OpenSheetMusicDisplay/Cursor.ts

@@ -0,0 +1,159 @@
+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 {OpenSheetMusicDisplay} from "./OpenSheetMusicDisplay";
+import {GraphicalMusicSheet} from "../MusicalScore/Graphical/GraphicalMusicSheet";
+
+/**
+ * A cursor which can iterate through the music sheet.
+ */
+export class Cursor {
+  constructor(container: HTMLElement, openSheetMusicDisplay: OpenSheetMusicDisplay) {
+    this.container = container;
+    this.openSheetMusicDisplay = openSheetMusicDisplay;
+    const curs: HTMLElement = document.createElement("img");
+    curs.style.position = "absolute";
+    curs.style.zIndex = "-1";
+    this.cursorElement = <HTMLImageElement>curs;
+    this.container.appendChild(curs);
+  }
+
+  private container: HTMLElement;
+  private openSheetMusicDisplay: OpenSheetMusicDisplay;
+  private manager: MusicPartManager;
+  private iterator: MusicPartManagerIterator;
+  private graphic: GraphicalMusicSheet;
+  private hidden: boolean = true;
+  private cursorElement: HTMLImageElement;
+
+  public init(manager: MusicPartManager, graphic: GraphicalMusicSheet): void {
+    this.manager = manager;
+    this.reset();
+    this.graphic = graphic;
+    this.hidden = true;
+    this.hide();
+  }
+
+  /**
+   * Make the cursor visible
+   */
+  public show(): void {
+    this.hidden = false;
+    this.update();
+    // Forcing the sheet to re-render is not necessary anymore,
+    // since the cursor is an HTML element.
+    // this.openSheetMusicDisplay.render();
+  }
+
+  public update(): void {
+    // Warning! This should NEVER call this.openSheetMusicDisplay.render()
+    if (this.hidden) {
+      return;
+    }
+    this.graphic.Cursors.length = 0;
+    const iterator: MusicPartManagerIterator = this.iterator;
+    if (iterator.EndReached || iterator.CurrentVoiceEntries === undefined || iterator.CurrentVoiceEntries.length === 0) {
+      return;
+    }
+    let x: number = 0, y: number = 0, height: number = 0;
+
+    const voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[0];
+    const measureIndex: number = voiceEntry.ParentSourceStaffEntry.VerticalContainerParent.ParentMeasure.measureListIndex;
+    const staffIndex: number = voiceEntry.ParentSourceStaffEntry.ParentStaff.idInMusicSheet;
+    const gse: VexFlowStaffEntry =
+      <VexFlowStaffEntry>this.graphic.findGraphicalStaffEntryFromMeasureList(staffIndex, measureIndex, voiceEntry.ParentSourceStaffEntry);
+
+    x = gse.PositionAndShape.AbsolutePosition.x;
+    const musicSystem: MusicSystem = gse.parentMeasure.parentMusicSystem;
+    y = musicSystem.PositionAndShape.AbsolutePosition.y + musicSystem.StaffLines[0].PositionAndShape.RelativePosition.y;
+    const 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, but it could come useful later):
+    // it highlights the notes under the cursor.
+    //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:
+    const cursorElement: HTMLImageElement = this.cursorElement;
+    cursorElement.style.top = (y * 10.0 * this.openSheetMusicDisplay.zoom) + "px";
+    cursorElement.style.left = ((x - 1.5) * 10.0 * this.openSheetMusicDisplay.zoom) + "px";
+    cursorElement.height = (height * 10.0 * this.openSheetMusicDisplay.zoom);
+    const newWidth: number = 3 * 10.0 * this.openSheetMusicDisplay.zoom;
+    if (newWidth !== cursorElement.width) {
+      cursorElement.width = newWidth;
+      this.updateStyle(newWidth);
+    }
+
+    // Show cursor
+    // // 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.openSheetMusicDisplay.render();
+    //}
+    this.hidden = true;
+  }
+
+  /**
+   * Go to next entry
+   */
+  public next(): void {
+    this.iterator.moveToNext();
+    if (!this.hidden) {
+      this.show();
+    }
+  }
+
+  /**
+   * reset cursor to start
+   */
+  public reset(): void {
+    this.iterator = this.manager.getIterator();
+    this.iterator.moveToNext();
+    this.update();
+  }
+
+  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
+    const c: HTMLCanvasElement = document.createElement("canvas");
+    c.width = this.cursorElement.width;
+    c.height = 1;
+    const ctx: CanvasRenderingContext2D = c.getContext("2d");
+    ctx.globalAlpha = 0.5;
+    // Generate the gradient
+    const 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");
+  }
+}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio