diff --git a/Gemfile b/Gemfile
index 1e2d8910c870b02d62d7134ad14af0276cfbcd88..a2389ab68edde805de231d9050d9cc7dda2f6c10 100644
--- a/Gemfile
+++ b/Gemfile
@@ -15,6 +15,7 @@ gem "jekyll", "< 4.0"
 # If you have any plugins, put them here!
 group :jekyll_plugins do
   gem 'execjs'
+  gem 'jekyll-seo-tag'
   gem 'jekyll-redirect-from'
   gem 'jekyll-target-blank'
   gem "jekyll-feed"
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000000000000000000000000000000000000..ecd5d235c394aebfe8791651f65c67b0f42a4484
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,171 @@
+PATH
+  remote: .
+  specs:
+    jekyll-theme-cayman (0.1.1)
+      jekyll (~> 3.1)
+      jekyll-seo-tag (~> 2.0)
+
+GEM
+  remote: https://rubygems.org/
+  specs:
+    addressable (2.7.0)
+      public_suffix (>= 2.0.2, < 5.0)
+    ast (2.4.0)
+    colorator (1.1.0)
+    concurrent-ruby (1.1.6)
+    em-websocket (0.5.1)
+      eventmachine (>= 0.12.9)
+      http_parser.rb (~> 0.6.0)
+    ethon (0.12.0)
+      ffi (>= 1.3.0)
+    eventmachine (1.2.7)
+    execjs (2.7.0)
+    extras (0.3.0)
+      forwardable-extended (~> 2.5)
+    fastimage (2.1.7)
+    ffi (1.12.2)
+    font-awesome-sass (5.12.0)
+      sassc (>= 1.11)
+    forwardable-extended (2.6.0)
+    hpricot (0.8.6)
+    html-proofer (3.15.1)
+      addressable (~> 2.3)
+      mercenary (~> 0.3)
+      nokogumbo (~> 2.0)
+      parallel (~> 1.3)
+      rainbow (~> 3.0)
+      typhoeus (~> 1.3)
+      yell (~> 2.0)
+    http_parser.rb (0.6.0)
+    i18n (0.9.5)
+      concurrent-ruby (~> 1.0)
+    jaro_winkler (1.5.4)
+    jekyll (3.7.4)
+      addressable (~> 2.4)
+      colorator (~> 1.0)
+      em-websocket (~> 0.5)
+      i18n (~> 0.7)
+      jekyll-sass-converter (~> 1.0)
+      jekyll-watch (~> 2.0)
+      kramdown (~> 1.14)
+      liquid (~> 4.0)
+      mercenary (~> 0.3.3)
+      pathutil (~> 0.9)
+      rouge (>= 1.7, < 4)
+      safe_yaml (~> 1.0)
+    jekyll-assets (2.4.0)
+      concurrent-ruby (~> 1.0)
+      extras (~> 0.2)
+      fastimage (~> 2.0, >= 1.8)
+      jekyll (~> 3.1, >= 3.0)
+      pathutil (>= 0.8)
+      rack (~> 1.6)
+      sprockets (~> 3.3, < 3.8)
+    jekyll-contentblocks (1.2.0)
+      jekyll
+    jekyll-feed (0.13.0)
+      jekyll (>= 3.7, < 5.0)
+    jekyll-font-awesome-sass (0.1.1)
+      font-awesome-sass (>= 4)
+      jekyll (>= 2.5, < 4.0)
+    jekyll-mermaid (1.0.0)
+    jekyll-paginate (1.1.0)
+    jekyll-redirect-from (0.16.0)
+      jekyll (>= 3.3, < 5.0)
+    jekyll-sass-converter (1.5.2)
+      sass (~> 3.4)
+    jekyll-seo-tag (2.6.1)
+      jekyll (>= 3.3, < 5.0)
+    jekyll-target-blank (2.0.0)
+      jekyll (>= 3.0, < 5.0)
+      nokogiri (~> 1.10)
+    jekyll-watch (2.2.1)
+      listen (~> 3.0)
+    json (2.3.0)
+    kramdown (1.17.0)
+    libv8 (3.16.14.19)
+    liquid (4.0.3)
+    listen (3.2.1)
+      rb-fsevent (~> 0.10, >= 0.10.3)
+      rb-inotify (~> 0.9, >= 0.9.10)
+    mercenary (0.3.6)
+    mini_portile2 (2.4.0)
+    nokogiri (1.10.8)
+      mini_portile2 (~> 2.4.0)
+    nokogumbo (2.0.2)
+      nokogiri (~> 1.8, >= 1.8.4)
+    parallel (1.19.1)
+    parser (2.7.0.2)
+      ast (~> 2.4.0)
+    pathutil (0.16.2)
+      forwardable-extended (~> 2.6)
+    public_suffix (4.0.3)
+    rack (1.6.10)
+    rainbow (3.0.0)
+    rb-fsevent (0.10.3)
+    rb-inotify (0.10.1)
+      ffi (~> 1.0)
+    ref (2.0.0)
+    remark (0.3.2)
+      hpricot (~> 0.8.2)
+    rexml (3.2.4)
+    rouge (3.16.0)
+    rubocop (0.80.0)
+      jaro_winkler (~> 1.5.1)
+      parallel (~> 1.10)
+      parser (>= 2.7.0.1)
+      rainbow (>= 2.2.2, < 4.0)
+      rexml
+      ruby-progressbar (~> 1.7)
+      unicode-display_width (>= 1.4.0, < 1.7)
+    ruby-progressbar (1.10.1)
+    safe_yaml (1.0.5)
+    sass (3.7.4)
+      sass-listen (~> 4.0.0)
+    sass-listen (4.0.0)
+      rb-fsevent (~> 0.9, >= 0.9.4)
+      rb-inotify (~> 0.9, >= 0.9.7)
+    sassc (2.2.1)
+      ffi (~> 1.9)
+    sprockets (3.7.2)
+      concurrent-ruby (~> 1.0)
+      rack (> 1, < 3)
+    therubyracer (0.12.3)
+      libv8 (~> 3.16.14.15)
+      ref
+    typhoeus (1.3.1)
+      ethon (>= 0.9.0)
+    uglifier (4.2.0)
+      execjs (>= 0.3.0, < 3)
+    unicode-display_width (1.6.1)
+    w3c_validators (1.3.4)
+      json (>= 1.8)
+      nokogiri (~> 1.6)
+    yell (2.2.2)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  execjs
+  html-proofer (~> 3.0)
+  jekyll (= 3.7.4)
+  jekyll-assets (= 2.4.0)
+  jekyll-contentblocks
+  jekyll-feed
+  jekyll-font-awesome-sass
+  jekyll-mermaid
+  jekyll-paginate
+  jekyll-redirect-from
+  jekyll-seo-tag
+  jekyll-target-blank
+  jekyll-theme-cayman!
+  remark
+  rubocop (~> 0.50)
+  therubyracer
+  tzinfo-data
+  uglifier
+  w3c_validators (~> 1.3)
+
+BUNDLED WITH
+   1.17.3
diff --git a/_config.yml b/_config.yml
index 40b8dbbea5bbde9b949cd49841bc0de1ea965a88..34b95420c600dc4eff1603e22b94f82b8c708b8a 100644
--- a/_config.yml
+++ b/_config.yml
@@ -36,9 +36,6 @@ reveal_theme: black.css
 markdown: kramdown
 theme: jekyll-theme-cayman
 
-gems:
-  - jekyll-font-awesome-sass
-
 plugins:
   - jekyll-feed
   - jekyll-seo-tag
diff --git a/_submodules/mermaid/.ackrc b/_submodules/mermaid/.ackrc
new file mode 100644
index 0000000000000000000000000000000000000000..5390d7b92156fb09773917c6b453ec3cf18d5948
--- /dev/null
+++ b/_submodules/mermaid/.ackrc
@@ -0,0 +1,4 @@
+--ignore-dir=dist
+--ignore-file=match:/^yarn\.lock$/
+--ignore-file=match:/^yarn-error\.log$/
+--ignore-dir=coverage
diff --git a/_submodules/mermaid/.babelrc b/_submodules/mermaid/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..b207aefdded3fae007f56bcc2f28d7a6c958fb26
--- /dev/null
+++ b/_submodules/mermaid/.babelrc
@@ -0,0 +1,5 @@
+{
+  "presets": [
+    "env"
+  ]
+}
diff --git a/_submodules/mermaid/.editorconfig b/_submodules/mermaid/.editorconfig
new file mode 100644
index 0000000000000000000000000000000000000000..ab70fef5e64a0b2cf3dcb28e5781c98fa7c36679
--- /dev/null
+++ b/_submodules/mermaid/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+indent_size = 4
diff --git a/_submodules/mermaid/.gitignore b/_submodules/mermaid/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f2c5e769955120f6793cbe1b3322a802dda60f47
--- /dev/null
+++ b/_submodules/mermaid/.gitignore
@@ -0,0 +1,9 @@
+.DS_Store
+
+node_modules/
+coverage/
+
+dist/*.js
+dist/*.map
+
+yarn-error.log
diff --git a/_submodules/mermaid/.travis.yml b/_submodules/mermaid/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d28528ef6b5ed96a03761e97e2fec02f1d759b46
--- /dev/null
+++ b/_submodules/mermaid/.travis.yml
@@ -0,0 +1,8 @@
+dist: trusty
+language: node_js
+node_js:
+  - "8"
+script:
+  - yarn test --coverage
+after_success:
+  - cat ./coverage/lcov.info | ./node_modules/.bin/coveralls
diff --git a/_submodules/mermaid/CHANGELOG.md b/_submodules/mermaid/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..3bbf313664ce7ec9f3820c9ba2c97c8a4a959e02
--- /dev/null
+++ b/_submodules/mermaid/CHANGELOG.md
@@ -0,0 +1,386 @@
+# Change Log
+
+## [Unreleased](https://github.com/knsv/mermaid/tree/HEAD)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.4.0...HEAD)
+
+**Implemented enhancements:**
+
+- Add a css file, mermaid.css, with default styling [\#122](https://github.com/knsv/mermaid/issues/122)
+
+**Closed issues:**
+
+- Some examples not displayed on Firefox 36.0.1 [\#138](https://github.com/knsv/mermaid/issues/138)
+
+- inoperable in an AMD/requirejs environment: IPython Notebook [\#127](https://github.com/knsv/mermaid/issues/127)
+
+- Add capability for gantt diagrams [\#118](https://github.com/knsv/mermaid/issues/118)
+
+- lower case v causes error in the parser [\#108](https://github.com/knsv/mermaid/issues/108)
+
+- Label's css conflict with boostrap's .label [\#67](https://github.com/knsv/mermaid/issues/67)
+
+**Merged pull requests:**
+
+- Adding init argument to the global API [\#137](https://github.com/knsv/mermaid/pull/137) ([bollwyvl](https://github.com/bollwyvl))
+
+- Add description of manual calling of init [\#136](https://github.com/knsv/mermaid/pull/136) ([bollwyvl](https://github.com/bollwyvl))
+
+- Allow other forms of node selection for init\(\) [\#135](https://github.com/knsv/mermaid/pull/135) ([bollwyvl](https://github.com/bollwyvl))
+
+- Use a library-level variable for assigning ids [\#134](https://github.com/knsv/mermaid/pull/134) ([bollwyvl](https://github.com/bollwyvl))
+
+## [0.4.0](https://github.com/knsv/mermaid/tree/0.4.0) (2015-03-01)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.5...0.4.0)
+
+**Implemented enhancements:**
+
+- Assymetric shapes not documented [\#82](https://github.com/knsv/mermaid/issues/82)
+
+- Improve arrows [\#3](https://github.com/knsv/mermaid/issues/3)
+
+**Fixed bugs:**
+
+- NoModificationAllowedError [\#23](https://github.com/knsv/mermaid/issues/23)
+
+**Closed issues:**
+
+- subgraph background is black in rendered flowchart PNG via CLI [\#121](https://github.com/knsv/mermaid/issues/121)
+
+- Integrate editor at https://github.com/naseer/mermaid-webapp [\#110](https://github.com/knsv/mermaid/issues/110)
+
+- Internet Explorer Support [\#99](https://github.com/knsv/mermaid/issues/99)
+
+## [0.3.5](https://github.com/knsv/mermaid/tree/0.3.5) (2015-02-15)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.4...0.3.5)
+
+## [0.3.4](https://github.com/knsv/mermaid/tree/0.3.4) (2015-02-15)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.3...0.3.4)
+
+**Implemented enhancements:**
+
+- Apply styling from css when using the CLI utility [\#85](https://github.com/knsv/mermaid/issues/85)
+
+- Generated SVG works poorly outside web browsers [\#58](https://github.com/knsv/mermaid/issues/58)
+
+- Generating SVG text blob for use in Node [\#2](https://github.com/knsv/mermaid/issues/2)
+
+**Closed issues:**
+
+- Subgraph syntax bug? [\#120](https://github.com/knsv/mermaid/issues/120)
+
+- Live editor [\#115](https://github.com/knsv/mermaid/issues/115)
+
+- Error in "Basic Syntax" wiki page [\#113](https://github.com/knsv/mermaid/issues/113)
+
+- semicolons, anyone? [\#111](https://github.com/knsv/mermaid/issues/111)
+
+- undefined `sequenceConfig` fails [\#109](https://github.com/knsv/mermaid/issues/109)
+
+- Sequence Diagrams: Show Actors below as well [\#106](https://github.com/knsv/mermaid/issues/106)
+
+- Allow overriding sequence diagram configuration \(SVG properties\) [\#103](https://github.com/knsv/mermaid/issues/103)
+
+- Error when rendering A-- This is the text -- B [\#102](https://github.com/knsv/mermaid/issues/102)
+
+- Clipping in documentation [\#97](https://github.com/knsv/mermaid/issues/97)
+
+- isolate class styling to the svg container [\#92](https://github.com/knsv/mermaid/issues/92)
+
+- Make the new graph declaration more visual [\#40](https://github.com/knsv/mermaid/issues/40)
+
+**Merged pull requests:**
+
+- Add live editor [\#119](https://github.com/knsv/mermaid/pull/119) ([naseer](https://github.com/naseer))
+
+- Adds CSS option to the CLI [\#116](https://github.com/knsv/mermaid/pull/116) ([fardog](https://github.com/fardog))
+
+- Update flowchart.md in response Issue \#113 [\#114](https://github.com/knsv/mermaid/pull/114) ([vijay40](https://github.com/vijay40))
+
+- Ignore all files except the license and dist/ folder when installing with Bower. [\#112](https://github.com/knsv/mermaid/pull/112) ([jasonbellamy](https://github.com/jasonbellamy))
+
+## [0.3.3](https://github.com/knsv/mermaid/tree/0.3.3) (2015-01-25)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.2...0.3.3)
+
+**Implemented enhancements:**
+
+- Support for dotted links [\#26](https://github.com/knsv/mermaid/issues/26)
+
+**Closed issues:**
+
+- Missing arrows in sequence diagram [\#98](https://github.com/knsv/mermaid/issues/98)
+
+- Error with \>9 linkStyles [\#95](https://github.com/knsv/mermaid/issues/95)
+
+**Merged pull requests:**
+
+- Require d3 directly to better support Node usage [\#107](https://github.com/knsv/mermaid/pull/107) ([markdalgleish](https://github.com/markdalgleish))
+
+- update doc with -c option [\#105](https://github.com/knsv/mermaid/pull/105) ([jjmr](https://github.com/jjmr))
+
+- Add new parameter to the console client to override the svg configuration in sequence diagrams [\#104](https://github.com/knsv/mermaid/pull/104) ([jjmr](https://github.com/jjmr))
+
+- Text based labels, new shape [\#101](https://github.com/knsv/mermaid/pull/101) ([bjowes](https://github.com/bjowes))
+
+- fix html tags in example usage [\#100](https://github.com/knsv/mermaid/pull/100) ([deiwin](https://github.com/deiwin))
+
+## [0.3.2](https://github.com/knsv/mermaid/tree/0.3.2) (2015-01-11)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.1...0.3.2)
+
+**Implemented enhancements:**
+
+- Make link text look like it is on the line [\#53](https://github.com/knsv/mermaid/issues/53)
+
+**Closed issues:**
+
+- disable auto render [\#91](https://github.com/knsv/mermaid/issues/91)
+
+- Tidy breaks mermaid \(linebreaks in <div\>\) [\#87](https://github.com/knsv/mermaid/issues/87)
+
+- Bug: <br\> being rendered as text in node [\#73](https://github.com/knsv/mermaid/issues/73)
+
+- Graph edges appear to render outside of the canvas [\#70](https://github.com/knsv/mermaid/issues/70)
+
+**Merged pull requests:**
+
+- Merge pull request \#1 from knsv/master [\#96](https://github.com/knsv/mermaid/pull/96) ([gkchic](https://github.com/gkchic))
+
+- Removed duplicated section in flowchart docs [\#94](https://github.com/knsv/mermaid/pull/94) ([kaime](https://github.com/kaime))
+
+- Grammar changes to sequence page [\#93](https://github.com/knsv/mermaid/pull/93) ([gkchic](https://github.com/gkchic))
+
+- Grammar changes to development page [\#90](https://github.com/knsv/mermaid/pull/90) ([gkchic](https://github.com/gkchic))
+
+- Github buttons [\#89](https://github.com/knsv/mermaid/pull/89) ([gkchic](https://github.com/gkchic))
+
+- Template change [\#88](https://github.com/knsv/mermaid/pull/88) ([gkchic](https://github.com/gkchic))
+
+- New content template [\#86](https://github.com/knsv/mermaid/pull/86) ([gkchic](https://github.com/gkchic))
+
+## [0.3.1](https://github.com/knsv/mermaid/tree/0.3.1) (2015-01-05)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.3.0...0.3.1)
+
+**Implemented enhancements:**
+
+- Support for sequence diagrams [\#16](https://github.com/knsv/mermaid/issues/16)
+
+- Client utility for mermaid [\#6](https://github.com/knsv/mermaid/issues/6)
+
+**Closed issues:**
+
+- Non ASCII chars in labels [\#84](https://github.com/knsv/mermaid/issues/84)
+
+- 'undefined' titles of Quicklinks on the usage page [\#80](https://github.com/knsv/mermaid/issues/80)
+
+- \[cli\] Enhancement proposal: not fail --version / --help if phantomjs isn't installed [\#71](https://github.com/knsv/mermaid/issues/71)
+
+**Merged pull requests:**
+
+- Formatting of the CONTRIBUTING file [\#83](https://github.com/knsv/mermaid/pull/83) ([Grahack](https://github.com/Grahack))
+
+- Flowchart doc: Text in the circle now in a circle [\#81](https://github.com/knsv/mermaid/pull/81) ([Grahack](https://github.com/Grahack))
+
+- Fix for issue \#73 [\#79](https://github.com/knsv/mermaid/pull/79) ([it0a](https://github.com/it0a))
+
+- Ink template [\#78](https://github.com/knsv/mermaid/pull/78) ([gkchic](https://github.com/gkchic))
+
+- Index template file [\#77](https://github.com/knsv/mermaid/pull/77) ([gkchic](https://github.com/gkchic))
+
+- Index template file [\#76](https://github.com/knsv/mermaid/pull/76) ([gkchic](https://github.com/gkchic))
+
+- Show help and version even if phantom isn't present. Fixes \#71 [\#75](https://github.com/knsv/mermaid/pull/75) ([fardog](https://github.com/fardog))
+
+- Add apostrophe & 'and' [\#72](https://github.com/knsv/mermaid/pull/72) ([sudodoki](https://github.com/sudodoki))
+
+## [0.3.0](https://github.com/knsv/mermaid/tree/0.3.0) (2014-12-22)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.16...0.3.0)
+
+**Implemented enhancements:**
+
+- How do I do comments? [\#47](https://github.com/knsv/mermaid/issues/47)
+
+- Improve readability with new line as terminator and whitespace [\#38](https://github.com/knsv/mermaid/issues/38)
+
+**Fixed bugs:**
+
+- This characters failed the lexical parsing [\#46](https://github.com/knsv/mermaid/issues/46)
+
+**Closed issues:**
+
+- Trailing whitespace at the end of lines is not ignored [\#55](https://github.com/knsv/mermaid/issues/55)
+
+- Use classes instead of inline style for easy styling [\#24](https://github.com/knsv/mermaid/issues/24)
+
+**Merged pull requests:**
+
+- Adds Command Line Interface for generating PNGs from mermaid description files [\#69](https://github.com/knsv/mermaid/pull/69) ([fardog](https://github.com/fardog))
+
+- Allow special symbols for direction along with acronyms [\#66](https://github.com/knsv/mermaid/pull/66) ([vijay40](https://github.com/vijay40))
+
+## [0.2.16](https://github.com/knsv/mermaid/tree/0.2.16) (2014-12-15)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.15...0.2.16)
+
+**Fixed bugs:**
+
+- Lines routed outside visible area [\#19](https://github.com/knsv/mermaid/issues/19)
+
+**Closed issues:**
+
+- Mermaid not rendering properly on Wordpress pages [\#59](https://github.com/knsv/mermaid/issues/59)
+
+- Improve example page with live demo [\#52](https://github.com/knsv/mermaid/issues/52)
+
+- Does not render upon AngularJS Updates [\#45](https://github.com/knsv/mermaid/issues/45)
+
+- Download link in README.MD doesn't work. [\#42](https://github.com/knsv/mermaid/issues/42)
+
+- linkStyle usage is not obvious [\#41](https://github.com/knsv/mermaid/issues/41)
+
+- Move \*.spec.js in src/ to test/ [\#35](https://github.com/knsv/mermaid/issues/35)
+
+**Merged pull requests:**
+
+- New grammar will allow statements ending without semicolon as disccused in Issue \#38 [\#63](https://github.com/knsv/mermaid/pull/63) ([vijay40](https://github.com/vijay40))
+
+- Class based styling [\#62](https://github.com/knsv/mermaid/pull/62) ([bjowes](https://github.com/bjowes))
+
+- Update from master [\#61](https://github.com/knsv/mermaid/pull/61) ([bjowes](https://github.com/bjowes))
+
+- Fix typos [\#60](https://github.com/knsv/mermaid/pull/60) ([sublimino](https://github.com/sublimino))
+
+- Included .DS\_Store in gitignore [\#57](https://github.com/knsv/mermaid/pull/57) ([alvynmcq](https://github.com/alvynmcq))
+
+- Improves readablity discussed in issue \#38 [\#56](https://github.com/knsv/mermaid/pull/56) ([vijay40](https://github.com/vijay40))
+
+- Added a linting task for gulp [\#43](https://github.com/knsv/mermaid/pull/43) ([serv](https://github.com/serv))
+
+## [0.2.15](https://github.com/knsv/mermaid/tree/0.2.15) (2014-12-05)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.14...0.2.15)
+
+**Fixed bugs:**
+
+- Error with some characters [\#25](https://github.com/knsv/mermaid/issues/25)
+
+- Cap-cased words break parser [\#8](https://github.com/knsv/mermaid/issues/8)
+
+**Closed issues:**
+
+- Question marks don't render properly with /dist/mermaid.full.min.js [\#30](https://github.com/knsv/mermaid/issues/30)
+
+- Provide parse function in browser widthout `require`? [\#21](https://github.com/knsv/mermaid/issues/21)
+
+- Better label text support [\#18](https://github.com/knsv/mermaid/issues/18)
+
+**Merged pull requests:**
+
+- Include bower\_components/ to .gitignore [\#33](https://github.com/knsv/mermaid/pull/33) ([serv](https://github.com/serv))
+
+- Fixed reference to Git repo. [\#32](https://github.com/knsv/mermaid/pull/32) ([guyellis](https://github.com/guyellis))
+
+## [0.2.14](https://github.com/knsv/mermaid/tree/0.2.14) (2014-12-03)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.13...0.2.14)
+
+## [0.2.13](https://github.com/knsv/mermaid/tree/0.2.13) (2014-12-03)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.10...0.2.13)
+
+**Implemented enhancements:**
+
+- Publish to NPM [\#7](https://github.com/knsv/mermaid/issues/7)
+
+**Closed issues:**
+
+- modified init to be applied more than once [\#29](https://github.com/knsv/mermaid/issues/29)
+
+- Wanted to know build process for the project. [\#28](https://github.com/knsv/mermaid/issues/28)
+
+- can not support Chinese description [\#20](https://github.com/knsv/mermaid/issues/20)
+
+- Support unicode chars in labels [\#9](https://github.com/knsv/mermaid/issues/9)
+
+**Merged pull requests:**
+
+- initial setup for editor page to generate graph through textarea input [\#14](https://github.com/knsv/mermaid/pull/14) ([ImanimalXI](https://github.com/ImanimalXI))
+
+## [0.2.10](https://github.com/knsv/mermaid/tree/0.2.10) (2014-12-01)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.9...0.2.10)
+
+## [0.2.9](https://github.com/knsv/mermaid/tree/0.2.9) (2014-12-01)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.8...0.2.9)
+
+**Closed issues:**
+
+- Add link to jsbin playground to README [\#11](https://github.com/knsv/mermaid/issues/11)
+
+- What are the requirements ? [\#10](https://github.com/knsv/mermaid/issues/10)
+
+**Merged pull requests:**
+
+- Allow unicode chars in labels [\#13](https://github.com/knsv/mermaid/pull/13) ([codebeige](https://github.com/codebeige))
+
+- Formatting Update [\#12](https://github.com/knsv/mermaid/pull/12) ([darrencauthon](https://github.com/darrencauthon))
+
+## [0.2.8](https://github.com/knsv/mermaid/tree/0.2.8) (2014-12-01)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.7...0.2.8)
+
+## [0.2.7](https://github.com/knsv/mermaid/tree/0.2.7) (2014-12-01)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.6...0.2.7)
+
+**Closed issues:**
+
+- Provide parser as separate module [\#4](https://github.com/knsv/mermaid/issues/4)
+
+## [0.2.6](https://github.com/knsv/mermaid/tree/0.2.6) (2014-11-27)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.5...0.2.6)
+
+## [0.2.5](https://github.com/knsv/mermaid/tree/0.2.5) (2014-11-27)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.4...0.2.5)
+
+**Merged pull requests:**
+
+- Added new shapes! [\#1](https://github.com/knsv/mermaid/pull/1) ([bjowes](https://github.com/bjowes))
+
+## [0.2.4](https://github.com/knsv/mermaid/tree/0.2.4) (2014-11-25)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.3...0.2.4)
+
+## [0.2.3](https://github.com/knsv/mermaid/tree/0.2.3) (2014-11-24)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.2...0.2.3)
+
+## [0.2.2](https://github.com/knsv/mermaid/tree/0.2.2) (2014-11-22)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.1...0.2.2)
+
+## [0.2.1](https://github.com/knsv/mermaid/tree/0.2.1) (2014-11-22)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.2.0...0.2.1)
+
+## [0.2.0](https://github.com/knsv/mermaid/tree/0.2.0) (2014-11-22)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.1.1...0.2.0)
+
+## [0.1.1](https://github.com/knsv/mermaid/tree/0.1.1) (2014-11-17)
+
+[Full Changelog](https://github.com/knsv/mermaid/compare/0.1.0...0.1.1)
+
+## [0.1.0](https://github.com/knsv/mermaid/tree/0.1.0) (2014-11-16)
+
+
+\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
\ No newline at end of file
diff --git a/_submodules/mermaid/LICENSE b/_submodules/mermaid/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..77b04c3aba988265626d86accec3a33d3979305b
--- /dev/null
+++ b/_submodules/mermaid/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 - 2018 Knut Sveidqvist
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/_submodules/mermaid/README.md b/_submodules/mermaid/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..45464c0ea22edc7152814c4d504d80a9e131d5e4
--- /dev/null
+++ b/_submodules/mermaid/README.md
@@ -0,0 +1,206 @@
+# mermaid
+
+[![Build Status](https://travis-ci.org/knsv/mermaid.svg?branch=master)](https://travis-ci.org/knsv/mermaid)
+[![Coverage Status](https://coveralls.io/repos/github/knsv/mermaid/badge.svg?branch=master)](https://coveralls.io/github/knsv/mermaid?branch=master)
+[![Join the chat at https://gitter.im/knsv/mermaid](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/knsv/mermaid?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+![banner](./img/header.png)
+
+Generation of diagrams and flowcharts from text in a similar manner as markdown.
+
+Ever wanted to simplify documentation and avoid heavy tools like Visio when explaining your code?
+
+This is why mermaid was born, a simple markdown-like script language for generating charts from text via javascript.
+
+
+### Flowchart
+
+```
+graph TD;
+    A-->B;
+    A-->C;
+    B-->D;
+    C-->D;
+```
+![Flowchart](./img/flow.png)
+
+
+### Sequence diagram
+
+```
+sequenceDiagram
+    participant Alice
+    participant Bob
+    Alice->>John: Hello John, how are you?
+    loop Healthcheck
+        John->>John: Fight against hypochondria
+    end
+    Note right of John: Rational thoughts <br/>prevail...
+    John-->>Alice: Great!
+    John->>Bob: How about you?
+    Bob-->>John: Jolly good!
+```
+![Sequence diagram](./img/sequence.png)
+
+
+### Gantt diagram
+
+```
+gantt
+dateFormat  YYYY-MM-DD
+title Adding GANTT diagram to mermaid
+
+section A section
+Completed task            :done,    des1, 2014-01-06,2014-01-08
+Active task               :active,  des2, 2014-01-09, 3d
+Future task               :         des3, after des2, 5d
+Future task2               :         des4, after des3, 5d
+```
+![Gantt diagram](./img/gantt.png)
+
+
+### Class diagram - :exclamation: experimental
+
+```
+classDiagram
+Class01 <|-- AveryLongClass : Cool
+Class03 *-- Class04
+Class05 o-- Class06
+Class07 .. Class08
+Class09 --> C2 : Where am i?
+Class09 --* C3
+Class09 --|> Class07
+Class07 : equals()
+Class07 : Object[] elementData
+Class01 : size()
+Class01 : int chimp
+Class01 : int gorilla
+Class08 <--> C2: Cool label
+```
+![Class diagram](./img/class.png)
+
+
+### Git graph - :exclamation: experimental
+
+```
+gitGraph:
+options
+{
+    "nodeSpacing": 150,
+    "nodeRadius": 10
+}
+end
+commit
+branch newbranch
+checkout newbranch
+commit
+commit
+checkout master
+commit
+commit
+merge newbranch
+
+```
+
+![Git graph](./img/git.png)
+
+
+## Installation
+
+### CDN
+
+```
+https://unpkg.com/mermaid@<version>/dist/
+```
+
+Replace `<version>` with expected version number.
+
+Example: https://unpkg.com/mermaid@7.1.0/dist/
+
+### Node.js
+
+```
+yarn add mermaid
+```
+
+
+## Documentation
+
+https://mermaidjs.github.io
+
+
+## Sibling projects
+
+- [mermaid CLI](https://github.com/mermaidjs/mermaid.cli)
+- [mermaid live editor](https://github.com/mermaidjs/mermaid-live-editor)
+- [mermaid webpack demo](https://github.com/mermaidjs/mermaid-webpack-demo)
+- [mermaid Parcel demo](https://github.com/mermaidjs/mermaid-parcel-demo)
+
+
+# Request for assistance
+
+Things are piling up and I have hard time keeping up. To remedy this
+it would be great if we could form a core team of developers to cooperate
+with the future development mermaid.
+
+As part of this team you would get write access to the repository and would
+represent the project when answering questions and issues.
+
+Together we could continue the work with things like:
+* adding more typers of diagrams like mindmaps, ert digrams etc
+* improving existing diagrams
+
+Don't hesitate to contact me if you want to get involved.
+
+
+# For contributors
+
+## Setup
+
+    yarn install
+
+
+## Build
+
+    yarn build:watch
+
+
+## Lint
+
+    yarn lint
+
+We use [JavaScript Standard Style](https://github.com/feross/standard).
+We recommend you installing [editor plugins](https://github.com/feross/standard#are-there-text-editor-plugins) so you can get real time lint result.
+
+
+## Test
+
+    yarn test
+
+Manual test in browser:
+
+    open dist/index.html
+
+
+## Release
+
+For those who have the permission to do so:
+
+Update version number in `package.json`.
+
+    npm publish
+
+Command above generates files into the `dist` folder and publishes them to npmjs.org.
+
+
+# Credits
+
+Many thanks to the [d3](http://d3js.org/) and [dagre-d3](https://github.com/cpettitt/dagre-d3) projects for providing the graphical layout and drawing libraries!
+
+Thanks also to the [js-sequence-diagram](http://bramp.github.io/js-sequence-diagrams) project for usage of the grammar for the sequence diagrams. Thanks to Jessica Peter for inspiration and starting point for gantt rendering.
+
+*Mermaid was created by Knut Sveidqvist for easier documentation.*
+
+*[Tyler Long](https://github.com/tylerlong) has became a collaborator since April 2017.*
+
+Here is the full list of the projects [contributors](https://github.com/knsv/mermaid/graphs/contributors).
diff --git a/_submodules/mermaid/__mocks__/d3.js b/_submodules/mermaid/__mocks__/d3.js
new file mode 100644
index 0000000000000000000000000000000000000000..218ed9754f70b895a3e1310c43e3be269b74fd2d
--- /dev/null
+++ b/_submodules/mermaid/__mocks__/d3.js
@@ -0,0 +1,38 @@
+let NewD3 = function () {
+  return {
+    append: function () {
+      return NewD3()
+    },
+    attr: function () {
+      return this
+    },
+    style: function () {
+      return this
+    },
+    text: function () {
+      return this
+    },
+    0: {
+      0: {
+        getBBox: function () {
+          return {
+            height: 10,
+            width: 20
+          }
+        }
+      }
+    }
+  }
+}
+
+export const select = function () {
+  return new NewD3()
+}
+
+export const selectAll = function () {
+  return new NewD3()
+}
+
+export const curveBasis = 'basis'
+export const curveLinear = 'linear'
+export const curveCardinal = 'cardinal'
diff --git a/_submodules/mermaid/gulpfile.js b/_submodules/mermaid/gulpfile.js
new file mode 100644
index 0000000000000000000000000000000000000000..613cf83c1430012153d05236eede68bd0ee060a2
--- /dev/null
+++ b/_submodules/mermaid/gulpfile.js
@@ -0,0 +1,10 @@
+import gulp from 'gulp'
+import jison from 'gulp-jison'
+import filelog from 'gulp-filelog'
+
+gulp.task('jison', function () {
+  return gulp.src('./src/**/*.jison')
+    .pipe(filelog('Jison file:'))
+    .pipe(jison({ 'token-stack': true }))
+    .pipe(gulp.dest('./src/'))
+})
diff --git a/_submodules/mermaid/img/class.png b/_submodules/mermaid/img/class.png
new file mode 100644
index 0000000000000000000000000000000000000000..bf6e379e1abf5db97fcd3d7bb06cad26400b8a7f
Binary files /dev/null and b/_submodules/mermaid/img/class.png differ
diff --git a/_submodules/mermaid/img/flow.png b/_submodules/mermaid/img/flow.png
new file mode 100644
index 0000000000000000000000000000000000000000..e324299764ef1540b37b891a76484514ce5d6d15
Binary files /dev/null and b/_submodules/mermaid/img/flow.png differ
diff --git a/_submodules/mermaid/img/gantt.png b/_submodules/mermaid/img/gantt.png
new file mode 100644
index 0000000000000000000000000000000000000000..64645cb5baf307d7804bf2050ab7f4fb1b2cb312
Binary files /dev/null and b/_submodules/mermaid/img/gantt.png differ
diff --git a/_submodules/mermaid/img/git.png b/_submodules/mermaid/img/git.png
new file mode 100644
index 0000000000000000000000000000000000000000..2da331a1eda912e1fb6057726dc8608985b045ac
Binary files /dev/null and b/_submodules/mermaid/img/git.png differ
diff --git a/_submodules/mermaid/img/header.png b/_submodules/mermaid/img/header.png
new file mode 100644
index 0000000000000000000000000000000000000000..06159b2c94e22cc09fb507db6c7a1562fe4bdeb7
Binary files /dev/null and b/_submodules/mermaid/img/header.png differ
diff --git a/_submodules/mermaid/img/sequence.png b/_submodules/mermaid/img/sequence.png
new file mode 100644
index 0000000000000000000000000000000000000000..b35e3b74db9e96de970ec63af9345f0046ec9fcd
Binary files /dev/null and b/_submodules/mermaid/img/sequence.png differ
diff --git a/_submodules/mermaid/package.json b/_submodules/mermaid/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c2a5b38f7448fa7827728acc36e46f067d61ea2
--- /dev/null
+++ b/_submodules/mermaid/package.json
@@ -0,0 +1,80 @@
+{
+  "name": "mermaid",
+  "version": "8.0.0-rc.8",
+  "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
+  "main": "dist/mermaid.core.js",
+  "keywords": [
+    "diagram",
+    "markdown",
+    "flowchart",
+    "sequence diagram",
+    "gantt",
+    "class diagram",
+    "git graph"
+  ],
+  "scripts": {
+    "build": "webpack --progress --colors",
+    "build:watch": "yarn build --watch",
+    "release": "yarn build -p --config webpack.config.prod.babel.js",
+    "upgrade": "yarn-upgrade-all",
+    "lint": "standard",
+    "test": "yarn lint && jest",
+    "test:watch": "jest --watch",
+    "jison": "node -r babel-register node_modules/.bin/gulp jison",
+    "prepublishOnly": "yarn build && yarn release && yarn test",
+    "prepush": "yarn test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/knsv/mermaid"
+  },
+  "author": "Knut Sveidqvist",
+  "license": "MIT",
+  "standard": {
+    "ignore": [
+      "**/parser/*.js",
+      "dist/**/*.js"
+    ]
+  },
+  "dependencies": {
+    "d3": "^4.13.0",
+    "dagre-d3-renderer": "^0.5.8",
+    "dagre-layout": "^0.8.8",
+    "graphlibrary": "^2.2.0",
+    "he": "^1.1.1",
+    "lodash": "^4.17.5",
+    "moment": "^2.21.0",
+    "scope-css": "^1.0.5"
+  },
+  "devDependencies": {
+    "babel-core": "^6.26.0",
+    "babel-loader": "^7.1.4",
+    "babel-preset-env": "^1.6.1",
+    "coveralls": "^3.0.0",
+    "css-loader": "^0.28.11",
+    "css-to-string-loader": "^0.1.3",
+    "gulp": "^3.9.1",
+    "gulp-filelog": "^0.4.1",
+    "gulp-jison": "^1.2.0",
+    "husky": "^0.14.3",
+    "identity-obj-proxy": "^3.0.0",
+    "jest": "^22.4.2",
+    "jison": "^0.4.18",
+    "node-sass": "^4.7.2",
+    "sass-loader": "^6.0.7",
+    "standard": "^11.0.1",
+    "webpack": "^4.1.1",
+    "webpack-cli": "^2.0.12",
+    "webpack-node-externals": "^1.6.0",
+    "yarn-upgrade-all": "^0.3.0"
+  },
+  "files": [
+    "dist",
+    "src"
+  ],
+  "jest": {
+    "moduleNameMapper": {
+      "\\.(css|scss)$": "identity-obj-proxy"
+    }
+  }
+}
diff --git a/_submodules/mermaid/src/diagrams/class/classDb.js b/_submodules/mermaid/src/diagrams/class/classDb.js
new file mode 100644
index 0000000000000000000000000000000000000000..6be4bb7736a1d5a324846d8618f3555d09e7e8c9
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/class/classDb.js
@@ -0,0 +1,89 @@
+
+import { logger } from '../../logger'
+
+let relations = []
+let classes = {}
+
+/**
+ * Function called by parser when a node definition has been found.
+ * @param id
+ * @param text
+ * @param type
+ * @param style
+ */
+export const addClass = function (id) {
+  if (typeof classes[id] === 'undefined') {
+    classes[id] = {
+      id: id,
+      methods: [],
+      members: []
+    }
+  }
+}
+
+export const clear = function () {
+  relations = []
+  classes = {}
+}
+
+export const getClass = function (id) {
+  return classes[id]
+}
+export const getClasses = function () {
+  return classes
+}
+
+export const getRelations = function () {
+  return relations
+}
+
+export const addRelation = function (relation) {
+  logger.debug('Adding relation: ' + JSON.stringify(relation))
+  addClass(relation.id1)
+  addClass(relation.id2)
+  relations.push(relation)
+}
+
+export const addMembers = function (className, MembersArr) {
+  const theClass = classes[className]
+  if (typeof MembersArr === 'string') {
+    if (MembersArr.substr(-1) === ')') {
+      theClass.methods.push(MembersArr)
+    } else {
+      theClass.members.push(MembersArr)
+    }
+  }
+}
+
+export const cleanupLabel = function (label) {
+  if (label.substring(0, 1) === ':') {
+    return label.substr(2).trim()
+  } else {
+    return label.trim()
+  }
+}
+
+export const lineType = {
+  LINE: 0,
+  DOTTED_LINE: 1
+}
+
+export const relationType = {
+  AGGREGATION: 0,
+  EXTENSION: 1,
+  COMPOSITION: 2,
+  DEPENDENCY: 3
+}
+
+export default {
+  addClass,
+  clear,
+  getClass,
+  getClasses,
+  getRelations,
+  addRelation,
+  addMembers,
+  cleanupLabel,
+  lineType,
+  relationType
+}
diff --git a/_submodules/mermaid/src/diagrams/class/classDiagram.spec.js b/_submodules/mermaid/src/diagrams/class/classDiagram.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..38657e11d2d0fa22a72e8c54f503df09acb420c4
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/class/classDiagram.spec.js
@@ -0,0 +1,208 @@
+/* eslint-env jasmine */
+import { parser } from './parser/classDiagram'
+import classDb from './classDb'
+
+describe('class diagram, ', function () {
+  describe('when parsing an info graph it', function () {
+    beforeEach(function () {
+      parser.yy = classDb
+    })
+
+    it('should handle relation definitions', function () {
+      const str = 'classDiagram\n' +
+'Class01 <|-- Class02\n' +
+'Class03 *-- Class04\n' +
+'Class05 o-- Class06\n' +
+'Class07 .. Class08\n' +
+'Class09 -- Class1'
+
+      parser.parse(str)
+    })
+    it('should handle relation definition of different types and directions', function () {
+      const str = 'classDiagram\n' +
+'Class11 <|.. Class12\n' +
+'Class13 --> Class14\n' +
+'Class15 ..> Class16\n' +
+'Class17 ..|> Class18\n' +
+'Class19 <--* Class20'
+
+      parser.parse(str)
+    })
+
+    it('should handle cardinality and labels', function () {
+      const str = 'classDiagram\n' +
+'Class01 "1" *-- "many" Class02 : contains\n' +
+'Class03 o-- Class04 : aggregation\n' +
+'Class05 --> "1" Class06'
+
+      parser.parse(str)
+    })
+    it('should handle class definitions', function () {
+      const str = 'classDiagram\n' +
+'class Car\n' +
+'Driver -- Car : drives >\n' +
+'Car *-- Wheel : have 4 >\n' +
+'Car -- Person : < owns'
+
+      parser.parse(str)
+    })
+
+    it('should handle method statements', function () {
+      const str = 'classDiagram\n' +
+'Object <|-- ArrayList\n' +
+'Object : equals()\n' +
+'ArrayList : Object[] elementData\n' +
+'ArrayList : size()'
+
+      parser.parse(str)
+    })
+    it('should handle parsing of method statements  grouped by brackets', function () {
+      const str = 'classDiagram\n' +
+'class Dummy {\n' +
+'String data\n' +
+'  void methods()\n' +
+'}\n' +
+'\n' +
+'class Flight {\n' +
+'   flightNumber : Integer\n' +
+'   departureTime : Date\n' +
+'}'
+
+      parser.parse(str)
+    })
+
+    it('should handle parsing of separators', function () {
+      const str = 'classDiagram\n' +
+                'class Foo1 {\n' +
+                '  You can use\n' +
+                '  several lines\n' +
+                '..\n' +
+                'as you want\n' +
+                'and group\n' +
+                '==\n' +
+                'things together.\n' +
+                '__\n' +
+                'You can have as many groups\n' +
+                'as you want\n' +
+                '--\n' +
+                'End of class\n' +
+                '}\n' +
+                '\n' +
+                'class User {\n' +
+                '.. Simple Getter ..\n' +
+                '+ getName()\n' +
+                '+ getAddress()\n' +
+                '.. Some setter ..\n' +
+                '+ setName()\n' +
+                '__ private data __\n' +
+                'int age\n' +
+                '-- encrypted --\n' +
+                'String password\n' +
+                '}'
+
+      parser.parse(str)
+    })
+  })
+
+  describe('when fetching data from an classDiagram graph it', function () {
+    beforeEach(function () {
+      parser.yy = classDb
+      parser.yy.clear()
+    })
+    it('should handle relation definitions EXTENSION', function () {
+      const str = 'classDiagram\n' +
+                        'Class01 <|-- Class02'
+
+      parser.parse(str)
+
+      const relations = parser.yy.getRelations()
+
+      expect(parser.yy.getClass('Class01').id).toBe('Class01')
+      expect(parser.yy.getClass('Class02').id).toBe('Class02')
+      expect(relations[0].relation.type1).toBe(classDb.relationType.EXTENSION)
+      expect(relations[0].relation.type2).toBe('none')
+      expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
+    })
+    it('should handle relation definitions AGGREGATION and dotted line', function () {
+      const str = 'classDiagram\n' +
+                        'Class01 o.. Class02'
+
+      parser.parse(str)
+
+      const relations = parser.yy.getRelations()
+
+      expect(parser.yy.getClass('Class01').id).toBe('Class01')
+      expect(parser.yy.getClass('Class02').id).toBe('Class02')
+      expect(relations[0].relation.type1).toBe(classDb.relationType.AGGREGATION)
+      expect(relations[0].relation.type2).toBe('none')
+      expect(relations[0].relation.lineType).toBe(classDb.lineType.DOTTED_LINE)
+    })
+    it('should handle relation definitions COMPOSITION on both sides', function () {
+      const str = 'classDiagram\n' +
+                       'Class01 *--* Class02'
+
+      parser.parse(str)
+
+      const relations = parser.yy.getRelations()
+
+      expect(parser.yy.getClass('Class01').id).toBe('Class01')
+      expect(parser.yy.getClass('Class02').id).toBe('Class02')
+      expect(relations[0].relation.type1).toBe(classDb.relationType.COMPOSITION)
+      expect(relations[0].relation.type2).toBe(classDb.relationType.COMPOSITION)
+      expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
+    })
+    it('should handle relation definitions no types', function () {
+      const str = 'classDiagram\n' +
+                        'Class01 -- Class02'
+
+      parser.parse(str)
+
+      const relations = parser.yy.getRelations()
+
+      expect(parser.yy.getClass('Class01').id).toBe('Class01')
+      expect(parser.yy.getClass('Class02').id).toBe('Class02')
+      expect(relations[0].relation.type1).toBe('none')
+      expect(relations[0].relation.type2).toBe('none')
+      expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
+    })
+    it('should handle relation definitions with type only on right side', function () {
+      const str = 'classDiagram\n' +
+                       'Class01 --|> Class02'
+
+      parser.parse(str)
+
+      const relations = parser.yy.getRelations()
+
+      expect(parser.yy.getClass('Class01').id).toBe('Class01')
+      expect(parser.yy.getClass('Class02').id).toBe('Class02')
+      expect(relations[0].relation.type1).toBe('none')
+      expect(relations[0].relation.type2).toBe(classDb.relationType.EXTENSION)
+      expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
+    })
+
+    it('should handle multiple classes and relation definitions', function () {
+      const str = 'classDiagram\n' +
+                        'Class01 <|-- Class02\n' +
+                        'Class03 *-- Class04\n' +
+                        'Class05 o-- Class06\n' +
+                        'Class07 .. Class08\n' +
+                        'Class09 -- Class10'
+
+      parser.parse(str)
+
+      const relations = parser.yy.getRelations()
+
+      expect(parser.yy.getClass('Class01').id).toBe('Class01')
+      expect(parser.yy.getClass('Class10').id).toBe('Class10')
+
+      expect(relations.length).toBe(5)
+
+      expect(relations[0].relation.type1).toBe(classDb.relationType.EXTENSION)
+      expect(relations[0].relation.type2).toBe('none')
+      expect(relations[0].relation.lineType).toBe(classDb.lineType.LINE)
+      expect(relations[3].relation.type1).toBe('none')
+      expect(relations[3].relation.type2).toBe('none')
+      expect(relations[3].relation.lineType).toBe(classDb.lineType.DOTTED_LINE)
+    })
+  })
+})
diff --git a/_submodules/mermaid/src/diagrams/class/classRenderer.js b/_submodules/mermaid/src/diagrams/class/classRenderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..5169fce4bf3db14087082700cd498423855a3a2c
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/class/classRenderer.js
@@ -0,0 +1,365 @@
+import dagre from 'dagre-layout'
+import graphlib from 'graphlibrary'
+import * as d3 from 'd3'
+
+import classDb from './classDb'
+import { logger } from '../../logger'
+import { parser } from './parser/classDiagram'
+
+parser.yy = classDb
+
+const idCache = {}
+
+let classCnt = 0
+const conf = {
+  dividerMargin: 10,
+  padding: 5,
+  textHeight: 10
+}
+
+// Todo optimize
+const getGraphId = function (label) {
+  const keys = Object.keys(idCache)
+
+  for (let i = 0; i < keys.length; i++) {
+    if (idCache[keys[i]].label === label) {
+      return keys[i]
+    }
+  }
+
+  return undefined
+}
+
+/**
+ * Setup arrow head and define the marker. The result is appended to the svg.
+ */
+const insertMarkers = function (elem) {
+  elem.append('defs').append('marker')
+    .attr('id', 'extensionStart')
+    .attr('class', 'extension')
+    .attr('refX', 0)
+    .attr('refY', 7)
+    .attr('markerWidth', 190)
+    .attr('markerHeight', 240)
+    .attr('orient', 'auto')
+    .append('path')
+    .attr('d', 'M 1,7 L18,13 V 1 Z')
+
+  elem.append('defs').append('marker')
+    .attr('id', 'extensionEnd')
+    .attr('refX', 19)
+    .attr('refY', 7)
+    .attr('markerWidth', 20)
+    .attr('markerHeight', 28)
+    .attr('orient', 'auto')
+    .append('path')
+    .attr('d', 'M 1,1 V 13 L18,7 Z') // this is actual shape for arrowhead
+
+  elem.append('defs').append('marker')
+    .attr('id', 'compositionStart')
+    .attr('class', 'extension')
+    .attr('refX', 0)
+    .attr('refY', 7)
+    .attr('markerWidth', 190)
+    .attr('markerHeight', 240)
+    .attr('orient', 'auto')
+    .append('path')
+    .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
+
+  elem.append('defs').append('marker')
+    .attr('id', 'compositionEnd')
+    .attr('refX', 19)
+    .attr('refY', 7)
+    .attr('markerWidth', 20)
+    .attr('markerHeight', 28)
+    .attr('orient', 'auto')
+    .append('path')
+    .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
+
+  elem.append('defs').append('marker')
+    .attr('id', 'aggregationStart')
+    .attr('class', 'extension')
+    .attr('refX', 0)
+    .attr('refY', 7)
+    .attr('markerWidth', 190)
+    .attr('markerHeight', 240)
+    .attr('orient', 'auto')
+    .append('path')
+    .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
+
+  elem.append('defs').append('marker')
+    .attr('id', 'aggregationEnd')
+    .attr('refX', 19)
+    .attr('refY', 7)
+    .attr('markerWidth', 20)
+    .attr('markerHeight', 28)
+    .attr('orient', 'auto')
+    .append('path')
+    .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z')
+
+  elem.append('defs').append('marker')
+    .attr('id', 'dependencyStart')
+    .attr('class', 'extension')
+    .attr('refX', 0)
+    .attr('refY', 7)
+    .attr('markerWidth', 190)
+    .attr('markerHeight', 240)
+    .attr('orient', 'auto')
+    .append('path')
+    .attr('d', 'M 5,7 L9,13 L1,7 L9,1 Z')
+
+  elem.append('defs').append('marker')
+    .attr('id', 'dependencyEnd')
+    .attr('refX', 19)
+    .attr('refY', 7)
+    .attr('markerWidth', 20)
+    .attr('markerHeight', 28)
+    .attr('orient', 'auto')
+    .append('path')
+    .attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z')
+}
+
+let edgeCount = 0
+const drawEdge = function (elem, path, relation) {
+  const getRelationType = function (type) {
+    switch (type) {
+      case classDb.relationType.AGGREGATION:
+        return 'aggregation'
+      case classDb.relationType.EXTENSION:
+        return 'extension'
+      case classDb.relationType.COMPOSITION:
+        return 'composition'
+      case classDb.relationType.DEPENDENCY:
+        return 'dependency'
+    }
+  }
+
+  // The data for our line
+  const lineData = path.points
+
+  // This is the accessor function we talked about above
+  const lineFunction = d3.line()
+    .x(function (d) {
+      return d.x
+    })
+    .y(function (d) {
+      return d.y
+    })
+    .curve(d3.curveBasis)
+
+  const svgPath = elem.append('path')
+    .attr('d', lineFunction(lineData))
+    .attr('id', 'edge' + edgeCount)
+    .attr('class', 'relation')
+  let url = ''
+  if (conf.arrowMarkerAbsolute) {
+    url = window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search
+    url = url.replace(/\(/g, '\\(')
+    url = url.replace(/\)/g, '\\)')
+  }
+
+  if (relation.relation.type1 !== 'none') {
+    svgPath.attr('marker-start', 'url(' + url + '#' + getRelationType(relation.relation.type1) + 'Start' + ')')
+  }
+  if (relation.relation.type2 !== 'none') {
+    svgPath.attr('marker-end', 'url(' + url + '#' + getRelationType(relation.relation.type2) + 'End' + ')')
+  }
+
+  let x, y
+  const l = path.points.length
+  if ((l % 2) !== 0) {
+    const p1 = path.points[Math.floor(l / 2)]
+    const p2 = path.points[Math.ceil(l / 2)]
+    x = (p1.x + p2.x) / 2
+    y = (p1.y + p2.y) / 2
+  } else {
+    const p = path.points[Math.floor(l / 2)]
+    x = p.x
+    y = p.y
+  }
+
+  if (typeof relation.title !== 'undefined') {
+    const g = elem.append('g')
+      .attr('class', 'classLabel')
+    const label = g.append('text')
+      .attr('class', 'label')
+      .attr('x', x)
+      .attr('y', y)
+      .attr('fill', 'red')
+      .attr('text-anchor', 'middle')
+      .text(relation.title)
+
+    window.label = label
+    const bounds = label.node().getBBox()
+
+    g.insert('rect', ':first-child')
+      .attr('class', 'box')
+      .attr('x', bounds.x - conf.padding / 2)
+      .attr('y', bounds.y - conf.padding / 2)
+      .attr('width', bounds.width + conf.padding)
+      .attr('height', bounds.height + conf.padding)
+  }
+
+  edgeCount++
+}
+
+const drawClass = function (elem, classDef) {
+  logger.info('Rendering class ' + classDef)
+
+  const addTspan = function (textEl, txt, isFirst) {
+    const tSpan = textEl.append('tspan')
+      .attr('x', conf.padding)
+      .text(txt)
+    if (!isFirst) {
+      tSpan.attr('dy', conf.textHeight)
+    }
+  }
+
+  const id = 'classId' + classCnt
+  const classInfo = {
+    id: id,
+    label: classDef.id,
+    width: 0,
+    height: 0
+  }
+
+  const g = elem.append('g')
+    .attr('id', id)
+    .attr('class', 'classGroup')
+  const title = g.append('text')
+    .attr('x', conf.padding)
+    .attr('y', conf.textHeight + conf.padding)
+    .text(classDef.id)
+
+  const titleHeight = title.node().getBBox().height
+
+  const membersLine = g.append('line') // text label for the x axis
+    .attr('x1', 0)
+    .attr('y1', conf.padding + titleHeight + conf.dividerMargin / 2)
+    .attr('y2', conf.padding + titleHeight + conf.dividerMargin / 2)
+
+  const members = g.append('text') // text label for the x axis
+    .attr('x', conf.padding)
+    .attr('y', titleHeight + (conf.dividerMargin) + conf.textHeight)
+    .attr('fill', 'white')
+    .attr('class', 'classText')
+
+  let isFirst = true
+  classDef.members.forEach(function (member) {
+    addTspan(members, member, isFirst)
+    isFirst = false
+  })
+
+  const membersBox = members.node().getBBox()
+
+  const methodsLine = g.append('line') // text label for the x axis
+    .attr('x1', 0)
+    .attr('y1', conf.padding + titleHeight + conf.dividerMargin + membersBox.height)
+    .attr('y2', conf.padding + titleHeight + conf.dividerMargin + membersBox.height)
+
+  const methods = g.append('text') // text label for the x axis
+    .attr('x', conf.padding)
+    .attr('y', titleHeight + 2 * conf.dividerMargin + membersBox.height + conf.textHeight)
+    .attr('fill', 'white')
+    .attr('class', 'classText')
+
+  isFirst = true
+
+  classDef.methods.forEach(function (method) {
+    addTspan(methods, method, isFirst)
+    isFirst = false
+  })
+
+  const classBox = g.node().getBBox()
+  g.insert('rect', ':first-child')
+    .attr('x', 0)
+    .attr('y', 0)
+    .attr('width', classBox.width + 2 * conf.padding)
+    .attr('height', classBox.height + conf.padding + 0.5 * conf.dividerMargin)
+
+  membersLine.attr('x2', classBox.width + 2 * conf.padding)
+  methodsLine.attr('x2', classBox.width + 2 * conf.padding)
+
+  classInfo.width = classBox.width + 2 * conf.padding
+  classInfo.height = classBox.height + conf.padding + 0.5 * conf.dividerMargin
+
+  idCache[id] = classInfo
+  classCnt++
+  return classInfo
+}
+
+export const setConf = function (cnf) {
+  const keys = Object.keys(cnf)
+
+  keys.forEach(function (key) {
+    conf[key] = cnf[key]
+  })
+}
+/**
+ * Draws a flowchart in the tag with id: id based on the graph definition in text.
+ * @param text
+ * @param id
+ */
+export const draw = function (text, id) {
+  parser.yy.clear()
+  parser.parse(text)
+
+  logger.info('Rendering diagram ' + text)
+
+  /// / Fetch the default direction, use TD if none was found
+  const diagram = d3.select(`[id="${id}"]`)
+  insertMarkers(diagram)
+
+  // Layout graph, Create a new directed graph
+  const g = new graphlib.Graph({
+    multigraph: true
+  })
+
+  // Set an object for the graph label
+  g.setGraph({
+    isMultiGraph: true
+  })
+
+  // Default to assigning a new object as a label for each new edge.
+  g.setDefaultEdgeLabel(function () {
+    return {}
+  })
+
+  const classes = classDb.getClasses()
+  const keys = Object.keys(classes)
+  for (let i = 0; i < keys.length; i++) {
+    const classDef = classes[keys[i]]
+    const node = drawClass(diagram, classDef)
+    // Add nodes to the graph. The first argument is the node id. The second is
+    // metadata about the node. In this case we're going to add labels to each of
+    // our nodes.
+    g.setNode(node.id, node)
+    logger.info('Org height: ' + node.height)
+  }
+
+  const relations = classDb.getRelations()
+  relations.forEach(function (relation) {
+    logger.info('tjoho' + getGraphId(relation.id1) + getGraphId(relation.id2) + JSON.stringify(relation))
+    g.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), { relation: relation })
+  })
+  dagre.layout(g)
+  g.nodes().forEach(function (v) {
+    if (typeof v !== 'undefined') {
+      logger.debug('Node ' + v + ': ' + JSON.stringify(g.node(v)))
+      d3.select('#' + v).attr('transform', 'translate(' + (g.node(v).x - (g.node(v).width / 2)) + ',' + (g.node(v).y - (g.node(v).height / 2)) + ' )')
+    }
+  })
+  g.edges().forEach(function (e) {
+    logger.debug('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e)))
+    drawEdge(diagram, g.edge(e), g.edge(e).relation)
+  })
+
+  diagram.attr('height', '100%')
+  diagram.attr('width', '100%')
+  diagram.attr('viewBox', '0 0 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20))
+}
+
+export default {
+  setConf,
+  draw
+}
diff --git a/_submodules/mermaid/src/diagrams/class/parser/classDiagram.jison b/_submodules/mermaid/src/diagrams/class/parser/classDiagram.jison
new file mode 100644
index 0000000000000000000000000000000000000000..f633d144cb260a86c0ffaf05fa80bfcc43825751
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/class/parser/classDiagram.jison
@@ -0,0 +1,196 @@
+/** mermaid
+ *  https://mermaidjs.github.io/
+ *  (c) 2015 Knut Sveidqvist
+ *  MIT license.
+ */
+
+/* lexical grammar */
+%lex
+%x string struct
+
+%%
+\%\%[^\n]*            /* do nothing */
+\n+                   return 'NEWLINE';
+\s+                     /* skip whitespace */
+"classDiagram"          return 'CLASS_DIAGRAM';
+[\{]                    { this.begin("struct"); /*console.log('Starting struct');*/return 'STRUCT_START';}
+<struct>\}           { /*console.log('Ending struct');*/this.popState(); return 'STRUCT_STOP';}}
+<struct>[\n]              /* nothing */
+<struct>[^\{\}\n]*     { /*console.log('lex-member: ' + yytext);*/  return "MEMBER";}
+
+
+
+"class"               return 'CLASS';
+["]                   this.begin("string");
+<string>["]           this.popState();
+<string>[^"]*         return "STR";
+
+
+\s*\<\|               return 'EXTENSION';
+\s*\|\>               return 'EXTENSION';
+\s*\>                 return 'DEPENDENCY';
+\s*\<                 return 'DEPENDENCY';
+\s*\*                  return 'COMPOSITION';
+\s*o                  return 'AGGREGATION';
+\-\-                  return 'LINE';
+\.\.                  return 'DOTTED_LINE';
+":"[^#\n;]+        return 'LABEL';
+\-                    return 'MINUS';
+"."                   return 'DOT';
+\+                    return 'PLUS';
+\%                    return 'PCT';
+"="                   return 'EQUALS';
+\=                    return 'EQUALS';
+[A-Za-z]+             return 'ALPHA';
+[!"#$%&'*+,-.`?\\_/]  return 'PUNCTUATION';
+[0-9]+                 return 'NUM';
+[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
+[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
+[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|
+[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|
+[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|
+[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|
+[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|
+[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|
+[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|
+[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|
+[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|
+[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|
+[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|
+[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|
+[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|
+[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|
+[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|
+[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|
+[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|
+[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|
+[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|
+[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|
+[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|
+[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|
+[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|
+[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|
+[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|
+[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|
+[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|
+[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|
+[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|
+[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|
+[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|
+[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|
+[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|
+[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|
+[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|
+[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|
+[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|
+[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|
+[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|
+[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|
+[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|
+[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|
+[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|
+[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|
+[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|
+[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|
+[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|
+[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|
+[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|
+[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|
+[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|
+[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|
+[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|
+[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|
+[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|
+[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|
+[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|
+[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|
+[\uFFD2-\uFFD7\uFFDA-\uFFDC]
+                      return 'UNICODE_TEXT';
+\s                    return 'SPACE';
+<<EOF>>               return 'EOF';
+
+/lex
+
+/* operator associations and precedence */
+
+%left '^'
+
+%start mermaidDoc
+
+%% /* language grammar */
+
+mermaidDoc: graphConfig;
+
+graphConfig
+    : CLASS_DIAGRAM NEWLINE statements EOF
+    ;
+
+statements
+    : statement
+    | statement NEWLINE statements
+    ;
+
+
+className
+    : alphaNumToken className { $$=$1+$2; }
+    | alphaNumToken { $$=$1; }
+    ;
+
+statement
+    : relationStatement       { yy.addRelation($1); }
+    | relationStatement LABEL { $1.title =  yy.cleanupLabel($2); yy.addRelation($1);        }
+    | classStatement
+    | methodStatement
+    ;
+
+classStatement
+    : CLASS className
+    | CLASS className STRUCT_START members STRUCT_STOP {/*console.log($2,JSON.stringify($4));*/yy.addMembers($2,$4);}
+    ;
+
+members
+    : MEMBER { $$ = [$1]; }
+    | MEMBER members { $2.push($1);$$=$2;}
+    ;
+
+methodStatement
+    : className {/*console.log('Rel found',$1);*/}
+    | className LABEL {yy.addMembers($1,yy.cleanupLabel($2));}
+    | MEMBER {console.warn('Member',$1);}
+    | SEPARATOR {/*console.log('sep found',$1);*/}
+    ;
+
+relationStatement
+    : className relation className          { $$ = {'id1':$1,'id2':$3, relation:$2, relationTitle1:'none', relationTitle2:'none'}; }
+    | className STR relation className      { $$ = {id1:$1, id2:$4, relation:$3, relationTitle1:$2, relationTitle2:'none'}}
+    | className relation STR className      { $$ = {id1:$1, id2:$4, relation:$2, relationTitle1:'none', relationTitle2:$3}; }
+    | className STR relation STR className  { $$ = {id1:$1, id2:$5, relation:$3, relationTitle1:$2, relationTitle2:$4} }
+    ;
+
+relation
+    : relationType lineType relationType { $$={type1:$1,type2:$3,lineType:$2}; }
+    | lineType relationType { $$={type1:'none',type2:$2,lineType:$1}; }
+    | relationType lineType { $$={type1:$1,type2:'none',lineType:$2}; }
+    | lineType { $$={type1:'none',type2:'none',lineType:$1}; }
+    ;
+
+relationType
+    : AGGREGATION { $$=yy.relationType.AGGREGATION;}
+    | EXTENSION   { $$=yy.relationType.EXTENSION;}
+    | COMPOSITION { $$=yy.relationType.COMPOSITION;}
+    | DEPENDENCY  { $$=yy.relationType.DEPENDENCY;}
+    ;
+
+lineType
+    : LINE          {$$=yy.lineType.LINE;}
+    | DOTTED_LINE   {$$=yy.lineType.DOTTED_LINE;}
+    ;
+
+commentToken   : textToken | graphCodeTokens ;
+
+textToken      : textNoTagsToken | TAGSTART | TAGEND | '=='  | '--' | PCT | DEFAULT;
+
+textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ;
+
+alphaNumToken  : UNICODE_TEXT | NUM | ALPHA;
+%%
diff --git a/_submodules/mermaid/src/diagrams/class/parser/classDiagram.js b/_submodules/mermaid/src/diagrams/class/parser/classDiagram.js
new file mode 100644
index 0000000000000000000000000000000000000000..5ff57a54d2a1b6d0baa3c3ce6a50f75381c4e3c9
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/class/parser/classDiagram.js
@@ -0,0 +1,740 @@
+/* parser generated by jison 0.4.18 */
+/*
+  Returns a Parser object of the following structure:
+
+  Parser: {
+    yy: {}
+  }
+
+  Parser.prototype: {
+    yy: {},
+    trace: function(),
+    symbols_: {associative list: name ==> number},
+    terminals_: {associative list: number ==> name},
+    productions_: [...],
+    performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
+    table: [...],
+    defaultActions: {...},
+    parseError: function(str, hash),
+    parse: function(input),
+
+    lexer: {
+        EOF: 1,
+        parseError: function(str, hash),
+        setInput: function(input),
+        input: function(),
+        unput: function(str),
+        more: function(),
+        less: function(n),
+        pastInput: function(),
+        upcomingInput: function(),
+        showPosition: function(),
+        test_match: function(regex_match_array, rule_index),
+        next: function(),
+        lex: function(),
+        begin: function(condition),
+        popState: function(),
+        _currentRules: function(),
+        topState: function(),
+        pushState: function(condition),
+
+        options: {
+            ranges: boolean           (optional: true ==> token location info will include a .range[] member)
+            flex: boolean             (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
+            backtrack_lexer: boolean  (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
+        },
+
+        performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
+        rules: [...],
+        conditions: {associative list: name ==> set},
+    }
+  }
+
+
+  token location info (@$, _$, etc.): {
+    first_line: n,
+    last_line: n,
+    first_column: n,
+    last_column: n,
+    range: [start_number, end_number]       (where the numbers are indexes into the input string, regular zero-based)
+  }
+
+
+  the parseError function receives a 'hash' object with these members for lexer and parser errors: {
+    text:        (matched text)
+    token:       (the produced terminal token, if any)
+    line:        (yylineno)
+  }
+  while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
+    loc:         (yylloc)
+    expected:    (string describing the set of expected tokens)
+    recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
+  }
+*/
+var parser = (function(){
+var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,11],$V1=[1,12],$V2=[1,13],$V3=[1,15],$V4=[1,16],$V5=[1,17],$V6=[6,8],$V7=[1,26],$V8=[1,27],$V9=[1,28],$Va=[1,29],$Vb=[1,30],$Vc=[1,31],$Vd=[6,8,13,17,23,26,27,28,29,30,31],$Ve=[6,8,13,17,23,26,27,28,29,30,31,45,46,47],$Vf=[23,45,46,47],$Vg=[23,30,31,45,46,47],$Vh=[23,26,27,28,29,45,46,47],$Vi=[6,8,13],$Vj=[1,46];
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"mermaidDoc":3,"graphConfig":4,"CLASS_DIAGRAM":5,"NEWLINE":6,"statements":7,"EOF":8,"statement":9,"className":10,"alphaNumToken":11,"relationStatement":12,"LABEL":13,"classStatement":14,"methodStatement":15,"CLASS":16,"STRUCT_START":17,"members":18,"STRUCT_STOP":19,"MEMBER":20,"SEPARATOR":21,"relation":22,"STR":23,"relationType":24,"lineType":25,"AGGREGATION":26,"EXTENSION":27,"COMPOSITION":28,"DEPENDENCY":29,"LINE":30,"DOTTED_LINE":31,"commentToken":32,"textToken":33,"graphCodeTokens":34,"textNoTagsToken":35,"TAGSTART":36,"TAGEND":37,"==":38,"--":39,"PCT":40,"DEFAULT":41,"SPACE":42,"MINUS":43,"keywords":44,"UNICODE_TEXT":45,"NUM":46,"ALPHA":47,"$accept":0,"$end":1},
+terminals_: {2:"error",5:"CLASS_DIAGRAM",6:"NEWLINE",8:"EOF",13:"LABEL",16:"CLASS",17:"STRUCT_START",19:"STRUCT_STOP",20:"MEMBER",21:"SEPARATOR",23:"STR",26:"AGGREGATION",27:"EXTENSION",28:"COMPOSITION",29:"DEPENDENCY",30:"LINE",31:"DOTTED_LINE",34:"graphCodeTokens",36:"TAGSTART",37:"TAGEND",38:"==",39:"--",40:"PCT",41:"DEFAULT",42:"SPACE",43:"MINUS",44:"keywords",45:"UNICODE_TEXT",46:"NUM",47:"ALPHA"},
+productions_: [0,[3,1],[4,4],[7,1],[7,3],[10,2],[10,1],[9,1],[9,2],[9,1],[9,1],[14,2],[14,5],[18,1],[18,2],[15,1],[15,2],[15,1],[15,1],[12,3],[12,4],[12,4],[12,5],[22,3],[22,2],[22,2],[22,1],[24,1],[24,1],[24,1],[24,1],[25,1],[25,1],[32,1],[32,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[33,1],[35,1],[35,1],[35,1],[35,1],[11,1],[11,1],[11,1]],
+performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
+/* this == yyval */
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 5:
+ this.$=$$[$0-1]+$$[$0]; 
+break;
+case 6:
+ this.$=$$[$0]; 
+break;
+case 7:
+ yy.addRelation($$[$0]); 
+break;
+case 8:
+ $$[$0-1].title =  yy.cleanupLabel($$[$0]); yy.addRelation($$[$0-1]);        
+break;
+case 12:
+/*console.log($$[$0-3],JSON.stringify($$[$0-1]));*/yy.addMembers($$[$0-3],$$[$0-1]);
+break;
+case 13:
+ this.$ = [$$[$0]]; 
+break;
+case 14:
+ $$[$0].push($$[$0-1]);this.$=$$[$0];
+break;
+case 15:
+/*console.log('Rel found',$$[$0]);*/
+break;
+case 16:
+yy.addMembers($$[$0-1],yy.cleanupLabel($$[$0]));
+break;
+case 17:
+console.warn('Member',$$[$0]);
+break;
+case 18:
+/*console.log('sep found',$$[$0]);*/
+break;
+case 19:
+ this.$ = {'id1':$$[$0-2],'id2':$$[$0], relation:$$[$0-1], relationTitle1:'none', relationTitle2:'none'}; 
+break;
+case 20:
+ this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-1], relationTitle1:$$[$0-2], relationTitle2:'none'}
+break;
+case 21:
+ this.$ = {id1:$$[$0-3], id2:$$[$0], relation:$$[$0-2], relationTitle1:'none', relationTitle2:$$[$0-1]}; 
+break;
+case 22:
+ this.$ = {id1:$$[$0-4], id2:$$[$0], relation:$$[$0-2], relationTitle1:$$[$0-3], relationTitle2:$$[$0-1]} 
+break;
+case 23:
+ this.$={type1:$$[$0-2],type2:$$[$0],lineType:$$[$0-1]}; 
+break;
+case 24:
+ this.$={type1:'none',type2:$$[$0],lineType:$$[$0-1]}; 
+break;
+case 25:
+ this.$={type1:$$[$0-1],type2:'none',lineType:$$[$0]}; 
+break;
+case 26:
+ this.$={type1:'none',type2:'none',lineType:$$[$0]}; 
+break;
+case 27:
+ this.$=yy.relationType.AGGREGATION;
+break;
+case 28:
+ this.$=yy.relationType.EXTENSION;
+break;
+case 29:
+ this.$=yy.relationType.COMPOSITION;
+break;
+case 30:
+ this.$=yy.relationType.DEPENDENCY;
+break;
+case 31:
+this.$=yy.lineType.LINE;
+break;
+case 32:
+this.$=yy.lineType.DOTTED_LINE;
+break;
+}
+},
+table: [{3:1,4:2,5:[1,3]},{1:[3]},{1:[2,1]},{6:[1,4]},{7:5,9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},{8:[1,18]},{6:[1,19],8:[2,3]},o($V6,[2,7],{13:[1,20]}),o($V6,[2,9]),o($V6,[2,10]),o($V6,[2,15],{22:21,24:24,25:25,13:[1,23],23:[1,22],26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc}),{10:32,11:14,45:$V3,46:$V4,47:$V5},o($V6,[2,17]),o($V6,[2,18]),o($Vd,[2,6],{11:14,10:33,45:$V3,46:$V4,47:$V5}),o($Ve,[2,46]),o($Ve,[2,47]),o($Ve,[2,48]),{1:[2,2]},{7:34,9:6,10:10,11:14,12:7,14:8,15:9,16:$V0,20:$V1,21:$V2,45:$V3,46:$V4,47:$V5},o($V6,[2,8]),{10:35,11:14,23:[1,36],45:$V3,46:$V4,47:$V5},{22:37,24:24,25:25,26:$V7,27:$V8,28:$V9,29:$Va,30:$Vb,31:$Vc},o($V6,[2,16]),{25:38,30:$Vb,31:$Vc},o($Vf,[2,26],{24:39,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vg,[2,27]),o($Vg,[2,28]),o($Vg,[2,29]),o($Vg,[2,30]),o($Vh,[2,31]),o($Vh,[2,32]),o($V6,[2,11],{17:[1,40]}),o($Vd,[2,5]),{8:[2,4]},o($Vi,[2,19]),{10:41,11:14,45:$V3,46:$V4,47:$V5},{10:42,11:14,23:[1,43],45:$V3,46:$V4,47:$V5},o($Vf,[2,25],{24:44,26:$V7,27:$V8,28:$V9,29:$Va}),o($Vf,[2,24]),{18:45,20:$Vj},o($Vi,[2,21]),o($Vi,[2,20]),{10:47,11:14,45:$V3,46:$V4,47:$V5},o($Vf,[2,23]),{19:[1,48]},{18:49,19:[2,13],20:$Vj},o($Vi,[2,22]),o($V6,[2,12]),{19:[2,14]}],
+defaultActions: {2:[2,1],18:[2,2],34:[2,4],49:[2,14]},
+parseError: function parseError(str, hash) {
+    if (hash.recoverable) {
+        this.trace(str);
+    } else {
+        var error = new Error(str);
+        error.hash = hash;
+        throw error;
+    }
+},
+parse: function parse(input) {
+    var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+    var args = lstack.slice.call(arguments, 1);
+    var lexer = Object.create(this.lexer);
+    var sharedState = { yy: {} };
+    for (var k in this.yy) {
+        if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
+            sharedState.yy[k] = this.yy[k];
+        }
+    }
+    lexer.setInput(input, sharedState.yy);
+    sharedState.yy.lexer = lexer;
+    sharedState.yy.parser = this;
+    if (typeof lexer.yylloc == 'undefined') {
+        lexer.yylloc = {};
+    }
+    var yyloc = lexer.yylloc;
+    lstack.push(yyloc);
+    var ranges = lexer.options && lexer.options.ranges;
+    if (typeof sharedState.yy.parseError === 'function') {
+        this.parseError = sharedState.yy.parseError;
+    } else {
+        this.parseError = Object.getPrototypeOf(this).parseError;
+    }
+    function popStack(n) {
+        stack.length = stack.length - 2 * n;
+        vstack.length = vstack.length - n;
+        lstack.length = lstack.length - n;
+    }
+            function lex() {
+            var token;
+            token = tstack.pop() || lexer.lex() || EOF;
+            if (typeof token !== 'number') {
+                if (token instanceof Array) {
+                    tstack = token;
+                    token = tstack.pop();
+                }
+                token = self.symbols_[token] || token;
+            }
+            return token;
+        }
+    var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+    while (true) {
+        state = stack[stack.length - 1];
+        if (this.defaultActions[state]) {
+            action = this.defaultActions[state];
+        } else {
+            if (symbol === null || typeof symbol == 'undefined') {
+                symbol = lex();
+            }
+            action = table[state] && table[state][symbol];
+        }
+        if (typeof action === 'undefined' || !action.length || !action[0]) {
+            var errStr = '';
+            expected = [];
+            for (p in table[state]) {
+                if (this.terminals_[p] && p > TERROR) {
+                    expected.push('\'' + this.terminals_[p] + '\'');
+                }
+            }
+            if (lexer.showPosition) {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
+            } else {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
+            }
+            this.parseError(errStr, {
+                text: lexer.match,
+                token: this.terminals_[symbol] || symbol,
+                line: lexer.yylineno,
+                loc: yyloc,
+                expected: expected
+            });
+        }
+        if (action[0] instanceof Array && action.length > 1) {
+            throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+        }
+        switch (action[0]) {
+        case 1:
+            stack.push(symbol);
+            vstack.push(lexer.yytext);
+            lstack.push(lexer.yylloc);
+            stack.push(action[1]);
+            symbol = null;
+            if (!preErrorSymbol) {
+                yyleng = lexer.yyleng;
+                yytext = lexer.yytext;
+                yylineno = lexer.yylineno;
+                yyloc = lexer.yylloc;
+                if (recovering > 0) {
+                    recovering--;
+                }
+            } else {
+                symbol = preErrorSymbol;
+                preErrorSymbol = null;
+            }
+            break;
+        case 2:
+            len = this.productions_[action[1]][1];
+            yyval.$ = vstack[vstack.length - len];
+            yyval._$ = {
+                first_line: lstack[lstack.length - (len || 1)].first_line,
+                last_line: lstack[lstack.length - 1].last_line,
+                first_column: lstack[lstack.length - (len || 1)].first_column,
+                last_column: lstack[lstack.length - 1].last_column
+            };
+            if (ranges) {
+                yyval._$.range = [
+                    lstack[lstack.length - (len || 1)].range[0],
+                    lstack[lstack.length - 1].range[1]
+                ];
+            }
+            r = this.performAction.apply(yyval, [
+                yytext,
+                yyleng,
+                yylineno,
+                sharedState.yy,
+                action[1],
+                vstack,
+                lstack
+            ].concat(args));
+            if (typeof r !== 'undefined') {
+                return r;
+            }
+            if (len) {
+                stack = stack.slice(0, -1 * len * 2);
+                vstack = vstack.slice(0, -1 * len);
+                lstack = lstack.slice(0, -1 * len);
+            }
+            stack.push(this.productions_[action[1]][0]);
+            vstack.push(yyval.$);
+            lstack.push(yyval._$);
+            newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+            stack.push(newState);
+            break;
+        case 3:
+            return true;
+        }
+    }
+    return true;
+}};
+
+/* generated by jison-lex 0.3.4 */
+var lexer = (function(){
+var lexer = ({
+
+EOF:1,
+
+parseError:function parseError(str, hash) {
+        if (this.yy.parser) {
+            this.yy.parser.parseError(str, hash);
+        } else {
+            throw new Error(str);
+        }
+    },
+
+// resets the lexer, sets new input
+setInput:function (input, yy) {
+        this.yy = yy || this.yy || {};
+        this._input = input;
+        this._more = this._backtrack = this.done = false;
+        this.yylineno = this.yyleng = 0;
+        this.yytext = this.matched = this.match = '';
+        this.conditionStack = ['INITIAL'];
+        this.yylloc = {
+            first_line: 1,
+            first_column: 0,
+            last_line: 1,
+            last_column: 0
+        };
+        if (this.options.ranges) {
+            this.yylloc.range = [0,0];
+        }
+        this.offset = 0;
+        return this;
+    },
+
+// consumes and returns one char from the input
+input:function () {
+        var ch = this._input[0];
+        this.yytext += ch;
+        this.yyleng++;
+        this.offset++;
+        this.match += ch;
+        this.matched += ch;
+        var lines = ch.match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno++;
+            this.yylloc.last_line++;
+        } else {
+            this.yylloc.last_column++;
+        }
+        if (this.options.ranges) {
+            this.yylloc.range[1]++;
+        }
+
+        this._input = this._input.slice(1);
+        return ch;
+    },
+
+// unshifts one char (or a string) into the input
+unput:function (ch) {
+        var len = ch.length;
+        var lines = ch.split(/(?:\r\n?|\n)/g);
+
+        this._input = ch + this._input;
+        this.yytext = this.yytext.substr(0, this.yytext.length - len);
+        //this.yyleng -= len;
+        this.offset -= len;
+        var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+        this.match = this.match.substr(0, this.match.length - 1);
+        this.matched = this.matched.substr(0, this.matched.length - 1);
+
+        if (lines.length - 1) {
+            this.yylineno -= lines.length - 1;
+        }
+        var r = this.yylloc.range;
+
+        this.yylloc = {
+            first_line: this.yylloc.first_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.first_column,
+            last_column: lines ?
+                (lines.length === oldLines.length ? this.yylloc.first_column : 0)
+                 + oldLines[oldLines.length - lines.length].length - lines[0].length :
+              this.yylloc.first_column - len
+        };
+
+        if (this.options.ranges) {
+            this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+        }
+        this.yyleng = this.yytext.length;
+        return this;
+    },
+
+// When called from action, caches matched text and appends it on next action
+more:function () {
+        this._more = true;
+        return this;
+    },
+
+// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
+reject:function () {
+        if (this.options.backtrack_lexer) {
+            this._backtrack = true;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+
+        }
+        return this;
+    },
+
+// retain first n characters of the match
+less:function (n) {
+        this.unput(this.match.slice(n));
+    },
+
+// displays already matched input, i.e. for error messages
+pastInput:function () {
+        var past = this.matched.substr(0, this.matched.length - this.match.length);
+        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+    },
+
+// displays upcoming input, i.e. for error messages
+upcomingInput:function () {
+        var next = this.match;
+        if (next.length < 20) {
+            next += this._input.substr(0, 20-next.length);
+        }
+        return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+    },
+
+// displays the character position where the lexing error occurred, i.e. for error messages
+showPosition:function () {
+        var pre = this.pastInput();
+        var c = new Array(pre.length + 1).join("-");
+        return pre + this.upcomingInput() + "\n" + c + "^";
+    },
+
+// test the lexed token: return FALSE when not a match, otherwise return token
+test_match:function (match, indexed_rule) {
+        var token,
+            lines,
+            backup;
+
+        if (this.options.backtrack_lexer) {
+            // save context
+            backup = {
+                yylineno: this.yylineno,
+                yylloc: {
+                    first_line: this.yylloc.first_line,
+                    last_line: this.last_line,
+                    first_column: this.yylloc.first_column,
+                    last_column: this.yylloc.last_column
+                },
+                yytext: this.yytext,
+                match: this.match,
+                matches: this.matches,
+                matched: this.matched,
+                yyleng: this.yyleng,
+                offset: this.offset,
+                _more: this._more,
+                _input: this._input,
+                yy: this.yy,
+                conditionStack: this.conditionStack.slice(0),
+                done: this.done
+            };
+            if (this.options.ranges) {
+                backup.yylloc.range = this.yylloc.range.slice(0);
+            }
+        }
+
+        lines = match[0].match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno += lines.length;
+        }
+        this.yylloc = {
+            first_line: this.yylloc.last_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.last_column,
+            last_column: lines ?
+                         lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
+                         this.yylloc.last_column + match[0].length
+        };
+        this.yytext += match[0];
+        this.match += match[0];
+        this.matches = match;
+        this.yyleng = this.yytext.length;
+        if (this.options.ranges) {
+            this.yylloc.range = [this.offset, this.offset += this.yyleng];
+        }
+        this._more = false;
+        this._backtrack = false;
+        this._input = this._input.slice(match[0].length);
+        this.matched += match[0];
+        token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
+        if (this.done && this._input) {
+            this.done = false;
+        }
+        if (token) {
+            return token;
+        } else if (this._backtrack) {
+            // recover context
+            for (var k in backup) {
+                this[k] = backup[k];
+            }
+            return false; // rule action called reject() implying the next rule should be tested instead.
+        }
+        return false;
+    },
+
+// return next match in input
+next:function () {
+        if (this.done) {
+            return this.EOF;
+        }
+        if (!this._input) {
+            this.done = true;
+        }
+
+        var token,
+            match,
+            tempMatch,
+            index;
+        if (!this._more) {
+            this.yytext = '';
+            this.match = '';
+        }
+        var rules = this._currentRules();
+        for (var i = 0; i < rules.length; i++) {
+            tempMatch = this._input.match(this.rules[rules[i]]);
+            if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+                match = tempMatch;
+                index = i;
+                if (this.options.backtrack_lexer) {
+                    token = this.test_match(tempMatch, rules[i]);
+                    if (token !== false) {
+                        return token;
+                    } else if (this._backtrack) {
+                        match = false;
+                        continue; // rule action called reject() implying a rule MISmatch.
+                    } else {
+                        // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+                        return false;
+                    }
+                } else if (!this.options.flex) {
+                    break;
+                }
+            }
+        }
+        if (match) {
+            token = this.test_match(match, rules[index]);
+            if (token !== false) {
+                return token;
+            }
+            // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+            return false;
+        }
+        if (this._input === "") {
+            return this.EOF;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+        }
+    },
+
+// return next match that has a token
+lex:function lex() {
+        var r = this.next();
+        if (r) {
+            return r;
+        } else {
+            return this.lex();
+        }
+    },
+
+// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
+begin:function begin(condition) {
+        this.conditionStack.push(condition);
+    },
+
+// pop the previously active lexer condition state off the condition stack
+popState:function popState() {
+        var n = this.conditionStack.length - 1;
+        if (n > 0) {
+            return this.conditionStack.pop();
+        } else {
+            return this.conditionStack[0];
+        }
+    },
+
+// produce the lexer rule set which is active for the currently active lexer condition state
+_currentRules:function _currentRules() {
+        if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
+            return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+        } else {
+            return this.conditions["INITIAL"].rules;
+        }
+    },
+
+// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
+topState:function topState(n) {
+        n = this.conditionStack.length - 1 - Math.abs(n || 0);
+        if (n >= 0) {
+            return this.conditionStack[n];
+        } else {
+            return "INITIAL";
+        }
+    },
+
+// alias for begin(condition)
+pushState:function pushState(condition) {
+        this.begin(condition);
+    },
+
+// return the number of states currently on the stack
+stateStackSize:function stateStackSize() {
+        return this.conditionStack.length;
+    },
+options: {},
+performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0:/* do nothing */
+break;
+case 1:return 6;
+break;
+case 2:/* skip whitespace */
+break;
+case 3:return 5;
+break;
+case 4: this.begin("struct"); /*console.log('Starting struct');*/return 17;
+break;
+case 5: /*console.log('Ending struct');*/this.popState(); return 19;
+break;
+case 6:/* nothing */
+break;
+case 7: /*console.log('lex-member: ' + yy_.yytext);*/  return "MEMBER";
+break;
+case 8:return 16;
+break;
+case 9:this.begin("string");
+break;
+case 10:this.popState();
+break;
+case 11:return "STR";
+break;
+case 12:return 27;
+break;
+case 13:return 27;
+break;
+case 14:return 29;
+break;
+case 15:return 29;
+break;
+case 16:return 28;
+break;
+case 17:return 26;
+break;
+case 18:return 30;
+break;
+case 19:return 31;
+break;
+case 20:return 13;
+break;
+case 21:return 43;
+break;
+case 22:return 'DOT';
+break;
+case 23:return 'PLUS';
+break;
+case 24:return 40;
+break;
+case 25:return 'EQUALS';
+break;
+case 26:return 'EQUALS';
+break;
+case 27:return 47;
+break;
+case 28:return 'PUNCTUATION';
+break;
+case 29:return 46;
+break;
+case 30:return 45;
+break;
+case 31:return 42;
+break;
+case 32:return 8;
+break;
+}
+},
+rules: [/^(?:%%[^\n]*)/,/^(?:\n+)/,/^(?:\s+)/,/^(?:classDiagram\b)/,/^(?:[\{])/,/^(?:\})/,/^(?:[\n])/,/^(?:[^\{\}\n]*)/,/^(?:class\b)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:\s*<\|)/,/^(?:\s*\|>)/,/^(?:\s*>)/,/^(?:\s*<)/,/^(?:\s*\*)/,/^(?:\s*o\b)/,/^(?:--)/,/^(?:\.\.)/,/^(?::[^#\n;]+)/,/^(?:-)/,/^(?:\.)/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:[A-Za-z]+)/,/^(?:[!"#$%&'*+,-.`?\\_\/])/,/^(?:[0-9]+)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\s)/,/^(?:$)/],
+conditions: {"string":{"rules":[10,11],"inclusive":false},"struct":{"rules":[5,6,7],"inclusive":false},"INITIAL":{"rules":[0,1,2,3,4,8,9,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"inclusive":true}}
+});
+return lexer;
+})();
+parser.lexer = lexer;
+function Parser () {
+  this.yy = {};
+}
+Parser.prototype = parser;parser.Parser = Parser;
+return new Parser;
+})();
+
+
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+exports.parser = parser;
+exports.Parser = parser.Parser;
+exports.parse = function () { return parser.parse.apply(parser, arguments); };
+exports.main = function commonjsMain(args) {
+    if (!args[1]) {
+        console.log('Usage: '+args[0]+' FILE');
+        process.exit(1);
+    }
+    var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8");
+    return exports.parser.parse(source);
+};
+if (typeof module !== 'undefined' && require.main === module) {
+  exports.main(process.argv.slice(1));
+}
+}
\ No newline at end of file
diff --git a/_submodules/mermaid/src/diagrams/flowchart/flowDb.js b/_submodules/mermaid/src/diagrams/flowchart/flowDb.js
new file mode 100644
index 0000000000000000000000000000000000000000..275e9c39df5c52839455f0adff0af0c4f131115f
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/flowchart/flowDb.js
@@ -0,0 +1,423 @@
+import * as d3 from 'd3'
+
+import { logger } from '../../logger'
+import utils from '../../utils'
+
+let vertices = {}
+let edges = []
+let classes = []
+let subGraphs = []
+let tooltips = {}
+let subCount = 0
+let direction
+// Functions to be run after graph rendering
+let funs = []
+/**
+ * Function called by parser when a node definition has been found
+ * @param id
+ * @param text
+ * @param type
+ * @param style
+ */
+export const addVertex = function (id, text, type, style) {
+  let txt
+
+  if (typeof id === 'undefined') {
+    return
+  }
+  if (id.trim().length === 0) {
+    return
+  }
+
+  if (typeof vertices[id] === 'undefined') {
+    vertices[id] = { id: id, styles: [], classes: [] }
+  }
+  if (typeof text !== 'undefined') {
+    txt = text.trim()
+
+    // strip quotes if string starts and exnds with a quote
+    if (txt[0] === '"' && txt[txt.length - 1] === '"') {
+      txt = txt.substring(1, txt.length - 1)
+    }
+
+    vertices[id].text = txt
+  }
+  if (typeof type !== 'undefined') {
+    vertices[id].type = type
+  }
+  if (typeof type !== 'undefined') {
+    vertices[id].type = type
+  }
+  if (typeof style !== 'undefined') {
+    if (style !== null) {
+      style.forEach(function (s) {
+        vertices[id].styles.push(s)
+      })
+    }
+  }
+}
+
+/**
+ * Function called by parser when a link/edge definition has been found
+ * @param start
+ * @param end
+ * @param type
+ * @param linktext
+ */
+export const addLink = function (start, end, type, linktext) {
+  logger.info('Got edge...', start, end)
+  const edge = { start: start, end: end, type: undefined, text: '' }
+  linktext = type.text
+
+  if (typeof linktext !== 'undefined') {
+    edge.text = linktext.trim()
+
+    // strip quotes if string starts and exnds with a quote
+    if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
+      edge.text = edge.text.substring(1, edge.text.length - 1)
+    }
+  }
+
+  if (typeof type !== 'undefined') {
+    edge.type = type.type
+    edge.stroke = type.stroke
+  }
+  edges.push(edge)
+}
+
+/**
+ * Updates a link's line interpolation algorithm
+ * @param pos
+ * @param interpolate
+ */
+export const updateLinkInterpolate = function (pos, interp) {
+  if (pos === 'default') {
+    edges.defaultInterpolate = interp
+  } else {
+    edges[pos].interpolate = interp
+  }
+}
+
+/**
+ * Updates a link with a style
+ * @param pos
+ * @param style
+ */
+export const updateLink = function (pos, style) {
+  if (pos === 'default') {
+    edges.defaultStyle = style
+  } else {
+    if (utils.isSubstringInArray('fill', style) === -1) {
+      style.push('fill:none')
+    }
+    edges[pos].style = style
+  }
+}
+
+export const addClass = function (id, style) {
+  if (typeof classes[id] === 'undefined') {
+    classes[id] = { id: id, styles: [] }
+  }
+
+  if (typeof style !== 'undefined') {
+    if (style !== null) {
+      style.forEach(function (s) {
+        classes[id].styles.push(s)
+      })
+    }
+  }
+}
+
+/**
+ * Called by parser when a graph definition is found, stores the direction of the chart.
+ * @param dir
+ */
+export const setDirection = function (dir) {
+  direction = dir
+}
+
+/**
+ * Called by parser when a graph definition is found, stores the direction of the chart.
+ * @param dir
+ */
+export const setClass = function (id, className) {
+  if (id.indexOf(',') > 0) {
+    id.split(',').forEach(function (id2) {
+      if (typeof vertices[id2] !== 'undefined') {
+        vertices[id2].classes.push(className)
+      }
+    })
+  } else {
+    if (typeof vertices[id] !== 'undefined') {
+      vertices[id].classes.push(className)
+    }
+  }
+}
+
+const setTooltip = function (id, tooltip) {
+  if (typeof tooltip !== 'undefined') {
+    tooltips[id] = tooltip
+  }
+}
+
+const setClickFun = function (id, functionName) {
+  if (typeof functionName === 'undefined') {
+    return
+  }
+  if (typeof vertices[id] !== 'undefined') {
+    funs.push(function (element) {
+      const elem = d3.select(element).select(`[id="${id}"]`)
+      if (elem !== null) {
+        elem.on('click', function () {
+          window[functionName](id)
+        })
+      }
+    })
+  }
+}
+
+const setLink = function (id, linkStr) {
+  if (typeof linkStr === 'undefined') {
+    return
+  }
+  if (typeof vertices[id] !== 'undefined') {
+    funs.push(function (element) {
+      const elem = d3.select(element).select(`[id="${id}"]`)
+      if (elem !== null) {
+        elem.on('click', function () {
+          window.open(linkStr, 'newTab')
+        })
+      }
+    })
+  }
+}
+export const getTooltip = function (id) {
+  return tooltips[id]
+}
+
+/**
+ * Called by parser when a graph definition is found, stores the direction of the chart.
+ * @param dir
+ */
+export const setClickEvent = function (id, functionName, link, tooltip) {
+  if (id.indexOf(',') > 0) {
+    id.split(',').forEach(function (id2) {
+      setTooltip(id2, tooltip)
+      setClickFun(id2, functionName)
+      setLink(id2, link)
+      setClass(id, 'clickable')
+    })
+  } else {
+    setTooltip(id, tooltip)
+    setClickFun(id, functionName)
+    setLink(id, link)
+    setClass(id, 'clickable')
+  }
+}
+
+export const bindFunctions = function (element) {
+  funs.forEach(function (fun) {
+    fun(element)
+  })
+}
+export const getDirection = function () {
+  return direction
+}
+/**
+ * Retrieval function for fetching the found nodes after parsing has completed.
+ * @returns {{}|*|vertices}
+ */
+export const getVertices = function () {
+  return vertices
+}
+
+/**
+ * Retrieval function for fetching the found links after parsing has completed.
+ * @returns {{}|*|edges}
+ */
+export const getEdges = function () {
+  return edges
+}
+
+/**
+ * Retrieval function for fetching the found class definitions after parsing has completed.
+ * @returns {{}|*|classes}
+ */
+export const getClasses = function () {
+  return classes
+}
+
+const setupToolTips = function (element) {
+  let tooltipElem = d3.select('.mermaidTooltip')
+  if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
+    tooltipElem = d3.select('body')
+      .append('div')
+      .attr('class', 'mermaidTooltip')
+      .style('opacity', 0)
+  }
+
+  const svg = d3.select(element).select('svg')
+
+  const nodes = svg.selectAll('g.node')
+  nodes
+    .on('mouseover', function () {
+      const el = d3.select(this)
+      const title = el.attr('title')
+      // Dont try to draw a tooltip if no data is provided
+      if (title === null) {
+        return
+      }
+      const rect = this.getBoundingClientRect()
+
+      tooltipElem.transition()
+        .duration(200)
+        .style('opacity', '.9')
+      tooltipElem.html(el.attr('title'))
+        .style('left', (rect.left + (rect.right - rect.left) / 2) + 'px')
+        .style('top', (rect.top - 14 + document.body.scrollTop) + 'px')
+      el.classed('hover', true)
+    })
+    .on('mouseout', function () {
+      tooltipElem.transition()
+        .duration(500)
+        .style('opacity', 0)
+      const el = d3.select(this)
+      el.classed('hover', false)
+    })
+}
+funs.push(setupToolTips)
+
+/**
+ * Clears the internal graph db so that a new graph can be parsed.
+ */
+export const clear = function () {
+  vertices = {}
+  classes = {}
+  edges = []
+  funs = []
+  funs.push(setupToolTips)
+  subGraphs = []
+  subCount = 0
+  tooltips = []
+}
+/**
+ *
+ * @returns {string}
+ */
+export const defaultStyle = function () {
+  return 'fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;'
+}
+
+/**
+ * Clears the internal graph db so that a new graph can be parsed.
+ */
+export const addSubGraph = function (list, title) {
+  function uniq (a) {
+    const prims = { 'boolean': {}, 'number': {}, 'string': {} }
+    const objs = []
+
+    return a.filter(function (item) {
+      const type = typeof item
+      if (item.trim() === '') {
+        return false
+      }
+      if (type in prims) { return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true) } else { return objs.indexOf(item) >= 0 ? false : objs.push(item) }
+    })
+  }
+
+  let nodeList = []
+
+  nodeList = uniq(nodeList.concat.apply(nodeList, list))
+
+  const subGraph = { id: 'subGraph' + subCount, nodes: nodeList, title: title.trim() }
+  subGraphs.push(subGraph)
+  subCount = subCount + 1
+  return subGraph.id
+}
+
+const getPosForId = function (id) {
+  for (let i = 0; i < subGraphs.length; i++) {
+    if (subGraphs[i].id === id) {
+      return i
+    }
+  }
+  return -1
+}
+let secCount = -1
+const posCrossRef = []
+const indexNodes2 = function (id, pos) {
+  const nodes = subGraphs[pos].nodes
+  secCount = secCount + 1
+  if (secCount > 2000) {
+    return
+  }
+  posCrossRef[secCount] = pos
+  // Check if match
+  if (subGraphs[pos].id === id) {
+    return {
+      result: true,
+      count: 0
+    }
+  }
+
+  let count = 0
+  let posCount = 1
+  while (count < nodes.length) {
+    const childPos = getPosForId(nodes[count])
+    // Ignore regular nodes (pos will be -1)
+    if (childPos >= 0) {
+      const res = indexNodes2(id, childPos)
+      if (res.result) {
+        return {
+          result: true,
+          count: posCount + res.count
+        }
+      } else {
+        posCount = posCount + res.count
+      }
+    }
+    count = count + 1
+  }
+
+  return {
+    result: false,
+    count: posCount
+  }
+}
+
+export const getDepthFirstPos = function (pos) {
+  return posCrossRef[pos]
+}
+export const indexNodes = function () {
+  secCount = -1
+  if (subGraphs.length > 0) {
+    indexNodes2('none', subGraphs.length - 1, 0)
+  }
+}
+
+export const getSubGraphs = function () {
+  return subGraphs
+}
+
+export default {
+  addVertex,
+  addLink,
+  updateLinkInterpolate,
+  updateLink,
+  addClass,
+  setDirection,
+  setClass,
+  getTooltip,
+  setClickEvent,
+  bindFunctions,
+  getDirection,
+  getVertices,
+  getEdges,
+  getClasses,
+  clear,
+  defaultStyle,
+  addSubGraph,
+  getDepthFirstPos,
+  indexNodes,
+  getSubGraphs
+}
diff --git a/_submodules/mermaid/src/diagrams/flowchart/flowRenderer.js b/_submodules/mermaid/src/diagrams/flowchart/flowRenderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..2375265055373bbbc2a609e7717fe0a3eadd8069
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/flowchart/flowRenderer.js
@@ -0,0 +1,473 @@
+import graphlib from 'graphlibrary'
+import * as d3 from 'd3'
+
+import flowDb from './flowDb'
+import flow from './parser/flow'
+import dagreD3 from 'dagre-d3-renderer'
+import { logger } from '../../logger'
+import { interpolateToCurve } from '../../utils'
+
+const conf = {
+}
+export const setConf = function (cnf) {
+  const keys = Object.keys(cnf)
+  for (let i = 0; i < keys.length; i++) {
+    conf[keys[i]] = cnf[keys[i]]
+  }
+}
+
+/**
+ * Function that adds the vertices found in the graph definition to the graph to be rendered.
+ * @param vert Object containing the vertices.
+ * @param g The graph that is to be drawn.
+ */
+export const addVertices = function (vert, g) {
+  const keys = Object.keys(vert)
+
+  const styleFromStyleArr = function (styleStr, arr) {
+    // Create a compound style definition from the style definitions found for the node in the graph definition
+    for (let i = 0; i < arr.length; i++) {
+      if (typeof arr[i] !== 'undefined') {
+        styleStr = styleStr + arr[i] + ';'
+      }
+    }
+
+    return styleStr
+  }
+
+  // Iterate through each item in the vertice object (containing all the vertices found) in the graph definition
+  keys.forEach(function (id) {
+    const vertice = vert[id]
+    let verticeText
+
+    /**
+     * Variable for storing the classes for the vertice
+     * @type {string}
+     */
+    let classStr = ''
+    if (vertice.classes.length > 0) {
+      classStr = vertice.classes.join(' ')
+    }
+
+    /**
+     * Variable for storing the extracted style for the vertice
+     * @type {string}
+     */
+    let style = ''
+    // Create a compound style definition from the style definitions found for the node in the graph definition
+    style = styleFromStyleArr(style, vertice.styles)
+
+    // Use vertice id as text in the box if no text is provided by the graph definition
+    if (typeof vertice.text === 'undefined') {
+      verticeText = vertice.id
+    } else {
+      verticeText = vertice.text
+    }
+
+    let labelTypeStr = ''
+    if (conf.htmlLabels) {
+      labelTypeStr = 'html'
+      verticeText = verticeText.replace(/fa:fa[\w-]+/g, function (s) {
+        return '<i class="fa ' + s.substring(3) + '"></i>'
+      })
+    } else {
+      const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text')
+
+      const rows = verticeText.split(/<br>/)
+
+      for (let j = 0; j < rows.length; j++) {
+        const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan')
+        tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve')
+        tspan.setAttribute('dy', '1em')
+        tspan.setAttribute('x', '1')
+        tspan.textContent = rows[j]
+        svgLabel.appendChild(tspan)
+      }
+
+      labelTypeStr = 'svg'
+      verticeText = svgLabel
+    }
+
+    let radious = 0
+    let _shape = ''
+    // Set the shape based parameters
+    switch (vertice.type) {
+      case 'round':
+        radious = 5
+        _shape = 'rect'
+        break
+      case 'square':
+        _shape = 'rect'
+        break
+      case 'diamond':
+        _shape = 'question'
+        break
+      case 'odd':
+        _shape = 'rect_left_inv_arrow'
+        break
+      case 'odd_right':
+        _shape = 'rect_left_inv_arrow'
+        break
+      case 'circle':
+        _shape = 'circle'
+        break
+      case 'ellipse':
+        _shape = 'ellipse'
+        break
+      case 'group':
+        _shape = 'rect'
+        // Need to create a text node if using svg labels, see #367
+        verticeText = conf.htmlLabels ? '' : document.createElementNS('http://www.w3.org/2000/svg', 'text')
+        break
+      default:
+        _shape = 'rect'
+    }
+    // Add the node
+    g.setNode(vertice.id, { labelType: labelTypeStr, shape: _shape, label: verticeText, rx: radious, ry: radious, 'class': classStr, style: style, id: vertice.id })
+  })
+}
+
+/**
+ * Add edges to graph based on parsed graph defninition
+ * @param {Object} edges The edges to add to the graph
+ * @param {Object} g The graph object
+ */
+export const addEdges = function (edges, g) {
+  let cnt = 0
+
+  let defaultStyle
+  if (typeof edges.defaultStyle !== 'undefined') {
+    defaultStyle = edges.defaultStyle.toString().replace(/,/g, ';')
+  }
+
+  edges.forEach(function (edge) {
+    cnt++
+    const edgeData = {}
+
+    // Set link type for rendering
+    if (edge.type === 'arrow_open') {
+      edgeData.arrowhead = 'none'
+    } else {
+      edgeData.arrowhead = 'normal'
+    }
+
+    let style = ''
+    if (typeof edge.style !== 'undefined') {
+      edge.style.forEach(function (s) {
+        style = style + s + ';'
+      })
+    } else {
+      switch (edge.stroke) {
+        case 'normal':
+          style = 'fill:none'
+          if (typeof defaultStyle !== 'undefined') {
+            style = defaultStyle
+          }
+          break
+        case 'dotted':
+          style = 'stroke: #333; fill:none;stroke-width:2px;stroke-dasharray:3;'
+          break
+        case 'thick':
+          style = 'stroke: #333; stroke-width: 3.5px;fill:none'
+          break
+      }
+    }
+    edgeData.style = style
+
+    if (typeof edge.interpolate !== 'undefined') {
+      edgeData.curve = interpolateToCurve(edge.interpolate, d3.curveLinear)
+    } else if (typeof edges.defaultInterpolate !== 'undefined') {
+      edgeData.curve = interpolateToCurve(edges.defaultInterpolate, d3.curveLinear)
+    } else {
+      edgeData.curve = interpolateToCurve(conf.curve, d3.curveLinear)
+    }
+
+    if (typeof edge.text === 'undefined') {
+      if (typeof edge.style !== 'undefined') {
+        edgeData.arrowheadStyle = 'fill: #333'
+      }
+    } else {
+      edgeData.arrowheadStyle = 'fill: #333'
+      if (typeof edge.style === 'undefined') {
+        edgeData.labelpos = 'c'
+        if (conf.htmlLabels) {
+          edgeData.labelType = 'html'
+          edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>'
+        } else {
+          edgeData.labelType = 'text'
+          edgeData.style = 'stroke: #333; stroke-width: 1.5px;fill:none'
+          edgeData.label = edge.text.replace(/<br>/g, '\n')
+        }
+      } else {
+        edgeData.label = edge.text.replace(/<br>/g, '\n')
+      }
+    }
+    // Add the edge to the graph
+    g.setEdge(edge.start, edge.end, edgeData, cnt)
+  })
+}
+
+/**
+ * Returns the all the styles from classDef statements in the graph definition.
+ * @returns {object} classDef styles
+ */
+export const getClasses = function (text) {
+  flowDb.clear()
+  const parser = flow.parser
+  parser.yy = flowDb
+
+  // Parse the graph definition
+  parser.parse(text)
+  return flowDb.getClasses()
+}
+
+/**
+ * Draws a flowchart in the tag with id: id based on the graph definition in text.
+ * @param text
+ * @param id
+ */
+export const draw = function (text, id) {
+  logger.debug('Drawing flowchart')
+  flowDb.clear()
+  const parser = flow.parser
+  parser.yy = flowDb
+
+  // Parse the graph definition
+  try {
+    parser.parse(text)
+  } catch (err) {
+    logger.debug('Parsing failed')
+  }
+
+  // Fetch the default direction, use TD if none was found
+  let dir = flowDb.getDirection()
+  if (typeof dir === 'undefined') {
+    dir = 'TD'
+  }
+
+  // Create the input mermaid.graph
+  const g = new graphlib.Graph({
+    multigraph: true,
+    compound: true
+  })
+    .setGraph({
+      rankdir: dir,
+      marginx: 20,
+      marginy: 20
+
+    })
+    .setDefaultEdgeLabel(function () {
+      return {}
+    })
+
+  let subG
+  const subGraphs = flowDb.getSubGraphs()
+  for (let i = subGraphs.length - 1; i >= 0; i--) {
+    subG = subGraphs[i]
+    flowDb.addVertex(subG.id, subG.title, 'group', undefined)
+  }
+
+  // Fetch the verices/nodes and edges/links from the parsed graph definition
+  const vert = flowDb.getVertices()
+
+  const edges = flowDb.getEdges()
+
+  let i = 0
+  for (i = subGraphs.length - 1; i >= 0; i--) {
+    subG = subGraphs[i]
+
+    d3.selectAll('cluster').append('text')
+
+    for (let j = 0; j < subG.nodes.length; j++) {
+      g.setParent(subG.nodes[j], subG.id)
+    }
+  }
+  addVertices(vert, g)
+  addEdges(edges, g)
+
+  // Create the renderer
+  const Render = dagreD3.render
+  const render = new Render()
+
+  // Add custom shape for rhombus type of boc (decision)
+  render.shapes().question = function (parent, bbox, node) {
+    const w = bbox.width
+    const h = bbox.height
+    const s = (w + h) * 0.9
+    const points = [
+      { x: s / 2, y: 0 },
+      { x: s, y: -s / 2 },
+      { x: s / 2, y: -s },
+      { x: 0, y: -s / 2 }
+    ]
+    const shapeSvg = parent.insert('polygon', ':first-child')
+      .attr('points', points.map(function (d) {
+        return d.x + ',' + d.y
+      }).join(' '))
+      .attr('rx', 5)
+      .attr('ry', 5)
+      .attr('transform', 'translate(' + (-s / 2) + ',' + (s * 2 / 4) + ')')
+    node.intersect = function (point) {
+      return dagreD3.intersect.polygon(node, points, point)
+    }
+    return shapeSvg
+  }
+
+  // Add custom shape for box with inverted arrow on left side
+  render.shapes().rect_left_inv_arrow = function (parent, bbox, node) {
+    const w = bbox.width
+    const h = bbox.height
+    const points = [
+      { x: -h / 2, y: 0 },
+      { x: w, y: 0 },
+      { x: w, y: -h },
+      { x: -h / 2, y: -h },
+      { x: 0, y: -h / 2 }
+    ]
+    const shapeSvg = parent.insert('polygon', ':first-child')
+      .attr('points', points.map(function (d) {
+        return d.x + ',' + d.y
+      }).join(' '))
+      .attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
+    node.intersect = function (point) {
+      return dagreD3.intersect.polygon(node, points, point)
+    }
+    return shapeSvg
+  }
+
+  // Add custom shape for box with inverted arrow on right side
+  render.shapes().rect_right_inv_arrow = function (parent, bbox, node) {
+    const w = bbox.width
+    const h = bbox.height
+    const points = [
+      { x: 0, y: 0 },
+      { x: w + h / 2, y: 0 },
+      { x: w, y: -h / 2 },
+      { x: w + h / 2, y: -h },
+      { x: 0, y: -h }
+    ]
+    const shapeSvg = parent.insert('polygon', ':first-child')
+      .attr('points', points.map(function (d) {
+        return d.x + ',' + d.y
+      }).join(' '))
+      .attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
+    node.intersect = function (point) {
+      return dagreD3.intersect.polygon(node, points, point)
+    }
+    return shapeSvg
+  }
+
+  // Add our custom arrow - an empty arrowhead
+  render.arrows().none = function normal (parent, id, edge, type) {
+    const marker = parent.append('marker')
+      .attr('id', id)
+      .attr('viewBox', '0 0 10 10')
+      .attr('refX', 9)
+      .attr('refY', 5)
+      .attr('markerUnits', 'strokeWidth')
+      .attr('markerWidth', 8)
+      .attr('markerHeight', 6)
+      .attr('orient', 'auto')
+
+    const path = marker.append('path')
+      .attr('d', 'M 0 0 L 0 0 L 0 0 z')
+    dagreD3.util.applyStyle(path, edge[type + 'Style'])
+  }
+
+  // Override normal arrowhead defined in d3. Remove style & add class to allow css styling.
+  render.arrows().normal = function normal (parent, id, edge, type) {
+    const marker = parent.append('marker')
+      .attr('id', id)
+      .attr('viewBox', '0 0 10 10')
+      .attr('refX', 9)
+      .attr('refY', 5)
+      .attr('markerUnits', 'strokeWidth')
+      .attr('markerWidth', 8)
+      .attr('markerHeight', 6)
+      .attr('orient', 'auto')
+
+    marker.append('path')
+      .attr('d', 'M 0 0 L 10 5 L 0 10 z')
+      .attr('class', 'arrowheadPath')
+      .style('stroke-width', 1)
+      .style('stroke-dasharray', '1,0')
+  }
+
+  // Set up an SVG group so that we can translate the final graph.
+  const svg = d3.select(`[id="${id}"]`)
+
+  // Run the renderer. This is what draws the final graph.
+  const element = d3.select('#' + id + ' g')
+  render(element, g)
+
+  element.selectAll('g.node')
+    .attr('title', function () {
+      return flowDb.getTooltip(this.id)
+    })
+
+  const padding = 8
+  const width = g.maxX - g.minX + padding * 2
+  const height = g.maxY - g.minY + padding * 2
+  svg.attr('width', '100%')
+  svg.attr('style', `max-width: ${width}px;`)
+  svg.attr('viewBox', `0 0 ${width} ${height}`)
+  svg.select('g').attr('transform', `translate(${padding - g.minX}, ${padding - g.minY})`)
+
+  // Index nodes
+  flowDb.indexNodes('subGraph' + i)
+
+  for (i = 0; i < subGraphs.length; i++) {
+    subG = subGraphs[i]
+
+    if (subG.title !== 'undefined') {
+      const clusterRects = document.querySelectorAll('#' + id + ' #' + subG.id + ' rect')
+      const clusterEl = document.querySelectorAll('#' + id + ' #' + subG.id)
+
+      const xPos = clusterRects[0].x.baseVal.value
+      const yPos = clusterRects[0].y.baseVal.value
+      const width = clusterRects[0].width.baseVal.value
+      const cluster = d3.select(clusterEl[0])
+      const te = cluster.append('text')
+      te.attr('x', xPos + width / 2)
+      te.attr('y', yPos + 14)
+      te.attr('fill', 'black')
+      te.attr('stroke', 'none')
+      te.attr('id', id + 'Text')
+      te.style('text-anchor', 'middle')
+
+      if (typeof subG.title === 'undefined') {
+        te.text('Undef')
+      } else {
+        te.text(subG.title)
+      }
+    }
+  }
+
+  // Add label rects for non html labels
+  if (!conf.htmlLabels) {
+    const labels = document.querySelectorAll('#' + id + ' .edgeLabel .label')
+    for (let k = 0; k < labels.length; k++) {
+      const label = labels[k]
+
+      // Get dimensions of label
+      const dim = label.getBBox()
+
+      const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
+      rect.setAttribute('rx', 0)
+      rect.setAttribute('ry', 0)
+      rect.setAttribute('width', dim.width)
+      rect.setAttribute('height', dim.height)
+      rect.setAttribute('style', 'fill:#e8e8e8;')
+
+      label.insertBefore(rect, label.firstChild)
+    }
+  }
+}
+
+export default {
+  setConf,
+  addVertices,
+  addEdges,
+  getClasses,
+  draw
+}
diff --git a/_submodules/mermaid/src/diagrams/flowchart/parser/flow.jison b/_submodules/mermaid/src/diagrams/flowchart/parser/flow.jison
new file mode 100644
index 0000000000000000000000000000000000000000..0bbb4281eafe0d5cf53a1b1f842e11d9acd455b1
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/flowchart/parser/flow.jison
@@ -0,0 +1,452 @@
+/** mermaid
+ *  https://mermaidjs.github.io/
+ *  (c) 2015 Knut Sveidqvist
+ *  MIT license.
+ */
+
+/* lexical grammar */
+%lex
+%x string
+
+%%
+\%\%[^\n]*            /* do nothing */
+["]                     this.begin("string");
+<string>["]             this.popState();
+<string>[^"]*           return "STR";
+"style"               return 'STYLE';
+"default"             return 'DEFAULT';
+"linkStyle"           return 'LINKSTYLE';
+"interpolate"         return 'INTERPOLATE';
+"classDef"            return 'CLASSDEF';
+"class"               return 'CLASS';
+"click"               return 'CLICK';
+"graph"               return 'GRAPH';
+"subgraph"            return 'subgraph';
+"end"\b\s*            return 'end';
+"LR"                  return 'DIR';
+"RL"                  return 'DIR';
+"TB"                  return 'DIR';
+"BT"                  return 'DIR';
+"TD"                  return 'DIR';
+"BR"                  return 'DIR';
+[0-9]+                 return 'NUM';
+\#                    return 'BRKT';
+":"                   return 'COLON';
+";"                   return 'SEMI';
+","                   return 'COMMA';
+"*"                   return 'MULT';
+"<"                   return 'TAGSTART';
+">"                   return 'TAGEND';
+"^"                   return 'UP';
+"v"                   return 'DOWN';
+\s*\-\-[x]\s*            return 'ARROW_CROSS';
+\s*\-\-\>\s*             return 'ARROW_POINT';
+\s*\-\-[o]\s*            return 'ARROW_CIRCLE';
+\s*\-\-\-\s*             return 'ARROW_OPEN';
+\s*\-\.\-[x]\s*          return 'DOTTED_ARROW_CROSS';
+\s*\-\.\-\>\s*           return 'DOTTED_ARROW_POINT';
+\s*\-\.\-[o]\s*          return 'DOTTED_ARROW_CIRCLE';
+\s*\-\.\-\s*             return 'DOTTED_ARROW_OPEN';
+\s*.\-[x]\s*             return 'DOTTED_ARROW_CROSS';
+\s*\.\-\>\s*             return 'DOTTED_ARROW_POINT';
+\s*\.\-[o]\s*            return 'DOTTED_ARROW_CIRCLE';
+\s*\.\-\s*               return 'DOTTED_ARROW_OPEN';
+\s*\=\=[x]\s*            return 'THICK_ARROW_CROSS';
+\s*\=\=\>\s*             return 'THICK_ARROW_POINT';
+\s*\=\=[o]\s*            return 'THICK_ARROW_CIRCLE';
+\s*\=\=[\=]\s*           return 'THICK_ARROW_OPEN';
+\s*\-\-\s*               return '--';
+\s*\-\.\s*               return '-.';
+\s*\=\=\s*               return '==';
+"(-"                  return '(-';
+"-)"                  return '-)';
+\-                    return 'MINUS';
+"."                   return 'DOT';
+\+                    return 'PLUS';
+\%                    return 'PCT';
+"="                   return 'EQUALS';
+\=                    return 'EQUALS';
+[A-Za-z]+             return 'ALPHA';
+[!"#$%&'*+,-.`?\\_/]  return 'PUNCTUATION';
+[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
+[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
+[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|
+[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|
+[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|
+[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|
+[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|
+[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|
+[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|
+[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|
+[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|
+[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|
+[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|
+[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|
+[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|
+[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|
+[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|
+[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|
+[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|
+[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|
+[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|
+[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|
+[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|
+[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|
+[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|
+[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|
+[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|
+[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|
+[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|
+[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|
+[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|
+[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|
+[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|
+[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|
+[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|
+[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|
+[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|
+[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|
+[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|
+[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|
+[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|
+[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|
+[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|
+[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|
+[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|
+[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|
+[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|
+[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|
+[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|
+[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|
+[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|
+[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|
+[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|
+[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|
+[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|
+[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|
+[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|
+[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|
+[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|
+[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|
+[\uFFD2-\uFFD7\uFFDA-\uFFDC]
+                      return 'UNICODE_TEXT';
+"|"                   return 'PIPE';
+"("                   return 'PS';
+")"                   return 'PE';
+"["                   return 'SQS';
+"]"                   return 'SQE';
+"{"                   return 'DIAMOND_START'
+"}"                   return 'DIAMOND_STOP'
+"\""                  return 'QUOTE';
+\n+                   return 'NEWLINE';
+\s                    return 'SPACE';
+<<EOF>>               return 'EOF';
+
+/lex
+
+/* operator associations and precedence */
+
+%left '^'
+
+%start mermaidDoc
+
+%% /* language grammar */
+
+mermaidDoc: graphConfig document;
+
+document
+	: /* empty */
+	{ $$ = [];}
+	| document line
+	{
+	    if($2 !== []){
+	        $1.push($2);
+	    }
+	    $$=$1;}
+	;
+
+line
+	: statement
+	{$$=$1;}
+	| SEMI
+	| NEWLINE
+	| SPACE
+	| EOF
+	;
+
+graphConfig
+    : SPACE graphConfig
+    | NEWLINE graphConfig
+    | GRAPH SPACE DIR FirstStmtSeperator
+        { yy.setDirection($3);$$ = $3;}
+    | GRAPH SPACE TAGEND FirstStmtSeperator
+        { yy.setDirection("LR");$$ = $3;}
+    | GRAPH SPACE TAGSTART FirstStmtSeperator
+        { yy.setDirection("RL");$$ = $3;}
+    | GRAPH SPACE UP FirstStmtSeperator
+        { yy.setDirection("BT");$$ = $3;}
+    | GRAPH SPACE DOWN FirstStmtSeperator
+        { yy.setDirection("TB");$$ = $3;}
+    ;
+
+ending: endToken ending
+      | endToken
+      ;
+
+endToken: NEWLINE | SPACE | EOF;
+
+FirstStmtSeperator
+    : SEMI | NEWLINE | spaceList NEWLINE ;
+
+
+spaceListNewline
+    : SPACE spaceListNewline
+    | NEWLINE spaceListNewline
+    | NEWLINE
+    | SPACE
+    ;
+
+
+spaceList
+    : SPACE spaceList
+    | SPACE
+    ;
+
+statement
+    : verticeStatement separator
+    {$$=$1}
+    | styleStatement separator
+    {$$=[];}
+    | linkStyleStatement separator
+    {$$=[];}
+    | classDefStatement separator
+    {$$=[];}
+    | classStatement separator
+    {$$=[];}
+    | clickStatement separator
+    {$$=[];}
+    | subgraph text separator document end
+    {$$=yy.addSubGraph($4,$2);}
+    | subgraph separator document end
+    {$$=yy.addSubGraph($3,undefined);}
+    ;
+
+separator: NEWLINE | SEMI | EOF ;
+
+verticeStatement:
+     vertex link vertex
+        { yy.addLink($1,$3,$2);$$ = [$1,$3];}
+     | vertex
+        {$$ = [$1];}
+    ;
+
+vertex:  alphaNum SQS text SQE
+        {$$ = $1;yy.addVertex($1,$3,'square');}
+    |  alphaNum SQS text SQE spaceList
+        {$$ = $1;yy.addVertex($1,$3,'square');}
+    | alphaNum PS PS text PE PE
+        {$$ = $1;yy.addVertex($1,$4,'circle');}
+    | alphaNum PS PS text PE PE spaceList
+        {$$ = $1;yy.addVertex($1,$4,'circle');}
+    | alphaNum '(-' text '-)'
+        {$$ = $1;yy.addVertex($1,$3,'ellipse');}
+    | alphaNum '(-' text '-)' spaceList
+        {$$ = $1;yy.addVertex($1,$3,'ellipse');}
+    | alphaNum PS text PE
+        {$$ = $1;yy.addVertex($1,$3,'round');}
+    | alphaNum PS text PE spaceList
+        {$$ = $1;yy.addVertex($1,$3,'round');}
+    | alphaNum DIAMOND_START text DIAMOND_STOP
+        {$$ = $1;yy.addVertex($1,$3,'diamond');}
+    | alphaNum DIAMOND_START text DIAMOND_STOP spaceList
+        {$$ = $1;yy.addVertex($1,$3,'diamond');}
+    | alphaNum TAGEND text SQE
+        {$$ = $1;yy.addVertex($1,$3,'odd');}
+    | alphaNum TAGEND text SQE spaceList
+        {$$ = $1;yy.addVertex($1,$3,'odd');}
+/*  | alphaNum SQS text TAGSTART
+        {$$ = $1;yy.addVertex($1,$3,'odd_right');}
+    | alphaNum SQS text TAGSTART spaceList
+        {$$ = $1;yy.addVertex($1,$3,'odd_right');} */
+    | alphaNum
+        {$$ = $1;yy.addVertex($1);}
+    | alphaNum spaceList
+        {$$ = $1;yy.addVertex($1);}
+    ;
+
+alphaNum
+    : alphaNumStatement
+    {$$=$1;}
+    | alphaNum alphaNumStatement
+    {$$=$1+''+$2;}
+    ;
+
+alphaNumStatement
+    : DIR
+        {$$=$1;}
+    | alphaNumToken
+        {$$=$1;}
+    | DOWN
+        {$$='v';}
+    | MINUS
+        {$$='-';}
+    ;
+
+link: linkStatement arrowText
+    {$1.text = $2;$$ = $1;}
+    | linkStatement TESTSTR SPACE
+    {$1.text = $2;$$ = $1;}
+    | linkStatement arrowText SPACE
+    {$1.text = $2;$$ = $1;}
+    | linkStatement
+    {$$ = $1;}
+    | '--' text ARROW_POINT
+        {$$ = {"type":"arrow","stroke":"normal","text":$2};}
+    | '--' text ARROW_CIRCLE
+        {$$ = {"type":"arrow_circle","stroke":"normal","text":$2};}
+    | '--' text ARROW_CROSS
+        {$$ = {"type":"arrow_cross","stroke":"normal","text":$2};}
+    | '--' text ARROW_OPEN
+        {$$ = {"type":"arrow_open","stroke":"normal","text":$2};}
+    | '-.' text DOTTED_ARROW_POINT
+        {$$ = {"type":"arrow","stroke":"dotted","text":$2};}
+    | '-.' text DOTTED_ARROW_CIRCLE
+        {$$ = {"type":"arrow_circle","stroke":"dotted","text":$2};}
+    | '-.' text DOTTED_ARROW_CROSS
+        {$$ = {"type":"arrow_cross","stroke":"dotted","text":$2};}
+    | '-.' text DOTTED_ARROW_OPEN
+        {$$ = {"type":"arrow_open","stroke":"dotted","text":$2};}
+    | '==' text THICK_ARROW_POINT
+        {$$ = {"type":"arrow","stroke":"thick","text":$2};}
+    | '==' text THICK_ARROW_CIRCLE
+        {$$ = {"type":"arrow_circle","stroke":"thick","text":$2};}
+    | '==' text THICK_ARROW_CROSS
+        {$$ = {"type":"arrow_cross","stroke":"thick","text":$2};}
+    | '==' text THICK_ARROW_OPEN
+        {$$ = {"type":"arrow_open","stroke":"thick","text":$2};}
+    ;
+
+linkStatement: ARROW_POINT
+        {$$ = {"type":"arrow","stroke":"normal"};}
+    | ARROW_CIRCLE
+        {$$ = {"type":"arrow_circle","stroke":"normal"};}
+    | ARROW_CROSS
+        {$$ = {"type":"arrow_cross","stroke":"normal"};}
+    | ARROW_OPEN
+        {$$ = {"type":"arrow_open","stroke":"normal"};}
+    | DOTTED_ARROW_POINT
+        {$$ = {"type":"arrow","stroke":"dotted"};}
+    | DOTTED_ARROW_CIRCLE
+        {$$ = {"type":"arrow_circle","stroke":"dotted"};}
+    | DOTTED_ARROW_CROSS
+        {$$ = {"type":"arrow_cross","stroke":"dotted"};}
+    | DOTTED_ARROW_OPEN
+        {$$ = {"type":"arrow_open","stroke":"dotted"};}
+    | THICK_ARROW_POINT
+        {$$ = {"type":"arrow","stroke":"thick"};}
+    | THICK_ARROW_CIRCLE
+        {$$ = {"type":"arrow_circle","stroke":"thick"};}
+    | THICK_ARROW_CROSS
+        {$$ = {"type":"arrow_cross","stroke":"thick"};}
+    | THICK_ARROW_OPEN
+        {$$ = {"type":"arrow_open","stroke":"thick"};}
+        ;
+
+arrowText:
+    PIPE text PIPE
+    {$$ = $2;}
+    ;
+
+text: textToken
+    {$$=$1;}
+    | text textToken
+    {$$=$1+''+$2;}
+    | STR
+    {$$=$1;}
+    ;
+
+
+
+commentText: commentToken
+    {$$=$1;}
+    | commentText commentToken
+    {$$=$1+''+$2;}
+    ;
+
+
+keywords
+    : STYLE | LINKSTYLE | CLASSDEF | CLASS | CLICK | GRAPH | DIR | subgraph | end | DOWN | UP;
+
+
+textNoTags: textNoTagsToken
+    {$$=$1;}
+    | textNoTags textNoTagsToken
+    {$$=$1+''+$2;}
+    ;
+
+
+classDefStatement:CLASSDEF SPACE DEFAULT SPACE stylesOpt
+    {$$ = $1;yy.addClass($3,$5);}
+    | CLASSDEF SPACE alphaNum SPACE stylesOpt
+          {$$ = $1;yy.addClass($3,$5);}
+    ;
+
+classStatement:CLASS SPACE alphaNum SPACE alphaNum
+    {$$ = $1;yy.setClass($3, $5);}
+    ;
+
+clickStatement
+    : CLICK SPACE alphaNum SPACE alphaNum           {$$ = $1;yy.setClickEvent($3,        $5, undefined, undefined);}
+    | CLICK SPACE alphaNum SPACE alphaNum SPACE STR {$$ = $1;yy.setClickEvent($3,        $5, undefined, $7)       ;}
+    | CLICK SPACE alphaNum SPACE STR                {$$ = $1;yy.setClickEvent($3, undefined,        $5, undefined);}
+    | CLICK SPACE alphaNum SPACE STR SPACE STR      {$$ = $1;yy.setClickEvent($3, undefined,        $5, $7       );}
+    ;
+
+styleStatement:STYLE SPACE alphaNum SPACE stylesOpt
+    {$$ = $1;yy.addVertex($3,undefined,undefined,$5);}
+    | STYLE SPACE HEX SPACE stylesOpt
+          {$$ = $1;yy.updateLink($3,$5);}
+    ;
+
+linkStyleStatement
+    : LINKSTYLE SPACE DEFAULT SPACE stylesOpt
+          {$$ = $1;yy.updateLink($3,$5);}
+    | LINKSTYLE SPACE NUM SPACE stylesOpt
+          {$$ = $1;yy.updateLink($3,$5);}
+    | LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
+          {$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);}
+    | LINKSTYLE SPACE NUM SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
+          {$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);}
+    | LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum
+          {$$ = $1;yy.updateLinkInterpolate($3,$7);}
+    | LINKSTYLE SPACE NUM SPACE INTERPOLATE SPACE alphaNum
+          {$$ = $1;yy.updateLinkInterpolate($3,$7);}
+    ;
+
+commentStatement: PCT PCT commentText;
+
+stylesOpt: style
+        {$$ = [$1]}
+    | stylesOpt COMMA style
+        {$1.push($3);$$ = $1;}
+    ;
+
+style: styleComponent
+    |style styleComponent
+    {$$ = $1 + $2;}
+    ;
+
+styleComponent: ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ;
+
+/* Token lists */
+
+commentToken   : textToken | graphCodeTokens ;
+
+textToken      : textNoTagsToken | TAGSTART | TAGEND | '=='  | '--' | PCT | DEFAULT;
+
+textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ;
+
+alphaNumToken  : ALPHA | PUNCTUATION | UNICODE_TEXT | NUM | COLON | COMMA | PLUS | EQUALS | MULT | DOT | BRKT ;
+
+graphCodeTokens:  PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAG_START | TAG_END | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI ;
+%%
diff --git a/_submodules/mermaid/src/diagrams/flowchart/parser/flow.js b/_submodules/mermaid/src/diagrams/flowchart/parser/flow.js
new file mode 100644
index 0000000000000000000000000000000000000000..f45c6fd71d64f1a4567f1f0c4ffd65c44331166f
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/flowchart/parser/flow.js
@@ -0,0 +1,961 @@
+/* parser generated by jison 0.4.18 */
+/*
+  Returns a Parser object of the following structure:
+
+  Parser: {
+    yy: {}
+  }
+
+  Parser.prototype: {
+    yy: {},
+    trace: function(),
+    symbols_: {associative list: name ==> number},
+    terminals_: {associative list: number ==> name},
+    productions_: [...],
+    performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
+    table: [...],
+    defaultActions: {...},
+    parseError: function(str, hash),
+    parse: function(input),
+
+    lexer: {
+        EOF: 1,
+        parseError: function(str, hash),
+        setInput: function(input),
+        input: function(),
+        unput: function(str),
+        more: function(),
+        less: function(n),
+        pastInput: function(),
+        upcomingInput: function(),
+        showPosition: function(),
+        test_match: function(regex_match_array, rule_index),
+        next: function(),
+        lex: function(),
+        begin: function(condition),
+        popState: function(),
+        _currentRules: function(),
+        topState: function(),
+        pushState: function(condition),
+
+        options: {
+            ranges: boolean           (optional: true ==> token location info will include a .range[] member)
+            flex: boolean             (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
+            backtrack_lexer: boolean  (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
+        },
+
+        performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
+        rules: [...],
+        conditions: {associative list: name ==> set},
+    }
+  }
+
+
+  token location info (@$, _$, etc.): {
+    first_line: n,
+    last_line: n,
+    first_column: n,
+    last_column: n,
+    range: [start_number, end_number]       (where the numbers are indexes into the input string, regular zero-based)
+  }
+
+
+  the parseError function receives a 'hash' object with these members for lexer and parser errors: {
+    text:        (matched text)
+    token:       (the produced terminal token, if any)
+    line:        (yylineno)
+  }
+  while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
+    loc:         (yylloc)
+    expected:    (string describing the set of expected tokens)
+    recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
+  }
+*/
+var parser = (function(){
+var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,4],$V1=[1,3],$V2=[1,5],$V3=[1,8,9,10,11,13,18,30,46,71,72,73,74,75,81,86,88,89,91,92,94,95,96,97,98],$V4=[2,2],$V5=[1,12],$V6=[1,13],$V7=[1,14],$V8=[1,15],$V9=[1,31],$Va=[1,33],$Vb=[1,22],$Vc=[1,34],$Vd=[1,24],$Ve=[1,25],$Vf=[1,26],$Vg=[1,27],$Vh=[1,28],$Vi=[1,38],$Vj=[1,40],$Vk=[1,35],$Vl=[1,39],$Vm=[1,45],$Vn=[1,44],$Vo=[1,36],$Vp=[1,37],$Vq=[1,41],$Vr=[1,42],$Vs=[1,43],$Vt=[1,8,9,10,11,13,18,30,32,46,71,72,73,74,75,81,86,88,89,91,92,94,95,96,97,98],$Vu=[1,53],$Vv=[1,52],$Vw=[1,54],$Vx=[1,72],$Vy=[1,80],$Vz=[1,81],$VA=[1,66],$VB=[1,65],$VC=[1,85],$VD=[1,84],$VE=[1,82],$VF=[1,83],$VG=[1,73],$VH=[1,68],$VI=[1,67],$VJ=[1,63],$VK=[1,75],$VL=[1,76],$VM=[1,77],$VN=[1,78],$VO=[1,79],$VP=[1,70],$VQ=[1,69],$VR=[8,9,11],$VS=[8,9,11,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64],$VT=[1,115],$VU=[8,9,10,11,13,15,18,36,38,40,42,46,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,81,86,88,89,91,92,94,95,96,97,98],$VV=[8,9,10,11,12,13,15,16,17,18,30,32,36,37,38,39,40,41,42,43,46,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,71,72,73,74,75,78,81,84,86,88,89,91,92,94,95,96,97,98],$VW=[1,117],$VX=[1,118],$VY=[8,9,10,11,13,18,30,32,46,71,72,73,74,75,81,86,88,89,91,92,94,95,96,97,98],$VZ=[8,9,10,11,12,13,15,16,17,18,30,32,37,39,41,43,46,50,51,52,53,54,56,57,58,59,60,61,62,63,64,65,71,72,73,74,75,78,81,84,86,88,89,91,92,94,95,96,97,98],$V_=[13,18,46,81,86,88,89,91,92,94,95,96,97,98],$V$=[13,18,46,49,65,81,86,88,89,91,92,94,95,96,97,98],$V01=[1,191],$V11=[1,188],$V21=[1,195],$V31=[1,192],$V41=[1,189],$V51=[1,196],$V61=[1,186],$V71=[1,187],$V81=[1,190],$V91=[1,193],$Va1=[1,194],$Vb1=[1,213],$Vc1=[8,9,11,86],$Vd1=[8,9,10,11,46,71,80,81,84,86,88,89,90,91,92];
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"mermaidDoc":3,"graphConfig":4,"document":5,"line":6,"statement":7,"SEMI":8,"NEWLINE":9,"SPACE":10,"EOF":11,"GRAPH":12,"DIR":13,"FirstStmtSeperator":14,"TAGEND":15,"TAGSTART":16,"UP":17,"DOWN":18,"ending":19,"endToken":20,"spaceList":21,"spaceListNewline":22,"verticeStatement":23,"separator":24,"styleStatement":25,"linkStyleStatement":26,"classDefStatement":27,"classStatement":28,"clickStatement":29,"subgraph":30,"text":31,"end":32,"vertex":33,"link":34,"alphaNum":35,"SQS":36,"SQE":37,"PS":38,"PE":39,"(-":40,"-)":41,"DIAMOND_START":42,"DIAMOND_STOP":43,"alphaNumStatement":44,"alphaNumToken":45,"MINUS":46,"linkStatement":47,"arrowText":48,"TESTSTR":49,"--":50,"ARROW_POINT":51,"ARROW_CIRCLE":52,"ARROW_CROSS":53,"ARROW_OPEN":54,"-.":55,"DOTTED_ARROW_POINT":56,"DOTTED_ARROW_CIRCLE":57,"DOTTED_ARROW_CROSS":58,"DOTTED_ARROW_OPEN":59,"==":60,"THICK_ARROW_POINT":61,"THICK_ARROW_CIRCLE":62,"THICK_ARROW_CROSS":63,"THICK_ARROW_OPEN":64,"PIPE":65,"textToken":66,"STR":67,"commentText":68,"commentToken":69,"keywords":70,"STYLE":71,"LINKSTYLE":72,"CLASSDEF":73,"CLASS":74,"CLICK":75,"textNoTags":76,"textNoTagsToken":77,"DEFAULT":78,"stylesOpt":79,"HEX":80,"NUM":81,"INTERPOLATE":82,"commentStatement":83,"PCT":84,"style":85,"COMMA":86,"styleComponent":87,"ALPHA":88,"COLON":89,"UNIT":90,"BRKT":91,"DOT":92,"graphCodeTokens":93,"PUNCTUATION":94,"UNICODE_TEXT":95,"PLUS":96,"EQUALS":97,"MULT":98,"TAG_START":99,"TAG_END":100,"QUOTE":101,"$accept":0,"$end":1},
+terminals_: {2:"error",8:"SEMI",9:"NEWLINE",10:"SPACE",11:"EOF",12:"GRAPH",13:"DIR",15:"TAGEND",16:"TAGSTART",17:"UP",18:"DOWN",30:"subgraph",32:"end",36:"SQS",37:"SQE",38:"PS",39:"PE",40:"(-",41:"-)",42:"DIAMOND_START",43:"DIAMOND_STOP",46:"MINUS",49:"TESTSTR",50:"--",51:"ARROW_POINT",52:"ARROW_CIRCLE",53:"ARROW_CROSS",54:"ARROW_OPEN",55:"-.",56:"DOTTED_ARROW_POINT",57:"DOTTED_ARROW_CIRCLE",58:"DOTTED_ARROW_CROSS",59:"DOTTED_ARROW_OPEN",60:"==",61:"THICK_ARROW_POINT",62:"THICK_ARROW_CIRCLE",63:"THICK_ARROW_CROSS",64:"THICK_ARROW_OPEN",65:"PIPE",67:"STR",71:"STYLE",72:"LINKSTYLE",73:"CLASSDEF",74:"CLASS",75:"CLICK",78:"DEFAULT",80:"HEX",81:"NUM",82:"INTERPOLATE",84:"PCT",86:"COMMA",88:"ALPHA",89:"COLON",90:"UNIT",91:"BRKT",92:"DOT",94:"PUNCTUATION",95:"UNICODE_TEXT",96:"PLUS",97:"EQUALS",98:"MULT",99:"TAG_START",100:"TAG_END",101:"QUOTE"},
+productions_: [0,[3,2],[5,0],[5,2],[6,1],[6,1],[6,1],[6,1],[6,1],[4,2],[4,2],[4,4],[4,4],[4,4],[4,4],[4,4],[19,2],[19,1],[20,1],[20,1],[20,1],[14,1],[14,1],[14,2],[22,2],[22,2],[22,1],[22,1],[21,2],[21,1],[7,2],[7,2],[7,2],[7,2],[7,2],[7,2],[7,5],[7,4],[24,1],[24,1],[24,1],[23,3],[23,1],[33,4],[33,5],[33,6],[33,7],[33,4],[33,5],[33,4],[33,5],[33,4],[33,5],[33,4],[33,5],[33,1],[33,2],[35,1],[35,2],[44,1],[44,1],[44,1],[44,1],[34,2],[34,3],[34,3],[34,1],[34,3],[34,3],[34,3],[34,3],[34,3],[34,3],[34,3],[34,3],[34,3],[34,3],[34,3],[34,3],[47,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,1],[48,3],[31,1],[31,2],[31,1],[68,1],[68,2],[70,1],[70,1],[70,1],[70,1],[70,1],[70,1],[70,1],[70,1],[70,1],[70,1],[70,1],[76,1],[76,2],[27,5],[27,5],[28,5],[29,5],[29,7],[29,5],[29,7],[25,5],[25,5],[26,5],[26,5],[26,9],[26,9],[26,7],[26,7],[83,3],[79,1],[79,3],[85,1],[85,2],[87,1],[87,1],[87,1],[87,1],[87,1],[87,1],[87,1],[87,1],[87,1],[87,1],[87,1],[69,1],[69,1],[66,1],[66,1],[66,1],[66,1],[66,1],[66,1],[66,1],[77,1],[77,1],[77,1],[77,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1],[93,1]],
+performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
+/* this == yyval */
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 2:
+ this.$ = [];
+break;
+case 3:
+
+	    if($$[$0] !== []){
+	        $$[$0-1].push($$[$0]);
+	    }
+	    this.$=$$[$0-1];
+break;
+case 4: case 57: case 59: case 60: case 92: case 94: case 95: case 108:
+this.$=$$[$0];
+break;
+case 11:
+ yy.setDirection($$[$0-1]);this.$ = $$[$0-1];
+break;
+case 12:
+ yy.setDirection("LR");this.$ = $$[$0-1];
+break;
+case 13:
+ yy.setDirection("RL");this.$ = $$[$0-1];
+break;
+case 14:
+ yy.setDirection("BT");this.$ = $$[$0-1];
+break;
+case 15:
+ yy.setDirection("TB");this.$ = $$[$0-1];
+break;
+case 30:
+this.$=$$[$0-1]
+break;
+case 31: case 32: case 33: case 34: case 35:
+this.$=[];
+break;
+case 36:
+this.$=yy.addSubGraph($$[$0-1],$$[$0-3]);
+break;
+case 37:
+this.$=yy.addSubGraph($$[$0-1],undefined);
+break;
+case 41:
+ yy.addLink($$[$0-2],$$[$0],$$[$0-1]);this.$ = [$$[$0-2],$$[$0]];
+break;
+case 42:
+this.$ = [$$[$0]];
+break;
+case 43:
+this.$ = $$[$0-3];yy.addVertex($$[$0-3],$$[$0-1],'square');
+break;
+case 44:
+this.$ = $$[$0-4];yy.addVertex($$[$0-4],$$[$0-2],'square');
+break;
+case 45:
+this.$ = $$[$0-5];yy.addVertex($$[$0-5],$$[$0-2],'circle');
+break;
+case 46:
+this.$ = $$[$0-6];yy.addVertex($$[$0-6],$$[$0-3],'circle');
+break;
+case 47:
+this.$ = $$[$0-3];yy.addVertex($$[$0-3],$$[$0-1],'ellipse');
+break;
+case 48:
+this.$ = $$[$0-4];yy.addVertex($$[$0-4],$$[$0-2],'ellipse');
+break;
+case 49:
+this.$ = $$[$0-3];yy.addVertex($$[$0-3],$$[$0-1],'round');
+break;
+case 50:
+this.$ = $$[$0-4];yy.addVertex($$[$0-4],$$[$0-2],'round');
+break;
+case 51:
+this.$ = $$[$0-3];yy.addVertex($$[$0-3],$$[$0-1],'diamond');
+break;
+case 52:
+this.$ = $$[$0-4];yy.addVertex($$[$0-4],$$[$0-2],'diamond');
+break;
+case 53:
+this.$ = $$[$0-3];yy.addVertex($$[$0-3],$$[$0-1],'odd');
+break;
+case 54:
+this.$ = $$[$0-4];yy.addVertex($$[$0-4],$$[$0-2],'odd');
+break;
+case 55:
+this.$ = $$[$0];yy.addVertex($$[$0]);
+break;
+case 56:
+this.$ = $$[$0-1];yy.addVertex($$[$0-1]);
+break;
+case 58: case 93: case 96: case 109:
+this.$=$$[$0-1]+''+$$[$0];
+break;
+case 61:
+this.$='v';
+break;
+case 62:
+this.$='-';
+break;
+case 63:
+$$[$0-1].text = $$[$0];this.$ = $$[$0-1];
+break;
+case 64: case 65:
+$$[$0-2].text = $$[$0-1];this.$ = $$[$0-2];
+break;
+case 66:
+this.$ = $$[$0];
+break;
+case 67:
+this.$ = {"type":"arrow","stroke":"normal","text":$$[$0-1]};
+break;
+case 68:
+this.$ = {"type":"arrow_circle","stroke":"normal","text":$$[$0-1]};
+break;
+case 69:
+this.$ = {"type":"arrow_cross","stroke":"normal","text":$$[$0-1]};
+break;
+case 70:
+this.$ = {"type":"arrow_open","stroke":"normal","text":$$[$0-1]};
+break;
+case 71:
+this.$ = {"type":"arrow","stroke":"dotted","text":$$[$0-1]};
+break;
+case 72:
+this.$ = {"type":"arrow_circle","stroke":"dotted","text":$$[$0-1]};
+break;
+case 73:
+this.$ = {"type":"arrow_cross","stroke":"dotted","text":$$[$0-1]};
+break;
+case 74:
+this.$ = {"type":"arrow_open","stroke":"dotted","text":$$[$0-1]};
+break;
+case 75:
+this.$ = {"type":"arrow","stroke":"thick","text":$$[$0-1]};
+break;
+case 76:
+this.$ = {"type":"arrow_circle","stroke":"thick","text":$$[$0-1]};
+break;
+case 77:
+this.$ = {"type":"arrow_cross","stroke":"thick","text":$$[$0-1]};
+break;
+case 78:
+this.$ = {"type":"arrow_open","stroke":"thick","text":$$[$0-1]};
+break;
+case 79:
+this.$ = {"type":"arrow","stroke":"normal"};
+break;
+case 80:
+this.$ = {"type":"arrow_circle","stroke":"normal"};
+break;
+case 81:
+this.$ = {"type":"arrow_cross","stroke":"normal"};
+break;
+case 82:
+this.$ = {"type":"arrow_open","stroke":"normal"};
+break;
+case 83:
+this.$ = {"type":"arrow","stroke":"dotted"};
+break;
+case 84:
+this.$ = {"type":"arrow_circle","stroke":"dotted"};
+break;
+case 85:
+this.$ = {"type":"arrow_cross","stroke":"dotted"};
+break;
+case 86:
+this.$ = {"type":"arrow_open","stroke":"dotted"};
+break;
+case 87:
+this.$ = {"type":"arrow","stroke":"thick"};
+break;
+case 88:
+this.$ = {"type":"arrow_circle","stroke":"thick"};
+break;
+case 89:
+this.$ = {"type":"arrow_cross","stroke":"thick"};
+break;
+case 90:
+this.$ = {"type":"arrow_open","stroke":"thick"};
+break;
+case 91:
+this.$ = $$[$0-1];
+break;
+case 110: case 111:
+this.$ = $$[$0-4];yy.addClass($$[$0-2],$$[$0]);
+break;
+case 112:
+this.$ = $$[$0-4];yy.setClass($$[$0-2], $$[$0]);
+break;
+case 113:
+this.$ = $$[$0-4];yy.setClickEvent($$[$0-2],        $$[$0], undefined, undefined);
+break;
+case 114:
+this.$ = $$[$0-6];yy.setClickEvent($$[$0-4],        $$[$0-2], undefined, $$[$0])       ;
+break;
+case 115:
+this.$ = $$[$0-4];yy.setClickEvent($$[$0-2], undefined,        $$[$0], undefined);
+break;
+case 116:
+this.$ = $$[$0-6];yy.setClickEvent($$[$0-4], undefined,        $$[$0-2], $$[$0]       );
+break;
+case 117:
+this.$ = $$[$0-4];yy.addVertex($$[$0-2],undefined,undefined,$$[$0]);
+break;
+case 118: case 119: case 120:
+this.$ = $$[$0-4];yy.updateLink($$[$0-2],$$[$0]);
+break;
+case 121: case 122:
+this.$ = $$[$0-8];yy.updateLinkInterpolate($$[$0-6],$$[$0-2]);yy.updateLink($$[$0-6],$$[$0]);
+break;
+case 123: case 124:
+this.$ = $$[$0-6];yy.updateLinkInterpolate($$[$0-4],$$[$0]);
+break;
+case 126:
+this.$ = [$$[$0]]
+break;
+case 127:
+$$[$0-2].push($$[$0]);this.$ = $$[$0-2];
+break;
+case 129:
+this.$ = $$[$0-1] + $$[$0];
+break;
+}
+},
+table: [{3:1,4:2,9:$V0,10:$V1,12:$V2},{1:[3]},o($V3,$V4,{5:6}),{4:7,9:$V0,10:$V1,12:$V2},{4:8,9:$V0,10:$V1,12:$V2},{10:[1,9]},{1:[2,1],6:10,7:11,8:$V5,9:$V6,10:$V7,11:$V8,13:$V9,18:$Va,23:16,25:17,26:18,27:19,28:20,29:21,30:$Vb,33:23,35:29,44:30,45:32,46:$Vc,71:$Vd,72:$Ve,73:$Vf,74:$Vg,75:$Vh,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($V3,[2,9]),o($V3,[2,10]),{13:[1,46],15:[1,47],16:[1,48],17:[1,49],18:[1,50]},o($Vt,[2,3]),o($Vt,[2,4]),o($Vt,[2,5]),o($Vt,[2,6]),o($Vt,[2,7]),o($Vt,[2,8]),{8:$Vu,9:$Vv,11:$Vw,24:51},{8:$Vu,9:$Vv,11:$Vw,24:55},{8:$Vu,9:$Vv,11:$Vw,24:56},{8:$Vu,9:$Vv,11:$Vw,24:57},{8:$Vu,9:$Vv,11:$Vw,24:58},{8:$Vu,9:$Vv,11:$Vw,24:59},{8:$Vu,9:$Vv,10:$Vx,11:$Vw,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,24:61,30:$VE,31:60,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($VR,[2,42],{34:86,47:87,50:[1,88],51:[1,91],52:[1,92],53:[1,93],54:[1,94],55:[1,89],56:[1,95],57:[1,96],58:[1,97],59:[1,98],60:[1,90],61:[1,99],62:[1,100],63:[1,101],64:[1,102]}),{10:[1,103]},{10:[1,104]},{10:[1,105]},{10:[1,106]},{10:[1,107]},o($VS,[2,55],{45:32,21:113,44:114,10:$VT,13:$V9,15:[1,112],18:$Va,36:[1,108],38:[1,109],40:[1,110],42:[1,111],46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs}),o($VU,[2,57]),o($VU,[2,59]),o($VU,[2,60]),o($VU,[2,61]),o($VU,[2,62]),o($VV,[2,154]),o($VV,[2,155]),o($VV,[2,156]),o($VV,[2,157]),o($VV,[2,158]),o($VV,[2,159]),o($VV,[2,160]),o($VV,[2,161]),o($VV,[2,162]),o($VV,[2,163]),o($VV,[2,164]),{8:$VW,9:$VX,10:$VT,14:116,21:119},{8:$VW,9:$VX,10:$VT,14:120,21:119},{8:$VW,9:$VX,10:$VT,14:121,21:119},{8:$VW,9:$VX,10:$VT,14:122,21:119},{8:$VW,9:$VX,10:$VT,14:123,21:119},o($Vt,[2,30]),o($Vt,[2,38]),o($Vt,[2,39]),o($Vt,[2,40]),o($Vt,[2,31]),o($Vt,[2,32]),o($Vt,[2,33]),o($Vt,[2,34]),o($Vt,[2,35]),{8:$Vu,9:$Vv,10:$Vx,11:$Vw,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,24:124,30:$VE,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($VY,$V4,{5:126}),o($VZ,[2,92]),o($VZ,[2,94]),o($VZ,[2,143]),o($VZ,[2,144]),o($VZ,[2,145]),o($VZ,[2,146]),o($VZ,[2,147]),o($VZ,[2,148]),o($VZ,[2,149]),o($VZ,[2,150]),o($VZ,[2,151]),o($VZ,[2,152]),o($VZ,[2,153]),o($VZ,[2,97]),o($VZ,[2,98]),o($VZ,[2,99]),o($VZ,[2,100]),o($VZ,[2,101]),o($VZ,[2,102]),o($VZ,[2,103]),o($VZ,[2,104]),o($VZ,[2,105]),o($VZ,[2,106]),o($VZ,[2,107]),{13:$V9,18:$Va,33:127,35:29,44:30,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($V_,[2,66],{48:128,49:[1,129],65:[1,130]}),{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:131,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:132,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:133,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($V$,[2,79]),o($V$,[2,80]),o($V$,[2,81]),o($V$,[2,82]),o($V$,[2,83]),o($V$,[2,84]),o($V$,[2,85]),o($V$,[2,86]),o($V$,[2,87]),o($V$,[2,88]),o($V$,[2,89]),o($V$,[2,90]),{13:$V9,18:$Va,35:134,44:30,45:32,46:$Vc,80:[1,135],81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{78:[1,136],81:[1,137]},{13:$V9,18:$Va,35:139,44:30,45:32,46:$Vc,78:[1,138],81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{13:$V9,18:$Va,35:140,44:30,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{13:$V9,18:$Va,35:141,44:30,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:142,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:144,32:$VF,38:[1,143],45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:145,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:146,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:147,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($VS,[2,56]),o($VU,[2,58]),o($VS,[2,29],{21:148,10:$VT}),o($V3,[2,11]),o($V3,[2,21]),o($V3,[2,22]),{9:[1,149]},o($V3,[2,12]),o($V3,[2,13]),o($V3,[2,14]),o($V3,[2,15]),o($VY,$V4,{5:150}),o($VZ,[2,93]),{6:10,7:11,8:$V5,9:$V6,10:$V7,11:$V8,13:$V9,18:$Va,23:16,25:17,26:18,27:19,28:20,29:21,30:$Vb,32:[1,151],33:23,35:29,44:30,45:32,46:$Vc,71:$Vd,72:$Ve,73:$Vf,74:$Vg,75:$Vh,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($VR,[2,41]),o($V_,[2,63],{10:[1,152]}),{10:[1,153]},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:154,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,45:71,46:$VG,50:$VH,51:[1,155],52:[1,156],53:[1,157],54:[1,158],60:$VI,66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,45:71,46:$VG,50:$VH,56:[1,159],57:[1,160],58:[1,161],59:[1,162],60:$VI,66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,45:71,46:$VG,50:$VH,60:$VI,61:[1,163],62:[1,164],63:[1,165],64:[1,166],66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:[1,167],13:$V9,18:$Va,44:114,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:[1,168]},{10:[1,169]},{10:[1,170]},{10:[1,171]},{10:[1,172],13:$V9,18:$Va,44:114,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:[1,173],13:$V9,18:$Va,44:114,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:[1,174],13:$V9,18:$Va,44:114,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,37:[1,175],45:71,46:$VG,50:$VH,60:$VI,66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,31:176,32:$VF,45:71,46:$VG,50:$VH,60:$VI,66:62,67:$VJ,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,39:[1,177],45:71,46:$VG,50:$VH,60:$VI,66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,41:[1,178],45:71,46:$VG,50:$VH,60:$VI,66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,43:[1,179],45:71,46:$VG,50:$VH,60:$VI,66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,37:[1,180],45:71,46:$VG,50:$VH,60:$VI,66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($VS,[2,28]),o($V3,[2,23]),{6:10,7:11,8:$V5,9:$V6,10:$V7,11:$V8,13:$V9,18:$Va,23:16,25:17,26:18,27:19,28:20,29:21,30:$Vb,32:[1,181],33:23,35:29,44:30,45:32,46:$Vc,71:$Vd,72:$Ve,73:$Vf,74:$Vg,75:$Vh,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($Vt,[2,37]),o($V_,[2,65]),o($V_,[2,64]),{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,45:71,46:$VG,50:$VH,60:$VI,65:[1,182],66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($V_,[2,67]),o($V_,[2,68]),o($V_,[2,69]),o($V_,[2,70]),o($V_,[2,71]),o($V_,[2,72]),o($V_,[2,73]),o($V_,[2,74]),o($V_,[2,75]),o($V_,[2,76]),o($V_,[2,77]),o($V_,[2,78]),{10:$V01,46:$V11,71:$V21,79:183,80:$V31,81:$V41,84:$V51,85:184,87:185,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1},{10:$V01,46:$V11,71:$V21,79:197,80:$V31,81:$V41,84:$V51,85:184,87:185,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1},{10:$V01,46:$V11,71:$V21,79:198,80:$V31,81:$V41,82:[1,199],84:$V51,85:184,87:185,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1},{10:$V01,46:$V11,71:$V21,79:200,80:$V31,81:$V41,82:[1,201],84:$V51,85:184,87:185,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1},{10:$V01,46:$V11,71:$V21,79:202,80:$V31,81:$V41,84:$V51,85:184,87:185,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1},{10:$V01,46:$V11,71:$V21,79:203,80:$V31,81:$V41,84:$V51,85:184,87:185,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1},{13:$V9,18:$Va,35:204,44:30,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{13:$V9,18:$Va,35:205,44:30,45:32,46:$Vc,67:[1,206],81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($VS,[2,43],{21:207,10:$VT}),{10:$Vx,12:$Vy,13:$Vz,15:$VA,16:$VB,17:$VC,18:$VD,30:$VE,32:$VF,39:[1,208],45:71,46:$VG,50:$VH,60:$VI,66:125,70:74,71:$VK,72:$VL,73:$VM,74:$VN,75:$VO,77:64,78:$VP,81:$Vi,84:$VQ,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},o($VS,[2,49],{21:209,10:$VT}),o($VS,[2,47],{21:210,10:$VT}),o($VS,[2,51],{21:211,10:$VT}),o($VS,[2,53],{21:212,10:$VT}),o($Vt,[2,36]),o([10,13,18,46,81,86,88,89,91,92,94,95,96,97,98],[2,91]),o($VR,[2,117],{86:$Vb1}),o($Vc1,[2,126],{87:214,10:$V01,46:$V11,71:$V21,80:$V31,81:$V41,84:$V51,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1}),o($Vd1,[2,128]),o($Vd1,[2,130]),o($Vd1,[2,131]),o($Vd1,[2,132]),o($Vd1,[2,133]),o($Vd1,[2,134]),o($Vd1,[2,135]),o($Vd1,[2,136]),o($Vd1,[2,137]),o($Vd1,[2,138]),o($Vd1,[2,139]),o($Vd1,[2,140]),o($VR,[2,118],{86:$Vb1}),o($VR,[2,119],{86:$Vb1}),{10:[1,215]},o($VR,[2,120],{86:$Vb1}),{10:[1,216]},o($VR,[2,110],{86:$Vb1}),o($VR,[2,111],{86:$Vb1}),o($VR,[2,112],{45:32,44:114,13:$V9,18:$Va,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs}),o($VR,[2,113],{45:32,44:114,10:[1,217],13:$V9,18:$Va,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs}),o($VR,[2,115],{10:[1,218]}),o($VS,[2,44]),{39:[1,219]},o($VS,[2,50]),o($VS,[2,48]),o($VS,[2,52]),o($VS,[2,54]),{10:$V01,46:$V11,71:$V21,80:$V31,81:$V41,84:$V51,85:220,87:185,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1},o($Vd1,[2,129]),{13:$V9,18:$Va,35:221,44:30,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{13:$V9,18:$Va,35:222,44:30,45:32,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs},{67:[1,223]},{67:[1,224]},o($VS,[2,45],{21:225,10:$VT}),o($Vc1,[2,127],{87:214,10:$V01,46:$V11,71:$V21,80:$V31,81:$V41,84:$V51,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1}),o($VR,[2,123],{45:32,44:114,10:[1,226],13:$V9,18:$Va,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs}),o($VR,[2,124],{45:32,44:114,10:[1,227],13:$V9,18:$Va,46:$Vc,81:$Vi,86:$Vj,88:$Vk,89:$Vl,91:$Vm,92:$Vn,94:$Vo,95:$Vp,96:$Vq,97:$Vr,98:$Vs}),o($VR,[2,114]),o($VR,[2,116]),o($VS,[2,46]),{10:$V01,46:$V11,71:$V21,79:228,80:$V31,81:$V41,84:$V51,85:184,87:185,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1},{10:$V01,46:$V11,71:$V21,79:229,80:$V31,81:$V41,84:$V51,85:184,87:185,88:$V61,89:$V71,90:$V81,91:$V91,92:$Va1},o($VR,[2,121],{86:$Vb1}),o($VR,[2,122],{86:$Vb1})],
+defaultActions: {},
+parseError: function parseError(str, hash) {
+    if (hash.recoverable) {
+        this.trace(str);
+    } else {
+        var error = new Error(str);
+        error.hash = hash;
+        throw error;
+    }
+},
+parse: function parse(input) {
+    var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+    var args = lstack.slice.call(arguments, 1);
+    var lexer = Object.create(this.lexer);
+    var sharedState = { yy: {} };
+    for (var k in this.yy) {
+        if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
+            sharedState.yy[k] = this.yy[k];
+        }
+    }
+    lexer.setInput(input, sharedState.yy);
+    sharedState.yy.lexer = lexer;
+    sharedState.yy.parser = this;
+    if (typeof lexer.yylloc == 'undefined') {
+        lexer.yylloc = {};
+    }
+    var yyloc = lexer.yylloc;
+    lstack.push(yyloc);
+    var ranges = lexer.options && lexer.options.ranges;
+    if (typeof sharedState.yy.parseError === 'function') {
+        this.parseError = sharedState.yy.parseError;
+    } else {
+        this.parseError = Object.getPrototypeOf(this).parseError;
+    }
+    function popStack(n) {
+        stack.length = stack.length - 2 * n;
+        vstack.length = vstack.length - n;
+        lstack.length = lstack.length - n;
+    }
+            function lex() {
+            var token;
+            token = tstack.pop() || lexer.lex() || EOF;
+            if (typeof token !== 'number') {
+                if (token instanceof Array) {
+                    tstack = token;
+                    token = tstack.pop();
+                }
+                token = self.symbols_[token] || token;
+            }
+            return token;
+        }
+    var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+    while (true) {
+        state = stack[stack.length - 1];
+        if (this.defaultActions[state]) {
+            action = this.defaultActions[state];
+        } else {
+            if (symbol === null || typeof symbol == 'undefined') {
+                symbol = lex();
+            }
+            action = table[state] && table[state][symbol];
+        }
+        if (typeof action === 'undefined' || !action.length || !action[0]) {
+            var errStr = '';
+            expected = [];
+            for (p in table[state]) {
+                if (this.terminals_[p] && p > TERROR) {
+                    expected.push('\'' + this.terminals_[p] + '\'');
+                }
+            }
+            if (lexer.showPosition) {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
+            } else {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
+            }
+            this.parseError(errStr, {
+                text: lexer.match,
+                token: this.terminals_[symbol] || symbol,
+                line: lexer.yylineno,
+                loc: yyloc,
+                expected: expected
+            });
+        }
+        if (action[0] instanceof Array && action.length > 1) {
+            throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+        }
+        switch (action[0]) {
+        case 1:
+            stack.push(symbol);
+            vstack.push(lexer.yytext);
+            lstack.push(lexer.yylloc);
+            stack.push(action[1]);
+            symbol = null;
+            if (!preErrorSymbol) {
+                yyleng = lexer.yyleng;
+                yytext = lexer.yytext;
+                yylineno = lexer.yylineno;
+                yyloc = lexer.yylloc;
+                if (recovering > 0) {
+                    recovering--;
+                }
+            } else {
+                symbol = preErrorSymbol;
+                preErrorSymbol = null;
+            }
+            break;
+        case 2:
+            len = this.productions_[action[1]][1];
+            yyval.$ = vstack[vstack.length - len];
+            yyval._$ = {
+                first_line: lstack[lstack.length - (len || 1)].first_line,
+                last_line: lstack[lstack.length - 1].last_line,
+                first_column: lstack[lstack.length - (len || 1)].first_column,
+                last_column: lstack[lstack.length - 1].last_column
+            };
+            if (ranges) {
+                yyval._$.range = [
+                    lstack[lstack.length - (len || 1)].range[0],
+                    lstack[lstack.length - 1].range[1]
+                ];
+            }
+            r = this.performAction.apply(yyval, [
+                yytext,
+                yyleng,
+                yylineno,
+                sharedState.yy,
+                action[1],
+                vstack,
+                lstack
+            ].concat(args));
+            if (typeof r !== 'undefined') {
+                return r;
+            }
+            if (len) {
+                stack = stack.slice(0, -1 * len * 2);
+                vstack = vstack.slice(0, -1 * len);
+                lstack = lstack.slice(0, -1 * len);
+            }
+            stack.push(this.productions_[action[1]][0]);
+            vstack.push(yyval.$);
+            lstack.push(yyval._$);
+            newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+            stack.push(newState);
+            break;
+        case 3:
+            return true;
+        }
+    }
+    return true;
+}};
+
+/* generated by jison-lex 0.3.4 */
+var lexer = (function(){
+var lexer = ({
+
+EOF:1,
+
+parseError:function parseError(str, hash) {
+        if (this.yy.parser) {
+            this.yy.parser.parseError(str, hash);
+        } else {
+            throw new Error(str);
+        }
+    },
+
+// resets the lexer, sets new input
+setInput:function (input, yy) {
+        this.yy = yy || this.yy || {};
+        this._input = input;
+        this._more = this._backtrack = this.done = false;
+        this.yylineno = this.yyleng = 0;
+        this.yytext = this.matched = this.match = '';
+        this.conditionStack = ['INITIAL'];
+        this.yylloc = {
+            first_line: 1,
+            first_column: 0,
+            last_line: 1,
+            last_column: 0
+        };
+        if (this.options.ranges) {
+            this.yylloc.range = [0,0];
+        }
+        this.offset = 0;
+        return this;
+    },
+
+// consumes and returns one char from the input
+input:function () {
+        var ch = this._input[0];
+        this.yytext += ch;
+        this.yyleng++;
+        this.offset++;
+        this.match += ch;
+        this.matched += ch;
+        var lines = ch.match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno++;
+            this.yylloc.last_line++;
+        } else {
+            this.yylloc.last_column++;
+        }
+        if (this.options.ranges) {
+            this.yylloc.range[1]++;
+        }
+
+        this._input = this._input.slice(1);
+        return ch;
+    },
+
+// unshifts one char (or a string) into the input
+unput:function (ch) {
+        var len = ch.length;
+        var lines = ch.split(/(?:\r\n?|\n)/g);
+
+        this._input = ch + this._input;
+        this.yytext = this.yytext.substr(0, this.yytext.length - len);
+        //this.yyleng -= len;
+        this.offset -= len;
+        var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+        this.match = this.match.substr(0, this.match.length - 1);
+        this.matched = this.matched.substr(0, this.matched.length - 1);
+
+        if (lines.length - 1) {
+            this.yylineno -= lines.length - 1;
+        }
+        var r = this.yylloc.range;
+
+        this.yylloc = {
+            first_line: this.yylloc.first_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.first_column,
+            last_column: lines ?
+                (lines.length === oldLines.length ? this.yylloc.first_column : 0)
+                 + oldLines[oldLines.length - lines.length].length - lines[0].length :
+              this.yylloc.first_column - len
+        };
+
+        if (this.options.ranges) {
+            this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+        }
+        this.yyleng = this.yytext.length;
+        return this;
+    },
+
+// When called from action, caches matched text and appends it on next action
+more:function () {
+        this._more = true;
+        return this;
+    },
+
+// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
+reject:function () {
+        if (this.options.backtrack_lexer) {
+            this._backtrack = true;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+
+        }
+        return this;
+    },
+
+// retain first n characters of the match
+less:function (n) {
+        this.unput(this.match.slice(n));
+    },
+
+// displays already matched input, i.e. for error messages
+pastInput:function () {
+        var past = this.matched.substr(0, this.matched.length - this.match.length);
+        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+    },
+
+// displays upcoming input, i.e. for error messages
+upcomingInput:function () {
+        var next = this.match;
+        if (next.length < 20) {
+            next += this._input.substr(0, 20-next.length);
+        }
+        return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+    },
+
+// displays the character position where the lexing error occurred, i.e. for error messages
+showPosition:function () {
+        var pre = this.pastInput();
+        var c = new Array(pre.length + 1).join("-");
+        return pre + this.upcomingInput() + "\n" + c + "^";
+    },
+
+// test the lexed token: return FALSE when not a match, otherwise return token
+test_match:function (match, indexed_rule) {
+        var token,
+            lines,
+            backup;
+
+        if (this.options.backtrack_lexer) {
+            // save context
+            backup = {
+                yylineno: this.yylineno,
+                yylloc: {
+                    first_line: this.yylloc.first_line,
+                    last_line: this.last_line,
+                    first_column: this.yylloc.first_column,
+                    last_column: this.yylloc.last_column
+                },
+                yytext: this.yytext,
+                match: this.match,
+                matches: this.matches,
+                matched: this.matched,
+                yyleng: this.yyleng,
+                offset: this.offset,
+                _more: this._more,
+                _input: this._input,
+                yy: this.yy,
+                conditionStack: this.conditionStack.slice(0),
+                done: this.done
+            };
+            if (this.options.ranges) {
+                backup.yylloc.range = this.yylloc.range.slice(0);
+            }
+        }
+
+        lines = match[0].match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno += lines.length;
+        }
+        this.yylloc = {
+            first_line: this.yylloc.last_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.last_column,
+            last_column: lines ?
+                         lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
+                         this.yylloc.last_column + match[0].length
+        };
+        this.yytext += match[0];
+        this.match += match[0];
+        this.matches = match;
+        this.yyleng = this.yytext.length;
+        if (this.options.ranges) {
+            this.yylloc.range = [this.offset, this.offset += this.yyleng];
+        }
+        this._more = false;
+        this._backtrack = false;
+        this._input = this._input.slice(match[0].length);
+        this.matched += match[0];
+        token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
+        if (this.done && this._input) {
+            this.done = false;
+        }
+        if (token) {
+            return token;
+        } else if (this._backtrack) {
+            // recover context
+            for (var k in backup) {
+                this[k] = backup[k];
+            }
+            return false; // rule action called reject() implying the next rule should be tested instead.
+        }
+        return false;
+    },
+
+// return next match in input
+next:function () {
+        if (this.done) {
+            return this.EOF;
+        }
+        if (!this._input) {
+            this.done = true;
+        }
+
+        var token,
+            match,
+            tempMatch,
+            index;
+        if (!this._more) {
+            this.yytext = '';
+            this.match = '';
+        }
+        var rules = this._currentRules();
+        for (var i = 0; i < rules.length; i++) {
+            tempMatch = this._input.match(this.rules[rules[i]]);
+            if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+                match = tempMatch;
+                index = i;
+                if (this.options.backtrack_lexer) {
+                    token = this.test_match(tempMatch, rules[i]);
+                    if (token !== false) {
+                        return token;
+                    } else if (this._backtrack) {
+                        match = false;
+                        continue; // rule action called reject() implying a rule MISmatch.
+                    } else {
+                        // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+                        return false;
+                    }
+                } else if (!this.options.flex) {
+                    break;
+                }
+            }
+        }
+        if (match) {
+            token = this.test_match(match, rules[index]);
+            if (token !== false) {
+                return token;
+            }
+            // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+            return false;
+        }
+        if (this._input === "") {
+            return this.EOF;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+        }
+    },
+
+// return next match that has a token
+lex:function lex() {
+        var r = this.next();
+        if (r) {
+            return r;
+        } else {
+            return this.lex();
+        }
+    },
+
+// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
+begin:function begin(condition) {
+        this.conditionStack.push(condition);
+    },
+
+// pop the previously active lexer condition state off the condition stack
+popState:function popState() {
+        var n = this.conditionStack.length - 1;
+        if (n > 0) {
+            return this.conditionStack.pop();
+        } else {
+            return this.conditionStack[0];
+        }
+    },
+
+// produce the lexer rule set which is active for the currently active lexer condition state
+_currentRules:function _currentRules() {
+        if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
+            return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+        } else {
+            return this.conditions["INITIAL"].rules;
+        }
+    },
+
+// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
+topState:function topState(n) {
+        n = this.conditionStack.length - 1 - Math.abs(n || 0);
+        if (n >= 0) {
+            return this.conditionStack[n];
+        } else {
+            return "INITIAL";
+        }
+    },
+
+// alias for begin(condition)
+pushState:function pushState(condition) {
+        this.begin(condition);
+    },
+
+// return the number of states currently on the stack
+stateStackSize:function stateStackSize() {
+        return this.conditionStack.length;
+    },
+options: {},
+performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0:/* do nothing */
+break;
+case 1:this.begin("string");
+break;
+case 2:this.popState();
+break;
+case 3:return "STR";
+break;
+case 4:return 71;
+break;
+case 5:return 78;
+break;
+case 6:return 72;
+break;
+case 7:return 82;
+break;
+case 8:return 73;
+break;
+case 9:return 74;
+break;
+case 10:return 75;
+break;
+case 11:return 12;
+break;
+case 12:return 30;
+break;
+case 13:return 32;
+break;
+case 14:return 13;
+break;
+case 15:return 13;
+break;
+case 16:return 13;
+break;
+case 17:return 13;
+break;
+case 18:return 13;
+break;
+case 19:return 13;
+break;
+case 20:return 81;
+break;
+case 21:return 91;
+break;
+case 22:return 89;
+break;
+case 23:return 8;
+break;
+case 24:return 86;
+break;
+case 25:return 98;
+break;
+case 26:return 16;
+break;
+case 27:return 15;
+break;
+case 28:return 17;
+break;
+case 29:return 18;
+break;
+case 30:return 53;
+break;
+case 31:return 51;
+break;
+case 32:return 52;
+break;
+case 33:return 54;
+break;
+case 34:return 58;
+break;
+case 35:return 56;
+break;
+case 36:return 57;
+break;
+case 37:return 59;
+break;
+case 38:return 58;
+break;
+case 39:return 56;
+break;
+case 40:return 57;
+break;
+case 41:return 59;
+break;
+case 42:return 63;
+break;
+case 43:return 61;
+break;
+case 44:return 62;
+break;
+case 45:return 64;
+break;
+case 46:return 50;
+break;
+case 47:return 55;
+break;
+case 48:return 60;
+break;
+case 49:return 40;
+break;
+case 50:return 41;
+break;
+case 51:return 46;
+break;
+case 52:return 92;
+break;
+case 53:return 96;
+break;
+case 54:return 84;
+break;
+case 55:return 97;
+break;
+case 56:return 97;
+break;
+case 57:return 88;
+break;
+case 58:return 94;
+break;
+case 59:return 95;
+break;
+case 60:return 65;
+break;
+case 61:return 38;
+break;
+case 62:return 39;
+break;
+case 63:return 36;
+break;
+case 64:return 37;
+break;
+case 65:return 42
+break;
+case 66:return 43
+break;
+case 67:return 101;
+break;
+case 68:return 9;
+break;
+case 69:return 10;
+break;
+case 70:return 11;
+break;
+}
+},
+rules: [/^(?:%%[^\n]*)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:style\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\b)/,/^(?:class\b)/,/^(?:click\b)/,/^(?:graph\b)/,/^(?:subgraph\b)/,/^(?:end\b\s*)/,/^(?:LR\b)/,/^(?:RL\b)/,/^(?:TB\b)/,/^(?:BT\b)/,/^(?:TD\b)/,/^(?:BR\b)/,/^(?:[0-9]+)/,/^(?:#)/,/^(?::)/,/^(?:;)/,/^(?:,)/,/^(?:\*)/,/^(?:<)/,/^(?:>)/,/^(?:\^)/,/^(?:v\b)/,/^(?:\s*--[x]\s*)/,/^(?:\s*-->\s*)/,/^(?:\s*--[o]\s*)/,/^(?:\s*---\s*)/,/^(?:\s*-\.-[x]\s*)/,/^(?:\s*-\.->\s*)/,/^(?:\s*-\.-[o]\s*)/,/^(?:\s*-\.-\s*)/,/^(?:\s*.-[x]\s*)/,/^(?:\s*\.->\s*)/,/^(?:\s*\.-[o]\s*)/,/^(?:\s*\.-\s*)/,/^(?:\s*==[x]\s*)/,/^(?:\s*==>\s*)/,/^(?:\s*==[o]\s*)/,/^(?:\s*==[\=]\s*)/,/^(?:\s*--\s*)/,/^(?:\s*-\.\s*)/,/^(?:\s*==\s*)/,/^(?:\(-)/,/^(?:-\))/,/^(?:-)/,/^(?:\.)/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:[A-Za-z]+)/,/^(?:[!"#$%&'*+,-.`?\\_\/])/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\|)/,/^(?:\()/,/^(?:\))/,/^(?:\[)/,/^(?:\])/,/^(?:\{)/,/^(?:\})/,/^(?:")/,/^(?:\n+)/,/^(?:\s)/,/^(?:$)/],
+conditions: {"string":{"rules":[2,3],"inclusive":false},"INITIAL":{"rules":[0,1,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70],"inclusive":true}}
+});
+return lexer;
+})();
+parser.lexer = lexer;
+function Parser () {
+  this.yy = {};
+}
+Parser.prototype = parser;parser.Parser = Parser;
+return new Parser;
+})();
+
+
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+exports.parser = parser;
+exports.Parser = parser.Parser;
+exports.parse = function () { return parser.parse.apply(parser, arguments); };
+exports.main = function commonjsMain(args) {
+    if (!args[1]) {
+        console.log('Usage: '+args[0]+' FILE');
+        process.exit(1);
+    }
+    var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8");
+    return exports.parser.parse(source);
+};
+if (typeof module !== 'undefined' && require.main === module) {
+  exports.main(process.argv.slice(1));
+}
+}
\ No newline at end of file
diff --git a/_submodules/mermaid/src/diagrams/flowchart/parser/flow.spec.js b/_submodules/mermaid/src/diagrams/flowchart/parser/flow.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..28d273bafe26097bf32323451349027b0decd928
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/flowchart/parser/flow.spec.js
@@ -0,0 +1,1346 @@
+import flowDb from '../flowDb'
+import flow from './flow'
+
+describe('when parsing ', function () {
+  beforeEach(function () {
+    flow.parser.yy = flowDb
+    flow.parser.yy.clear()
+  })
+
+  it('should handle a nodes and edges', function () {
+    const res = flow.parser.parse('graph TD;\nA-->B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('should handle subgraph with tab indentation', function () {
+    const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2\nend')
+    const subgraphs = flow.parser.yy.getSubGraphs()
+    expect(subgraphs.length).toBe(1)
+    const subgraph = subgraphs[0]
+    expect(subgraph.nodes.length).toBe(2)
+    expect(subgraph.nodes[0]).toBe('a1')
+    expect(subgraph.nodes[1]).toBe('a2')
+    expect(subgraph.title).toBe('One')
+  })
+
+  it('should handle angle bracket ' > ' as direction LR', function () {
+    const res = flow.parser.parse('graph >;A-->B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+    const direction = flow.parser.yy.getDirection()
+
+    expect(direction).toBe('LR')
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('should handle angle bracket ' < ' as direction RL', function () {
+    const res = flow.parser.parse('graph <;A-->B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+    const direction = flow.parser.yy.getDirection()
+
+    expect(direction).toBe('RL')
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('should handle caret ' ^ ' as direction BT', function () {
+    const res = flow.parser.parse('graph ^;A-->B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+    const direction = flow.parser.yy.getDirection()
+
+    expect(direction).toBe('BT')
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('should handle lower-case \'v\' as direction TB', function () {
+    const res = flow.parser.parse('graph v;A-->B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+    const direction = flow.parser.yy.getDirection()
+
+    expect(direction).toBe('TB')
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('should handle a nodes and edges and a space between link and node', function () {
+    const res = flow.parser.parse('graph TD;A --> B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('should handle a nodes and edges, a space between link and node and each line ending without semicolon', function () {
+    const res = flow.parser.parse('graph TD\nA --> B\n style e red')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+  it('should handle statements ending without semicolon', function () {
+    const res = flow.parser.parse('graph TD\nA-->B\nB-->C')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(2)
+    expect(edges[1].start).toBe('B')
+    expect(edges[1].end).toBe('C')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('should handle a comments', function () {
+    const res = flow.parser.parse('graph TD;\n%% CComment\n A-->B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+  it('should handle comments a at the start', function () {
+    const res = flow.parser.parse('%% Comment\ngraph TD;\n A-->B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+  it('should handle comments at the end', function () {
+    const res = flow.parser.parse('graph TD;\n A-->B\n %% Comment at the find\n')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+  it('should handle comments at the end no trailing newline', function () {
+    const res = flow.parser.parse('graph TD;\n A-->B\n%% Commento')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+  it('should handle comments at the end many trailing newlines', function () {
+    const res = flow.parser.parse('graph TD;\n A-->B\n%% Commento\n\n\n')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+  it('should handle no trailing newlines', function () {
+    const res = flow.parser.parse('graph TD;\n A-->B')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+  it('should handle many trailing newlines', function () {
+    const res = flow.parser.parse('graph TD;\n A-->B\n\n')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+  it('should handle a comments with blank rows in-between', function () {
+    const res = flow.parser.parse('graph TD;\n\n\n %% Comment\n A-->B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('should handle a comments mermaid flowchart code in them', function () {
+    const res = flow.parser.parse('graph TD;\n\n\n %% Test od>Odd shape]-->|Two line<br>edge comment|ro;\n A-->B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(1)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('it should handle a trailing whitespaces after statememnts', function () {
+    const res = flow.parser.parse('graph TD;\n\n\n %% Comment\n A-->B; \n B-->C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(2)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].type).toBe('arrow')
+    expect(edges[0].text).toBe('')
+  })
+
+  it('should handle node names with "end" substring', function () {
+    const res = flow.parser.parse('graph TD\nendpoint --> sender')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['endpoint'].id).toBe('endpoint')
+    expect(vert['sender'].id).toBe('sender')
+    expect(edges[0].start).toBe('endpoint')
+    expect(edges[0].end).toBe('sender')
+  })
+
+  it('should handle node names ending with keywords', function () {
+    const res = flow.parser.parse('graph TD\nblend --> monograph')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['blend'].id).toBe('blend')
+    expect(vert['monograph'].id).toBe('monograph')
+    expect(edges[0].start).toBe('blend')
+    expect(edges[0].end).toBe('monograph')
+  })
+
+  it('should handle open ended edges', function () {
+    const res = flow.parser.parse('graph TD;A---B;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow_open')
+  })
+
+  it('should handle cross ended edges', function () {
+    const res = flow.parser.parse('graph TD;A--xB;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow_cross')
+  })
+
+  it('should handle open ended edges', function () {
+    const res = flow.parser.parse('graph TD;A--oB;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow_circle')
+  })
+  it('should handle subgraphs', function () {
+    const res = flow.parser.parse('graph TD;A-->B;subgraph myTitle;c-->d;end;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow')
+  })
+
+  it('should handle subgraphs', function () {
+    const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\n\n c-->d \nend\n')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow')
+  })
+
+  it('should handle nested subgraphs', function () {
+    const str = 'graph TD\n' +
+            'A-->B\n' +
+            'subgraph myTitle\n\n' +
+            ' c-->d \n\n' +
+            ' subgraph inner\n\n   e-->f \n end \n\n' +
+            ' subgraph inner\n\n   h-->i \n end \n\n' +
+            'end\n'
+    const res = flow.parser.parse(str)
+  })
+
+  it('should handle subgraphs', function () {
+    const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-->d\nend;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow')
+  })
+
+  it('should handle subgraphs', function () {
+    const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-- text -->d\nd-->e\n end;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow')
+  })
+
+  it('should handle classDefs with style in classes', function () {
+    const res = flow.parser.parse('graph TD\nA-->B\nclassDef exClass font-style:bold;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow')
+  })
+
+  it('should handle classDefs with % in classes', function () {
+    const res = flow.parser.parse('graph TD\nA-->B\nclassDef exClass fill:#f96,stroke:#333,stroke-width:4px,font-size:50%,font-style:bold;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow')
+  })
+
+  it('should handle style definitons with more then 1 digit in a row', function () {
+    const res = flow.parser.parse('graph TD\n' +
+        'A-->B1\n' +
+        'A-->B2\n' +
+        'A-->B3\n' +
+        'A-->B4\n' +
+        'A-->B5\n' +
+        'A-->B6\n' +
+        'A-->B7\n' +
+        'A-->B8\n' +
+        'A-->B9\n' +
+        'A-->B10\n' +
+        'A-->B11\n' +
+        'linkStyle 10 stroke-width:1px;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow')
+  })
+
+  it('should handle line interpolation default definitions', function () {
+    const res = flow.parser.parse('graph TD\n' +
+        'A-->B\n' +
+        'linkStyle default interpolate basis')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.defaultInterpolate).toBe('basis')
+  })
+
+  it('should handle line interpolation numbered definitions', function () {
+    const res = flow.parser.parse('graph TD\n' +
+        'A-->B\n' +
+        'A-->C\n' +
+        'linkStyle 0 interpolate basis\n' +
+        'linkStyle 1 interpolate cardinal')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].interpolate).toBe('basis')
+    expect(edges[1].interpolate).toBe('cardinal')
+  })
+
+  it('should handle line interpolation default with style', function () {
+    const res = flow.parser.parse('graph TD\n' +
+        'A-->B\n' +
+        'linkStyle default interpolate basis stroke-width:1px;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.defaultInterpolate).toBe('basis')
+  })
+
+  it('should handle line interpolation numbered with style', function () {
+    const res = flow.parser.parse('graph TD\n' +
+        'A-->B\n' +
+        'A-->C\n' +
+        'linkStyle 0 interpolate basis stroke-width:1px;\n' +
+        'linkStyle 1 interpolate cardinal stroke-width:1px;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].interpolate).toBe('basis')
+    expect(edges[1].interpolate).toBe('cardinal')
+  })
+
+  describe('it should handle interaction, ', function () {
+    it('it should be possible to use click to a callback', function () {
+      spyOn(flowDb, 'setClickEvent')
+      const res = flow.parser.parse('graph TD\nA-->B\nclick A callback')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback', undefined, undefined)
+    })
+
+    it('it should be possible to use click to a callback with toolip', function () {
+      spyOn(flowDb, 'setClickEvent')
+      const res = flow.parser.parse('graph TD\nA-->B\nclick A callback "tooltip"')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback', undefined, 'tooltip')
+    })
+
+    it('should handle interaction - click to a link', function () {
+      spyOn(flowDb, 'setClickEvent')
+      const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html"')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', undefined, 'click.html', undefined)
+    })
+    it('should handle interaction - click to a link with tooltip', function () {
+      spyOn(flowDb, 'setClickEvent')
+      const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html" "tooltip"')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', undefined, 'click.html', 'tooltip')
+    })
+  })
+
+  describe('it should handle text on edges', function () {
+    it('it should handle text without space', function () {
+      const res = flow.parser.parse('graph TD;A--x|textNoSpace|B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+    })
+
+    it('should handle  with space', function () {
+      const res = flow.parser.parse('graph TD;A--x|text including space|B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+    })
+
+    it('it should handle text with /', function () {
+      const res = flow.parser.parse('graph TD;A--x|text with / should work|B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].text).toBe('text with / should work')
+    })
+
+    it('it should handle space and space between vertices and link', function () {
+      const res = flow.parser.parse('graph TD;A --x|textNoSpace| B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+    })
+
+    it('should handle space and CAPS', function () {
+      const res = flow.parser.parse('graph TD;A--x|text including CAPS space|B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+    })
+
+    it('should handle space and dir', function () {
+      const res = flow.parser.parse('graph TD;A--x|text including URL space|B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+      expect(edges[0].text).toBe('text including URL space')
+    })
+
+    it('should handle space and send', function () {
+      const res = flow.parser.parse('graph TD;A--text including URL space and send-->B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow')
+      expect(edges[0].text).toBe('text including URL space and send')
+    })
+    it('should handle space and send', function () {
+      const res = flow.parser.parse('graph TD;A-- text including URL space and send -->B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow')
+      expect(edges[0].text).toBe('text including URL space and send')
+    })
+
+    it('should handle space and dir (TD)', function () {
+      const res = flow.parser.parse('graph TD;A--x|text including R TD space|B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+      expect(edges[0].text).toBe('text including R TD space')
+    })
+    it('should handle `', function () {
+      const res = flow.parser.parse('graph TD;A--x|text including `|B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+      expect(edges[0].text).toBe('text including `')
+    })
+    it('should handle v in node ids only v', function () {
+            // only v
+      const res = flow.parser.parse('graph TD;A--xv(my text);')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+      expect(vert['v'].text).toBe('my text')
+    })
+    it('should handle v in node ids v at end', function () {
+            // v at end
+      const res = flow.parser.parse('graph TD;A--xcsv(my text);')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+      expect(vert['csv'].text).toBe('my text')
+    })
+    it('should handle v in node ids v in middle', function () {
+            // v in middle
+      const res = flow.parser.parse('graph TD;A--xava(my text);')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+      expect(vert['ava'].text).toBe('my text')
+    })
+    it('should handle v in node ids, v at start', function () {
+            // v at start
+      const res = flow.parser.parse('graph TD;A--xva(my text);')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+      expect(vert['va'].text).toBe('my text')
+    })
+    it('should handle keywords', function () {
+      const res = flow.parser.parse('graph TD;A--x|text including graph space|B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].text).toBe('text including graph space')
+    })
+    it('should handle keywords', function () {
+      const res = flow.parser.parse('graph TD;V-->a[v]')
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+      expect(vert['a'].text).toBe('v')
+    })
+    it('should handle keywords', function () {
+      const res = flow.parser.parse('graph TD;V-->a[v]')
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+      expect(vert['a'].text).toBe('v')
+    })
+    it('should handle quoted text', function () {
+      const res = flow.parser.parse('graph TD;V-- "test string()" -->a[v]')
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+      expect(edges[0].text).toBe('test string()')
+    })
+  })
+
+  describe('it should handle new line type notation', function () {
+    it('it should handle regular lines', function () {
+      const res = flow.parser.parse('graph TD;A-->B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].stroke).toBe('normal')
+    })
+    it('it should handle dotted lines', function () {
+      const res = flow.parser.parse('graph TD;A-.->B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].stroke).toBe('dotted')
+    })
+    it('it should handle dotted lines', function () {
+      const res = flow.parser.parse('graph TD;A==>B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].stroke).toBe('thick')
+    })
+    it('it should handle text on lines', function () {
+      const res = flow.parser.parse('graph TD;A-- test text with == -->B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].stroke).toBe('normal')
+    })
+    it('it should handle text on lines', function () {
+      const res = flow.parser.parse('graph TD;A-. test text with == .->B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].stroke).toBe('dotted')
+    })
+    it('it should handle text on lines', function () {
+      const res = flow.parser.parse('graph TD;A== test text with - ==>B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].stroke).toBe('thick')
+    })
+  })
+
+  describe('it should handle text on edges using the new notation', function () {
+    it('it should handle text without space', function () {
+      const res = flow.parser.parse('graph TD;A-- textNoSpace --xB;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+    })
+
+    it('it should handle text with multiple leading space', function () {
+      const res = flow.parser.parse('graph TD;A--    textNoSpace --xB;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+    })
+
+    it('should handle  with space', function () {
+      const res = flow.parser.parse('graph TD;A-- text including space --xB;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+    })
+
+    it('it should handle text with /', function () {
+      const res = flow.parser.parse('graph TD;A -- text with / should work --x B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].text).toBe('text with / should work')
+    })
+
+    it('it should handle space and space between vertices and link', function () {
+      const res = flow.parser.parse('graph TD;A -- textNoSpace --x B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+    })
+
+    it('should handle space and CAPS', function () {
+      const res = flow.parser.parse('graph TD;A-- text including CAPS space --xB;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+    })
+
+    it('should handle space and dir', function () {
+      const res = flow.parser.parse('graph TD;A-- text including URL space --xB;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+      expect(edges[0].text).toBe('text including URL space')
+    })
+
+    it('should handle space and dir (TD)', function () {
+      const res = flow.parser.parse('graph TD;A-- text including R TD space --xB;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].type).toBe('arrow_cross')
+      expect(edges[0].text).toBe('text including R TD space')
+    })
+    it('should handle keywords', function () {
+      const res = flow.parser.parse('graph TD;A-- text including graph space and v --xB;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].text).toBe('text including graph space and v')
+    })
+    it('should handle keywords', function () {
+      const res = flow.parser.parse('graph TD;A-- text including graph space and v --xB[blav]')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(edges[0].text).toBe('text including graph space and v')
+    })
+        // xit('should handle text on open links',function(){
+        //    const res = flow.parser.parse('graph TD;A-- text including graph space --B');
+        //
+        //    const vert = flow.parser.yy.getVertices();
+        //    const edges = flow.parser.yy.getEdges();
+        //
+        //    expect(edges[0].text).toBe('text including graph space');
+        //
+        // });
+  })
+
+  it('should handle multi-line text', function () {
+    const res = flow.parser.parse('graph TD;A--o|text space|B;\n B-->|more text with space|C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges[0].type).toBe('arrow_circle')
+    expect(edges[1].type).toBe('arrow')
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(vert['C'].id).toBe('C')
+    expect(edges.length).toBe(2)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+        // expect(edges[0].text).toBe('text space');
+    expect(edges[1].start).toBe('B')
+    expect(edges[1].end).toBe('C')
+    expect(edges[1].text).toBe('more text with space')
+  })
+
+  it('should handle multiple edges', function () {
+    const res = flow.parser.parse('graph TD;A---|This is the 123 s text|B;\nA---|This is the second edge|B;')
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].id).toBe('A')
+    expect(vert['B'].id).toBe('B')
+    expect(edges.length).toBe(2)
+    expect(edges[0].start).toBe('A')
+    expect(edges[0].end).toBe('B')
+    expect(edges[0].text).toBe('This is the 123 s text')
+    expect(edges[1].start).toBe('A')
+    expect(edges[1].end).toBe('B')
+    expect(edges[1].text).toBe('This is the second edge')
+  })
+
+  it('should handle text in vertices with space', function () {
+    const res = flow.parser.parse('graph TD;A[chimpansen hoppar]-->C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].type).toBe('square')
+    expect(vert['A'].text).toBe('chimpansen hoppar')
+  })
+
+  it('should handle text in vertices with space with spaces between vertices and link', function () {
+    const res = flow.parser.parse('graph TD;A[chimpansen hoppar] --> C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].type).toBe('square')
+    expect(vert['A'].text).toBe('chimpansen hoppar')
+  })
+
+  it('should handle quoted text in vertices ', function () {
+    const res = flow.parser.parse('graph TD;A["chimpansen hoppar ()[]"] --> C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].type).toBe('square')
+    expect(vert['A'].text).toBe('chimpansen hoppar ()[]')
+  })
+
+  it('should handle text in circle vertices with space', function () {
+    const res = flow.parser.parse('graph TD;A((chimpansen hoppar))-->C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].type).toBe('circle')
+    expect(vert['A'].text).toBe('chimpansen hoppar')
+  })
+
+  it('should handle text in ellipse vertices', function () {
+    const res = flow.parser.parse('graph TD\nA(-this is an ellipse-)-->B')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].type).toBe('ellipse')
+    expect(vert['A'].text).toBe('this is an ellipse')
+  })
+
+  it('should handle text in diamond vertices with space', function () {
+    const res = flow.parser.parse('graph TD;A(chimpansen hoppar)-->C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].type).toBe('round')
+    expect(vert['A'].text).toBe('chimpansen hoppar')
+  })
+
+  it('should handle text in with ?', function () {
+    const res = flow.parser.parse('graph TD;A(?)-->|?|C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].text).toBe('?')
+    expect(edges[0].text).toBe('?')
+  })
+  it('should handle text in with éèêàçô', function () {
+    const res = flow.parser.parse('graph TD;A(éèêàçô)-->|éèêàçô|C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].text).toBe('éèêàçô')
+    expect(edges[0].text).toBe('éèêàçô')
+  })
+
+  it('should handle text in with ,.?!+-*', function () {
+    const res = flow.parser.parse('graph TD;A(,.?!+-*)-->|,.?!+-*|C;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['A'].text).toBe(',.?!+-*')
+    expect(edges[0].text).toBe(',.?!+-*')
+  })
+
+  describe('it should handle text in vertices, ', function () {
+    it('it should handle space', function () {
+      const res = flow.parser.parse('graph TD;A-->C(Chimpansen hoppar);')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(vert['C'].type).toBe('round')
+      expect(vert['C'].text).toBe('Chimpansen hoppar')
+    })
+    it('it should handle åäö and minus', function () {
+      const res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(vert['C'].type).toBe('diamond')
+      expect(vert['C'].text).toBe('Chimpansen hoppar åäö-ÅÄÖ')
+    })
+
+    it('it should handle with åäö, minus and space and br', function () {
+      const res = flow.parser.parse('graph TD;A-->C(Chimpansen hoppar åäö  <br> -  ÅÄÖ);')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(vert['C'].type).toBe('round')
+      expect(vert['C'].text).toBe('Chimpansen hoppar åäö  <br> -  ÅÄÖ')
+    })
+        // xit('it should handle åäö, minus and space and br',function(){
+        //    const res = flow.parser.parse('graph TD; A[Object&#40;foo,bar&#41;]-->B(Thing);');
+        //
+        //    const vert = flow.parser.yy.getVertices();
+        //    const edges = flow.parser.yy.getEdges();
+        //
+        //    expect(vert['C'].type).toBe('round');
+        //    expect(vert['C'].text).toBe(' A[Object&#40;foo,bar&#41;]-->B(Thing);');
+        // });
+    it('it should handle unicode chars', function () {
+      const res = flow.parser.parse('graph TD;A-->C(Начало);')
+
+      const vert = flow.parser.yy.getVertices()
+
+      expect(vert['C'].text).toBe('Начало')
+    })
+    it('it should handle backslask', function () {
+      const res = flow.parser.parse('graph TD;A-->C(c:\\windows);')
+
+      const vert = flow.parser.yy.getVertices()
+
+      expect(vert['C'].text).toBe('c:\\windows')
+    })
+    it('it should handle CAPS', function () {
+      const res = flow.parser.parse('graph TD;A-->C(some CAPS);')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(vert['C'].type).toBe('round')
+      expect(vert['C'].text).toBe('some CAPS')
+    })
+    it('it should handle directions', function () {
+      const res = flow.parser.parse('graph TD;A-->C(some URL);')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(vert['C'].type).toBe('round')
+      expect(vert['C'].text).toBe('some URL')
+    })
+  })
+
+  it('should handle a single node', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;A;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['A'].styles.length).toBe(0)
+  })
+
+  it('should handle a single square node', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;a[A];')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['a'].styles.length).toBe(0)
+    expect(vert['a'].type).toBe('square')
+  })
+  it('should handle a single round square node', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;a[A];')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['a'].styles.length).toBe(0)
+    expect(vert['a'].type).toBe('square')
+  })
+  it('should handle a single circle node', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;a((A));')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['a'].type).toBe('circle')
+  })
+  it('should handle a single round node', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;a(A);')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['a'].type).toBe('round')
+  })
+  it('should handle a single odd node', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;a>A];')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['a'].type).toBe('odd')
+  })
+  it('should handle a single diamond node', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;a{A};')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['a'].type).toBe('diamond')
+  })
+  it('should handle a single diamond node with html in it', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;a{A <br> end};')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['a'].type).toBe('diamond')
+    expect(vert['a'].text).toBe('A <br> end')
+  })
+  it('should handle a single round node with html in it', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;a(A <br> end);')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['a'].type).toBe('round')
+    expect(vert['a'].text).toBe('A <br> end')
+  })
+  it('should handle a single node with alphanumerics starting on a char', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;id1;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['id1'].styles.length).toBe(0)
+  })
+  it('should handle a single node with alphanumerics starting on a num', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;1id;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['1id'].styles.length).toBe(0)
+  })
+  it('should handle a single node with alphanumerics containing a minus sign', function () {
+        // Silly but syntactically correct
+    const res = flow.parser.parse('graph TD;i-d;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(0)
+    expect(vert['i-d'].styles.length).toBe(0)
+  })
+    // log.debug(flow.parser.parse('graph TD;style Q background:#fff;'));
+  it('should handle styles for vertices', function () {
+    const res = flow.parser.parse('graph TD;style Q background:#fff;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    const style = vert['Q'].styles[0]
+
+    expect(vert['Q'].styles.length).toBe(1)
+    expect(vert['Q'].styles[0]).toBe('background:#fff')
+  })
+
+    // log.debug(flow.parser.parse('graph TD;style Q background:#fff;'));
+  it('should handle styles for edges', function () {
+    const res = flow.parser.parse('graph TD;a-->b;\nstyle #0 stroke: #f66;')
+
+    const edges = flow.parser.yy.getEdges()
+
+    expect(edges.length).toBe(1)
+  })
+
+  it('should handle multiple styles for a vortex', function () {
+    const res = flow.parser.parse('graph TD;style R background:#fff,border:1px solid red;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['R'].styles.length).toBe(2)
+    expect(vert['R'].styles[0]).toBe('background:#fff')
+    expect(vert['R'].styles[1]).toBe('border:1px solid red')
+  })
+
+  it('should handle multiple styles in a graph', function () {
+    const res = flow.parser.parse('graph TD;style S background:#aaa;\nstyle T background:#bbb,border:1px solid red;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['S'].styles.length).toBe(1)
+    expect(vert['T'].styles.length).toBe(2)
+    expect(vert['S'].styles[0]).toBe('background:#aaa')
+    expect(vert['T'].styles[0]).toBe('background:#bbb')
+    expect(vert['T'].styles[1]).toBe('border:1px solid red')
+  })
+
+  it('should handle styles and graph definitons in a graph', function () {
+    const res = flow.parser.parse('graph TD;S-->T;\nstyle S background:#aaa;\nstyle T background:#bbb,border:1px solid red;')
+
+    const vert = flow.parser.yy.getVertices()
+    const edges = flow.parser.yy.getEdges()
+
+    expect(vert['S'].styles.length).toBe(1)
+    expect(vert['T'].styles.length).toBe(2)
+    expect(vert['S'].styles[0]).toBe('background:#aaa')
+    expect(vert['T'].styles[0]).toBe('background:#bbb')
+    expect(vert['T'].styles[1]).toBe('border:1px solid red')
+  })
+  it('should handle styles and graph definitons in a graph', function () {
+    const res = flow.parser.parse('graph TD;style T background:#bbb,border:1px solid red;')
+        // const res = flow.parser.parse('graph TD;style T background: #bbb;');
+
+    const vert = flow.parser.yy.getVertices()
+
+    expect(vert['T'].styles.length).toBe(2)
+    expect(vert['T'].styles[0]).toBe('background:#bbb')
+    expect(vert['T'].styles[1]).toBe('border:1px solid red')
+  })
+
+  describe('special characters should be be handled.', function () {
+    const charTest = function (char) {
+      const res = flow.parser.parse('graph TD;A(' + char + ')-->B;')
+
+      const vert = flow.parser.yy.getVertices()
+      const edges = flow.parser.yy.getEdges()
+
+      expect(vert['A'].id).toBe('A')
+      expect(vert['B'].id).toBe('B')
+      expect(vert['A'].text).toBe(char)
+    }
+
+    it('it should be able to parse a \'.\'', function () {
+      charTest('.')
+      charTest('Start 103a.a1')
+    })
+
+    it('it should be able to parse text containing \'_\'', function () {
+      charTest('_')
+    })
+
+    it('it should be able to parse a \':\'', function () {
+      charTest(':')
+    })
+
+    it('it should be able to parse a \',\'', function () {
+      charTest(',')
+    })
+
+    it('it should be able to parse text containing \'-\'', function () {
+      charTest('a-b')
+    })
+
+    it('it should be able to parse a \'+\'', function () {
+      charTest('+')
+    })
+
+    it('it should be able to parse a \'*\'', function () {
+      charTest('*')
+    })
+
+    it('it should be able to parse a \'<\'', function () {
+      charTest('<')
+    })
+
+    it('it should be able to parse a \'>\'', function () {
+      charTest('>')
+    })
+
+    it('it should be able to parse a \'=\'', function () {
+      charTest('=')
+    })
+  })
+
+  it('should be possible to declare a class', function () {
+    const res = flow.parser.parse('graph TD;classDef exClass background:#bbb,border:1px solid red;')
+        // const res = flow.parser.parse('graph TD;style T background: #bbb;');
+
+    const classes = flow.parser.yy.getClasses()
+
+    expect(classes['exClass'].styles.length).toBe(2)
+    expect(classes['exClass'].styles[0]).toBe('background:#bbb')
+    expect(classes['exClass'].styles[1]).toBe('border:1px solid red')
+  })
+
+  it('should be possible to declare a class with a dot in the style', function () {
+    const res = flow.parser.parse('graph TD;classDef exClass background:#bbb,border:1.5px solid red;')
+        // const res = flow.parser.parse('graph TD;style T background: #bbb;');
+
+    const classes = flow.parser.yy.getClasses()
+
+    expect(classes['exClass'].styles.length).toBe(2)
+    expect(classes['exClass'].styles[0]).toBe('background:#bbb')
+    expect(classes['exClass'].styles[1]).toBe('border:1.5px solid red')
+  })
+  it('should be possible to declare a class with a space in the style', function () {
+    const res = flow.parser.parse('graph TD;classDef exClass background:  #bbb,border:1.5px solid red;')
+        // const res = flow.parser.parse('graph TD;style T background  :  #bbb;');
+
+    const classes = flow.parser.yy.getClasses()
+
+    expect(classes['exClass'].styles.length).toBe(2)
+    expect(classes['exClass'].styles[0]).toBe('background:  #bbb')
+    expect(classes['exClass'].styles[1]).toBe('border:1.5px solid red')
+  })
+  it('should be possible to apply a class to a vertex', function () {
+    let statement = ''
+
+    statement = statement + 'graph TD;' + '\n'
+    statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n'
+    statement = statement + 'a-->b;' + '\n'
+    statement = statement + 'class a exClass;'
+
+    const res = flow.parser.parse(statement)
+
+    const classes = flow.parser.yy.getClasses()
+
+    expect(classes['exClass'].styles.length).toBe(2)
+    expect(classes['exClass'].styles[0]).toBe('background:#bbb')
+    expect(classes['exClass'].styles[1]).toBe('border:1px solid red')
+  })
+  it('should be possible to apply a class to a comma separated list of vertices', function () {
+    let statement = ''
+
+    statement = statement + 'graph TD;' + '\n'
+    statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n'
+    statement = statement + 'a-->b;' + '\n'
+    statement = statement + 'class a,b exClass;'
+
+    const res = flow.parser.parse(statement)
+
+    const classes = flow.parser.yy.getClasses()
+    const vertices = flow.parser.yy.getVertices()
+
+    expect(classes['exClass'].styles.length).toBe(2)
+    expect(classes['exClass'].styles[0]).toBe('background:#bbb')
+    expect(classes['exClass'].styles[1]).toBe('border:1px solid red')
+    expect(vertices['a'].classes[0]).toBe('exClass')
+    expect(vertices['b'].classes[0]).toBe('exClass')
+  })
+})
diff --git a/_submodules/mermaid/src/diagrams/gantt/gantt.spec.js b/_submodules/mermaid/src/diagrams/gantt/gantt.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a170be6e1ffc4ba4dcf4a5366e5ed5c2d9e6a97b
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/gantt/gantt.spec.js
@@ -0,0 +1,48 @@
+/* eslint-env jasmine */
+import { parser } from './parser/gantt'
+import ganttDb from './ganttDb'
+
+describe('when parsing a gantt diagram it', function () {
+  beforeEach(function () {
+    parser.yy = ganttDb
+  })
+
+  it('should handle a dateFormat definition', function () {
+    const str = 'gantt\ndateFormat yyyy-mm-dd'
+
+    parser.parse(str)
+  })
+  it('should handle a title definition', function () {
+    const str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
+
+    parser.parse(str)
+  })
+  it('should handle a section definition', function () {
+    const str = 'gantt\n' +
+      'dateFormat yyyy-mm-dd\n' +
+      'title Adding gantt diagram functionality to mermaid\n' +
+      'section Documentation'
+
+    parser.parse(str)
+  })
+  /**
+   * Beslutsflöde inligt nedan. Obs bla bla bla
+   * ```
+   * graph TD
+   * A[Hard pledge] -- text on link -->B(Round edge)
+   * B --> C{to do or not to do}
+   * C -->|Too| D[Result one]
+   * C -->|Doo| E[Result two]
+   ```
+   * params bapa - a unique bapap
+   */
+  it('should handle a task definition', function () {
+    const str = 'gantt\n' +
+      'dateFormat yyyy-mm-dd\n' +
+      'title Adding gantt diagram functionality to mermaid\n' +
+      'section Documentation\n' +
+      'Design jison grammar:des1, 2014-01-01, 2014-01-04'
+
+    parser.parse(str)
+  })
+})
diff --git a/_submodules/mermaid/src/diagrams/gantt/ganttDb.js b/_submodules/mermaid/src/diagrams/gantt/ganttDb.js
new file mode 100644
index 0000000000000000000000000000000000000000..b4e4fda763a9305d5d7b3fb5065d8aacc6e213a2
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/gantt/ganttDb.js
@@ -0,0 +1,363 @@
+import moment from 'moment'
+import { logger } from '../../logger'
+
+let dateFormat = ''
+let axisFormat = ''
+let title = ''
+let sections = []
+let tasks = []
+let currentSection = ''
+
+export const clear = function () {
+  sections = []
+  tasks = []
+  currentSection = ''
+  title = ''
+  taskCnt = 0
+  lastTask = undefined
+  lastTaskID = undefined
+  rawTasks = []
+}
+
+export const setAxisFormat = function (txt) {
+  axisFormat = txt
+}
+
+export const getAxisFormat = function () {
+  return axisFormat
+}
+
+export const setDateFormat = function (txt) {
+  dateFormat = txt
+}
+
+export const setTitle = function (txt) {
+  title = txt
+}
+
+export const getTitle = function () {
+  return title
+}
+
+export const addSection = function (txt) {
+  currentSection = txt
+  sections.push(txt)
+}
+
+export const getTasks = function () {
+  let allItemsPricessed = compileTasks()
+  const maxDepth = 10
+  let iterationCount = 0
+  while (!allItemsPricessed && (iterationCount < maxDepth)) {
+    allItemsPricessed = compileTasks()
+    iterationCount++
+  }
+
+  tasks = rawTasks
+
+  return tasks
+}
+
+const getStartDate = function (prevTime, dateFormat, str) {
+  str = str.trim()
+
+  // Test for after
+  const re = /^after\s+([\d\w-]+)/
+  const afterStatement = re.exec(str.trim())
+
+  if (afterStatement !== null) {
+    const task = findTaskById(afterStatement[1])
+
+    if (typeof task === 'undefined') {
+      const dt = new Date()
+      dt.setHours(0, 0, 0, 0)
+      return dt
+    }
+    return task.endTime
+  }
+
+  // Check for actual date set
+  if (moment(str, dateFormat.trim(), true).isValid()) {
+    return moment(str, dateFormat.trim(), true).toDate()
+  } else {
+    logger.debug('Invalid date:' + str)
+    logger.debug('With date format:' + dateFormat.trim())
+  }
+
+  // Default date - now
+  return new Date()
+}
+
+const getEndDate = function (prevTime, dateFormat, str) {
+  str = str.trim()
+
+  // Check for actual date
+  if (moment(str, dateFormat.trim(), true).isValid()) {
+    return moment(str, dateFormat.trim()).toDate()
+  }
+
+  const d = moment(prevTime)
+  // Check for length
+  const re = /^([\d]+)([wdhms])/
+  const durationStatement = re.exec(str.trim())
+
+  if (durationStatement !== null) {
+    switch (durationStatement[2]) {
+      case 's':
+        d.add(durationStatement[1], 'seconds')
+        break
+      case 'm':
+        d.add(durationStatement[1], 'minutes')
+        break
+      case 'h':
+        d.add(durationStatement[1], 'hours')
+        break
+      case 'd':
+        d.add(durationStatement[1], 'days')
+        break
+      case 'w':
+        d.add(durationStatement[1], 'weeks')
+        break
+    }
+    return d.toDate()
+  }
+  // Default date - now
+  return d.toDate()
+}
+
+let taskCnt = 0
+const parseId = function (idStr) {
+  if (typeof idStr === 'undefined') {
+    taskCnt = taskCnt + 1
+    return 'task' + taskCnt
+  }
+  return idStr
+}
+// id, startDate, endDate
+// id, startDate, length
+// id, after x, endDate
+// id, after x, length
+// startDate, endDate
+// startDate, length
+// after x, endDate
+// after x, length
+// endDate
+// length
+
+const compileData = function (prevTask, dataStr) {
+  let ds
+
+  if (dataStr.substr(0, 1) === ':') {
+    ds = dataStr.substr(1, dataStr.length)
+  } else {
+    ds = dataStr
+  }
+
+  const data = ds.split(',')
+
+  const task = {}
+
+  // Get tags like active, done cand crit
+  let matchFound = true
+  while (matchFound) {
+    matchFound = false
+    if (data[0].match(/^\s*active\s*$/)) {
+      task.active = true
+      data.shift(1)
+      matchFound = true
+    }
+    if (data[0].match(/^\s*done\s*$/)) {
+      task.done = true
+      data.shift(1)
+      matchFound = true
+    }
+    if (data[0].match(/^\s*crit\s*$/)) {
+      task.crit = true
+      data.shift(1)
+      matchFound = true
+    }
+  }
+  for (let i = 0; i < data.length; i++) {
+    data[i] = data[i].trim()
+  }
+
+  switch (data.length) {
+    case 1:
+      task.id = parseId()
+      task.startTime = prevTask.endTime
+      task.endTime = getEndDate(task.startTime, dateFormat, data[0])
+      break
+    case 2:
+      task.id = parseId()
+      task.startTime = getStartDate(undefined, dateFormat, data[0])
+      task.endTime = getEndDate(task.startTime, dateFormat, data[1])
+      break
+    case 3:
+      task.id = parseId(data[0])
+      task.startTime = getStartDate(undefined, dateFormat, data[1])
+      task.endTime = getEndDate(task.startTime, dateFormat, data[2])
+      break
+    default:
+  }
+
+  return task
+}
+
+const parseData = function (prevTaskId, dataStr) {
+  let ds
+  if (dataStr.substr(0, 1) === ':') {
+    ds = dataStr.substr(1, dataStr.length)
+  } else {
+    ds = dataStr
+  }
+
+  const data = ds.split(',')
+
+  const task = {}
+
+  // Get tags like active, done cand crit
+  let matchFound = true
+  while (matchFound) {
+    matchFound = false
+    if (data[0].match(/^\s*active\s*$/)) {
+      task.active = true
+      data.shift(1)
+      matchFound = true
+    }
+    if (data[0].match(/^\s*done\s*$/)) {
+      task.done = true
+      data.shift(1)
+      matchFound = true
+    }
+    if (data[0].match(/^\s*crit\s*$/)) {
+      task.crit = true
+      data.shift(1)
+      matchFound = true
+    }
+  }
+  for (let i = 0; i < data.length; i++) {
+    data[i] = data[i].trim()
+  }
+
+  switch (data.length) {
+    case 1:
+      task.id = parseId()
+      task.startTime = { type: 'prevTaskEnd', id: prevTaskId }
+      task.endTime = { data: data[0] }
+      break
+    case 2:
+      task.id = parseId()
+      task.startTime = { type: 'getStartDate', startData: data[0] }
+      task.endTime = { data: data[1] }
+      break
+    case 3:
+      task.id = parseId(data[0])
+      task.startTime = { type: 'getStartDate', startData: data[1] }
+      task.endTime = { data: data[2] }
+      break
+    default:
+  }
+
+  return task
+}
+
+let lastTask
+let lastTaskID
+let rawTasks = []
+const taskDb = {}
+export const addTask = function (descr, data) {
+  const rawTask = {
+    section: currentSection,
+    type: currentSection,
+    processed: false,
+    raw: { data: data },
+    task: descr
+  }
+  const taskInfo = parseData(lastTaskID, data)
+  rawTask.raw.startTime = taskInfo.startTime
+  rawTask.raw.endTime = taskInfo.endTime
+  rawTask.id = taskInfo.id
+  rawTask.prevTaskId = lastTaskID
+  rawTask.active = taskInfo.active
+  rawTask.done = taskInfo.done
+  rawTask.crit = taskInfo.crit
+
+  const pos = rawTasks.push(rawTask)
+
+  lastTaskID = rawTask.id
+  // Store cross ref
+  taskDb[rawTask.id] = pos - 1
+}
+
+export const findTaskById = function (id) {
+  const pos = taskDb[id]
+  return rawTasks[pos]
+}
+
+export const addTaskOrg = function (descr, data) {
+  const newTask = {
+    section: currentSection,
+    type: currentSection,
+    description: descr,
+    task: descr
+  }
+  const taskInfo = compileData(lastTask, data)
+  newTask.startTime = taskInfo.startTime
+  newTask.endTime = taskInfo.endTime
+  newTask.id = taskInfo.id
+  newTask.active = taskInfo.active
+  newTask.done = taskInfo.done
+  newTask.crit = taskInfo.crit
+  lastTask = newTask
+  tasks.push(newTask)
+}
+
+const compileTasks = function () {
+  const compileTask = function (pos) {
+    const task = rawTasks[pos]
+    let startTime = ''
+    switch (rawTasks[pos].raw.startTime.type) {
+      case 'prevTaskEnd':
+        const prevTask = findTaskById(task.prevTaskId)
+        task.startTime = prevTask.endTime
+        break
+      case 'getStartDate':
+        startTime = getStartDate(undefined, dateFormat, rawTasks[pos].raw.startTime.startData)
+        if (startTime) {
+          rawTasks[pos].startTime = startTime
+        }
+        break
+    }
+
+    if (rawTasks[pos].startTime) {
+      rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, dateFormat, rawTasks[pos].raw.endTime.data)
+      if (rawTasks[pos].endTime) {
+        rawTasks[pos].processed = true
+      }
+    }
+
+    return rawTasks[pos].processed
+  }
+
+  let allProcessed = true
+  for (let i = 0; i < rawTasks.length; i++) {
+    compileTask(i)
+
+    allProcessed = allProcessed && rawTasks[i].processed
+  }
+  return allProcessed
+}
+
+export default {
+  clear,
+  setDateFormat,
+  setAxisFormat,
+  getAxisFormat,
+  setTitle,
+  getTitle,
+  addSection,
+  getTasks,
+  addTask,
+  findTaskById,
+  addTaskOrg
+}
diff --git a/_submodules/mermaid/src/diagrams/gantt/ganttDb.spec.js b/_submodules/mermaid/src/diagrams/gantt/ganttDb.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a956e3605324b0bf9b671071856444f03c6b262e
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/gantt/ganttDb.spec.js
@@ -0,0 +1,175 @@
+/* eslint-env jasmine */
+import moment from 'moment'
+import ganttDb from './ganttDb'
+
+describe('when using the ganttDb', function () {
+  beforeEach(function () {
+    ganttDb.clear()
+  })
+
+  it('should handle an fixed dates', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2013-01-12')
+    const tasks = ganttDb.getTasks()
+    expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].endTime).toEqual(moment('2013-01-12', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].id).toEqual('id1')
+    expect(tasks[0].task).toEqual('test1')
+  })
+  it('should handle duration (days) instead of fixed date to determine end date', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2d')
+    const tasks = ganttDb.getTasks()
+    expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].endTime).toEqual(moment('2013-01-03', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].id).toEqual('id1')
+    expect(tasks[0].task).toEqual('test1')
+  })
+  it('should handle duration (hours) instead of fixed date to determine end date', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2h')
+    const tasks = ganttDb.getTasks()
+    expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].endTime).toEqual(moment('2013-01-01 2:00', 'YYYY-MM-DD hh:mm').toDate())
+    expect(tasks[0].id).toEqual('id1')
+    expect(tasks[0].task).toEqual('test1')
+  })
+  it('should handle duration (minutes) instead of fixed date to determine end date', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2m')
+    const tasks = ganttDb.getTasks()
+    expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].endTime).toEqual(moment('2013-01-01 00:02', 'YYYY-MM-DD hh:mm').toDate())
+    expect(tasks[0].id).toEqual('id1')
+    expect(tasks[0].task).toEqual('test1')
+  })
+  it('should handle duration (seconds) instead of fixed date to determine end date', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2s')
+    const tasks = ganttDb.getTasks()
+    expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].endTime).toEqual(moment('2013-01-01 00:00:02', 'YYYY-MM-DD hh:mm:ss').toDate())
+    expect(tasks[0].id).toEqual('id1')
+    expect(tasks[0].task).toEqual('test1')
+  })
+  it('should handle duration (weeks) instead of fixed date to determine end date', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2w')
+    const tasks = ganttDb.getTasks()
+    expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].endTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].id).toEqual('id1')
+    expect(tasks[0].task).toEqual('test1')
+  })
+
+  it('should handle relative start date based on id', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2w')
+    ganttDb.addTask('test2', 'id2,after id1,1d')
+
+    const tasks = ganttDb.getTasks()
+
+    expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
+    expect(tasks[1].id).toEqual('id2')
+    expect(tasks[1].task).toEqual('test2')
+  })
+
+  it('should handle relative start date based on id when id is invalid', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2w')
+    ganttDb.addTask('test2', 'id2,after id3,1d')
+    const tasks = ganttDb.getTasks()
+    expect(tasks[1].startTime).toEqual(new Date((new Date()).setHours(0, 0, 0, 0)))
+    expect(tasks[1].id).toEqual('id2')
+    expect(tasks[1].task).toEqual('test2')
+  })
+
+  it('should handle fixed dates without id', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', '2013-01-01,2013-01-12')
+    const tasks = ganttDb.getTasks()
+    expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].endTime).toEqual(moment('2013-01-12', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].id).toEqual('task1')
+    expect(tasks[0].task).toEqual('test1')
+  })
+
+  it('should handle duration instead of a fixed date to determine end date without id', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', '2013-01-01,4d')
+    const tasks = ganttDb.getTasks()
+    expect(tasks[0].startTime).toEqual(moment('2013-01-01', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].endTime).toEqual(moment('2013-01-05', 'YYYY-MM-DD').toDate())
+    expect(tasks[0].id).toEqual('task1')
+    expect(tasks[0].task).toEqual('test1')
+  })
+
+  it('should handle relative start date of a fixed date to determine end date without id', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2w')
+    ganttDb.addTask('test2', 'after id1,1d')
+
+    const tasks = ganttDb.getTasks()
+
+    expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
+    expect(tasks[1].id).toEqual('task1')
+    expect(tasks[1].task).toEqual('test2')
+  })
+  it('should handle a new task with only an end date as definition', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2w')
+    ganttDb.addTask('test2', '2013-01-26')
+
+    const tasks = ganttDb.getTasks()
+
+    expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
+    expect(tasks[1].endTime).toEqual(moment('2013-01-26', 'YYYY-MM-DD').toDate())
+    expect(tasks[1].id).toEqual('task1')
+    expect(tasks[1].task).toEqual('test2')
+  })
+  it('should handle a new task with only an end date as definition', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2w')
+    ganttDb.addTask('test2', '2d')
+
+    const tasks = ganttDb.getTasks()
+
+    expect(tasks[1].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
+    expect(tasks[1].endTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
+    expect(tasks[1].id).toEqual('task1')
+    expect(tasks[1].task).toEqual('test2')
+  })
+  it('should handle relative start date based on id regardless of sections', function () {
+    ganttDb.setDateFormat('YYYY-MM-DD')
+    ganttDb.addSection('testa1')
+    ganttDb.addTask('test1', 'id1,2013-01-01,2w')
+    ganttDb.addTask('test2', 'id2,after id3,1d')
+    ganttDb.addSection('testa2')
+    ganttDb.addTask('test3', 'id3,after id1,2d')
+
+    const tasks = ganttDb.getTasks()
+
+    expect(tasks[1].startTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
+    expect(tasks[1].endTime).toEqual(moment('2013-01-18', 'YYYY-MM-DD').toDate())
+    expect(tasks[1].id).toEqual('id2')
+    expect(tasks[1].task).toEqual('test2')
+
+    expect(tasks[2].id).toEqual('id3')
+    expect(tasks[2].task).toEqual('test3')
+    expect(tasks[2].startTime).toEqual(moment('2013-01-15', 'YYYY-MM-DD').toDate())
+    expect(tasks[2].endTime).toEqual(moment('2013-01-17', 'YYYY-MM-DD').toDate())
+  })
+})
diff --git a/_submodules/mermaid/src/diagrams/gantt/ganttRenderer.js b/_submodules/mermaid/src/diagrams/gantt/ganttRenderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..7d24df2a4c4defa0cd12be30e28a0bbd478f0063
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/gantt/ganttRenderer.js
@@ -0,0 +1,344 @@
+import * as d3 from 'd3'
+
+import { parser } from './parser/gantt'
+import ganttDb from './ganttDb'
+
+parser.yy = ganttDb
+
+const conf = {
+  titleTopMargin: 25,
+  barHeight: 20,
+  barGap: 4,
+  topPadding: 50,
+  rightPadding: 75,
+  leftPadding: 75,
+  gridLineStartPadding: 35,
+  fontSize: 11,
+  fontFamily: '"Open-Sans", "sans-serif"'
+}
+export const setConf = function (cnf) {
+  const keys = Object.keys(cnf)
+
+  keys.forEach(function (key) {
+    conf[key] = cnf[key]
+  })
+}
+let w
+export const draw = function (text, id) {
+  parser.yy.clear()
+  parser.parse(text)
+
+  const elem = document.getElementById(id)
+  w = elem.parentElement.offsetWidth
+
+  if (typeof w === 'undefined') {
+    w = 1200
+  }
+
+  if (typeof conf.useWidth !== 'undefined') {
+    w = conf.useWidth
+  }
+
+  const taskArray = parser.yy.getTasks()
+
+  // Set height based on number of tasks
+  const h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding
+
+  elem.setAttribute('height', '100%')
+  // Set viewBox
+  elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h)
+  const svg = d3.select(`[id="${id}"]`)
+
+  // Set timescale
+  const timeScale = d3.scaleTime()
+    .domain([d3.min(taskArray, function (d) {
+      return d.startTime
+    }),
+    d3.max(taskArray, function (d) {
+      return d.endTime
+    })])
+    .rangeRound([0, w - conf.leftPadding - conf.rightPadding])
+
+  let categories = []
+
+  for (let i = 0; i < taskArray.length; i++) {
+    categories.push(taskArray[i].type)
+  }
+
+  const catsUnfiltered = categories // for vert labels
+
+  categories = checkUnique(categories)
+
+  makeGant(taskArray, w, h)
+  if (typeof conf.useWidth !== 'undefined') {
+    elem.setAttribute('width', w)
+  }
+
+  svg.append('text')
+    .text(parser.yy.getTitle())
+    .attr('x', w / 2)
+    .attr('y', conf.titleTopMargin)
+    .attr('class', 'titleText')
+
+  function makeGant (tasks, pageWidth, pageHeight) {
+    const barHeight = conf.barHeight
+    const gap = barHeight + conf.barGap
+    const topPadding = conf.topPadding
+    const leftPadding = conf.leftPadding
+
+    const colorScale = d3.scaleLinear()
+      .domain([0, categories.length])
+      .range(['#00B9FA', '#F95002'])
+      .interpolate(d3.interpolateHcl)
+
+    makeGrid(leftPadding, topPadding, pageWidth, pageHeight)
+    drawRects(tasks, gap, topPadding, leftPadding, barHeight, colorScale, pageWidth, pageHeight)
+    vertLabels(gap, topPadding, leftPadding, barHeight, colorScale)
+    drawToday(leftPadding, topPadding, pageWidth, pageHeight)
+  }
+
+  function drawRects (theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w, h) {
+    svg.append('g')
+      .selectAll('rect')
+      .data(theArray)
+      .enter()
+      .append('rect')
+      .attr('x', 0)
+      .attr('y', function (d, i) {
+        return i * theGap + theTopPad - 2
+      })
+      .attr('width', function () {
+        return w - conf.rightPadding / 2
+      })
+      .attr('height', theGap)
+      .attr('class', function (d) {
+        for (let i = 0; i < categories.length; i++) {
+          if (d.type === categories[i]) {
+            return 'section section' + (i % conf.numberSectionStyles)
+          }
+        }
+        return 'section section0'
+      })
+
+    const rectangles = svg.append('g')
+      .selectAll('rect')
+      .data(theArray)
+      .enter()
+
+    rectangles.append('rect')
+      .attr('rx', 3)
+      .attr('ry', 3)
+      .attr('x', function (d) {
+        return timeScale(d.startTime) + theSidePad
+      })
+      .attr('y', function (d, i) {
+        return i * theGap + theTopPad
+      })
+      .attr('width', function (d) {
+        return (timeScale(d.endTime) - timeScale(d.startTime))
+      })
+      .attr('height', theBarHeight)
+      .attr('class', function (d) {
+        const res = 'task '
+
+        let secNum = 0
+        for (let i = 0; i < categories.length; i++) {
+          if (d.type === categories[i]) {
+            secNum = (i % conf.numberSectionStyles)
+          }
+        }
+
+        if (d.active) {
+          if (d.crit) {
+            return res + ' activeCrit' + secNum
+          } else {
+            return res + ' active' + secNum
+          }
+        }
+
+        if (d.done) {
+          if (d.crit) {
+            return res + ' doneCrit' + secNum
+          } else {
+            return res + ' done' + secNum
+          }
+        }
+
+        if (d.crit) {
+          return res + ' crit' + secNum
+        }
+
+        return res + ' task' + secNum
+      })
+
+    rectangles.append('text')
+      .text(function (d) {
+        return d.task
+      })
+      .attr('font-size', conf.fontSize)
+      .attr('x', function (d) {
+        const startX = timeScale(d.startTime)
+        const endX = timeScale(d.endTime)
+        const textWidth = this.getBBox().width
+
+        // Check id text width > width of rectangle
+        if (textWidth > (endX - startX)) {
+          if (endX + textWidth + 1.5 * conf.leftPadding > w) {
+            return startX + theSidePad - 5
+          } else {
+            return endX + theSidePad + 5
+          }
+        } else {
+          return (endX - startX) / 2 + startX + theSidePad
+        }
+      })
+      .attr('y', function (d, i) {
+        return i * theGap + (conf.barHeight / 2) + (conf.fontSize / 2 - 2) + theTopPad
+      })
+      .attr('text-height', theBarHeight)
+      .attr('class', function (d) {
+        const startX = timeScale(d.startTime)
+        const endX = timeScale(d.endTime)
+        const textWidth = this.getBBox().width
+        let secNum = 0
+        for (let i = 0; i < categories.length; i++) {
+          if (d.type === categories[i]) {
+            secNum = (i % conf.numberSectionStyles)
+          }
+        }
+
+        let taskType = ''
+        if (d.active) {
+          if (d.crit) {
+            taskType = 'activeCritText' + secNum
+          } else {
+            taskType = 'activeText' + secNum
+          }
+        }
+
+        if (d.done) {
+          if (d.crit) {
+            taskType = taskType + ' doneCritText' + secNum
+          } else {
+            taskType = taskType + ' doneText' + secNum
+          }
+        } else {
+          if (d.crit) {
+            taskType = taskType + ' critText' + secNum
+          }
+        }
+
+        // Check id text width > width of rectangle
+        if (textWidth > (endX - startX)) {
+          if (endX + textWidth + 1.5 * conf.leftPadding > w) {
+            return 'taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType
+          } else {
+            return 'taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType
+          }
+        } else {
+          return 'taskText taskText' + secNum + ' ' + taskType
+        }
+      })
+  }
+
+  function makeGrid (theSidePad, theTopPad, w, h) {
+    let xAxis = d3.axisBottom(timeScale)
+      .tickSize(-h + theTopPad + conf.gridLineStartPadding)
+      .tickFormat(d3.timeFormat(parser.yy.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'))
+
+    svg.append('g')
+      .attr('class', 'grid')
+      .attr('transform', 'translate(' + theSidePad + ', ' + (h - 50) + ')')
+      .call(xAxis)
+      .selectAll('text')
+      .style('text-anchor', 'middle')
+      .attr('fill', '#000')
+      .attr('stroke', 'none')
+      .attr('font-size', 10)
+      .attr('dy', '1em')
+  }
+
+  function vertLabels (theGap, theTopPad) {
+    const numOccurances = []
+    let prevGap = 0
+
+    for (let i = 0; i < categories.length; i++) {
+      numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)]
+    }
+
+    svg.append('g') // without doing this, impossible to put grid lines behind text
+      .selectAll('text')
+      .data(numOccurances)
+      .enter()
+      .append('text')
+      .text(function (d) {
+        return d[0]
+      })
+      .attr('x', 10)
+      .attr('y', function (d, i) {
+        if (i > 0) {
+          for (let j = 0; j < i; j++) {
+            prevGap += numOccurances[i - 1][1]
+            return d[1] * theGap / 2 + prevGap * theGap + theTopPad
+          }
+        } else {
+          return d[1] * theGap / 2 + theTopPad
+        }
+      })
+      .attr('class', function (d) {
+        for (let i = 0; i < categories.length; i++) {
+          if (d[0] === categories[i]) {
+            return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles)
+          }
+        }
+        return 'sectionTitle'
+      })
+  }
+
+  function drawToday (theSidePad, theTopPad, w, h) {
+    const todayG = svg.append('g')
+      .attr('class', 'today')
+
+    const today = new Date()
+
+    todayG.append('line')
+      .attr('x1', timeScale(today) + theSidePad)
+      .attr('x2', timeScale(today) + theSidePad)
+      .attr('y1', conf.titleTopMargin)
+      .attr('y2', h - conf.titleTopMargin)
+      .attr('class', 'today')
+  }
+
+  // from this stackexchange question: http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
+  function checkUnique (arr) {
+    const hash = {}
+    const result = []
+    for (let i = 0, l = arr.length; i < l; ++i) {
+      if (!hash.hasOwnProperty(arr[i])) { // it works with objects! in FF, at least
+        hash[arr[i]] = true
+        result.push(arr[i])
+      }
+    }
+    return result
+  }
+
+  // from this stackexchange question: http://stackoverflow.com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array
+  function getCounts (arr) {
+    let i = arr.length // const to loop over
+    const obj = {} // obj to store results
+    while (i) {
+      obj[arr[--i]] = (obj[arr[i]] || 0) + 1 // count occurrences
+    }
+    return obj
+  }
+
+  // get specific from everything
+  function getCount (word, arr) {
+    return getCounts(arr)[word] || 0
+  }
+}
+
+export default {
+  setConf,
+  draw
+}
diff --git a/_submodules/mermaid/src/diagrams/gantt/parser/gantt.jison b/_submodules/mermaid/src/diagrams/gantt/parser/gantt.jison
new file mode 100644
index 0000000000000000000000000000000000000000..49ab3ad477e027544f2ef62de2c709c7caeda6af
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/gantt/parser/gantt.jison
@@ -0,0 +1,64 @@
+/** mermaid
+ *  https://mermaidjs.github.io/
+ *  (c) 2015 Knut Sveidqvist
+ *  MIT license.
+ */
+%lex
+
+%options case-insensitive
+
+%{
+	// Pre-lexer code can go here
+%}
+
+%%
+
+[\n]+                   return 'NL';
+\s+                     /* skip whitespace */
+\#[^\n]*                /* skip comments */
+\%%[^\n]*               /* skip comments */
+"gantt"     	        return 'gantt';
+"dateFormat"\s[^#\n;]+  return 'dateFormat';
+"axisFormat"\s[^#\n;]+  return 'axisFormat';
+\d\d\d\d"-"\d\d"-"\d\d  return 'date';
+"title"\s[^#\n;]+       return 'title';
+"section"\s[^#:\n;]+    return 'section';
+[^#:\n;]+               return 'taskTxt';
+":"[^#\n;]+             return 'taskData';
+":"                         return ':';
+<<EOF>>                     return 'EOF';
+.                           return 'INVALID';
+
+/lex
+
+%left '^'
+
+%start start
+
+%% /* language grammar */
+
+start
+	: gantt document 'EOF' { return $2; }
+	;
+
+document
+	: /* empty */ { $$ = [] }
+	| document line {$1.push($2);$$ = $1}
+	;
+
+line
+	: SPACE statement { $$ = $2 }
+	| statement { $$ = $1 }
+	| NL { $$=[];}
+	| EOF { $$=[];}
+	;
+
+statement
+	: 'dateFormat' {yy.setDateFormat($1.substr(11));$$=$1.substr(11);}
+  | 'axisFormat' {yy.setAxisFormat($1.substr(11));$$=$1.substr(11);}
+	| title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
+	| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
+	| taskTxt taskData {yy.addTask($1,$2);$$='task';}
+	;
+
+%%
diff --git a/_submodules/mermaid/src/diagrams/gantt/parser/gantt.js b/_submodules/mermaid/src/diagrams/gantt/parser/gantt.js
new file mode 100644
index 0000000000000000000000000000000000000000..38ff324ee092da87330859d58b37870f000f39b8
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/gantt/parser/gantt.js
@@ -0,0 +1,661 @@
+/* parser generated by jison 0.4.18 */
+/*
+  Returns a Parser object of the following structure:
+
+  Parser: {
+    yy: {}
+  }
+
+  Parser.prototype: {
+    yy: {},
+    trace: function(),
+    symbols_: {associative list: name ==> number},
+    terminals_: {associative list: number ==> name},
+    productions_: [...],
+    performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
+    table: [...],
+    defaultActions: {...},
+    parseError: function(str, hash),
+    parse: function(input),
+
+    lexer: {
+        EOF: 1,
+        parseError: function(str, hash),
+        setInput: function(input),
+        input: function(),
+        unput: function(str),
+        more: function(),
+        less: function(n),
+        pastInput: function(),
+        upcomingInput: function(),
+        showPosition: function(),
+        test_match: function(regex_match_array, rule_index),
+        next: function(),
+        lex: function(),
+        begin: function(condition),
+        popState: function(),
+        _currentRules: function(),
+        topState: function(),
+        pushState: function(condition),
+
+        options: {
+            ranges: boolean           (optional: true ==> token location info will include a .range[] member)
+            flex: boolean             (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
+            backtrack_lexer: boolean  (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
+        },
+
+        performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
+        rules: [...],
+        conditions: {associative list: name ==> set},
+    }
+  }
+
+
+  token location info (@$, _$, etc.): {
+    first_line: n,
+    last_line: n,
+    first_column: n,
+    last_column: n,
+    range: [start_number, end_number]       (where the numbers are indexes into the input string, regular zero-based)
+  }
+
+
+  the parseError function receives a 'hash' object with these members for lexer and parser errors: {
+    text:        (matched text)
+    token:       (the produced terminal token, if any)
+    line:        (yylineno)
+  }
+  while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
+    loc:         (yylloc)
+    expected:    (string describing the set of expected tokens)
+    recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
+  }
+*/
+var parser = (function(){
+var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[6,8,10,11,12,13,14,15],$V1=[1,9],$V2=[1,10],$V3=[1,11],$V4=[1,12],$V5=[1,13];
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"start":3,"gantt":4,"document":5,"EOF":6,"line":7,"SPACE":8,"statement":9,"NL":10,"dateFormat":11,"axisFormat":12,"title":13,"section":14,"taskTxt":15,"taskData":16,"$accept":0,"$end":1},
+terminals_: {2:"error",4:"gantt",6:"EOF",8:"SPACE",10:"NL",11:"dateFormat",12:"axisFormat",13:"title",14:"section",15:"taskTxt",16:"taskData"},
+productions_: [0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,1],[9,1],[9,1],[9,2]],
+performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
+/* this == yyval */
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1:
+ return $$[$0-1]; 
+break;
+case 2:
+ this.$ = [] 
+break;
+case 3:
+$$[$0-1].push($$[$0]);this.$ = $$[$0-1]
+break;
+case 4: case 5:
+ this.$ = $$[$0] 
+break;
+case 6: case 7:
+ this.$=[];
+break;
+case 8:
+yy.setDateFormat($$[$0].substr(11));this.$=$$[$0].substr(11);
+break;
+case 9:
+yy.setAxisFormat($$[$0].substr(11));this.$=$$[$0].substr(11);
+break;
+case 10:
+yy.setTitle($$[$0].substr(6));this.$=$$[$0].substr(6);
+break;
+case 11:
+yy.addSection($$[$0].substr(8));this.$=$$[$0].substr(8);
+break;
+case 12:
+yy.addTask($$[$0-1],$$[$0]);this.$='task';
+break;
+}
+},
+table: [{3:1,4:[1,2]},{1:[3]},o($V0,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:$V1,12:$V2,13:$V3,14:$V4,15:$V5},o($V0,[2,7],{1:[2,1]}),o($V0,[2,3]),{9:14,11:$V1,12:$V2,13:$V3,14:$V4,15:$V5},o($V0,[2,5]),o($V0,[2,6]),o($V0,[2,8]),o($V0,[2,9]),o($V0,[2,10]),o($V0,[2,11]),{16:[1,15]},o($V0,[2,4]),o($V0,[2,12])],
+defaultActions: {},
+parseError: function parseError(str, hash) {
+    if (hash.recoverable) {
+        this.trace(str);
+    } else {
+        var error = new Error(str);
+        error.hash = hash;
+        throw error;
+    }
+},
+parse: function parse(input) {
+    var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+    var args = lstack.slice.call(arguments, 1);
+    var lexer = Object.create(this.lexer);
+    var sharedState = { yy: {} };
+    for (var k in this.yy) {
+        if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
+            sharedState.yy[k] = this.yy[k];
+        }
+    }
+    lexer.setInput(input, sharedState.yy);
+    sharedState.yy.lexer = lexer;
+    sharedState.yy.parser = this;
+    if (typeof lexer.yylloc == 'undefined') {
+        lexer.yylloc = {};
+    }
+    var yyloc = lexer.yylloc;
+    lstack.push(yyloc);
+    var ranges = lexer.options && lexer.options.ranges;
+    if (typeof sharedState.yy.parseError === 'function') {
+        this.parseError = sharedState.yy.parseError;
+    } else {
+        this.parseError = Object.getPrototypeOf(this).parseError;
+    }
+    function popStack(n) {
+        stack.length = stack.length - 2 * n;
+        vstack.length = vstack.length - n;
+        lstack.length = lstack.length - n;
+    }
+            function lex() {
+            var token;
+            token = tstack.pop() || lexer.lex() || EOF;
+            if (typeof token !== 'number') {
+                if (token instanceof Array) {
+                    tstack = token;
+                    token = tstack.pop();
+                }
+                token = self.symbols_[token] || token;
+            }
+            return token;
+        }
+    var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+    while (true) {
+        state = stack[stack.length - 1];
+        if (this.defaultActions[state]) {
+            action = this.defaultActions[state];
+        } else {
+            if (symbol === null || typeof symbol == 'undefined') {
+                symbol = lex();
+            }
+            action = table[state] && table[state][symbol];
+        }
+        if (typeof action === 'undefined' || !action.length || !action[0]) {
+            var errStr = '';
+            expected = [];
+            for (p in table[state]) {
+                if (this.terminals_[p] && p > TERROR) {
+                    expected.push('\'' + this.terminals_[p] + '\'');
+                }
+            }
+            if (lexer.showPosition) {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
+            } else {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
+            }
+            this.parseError(errStr, {
+                text: lexer.match,
+                token: this.terminals_[symbol] || symbol,
+                line: lexer.yylineno,
+                loc: yyloc,
+                expected: expected
+            });
+        }
+        if (action[0] instanceof Array && action.length > 1) {
+            throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+        }
+        switch (action[0]) {
+        case 1:
+            stack.push(symbol);
+            vstack.push(lexer.yytext);
+            lstack.push(lexer.yylloc);
+            stack.push(action[1]);
+            symbol = null;
+            if (!preErrorSymbol) {
+                yyleng = lexer.yyleng;
+                yytext = lexer.yytext;
+                yylineno = lexer.yylineno;
+                yyloc = lexer.yylloc;
+                if (recovering > 0) {
+                    recovering--;
+                }
+            } else {
+                symbol = preErrorSymbol;
+                preErrorSymbol = null;
+            }
+            break;
+        case 2:
+            len = this.productions_[action[1]][1];
+            yyval.$ = vstack[vstack.length - len];
+            yyval._$ = {
+                first_line: lstack[lstack.length - (len || 1)].first_line,
+                last_line: lstack[lstack.length - 1].last_line,
+                first_column: lstack[lstack.length - (len || 1)].first_column,
+                last_column: lstack[lstack.length - 1].last_column
+            };
+            if (ranges) {
+                yyval._$.range = [
+                    lstack[lstack.length - (len || 1)].range[0],
+                    lstack[lstack.length - 1].range[1]
+                ];
+            }
+            r = this.performAction.apply(yyval, [
+                yytext,
+                yyleng,
+                yylineno,
+                sharedState.yy,
+                action[1],
+                vstack,
+                lstack
+            ].concat(args));
+            if (typeof r !== 'undefined') {
+                return r;
+            }
+            if (len) {
+                stack = stack.slice(0, -1 * len * 2);
+                vstack = vstack.slice(0, -1 * len);
+                lstack = lstack.slice(0, -1 * len);
+            }
+            stack.push(this.productions_[action[1]][0]);
+            vstack.push(yyval.$);
+            lstack.push(yyval._$);
+            newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+            stack.push(newState);
+            break;
+        case 3:
+            return true;
+        }
+    }
+    return true;
+}};
+
+/* generated by jison-lex 0.3.4 */
+var lexer = (function(){
+var lexer = ({
+
+EOF:1,
+
+parseError:function parseError(str, hash) {
+        if (this.yy.parser) {
+            this.yy.parser.parseError(str, hash);
+        } else {
+            throw new Error(str);
+        }
+    },
+
+// resets the lexer, sets new input
+setInput:function (input, yy) {
+        this.yy = yy || this.yy || {};
+        this._input = input;
+        this._more = this._backtrack = this.done = false;
+        this.yylineno = this.yyleng = 0;
+        this.yytext = this.matched = this.match = '';
+        this.conditionStack = ['INITIAL'];
+        this.yylloc = {
+            first_line: 1,
+            first_column: 0,
+            last_line: 1,
+            last_column: 0
+        };
+        if (this.options.ranges) {
+            this.yylloc.range = [0,0];
+        }
+        this.offset = 0;
+        return this;
+    },
+
+// consumes and returns one char from the input
+input:function () {
+        var ch = this._input[0];
+        this.yytext += ch;
+        this.yyleng++;
+        this.offset++;
+        this.match += ch;
+        this.matched += ch;
+        var lines = ch.match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno++;
+            this.yylloc.last_line++;
+        } else {
+            this.yylloc.last_column++;
+        }
+        if (this.options.ranges) {
+            this.yylloc.range[1]++;
+        }
+
+        this._input = this._input.slice(1);
+        return ch;
+    },
+
+// unshifts one char (or a string) into the input
+unput:function (ch) {
+        var len = ch.length;
+        var lines = ch.split(/(?:\r\n?|\n)/g);
+
+        this._input = ch + this._input;
+        this.yytext = this.yytext.substr(0, this.yytext.length - len);
+        //this.yyleng -= len;
+        this.offset -= len;
+        var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+        this.match = this.match.substr(0, this.match.length - 1);
+        this.matched = this.matched.substr(0, this.matched.length - 1);
+
+        if (lines.length - 1) {
+            this.yylineno -= lines.length - 1;
+        }
+        var r = this.yylloc.range;
+
+        this.yylloc = {
+            first_line: this.yylloc.first_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.first_column,
+            last_column: lines ?
+                (lines.length === oldLines.length ? this.yylloc.first_column : 0)
+                 + oldLines[oldLines.length - lines.length].length - lines[0].length :
+              this.yylloc.first_column - len
+        };
+
+        if (this.options.ranges) {
+            this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+        }
+        this.yyleng = this.yytext.length;
+        return this;
+    },
+
+// When called from action, caches matched text and appends it on next action
+more:function () {
+        this._more = true;
+        return this;
+    },
+
+// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
+reject:function () {
+        if (this.options.backtrack_lexer) {
+            this._backtrack = true;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+
+        }
+        return this;
+    },
+
+// retain first n characters of the match
+less:function (n) {
+        this.unput(this.match.slice(n));
+    },
+
+// displays already matched input, i.e. for error messages
+pastInput:function () {
+        var past = this.matched.substr(0, this.matched.length - this.match.length);
+        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+    },
+
+// displays upcoming input, i.e. for error messages
+upcomingInput:function () {
+        var next = this.match;
+        if (next.length < 20) {
+            next += this._input.substr(0, 20-next.length);
+        }
+        return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+    },
+
+// displays the character position where the lexing error occurred, i.e. for error messages
+showPosition:function () {
+        var pre = this.pastInput();
+        var c = new Array(pre.length + 1).join("-");
+        return pre + this.upcomingInput() + "\n" + c + "^";
+    },
+
+// test the lexed token: return FALSE when not a match, otherwise return token
+test_match:function (match, indexed_rule) {
+        var token,
+            lines,
+            backup;
+
+        if (this.options.backtrack_lexer) {
+            // save context
+            backup = {
+                yylineno: this.yylineno,
+                yylloc: {
+                    first_line: this.yylloc.first_line,
+                    last_line: this.last_line,
+                    first_column: this.yylloc.first_column,
+                    last_column: this.yylloc.last_column
+                },
+                yytext: this.yytext,
+                match: this.match,
+                matches: this.matches,
+                matched: this.matched,
+                yyleng: this.yyleng,
+                offset: this.offset,
+                _more: this._more,
+                _input: this._input,
+                yy: this.yy,
+                conditionStack: this.conditionStack.slice(0),
+                done: this.done
+            };
+            if (this.options.ranges) {
+                backup.yylloc.range = this.yylloc.range.slice(0);
+            }
+        }
+
+        lines = match[0].match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno += lines.length;
+        }
+        this.yylloc = {
+            first_line: this.yylloc.last_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.last_column,
+            last_column: lines ?
+                         lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
+                         this.yylloc.last_column + match[0].length
+        };
+        this.yytext += match[0];
+        this.match += match[0];
+        this.matches = match;
+        this.yyleng = this.yytext.length;
+        if (this.options.ranges) {
+            this.yylloc.range = [this.offset, this.offset += this.yyleng];
+        }
+        this._more = false;
+        this._backtrack = false;
+        this._input = this._input.slice(match[0].length);
+        this.matched += match[0];
+        token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
+        if (this.done && this._input) {
+            this.done = false;
+        }
+        if (token) {
+            return token;
+        } else if (this._backtrack) {
+            // recover context
+            for (var k in backup) {
+                this[k] = backup[k];
+            }
+            return false; // rule action called reject() implying the next rule should be tested instead.
+        }
+        return false;
+    },
+
+// return next match in input
+next:function () {
+        if (this.done) {
+            return this.EOF;
+        }
+        if (!this._input) {
+            this.done = true;
+        }
+
+        var token,
+            match,
+            tempMatch,
+            index;
+        if (!this._more) {
+            this.yytext = '';
+            this.match = '';
+        }
+        var rules = this._currentRules();
+        for (var i = 0; i < rules.length; i++) {
+            tempMatch = this._input.match(this.rules[rules[i]]);
+            if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+                match = tempMatch;
+                index = i;
+                if (this.options.backtrack_lexer) {
+                    token = this.test_match(tempMatch, rules[i]);
+                    if (token !== false) {
+                        return token;
+                    } else if (this._backtrack) {
+                        match = false;
+                        continue; // rule action called reject() implying a rule MISmatch.
+                    } else {
+                        // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+                        return false;
+                    }
+                } else if (!this.options.flex) {
+                    break;
+                }
+            }
+        }
+        if (match) {
+            token = this.test_match(match, rules[index]);
+            if (token !== false) {
+                return token;
+            }
+            // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+            return false;
+        }
+        if (this._input === "") {
+            return this.EOF;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+        }
+    },
+
+// return next match that has a token
+lex:function lex() {
+        var r = this.next();
+        if (r) {
+            return r;
+        } else {
+            return this.lex();
+        }
+    },
+
+// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
+begin:function begin(condition) {
+        this.conditionStack.push(condition);
+    },
+
+// pop the previously active lexer condition state off the condition stack
+popState:function popState() {
+        var n = this.conditionStack.length - 1;
+        if (n > 0) {
+            return this.conditionStack.pop();
+        } else {
+            return this.conditionStack[0];
+        }
+    },
+
+// produce the lexer rule set which is active for the currently active lexer condition state
+_currentRules:function _currentRules() {
+        if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
+            return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+        } else {
+            return this.conditions["INITIAL"].rules;
+        }
+    },
+
+// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
+topState:function topState(n) {
+        n = this.conditionStack.length - 1 - Math.abs(n || 0);
+        if (n >= 0) {
+            return this.conditionStack[n];
+        } else {
+            return "INITIAL";
+        }
+    },
+
+// alias for begin(condition)
+pushState:function pushState(condition) {
+        this.begin(condition);
+    },
+
+// return the number of states currently on the stack
+stateStackSize:function stateStackSize() {
+        return this.conditionStack.length;
+    },
+options: {"case-insensitive":true},
+performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+	// Pre-lexer code can go here
+
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0:return 10;
+break;
+case 1:/* skip whitespace */
+break;
+case 2:/* skip comments */
+break;
+case 3:/* skip comments */
+break;
+case 4:return 4;
+break;
+case 5:return 11;
+break;
+case 6:return 12;
+break;
+case 7:return 'date';
+break;
+case 8:return 13;
+break;
+case 9:return 14;
+break;
+case 10:return 15;
+break;
+case 11:return 16;
+break;
+case 12:return ':';
+break;
+case 13:return 6;
+break;
+case 14:return 'INVALID';
+break;
+}
+},
+rules: [/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],
+conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14],"inclusive":true}}
+});
+return lexer;
+})();
+parser.lexer = lexer;
+function Parser () {
+  this.yy = {};
+}
+Parser.prototype = parser;parser.Parser = Parser;
+return new Parser;
+})();
+
+
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+exports.parser = parser;
+exports.Parser = parser.Parser;
+exports.parse = function () { return parser.parse.apply(parser, arguments); };
+exports.main = function commonjsMain(args) {
+    if (!args[1]) {
+        console.log('Usage: '+args[0]+' FILE');
+        process.exit(1);
+    }
+    var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8");
+    return exports.parser.parse(source);
+};
+if (typeof module !== 'undefined' && require.main === module) {
+  exports.main(process.argv.slice(1));
+}
+}
\ No newline at end of file
diff --git a/_submodules/mermaid/src/diagrams/git/gitGraphAst.js b/_submodules/mermaid/src/diagrams/git/gitGraphAst.js
new file mode 100644
index 0000000000000000000000000000000000000000..e734453b761e66a73d1ab5ef5d668774fb166e83
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/git/gitGraphAst.js
@@ -0,0 +1,228 @@
+import _ from 'lodash'
+
+import { logger } from '../../logger'
+
+let commits = {}
+let head = null
+let branches = { 'master': head }
+let curBranch = 'master'
+let direction = 'LR'
+let seq = 0
+
+function getRandomInt (min, max) {
+  return Math.floor(Math.random() * (max - min)) + min
+}
+
+function getId () {
+  const pool = '0123456789abcdef'
+  let id = ''
+  for (let i = 0; i < 7; i++) {
+    id += pool[getRandomInt(0, 16)]
+  }
+  return id
+}
+
+function isfastforwardable (currentCommit, otherCommit) {
+  logger.debug('Entering isfastforwardable:', currentCommit.id, otherCommit.id)
+  while (currentCommit.seq <= otherCommit.seq && currentCommit !== otherCommit) {
+    // only if other branch has more commits
+    if (otherCommit.parent == null) break
+    if (Array.isArray(otherCommit.parent)) {
+      logger.debug('In merge commit:', otherCommit.parent)
+      return isfastforwardable(currentCommit, commits[otherCommit.parent[0]]) ||
+        isfastforwardable(currentCommit, commits[otherCommit.parent[1]])
+    } else {
+      otherCommit = commits[otherCommit.parent]
+    }
+  }
+  logger.debug(currentCommit.id, otherCommit.id)
+  return currentCommit.id === otherCommit.id
+}
+
+function isReachableFrom (currentCommit, otherCommit) {
+  const currentSeq = currentCommit.seq
+  const otherSeq = otherCommit.seq
+  if (currentSeq > otherSeq) return isfastforwardable(otherCommit, currentCommit)
+  return false
+}
+
+export const setDirection = function (dir) {
+  direction = dir
+}
+let options = {}
+export const setOptions = function (rawOptString) {
+  logger.debug('options str', rawOptString)
+  rawOptString = rawOptString && rawOptString.trim()
+  rawOptString = rawOptString || '{}'
+  try {
+    options = JSON.parse(rawOptString)
+  } catch (e) {
+    logger.error('error while parsing gitGraph options', e.message)
+  }
+}
+
+export const getOptions = function () {
+  return options
+}
+
+export const commit = function (msg) {
+  const commit = {
+    id: getId(),
+    message: msg,
+    seq: seq++,
+    parent: head == null ? null : head.id
+  }
+  head = commit
+  commits[commit.id] = commit
+  branches[curBranch] = commit.id
+  logger.debug('in pushCommit ' + commit.id)
+}
+
+export const branch = function (name) {
+  branches[name] = head != null ? head.id : null
+  logger.debug('in createBranch')
+}
+
+export const merge = function (otherBranch) {
+  const currentCommit = commits[branches[curBranch]]
+  const otherCommit = commits[branches[otherBranch]]
+  if (isReachableFrom(currentCommit, otherCommit)) {
+    logger.debug('Already merged')
+    return
+  }
+  if (isfastforwardable(currentCommit, otherCommit)) {
+    branches[curBranch] = branches[otherBranch]
+    head = commits[branches[curBranch]]
+  } else {
+    // create merge commit
+    const commit = {
+      id: getId(),
+      message: 'merged branch ' + otherBranch + ' into ' + curBranch,
+      seq: seq++,
+      parent: [head == null ? null : head.id, branches[otherBranch]]
+    }
+    head = commit
+    commits[commit.id] = commit
+    branches[curBranch] = commit.id
+  }
+  logger.debug(branches)
+  logger.debug('in mergeBranch')
+}
+
+export const checkout = function (branch) {
+  logger.debug('in checkout')
+  curBranch = branch
+  const id = branches[curBranch]
+  head = commits[id]
+}
+
+export const reset = function (commitRef) {
+  logger.debug('in reset', commitRef)
+  const ref = commitRef.split(':')[0]
+  let parentCount = parseInt(commitRef.split(':')[1])
+  let commit = ref === 'HEAD' ? head : commits[branches[ref]]
+  logger.debug(commit, parentCount)
+  while (parentCount > 0) {
+    commit = commits[commit.parent]
+    parentCount--
+    if (!commit) {
+      const err = 'Critical error - unique parent commit not found during reset'
+      logger.error(err)
+      throw err
+    }
+  }
+  head = commit
+  branches[curBranch] = commit.id
+}
+
+function upsert (arr, key, newval) {
+  const index = arr.indexOf(key)
+  if (index === -1) {
+    arr.push(newval)
+  } else {
+    arr.splice(index, 1, newval)
+  }
+}
+
+function prettyPrintCommitHistory (commitArr) {
+  const commit = _.maxBy(commitArr, 'seq')
+  let line = ''
+  commitArr.forEach(function (c) {
+    if (c === commit) {
+      line += '\t*'
+    } else {
+      line += '\t|'
+    }
+  })
+  const label = [line, commit.id, commit.seq]
+  _.each(branches, function (value, key) {
+    if (value === commit.id) label.push(key)
+  })
+  logger.debug(label.join(' '))
+  if (Array.isArray(commit.parent)) {
+    const newCommit = commits[commit.parent[0]]
+    upsert(commitArr, commit, newCommit)
+    commitArr.push(commits[commit.parent[1]])
+  } else if (commit.parent == null) {
+    return
+  } else {
+    const nextCommit = commits[commit.parent]
+    upsert(commitArr, commit, nextCommit)
+  }
+  commitArr = _.uniqBy(commitArr, 'id')
+  prettyPrintCommitHistory(commitArr)
+}
+
+export const prettyPrint = function () {
+  logger.debug(commits)
+  const node = getCommitsArray()[0]
+  prettyPrintCommitHistory([node])
+}
+
+export const clear = function () {
+  commits = {}
+  head = null
+  branches = { 'master': head }
+  curBranch = 'master'
+  seq = 0
+}
+
+export const getBranchesAsObjArray = function () {
+  const branchArr = _.map(branches, function (value, key) {
+    return { 'name': key, 'commit': commits[value] }
+  })
+  return branchArr
+}
+
+export const getBranches = function () { return branches }
+export const getCommits = function () { return commits }
+export const getCommitsArray = function () {
+  const commitArr = Object.keys(commits).map(function (key) {
+    return commits[key]
+  })
+  commitArr.forEach(function (o) { logger.debug(o.id) })
+  return _.orderBy(commitArr, ['seq'], ['desc'])
+}
+export const getCurrentBranch = function () { return curBranch }
+export const getDirection = function () { return direction }
+export const getHead = function () { return head }
+
+export default {
+  setDirection,
+  setOptions,
+  getOptions,
+  commit,
+  branch,
+  merge,
+  checkout,
+  reset,
+  prettyPrint,
+  clear,
+  getBranchesAsObjArray,
+  getBranches,
+  getCommits,
+  getCommitsArray,
+  getCurrentBranch,
+  getDirection,
+  getHead
+}
diff --git a/_submodules/mermaid/src/diagrams/git/gitGraphParser.spec.js b/_submodules/mermaid/src/diagrams/git/gitGraphParser.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..aba2812d99ac3e2166a694de1fb700960ea9b30e
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/git/gitGraphParser.spec.js
@@ -0,0 +1,241 @@
+/* eslint-env jasmine */
+import gitGraphAst from './gitGraphAst'
+import { parser } from './parser/gitGraph'
+
+describe('when parsing a gitGraph', function () {
+  beforeEach(function () {
+    parser.yy = gitGraphAst
+    parser.yy.clear()
+  })
+  it('should handle a gitGraph defintion', function () {
+    const str = 'gitGraph:\n' +
+      'commit\n'
+
+    parser.parse(str)
+    const commits = parser.yy.getCommits()
+
+    expect(Object.keys(commits).length).toBe(1)
+    expect(parser.yy.getCurrentBranch()).toBe('master')
+    expect(parser.yy.getDirection()).toBe('LR')
+    expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
+  })
+
+  it('should handle a gitGraph defintion with empty options', function () {
+    const str = 'gitGraph:\n' +
+      'options\n' +
+      'end\n' +
+      'commit\n'
+
+    parser.parse(str)
+    const commits = parser.yy.getCommits()
+
+    expect(parser.yy.getOptions()).toEqual({})
+    expect(Object.keys(commits).length).toBe(1)
+    expect(parser.yy.getCurrentBranch()).toBe('master')
+    expect(parser.yy.getDirection()).toBe('LR')
+    expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
+  })
+
+  it('should handle a gitGraph defintion with valid options', function () {
+    const str = 'gitGraph:\n' +
+      'options\n' +
+      '{"key": "value"}\n' +
+      'end\n' +
+      'commit\n'
+
+    parser.parse(str)
+    const commits = parser.yy.getCommits()
+    expect(parser.yy.getOptions()['key']).toBe('value')
+    expect(Object.keys(commits).length).toBe(1)
+    expect(parser.yy.getCurrentBranch()).toBe('master')
+    expect(parser.yy.getDirection()).toBe('LR')
+    expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
+  })
+
+  it('should not fail on a gitGraph with malformed json', function () {
+    const str = 'gitGraph:\n' +
+      'options\n' +
+      '{"key": "value"\n' +
+      'end\n' +
+      'commit\n'
+
+    parser.parse(str)
+    const commits = parser.yy.getCommits()
+    expect(Object.keys(commits).length).toBe(1)
+    expect(parser.yy.getCurrentBranch()).toBe('master')
+    expect(parser.yy.getDirection()).toBe('LR')
+    expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
+  })
+
+  it('should handle set direction', function () {
+    const str = 'gitGraph BT:\n' +
+      'commit\n'
+
+    parser.parse(str)
+    const commits = parser.yy.getCommits()
+
+    expect(Object.keys(commits).length).toBe(1)
+    expect(parser.yy.getCurrentBranch()).toBe('master')
+    expect(parser.yy.getDirection()).toBe('BT')
+    expect(Object.keys(parser.yy.getBranches()).length).toBe(1)
+  })
+
+  it('should checkout a branch', function () {
+    const str = 'gitGraph:\n' +
+      'branch new\n' +
+      'checkout new\n'
+
+    parser.parse(str)
+    const commits = parser.yy.getCommits()
+
+    expect(Object.keys(commits).length).toBe(0)
+    expect(parser.yy.getCurrentBranch()).toBe('new')
+  })
+
+  it('should add commits to checked out branch', function () {
+    const str = 'gitGraph:\n' +
+      'branch new\n' +
+      'checkout new\n' +
+      'commit\n' +
+      'commit\n'
+
+    parser.parse(str)
+    const commits = parser.yy.getCommits()
+
+    expect(Object.keys(commits).length).toBe(2)
+    expect(parser.yy.getCurrentBranch()).toBe('new')
+    const branchCommit = parser.yy.getBranches()['new']
+    expect(branchCommit).not.toBeNull()
+    expect(commits[branchCommit].parent).not.toBeNull()
+  })
+  it('should handle commit with args', function () {
+    const str = 'gitGraph:\n' +
+      'commit "a commit"\n'
+
+    parser.parse(str)
+    const commits = parser.yy.getCommits()
+
+    expect(Object.keys(commits).length).toBe(1)
+    const key = Object.keys(commits)[0]
+    expect(commits[key].message).toBe('a commit')
+    expect(parser.yy.getCurrentBranch()).toBe('master')
+  })
+
+  it('it should reset a branch', function () {
+    const str = 'gitGraph:\n' +
+      'commit\n' +
+      'commit\n' +
+      'branch newbranch\n' +
+      'checkout newbranch\n' +
+      'commit\n' +
+      'reset master\n'
+
+    parser.parse(str)
+
+    const commits = parser.yy.getCommits()
+    expect(Object.keys(commits).length).toBe(3)
+    expect(parser.yy.getCurrentBranch()).toBe('newbranch')
+    expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master'])
+    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch'])
+  })
+
+  it('reset can take an argument', function () {
+    const str = 'gitGraph:\n' +
+      'commit\n' +
+      'commit\n' +
+      'branch newbranch\n' +
+      'checkout newbranch\n' +
+      'commit\n' +
+      'reset master^\n'
+
+    parser.parse(str)
+
+    const commits = parser.yy.getCommits()
+    expect(Object.keys(commits).length).toBe(3)
+    expect(parser.yy.getCurrentBranch()).toBe('newbranch')
+    const master = commits[parser.yy.getBranches()['master']]
+    expect(parser.yy.getHead().id).toEqual(master.parent)
+  })
+
+  it('it should handle fast forwardable merges', function () {
+    const str = 'gitGraph:\n' +
+      'commit\n' +
+      'branch newbranch\n' +
+      'checkout newbranch\n' +
+      'commit\n' +
+      'commit\n' +
+      'checkout master\n' +
+      'merge newbranch\n'
+
+    parser.parse(str)
+
+    const commits = parser.yy.getCommits()
+    expect(Object.keys(commits).length).toBe(3)
+    expect(parser.yy.getCurrentBranch()).toBe('master')
+    expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master'])
+    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch'])
+  })
+
+  it('it should handle cases when merge is a noop', function () {
+    const str = 'gitGraph:\n' +
+      'commit\n' +
+      'branch newbranch\n' +
+      'checkout newbranch\n' +
+      'commit\n' +
+      'commit\n' +
+      'merge master\n'
+
+    parser.parse(str)
+
+    const commits = parser.yy.getCommits()
+    expect(Object.keys(commits).length).toBe(3)
+    expect(parser.yy.getCurrentBranch()).toBe('newbranch')
+    expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master'])
+    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch'])
+  })
+
+  it('it should handle merge with 2 parents', function () {
+    const str = 'gitGraph:\n' +
+      'commit\n' +
+      'branch newbranch\n' +
+      'checkout newbranch\n' +
+      'commit\n' +
+      'commit\n' +
+      'checkout master\n' +
+      'commit\n' +
+      'merge newbranch\n'
+
+    parser.parse(str)
+
+    const commits = parser.yy.getCommits()
+    expect(Object.keys(commits).length).toBe(5)
+    expect(parser.yy.getCurrentBranch()).toBe('master')
+    expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master'])
+    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master'])
+  })
+
+  it('it should handle ff merge when history walk has two parents (merge commit)', function () {
+    const str = 'gitGraph:\n' +
+      'commit\n' +
+      'branch newbranch\n' +
+      'checkout newbranch\n' +
+      'commit\n' +
+      'commit\n' +
+      'checkout master\n' +
+      'commit\n' +
+      'merge newbranch\n' +
+      'commit\n' +
+      'checkout newbranch\n' +
+      'merge master\n'
+
+    parser.parse(str)
+
+    const commits = parser.yy.getCommits()
+    expect(Object.keys(commits).length).toBe(6)
+    expect(parser.yy.getCurrentBranch()).toBe('newbranch')
+    expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master'])
+    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master'])
+
+    parser.yy.prettyPrint()
+  })
+})
diff --git a/_submodules/mermaid/src/diagrams/git/gitGraphRenderer.js b/_submodules/mermaid/src/diagrams/git/gitGraphRenderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..3ef56478da7e4d3ffddd0c9d23c0dd639c5dcc84
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/git/gitGraphRenderer.js
@@ -0,0 +1,280 @@
+import _ from 'lodash'
+import * as d3 from 'd3'
+
+import db from './gitGraphAst'
+import gitGraphParser from './parser/gitGraph'
+import { logger } from '../../logger'
+import { interpolateToCurve } from '../../utils'
+
+let allCommitsDict = {}
+let branchNum
+let config = {
+  nodeSpacing: 150,
+  nodeFillColor: 'yellow',
+  nodeStrokeWidth: 2,
+  nodeStrokeColor: 'grey',
+  lineStrokeWidth: 4,
+  branchOffset: 50,
+  lineColor: 'grey',
+  leftMargin: 50,
+  branchColors: ['#442f74', '#983351', '#609732', '#AA9A39'],
+  nodeRadius: 10,
+  nodeLabel: {
+    width: 75,
+    height: 100,
+    x: -25,
+    y: 0
+  }
+}
+let apiConfig = {}
+export const setConf = function (c) {
+  apiConfig = c
+}
+
+function svgCreateDefs (svg) {
+  svg
+    .append('defs')
+    .append('g')
+    .attr('id', 'def-commit')
+    .append('circle')
+    .attr('r', config.nodeRadius)
+    .attr('cx', 0)
+    .attr('cy', 0)
+  svg.select('#def-commit')
+    .append('foreignObject')
+    .attr('width', config.nodeLabel.width)
+    .attr('height', config.nodeLabel.height)
+    .attr('x', config.nodeLabel.x)
+    .attr('y', config.nodeLabel.y)
+    .attr('class', 'node-label')
+    .attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
+    .append('p')
+    .html('')
+}
+
+function svgDrawLine (svg, points, colorIdx, interpolate) {
+  const curve = interpolateToCurve(interpolate, d3.curveBasis)
+  const color = config.branchColors[colorIdx % config.branchColors.length]
+  const lineGen = d3.line()
+    .x(function (d) {
+      return Math.round(d.x)
+    })
+    .y(function (d) {
+      return Math.round(d.y)
+    })
+    .curve(curve)
+
+  svg
+    .append('svg:path')
+    .attr('d', lineGen(points))
+    .style('stroke', color)
+    .style('stroke-width', config.lineStrokeWidth)
+    .style('fill', 'none')
+}
+
+// Pass in the element and its pre-transform coords
+function getElementCoords (element, coords) {
+  coords = coords || element.node().getBBox()
+  const ctm = element.node().getCTM()
+  const xn = ctm.e + coords.x * ctm.a
+  const yn = ctm.f + coords.y * ctm.d
+  return {
+    left: xn,
+    top: yn,
+    width: coords.width,
+    height: coords.height
+  }
+}
+
+function svgDrawLineForCommits (svg, fromId, toId, direction, color) {
+  logger.debug('svgDrawLineForCommits: ', fromId, toId)
+  const fromBbox = getElementCoords(svg.select('#node-' + fromId + ' circle'))
+  const toBbox = getElementCoords(svg.select('#node-' + toId + ' circle'))
+  switch (direction) {
+    case 'LR':
+      // (toBbox)
+      //  +--------
+      //          + (fromBbox)
+      if (fromBbox.left - toBbox.left > config.nodeSpacing) {
+        const lineStart = { x: fromBbox.left - config.nodeSpacing, y: toBbox.top + toBbox.height / 2 }
+        const lineEnd = { x: toBbox.left + toBbox.width, y: toBbox.top + toBbox.height / 2 }
+        svgDrawLine(svg, [lineStart, lineEnd], color, 'linear')
+        svgDrawLine(svg, [
+          { x: fromBbox.left, y: fromBbox.top + fromBbox.height / 2 },
+          { x: fromBbox.left - config.nodeSpacing / 2, y: fromBbox.top + fromBbox.height / 2 },
+          { x: fromBbox.left - config.nodeSpacing / 2, y: lineStart.y },
+          lineStart], color)
+      } else {
+        svgDrawLine(svg, [{
+          'x': fromBbox.left,
+          'y': fromBbox.top + fromBbox.height / 2
+        }, {
+          'x': fromBbox.left - config.nodeSpacing / 2,
+          'y': fromBbox.top + fromBbox.height / 2
+        }, {
+          'x': fromBbox.left - config.nodeSpacing / 2,
+          'y': toBbox.top + toBbox.height / 2
+        }, {
+          'x': toBbox.left + toBbox.width,
+          'y': toBbox.top + toBbox.height / 2
+        }], color)
+      }
+      break
+    case 'BT':
+      //      +           (fromBbox)
+      //      |
+      //      |
+      //              +   (toBbox)
+      if (toBbox.top - fromBbox.top > config.nodeSpacing) {
+        const lineStart = { x: toBbox.left + toBbox.width / 2, y: fromBbox.top + fromBbox.height + config.nodeSpacing }
+        const lineEnd = { x: toBbox.left + toBbox.width / 2, y: toBbox.top }
+        svgDrawLine(svg, [lineStart, lineEnd], color, 'linear')
+        svgDrawLine(svg, [
+          { x: fromBbox.left + fromBbox.width / 2, y: fromBbox.top + fromBbox.height },
+          { x: fromBbox.left + fromBbox.width / 2, y: fromBbox.top + fromBbox.height + config.nodeSpacing / 2 },
+          { x: toBbox.left + toBbox.width / 2, y: lineStart.y - config.nodeSpacing / 2 },
+          lineStart], color)
+      } else {
+        svgDrawLine(svg, [{
+          'x': fromBbox.left + fromBbox.width / 2,
+          'y': fromBbox.top + fromBbox.height
+        }, {
+          'x': fromBbox.left + fromBbox.width / 2,
+          'y': fromBbox.top + config.nodeSpacing / 2
+        }, {
+          'x': toBbox.left + toBbox.width / 2,
+          'y': toBbox.top - config.nodeSpacing / 2
+        }, {
+          'x': toBbox.left + toBbox.width / 2,
+          'y': toBbox.top
+        }], color)
+      }
+      break
+  }
+}
+
+function cloneNode (svg, selector) {
+  return svg.select(selector).node().cloneNode(true)
+}
+
+function renderCommitHistory (svg, commitid, branches, direction) {
+  let commit
+  const numCommits = Object.keys(allCommitsDict).length
+  if (_.isString(commitid)) {
+    do {
+      commit = allCommitsDict[commitid]
+      logger.debug('in renderCommitHistory', commit.id, commit.seq)
+      if (svg.select('#node-' + commitid).size() > 0) {
+        return
+      }
+      svg
+        .append(function () {
+          return cloneNode(svg, '#def-commit')
+        })
+        .attr('class', 'commit')
+        .attr('id', function () {
+          return 'node-' + commit.id
+        })
+        .attr('transform', function () {
+          switch (direction) {
+            case 'LR':
+              return 'translate(' + (commit.seq * config.nodeSpacing + config.leftMargin) + ', ' +
+                (branchNum * config.branchOffset) + ')'
+            case 'BT':
+              return 'translate(' + (branchNum * config.branchOffset + config.leftMargin) + ', ' +
+                ((numCommits - commit.seq) * config.nodeSpacing) + ')'
+          }
+        })
+        .attr('fill', config.nodeFillColor)
+        .attr('stroke', config.nodeStrokeColor)
+        .attr('stroke-width', config.nodeStrokeWidth)
+
+      const branch = _.find(branches, ['commit', commit])
+      if (branch) {
+        logger.debug('found branch ', branch.name)
+        svg.select('#node-' + commit.id + ' p')
+          .append('xhtml:span')
+          .attr('class', 'branch-label')
+          .text(branch.name + ', ')
+      }
+      svg.select('#node-' + commit.id + ' p')
+        .append('xhtml:span')
+        .attr('class', 'commit-id')
+        .text(commit.id)
+      if (commit.message !== '' && direction === 'BT') {
+        svg.select('#node-' + commit.id + ' p')
+          .append('xhtml:span')
+          .attr('class', 'commit-msg')
+          .text(', ' + commit.message)
+      }
+      commitid = commit.parent
+    } while (commitid && allCommitsDict[commitid])
+  }
+
+  if (_.isArray(commitid)) {
+    logger.debug('found merge commmit', commitid)
+    renderCommitHistory(svg, commitid[0], branches, direction)
+    branchNum++
+    renderCommitHistory(svg, commitid[1], branches, direction)
+    branchNum--
+  }
+}
+
+function renderLines (svg, commit, direction, branchColor) {
+  branchColor = branchColor || 0
+  while (commit.seq > 0 && !commit.lineDrawn) {
+    if (_.isString(commit.parent)) {
+      svgDrawLineForCommits(svg, commit.id, commit.parent, direction, branchColor)
+      commit.lineDrawn = true
+      commit = allCommitsDict[commit.parent]
+    } else if (_.isArray(commit.parent)) {
+      svgDrawLineForCommits(svg, commit.id, commit.parent[0], direction, branchColor)
+      svgDrawLineForCommits(svg, commit.id, commit.parent[1], direction, branchColor + 1)
+      renderLines(svg, allCommitsDict[commit.parent[1]], direction, branchColor + 1)
+      commit.lineDrawn = true
+      commit = allCommitsDict[commit.parent[0]]
+    }
+  }
+}
+
+export const draw = function (txt, id, ver) {
+  try {
+    const parser = gitGraphParser.parser
+    parser.yy = db
+
+    logger.debug('in gitgraph renderer', txt, id, ver)
+    // Parse the graph definition
+    parser.parse(txt + '\n')
+
+    config = _.extend(config, apiConfig, db.getOptions())
+    logger.debug('effective options', config)
+    const direction = db.getDirection()
+    allCommitsDict = db.getCommits()
+    const branches = db.getBranchesAsObjArray()
+    if (direction === 'BT') {
+      config.nodeLabel.x = branches.length * config.branchOffset
+      config.nodeLabel.width = '100%'
+      config.nodeLabel.y = -1 * 2 * config.nodeRadius
+    }
+    const svg = d3.select(`[id="${id}"]`)
+    svgCreateDefs(svg)
+    branchNum = 1
+    _.each(branches, function (v) {
+      renderCommitHistory(svg, v.commit.id, branches, direction)
+      renderLines(svg, v.commit, direction)
+      branchNum++
+    })
+    svg.attr('height', function () {
+      if (direction === 'BT') return Object.keys(allCommitsDict).length * config.nodeSpacing
+      return (branches.length + 1) * config.branchOffset
+    })
+  } catch (e) {
+    logger.error('Error while rendering gitgraph')
+    logger.error(e.message)
+  }
+}
+
+export default {
+  setConf,
+  draw
+}
diff --git a/_submodules/mermaid/src/diagrams/git/parser/gitGraph.jison b/_submodules/mermaid/src/diagrams/git/parser/gitGraph.jison
new file mode 100644
index 0000000000000000000000000000000000000000..e675a56e45fba82802e940bc7b8eb074a3d89192
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/git/parser/gitGraph.jison
@@ -0,0 +1,92 @@
+
+/*
+ * Parse following
+ * gitGraph:
+ *  commit
+ *  commit
+ *  branch 
+ */
+%lex
+
+%x string
+%x options
+%options case-insensitive
+
+%%
+
+(\r?\n)+                           return 'NL';
+\s+                             /* skip all whitespace */
+\#[^\n]*                        /* skip comments */
+\%%[^\n]*                       /* skip comments */
+"gitGraph"                      return 'GG';
+"commit"                        return 'COMMIT';
+"branch"                        return 'BRANCH';
+"merge"                         return 'MERGE';
+"reset"                         return 'RESET';
+"checkout"                         return 'CHECKOUT';
+"LR"                            return 'DIR';
+"BT"                            return 'DIR';
+":"                             return ':';
+"^"                             return 'CARET'
+"options"\r?\n                       this.begin("options");
+<options>"end"\r?\n                   this.popState();
+<options>[^\n]+\r?\n                 return 'OPT';
+["]                             this.begin("string");
+<string>["]                     this.popState();
+<string>[^"]*                     return 'STR';
+[a-zA-Z][a-zA-Z0-9_]+           return 'ID';
+<<EOF>>                         return 'EOF';
+
+/lex
+
+%left '^'
+
+%start start
+
+%% /* language grammar */
+
+start
+    : GG ':' document EOF{ return $3; }
+    | GG DIR ':' document EOF {yy.setDirection($2); return $4;}
+    ;
+
+
+document
+    : /*empty*/
+    | options body { yy.setOptions($1); $$ = $2}
+    ;
+
+options
+    : options OPT {$1 +=$2; $$=$1}
+    | NL
+    ;
+body
+    : /*emmpty*/ {$$ = []}
+    | body line {$1.push($2); $$=$1;}
+    ;
+line
+    : statement NL{$$ =$1}
+    | NL
+    ;
+
+statement
+    : COMMIT commit_arg {yy.commit($2)}
+    | BRANCH ID {yy.branch($2)}
+    | CHECKOUT ID {yy.checkout($2)}
+    | MERGE ID {yy.merge($2)}
+    | RESET reset_arg {yy.reset($2)}
+    ;
+
+commit_arg
+    : /* empty */ {$$ = ""}
+    | STR {$$=$1}
+    ;
+
+reset_arg
+    : 'HEAD' reset_parents{$$ = $1+ ":" + $2 }
+    | ID reset_parents{$$ = $1+ ":"  + yy.count; yy.count = 0}
+    ;
+reset_parents
+    : /* empty */ {yy.count = 0}
+    | CARET reset_parents { yy.count += 1 }
+    ;
diff --git a/_submodules/mermaid/src/diagrams/git/parser/gitGraph.js b/_submodules/mermaid/src/diagrams/git/parser/gitGraph.js
new file mode 100644
index 0000000000000000000000000000000000000000..fd981cafb53fe346f0457b00a881b850530ce586
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/git/parser/gitGraph.js
@@ -0,0 +1,696 @@
+/* parser generated by jison 0.4.18 */
+/*
+  Returns a Parser object of the following structure:
+
+  Parser: {
+    yy: {}
+  }
+
+  Parser.prototype: {
+    yy: {},
+    trace: function(),
+    symbols_: {associative list: name ==> number},
+    terminals_: {associative list: number ==> name},
+    productions_: [...],
+    performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
+    table: [...],
+    defaultActions: {...},
+    parseError: function(str, hash),
+    parse: function(input),
+
+    lexer: {
+        EOF: 1,
+        parseError: function(str, hash),
+        setInput: function(input),
+        input: function(),
+        unput: function(str),
+        more: function(),
+        less: function(n),
+        pastInput: function(),
+        upcomingInput: function(),
+        showPosition: function(),
+        test_match: function(regex_match_array, rule_index),
+        next: function(),
+        lex: function(),
+        begin: function(condition),
+        popState: function(),
+        _currentRules: function(),
+        topState: function(),
+        pushState: function(condition),
+
+        options: {
+            ranges: boolean           (optional: true ==> token location info will include a .range[] member)
+            flex: boolean             (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
+            backtrack_lexer: boolean  (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
+        },
+
+        performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
+        rules: [...],
+        conditions: {associative list: name ==> set},
+    }
+  }
+
+
+  token location info (@$, _$, etc.): {
+    first_line: n,
+    last_line: n,
+    first_column: n,
+    last_column: n,
+    range: [start_number, end_number]       (where the numbers are indexes into the input string, regular zero-based)
+  }
+
+
+  the parseError function receives a 'hash' object with these members for lexer and parser errors: {
+    text:        (matched text)
+    token:       (the produced terminal token, if any)
+    line:        (yylineno)
+  }
+  while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
+    loc:         (yylloc)
+    expected:    (string describing the set of expected tokens)
+    recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
+  }
+*/
+var parser = (function(){
+var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[2,3],$V1=[1,7],$V2=[7,12,15,17,19,20,21],$V3=[7,11,12,15,17,19,20,21],$V4=[2,20],$V5=[1,32];
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"start":3,"GG":4,":":5,"document":6,"EOF":7,"DIR":8,"options":9,"body":10,"OPT":11,"NL":12,"line":13,"statement":14,"COMMIT":15,"commit_arg":16,"BRANCH":17,"ID":18,"CHECKOUT":19,"MERGE":20,"RESET":21,"reset_arg":22,"STR":23,"HEAD":24,"reset_parents":25,"CARET":26,"$accept":0,"$end":1},
+terminals_: {2:"error",4:"GG",5:":",7:"EOF",8:"DIR",11:"OPT",12:"NL",15:"COMMIT",17:"BRANCH",18:"ID",19:"CHECKOUT",20:"MERGE",21:"RESET",23:"STR",24:"HEAD",26:"CARET"},
+productions_: [0,[3,4],[3,5],[6,0],[6,2],[9,2],[9,1],[10,0],[10,2],[13,2],[13,1],[14,2],[14,2],[14,2],[14,2],[14,2],[16,0],[16,1],[22,2],[22,2],[25,0],[25,2]],
+performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
+/* this == yyval */
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1:
+ return $$[$0-1]; 
+break;
+case 2:
+yy.setDirection($$[$0-3]); return $$[$0-1];
+break;
+case 4:
+ yy.setOptions($$[$0-1]); this.$ = $$[$0]
+break;
+case 5:
+$$[$0-1] +=$$[$0]; this.$=$$[$0-1]
+break;
+case 7:
+this.$ = []
+break;
+case 8:
+$$[$0-1].push($$[$0]); this.$=$$[$0-1];
+break;
+case 9:
+this.$ =$$[$0-1]
+break;
+case 11:
+yy.commit($$[$0])
+break;
+case 12:
+yy.branch($$[$0])
+break;
+case 13:
+yy.checkout($$[$0])
+break;
+case 14:
+yy.merge($$[$0])
+break;
+case 15:
+yy.reset($$[$0])
+break;
+case 16:
+this.$ = ""
+break;
+case 17:
+this.$=$$[$0]
+break;
+case 18:
+this.$ = $$[$0-1]+ ":" + $$[$0] 
+break;
+case 19:
+this.$ = $$[$0-1]+ ":"  + yy.count; yy.count = 0
+break;
+case 20:
+yy.count = 0
+break;
+case 21:
+ yy.count += 1 
+break;
+}
+},
+table: [{3:1,4:[1,2]},{1:[3]},{5:[1,3],8:[1,4]},{6:5,7:$V0,9:6,12:$V1},{5:[1,8]},{7:[1,9]},o($V2,[2,7],{10:10,11:[1,11]}),o($V3,[2,6]),{6:12,7:$V0,9:6,12:$V1},{1:[2,1]},{7:[2,4],12:[1,15],13:13,14:14,15:[1,16],17:[1,17],19:[1,18],20:[1,19],21:[1,20]},o($V3,[2,5]),{7:[1,21]},o($V2,[2,8]),{12:[1,22]},o($V2,[2,10]),{12:[2,16],16:23,23:[1,24]},{18:[1,25]},{18:[1,26]},{18:[1,27]},{18:[1,30],22:28,24:[1,29]},{1:[2,2]},o($V2,[2,9]),{12:[2,11]},{12:[2,17]},{12:[2,12]},{12:[2,13]},{12:[2,14]},{12:[2,15]},{12:$V4,25:31,26:$V5},{12:$V4,25:33,26:$V5},{12:[2,18]},{12:$V4,25:34,26:$V5},{12:[2,19]},{12:[2,21]}],
+defaultActions: {9:[2,1],21:[2,2],23:[2,11],24:[2,17],25:[2,12],26:[2,13],27:[2,14],28:[2,15],31:[2,18],33:[2,19],34:[2,21]},
+parseError: function parseError(str, hash) {
+    if (hash.recoverable) {
+        this.trace(str);
+    } else {
+        var error = new Error(str);
+        error.hash = hash;
+        throw error;
+    }
+},
+parse: function parse(input) {
+    var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+    var args = lstack.slice.call(arguments, 1);
+    var lexer = Object.create(this.lexer);
+    var sharedState = { yy: {} };
+    for (var k in this.yy) {
+        if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
+            sharedState.yy[k] = this.yy[k];
+        }
+    }
+    lexer.setInput(input, sharedState.yy);
+    sharedState.yy.lexer = lexer;
+    sharedState.yy.parser = this;
+    if (typeof lexer.yylloc == 'undefined') {
+        lexer.yylloc = {};
+    }
+    var yyloc = lexer.yylloc;
+    lstack.push(yyloc);
+    var ranges = lexer.options && lexer.options.ranges;
+    if (typeof sharedState.yy.parseError === 'function') {
+        this.parseError = sharedState.yy.parseError;
+    } else {
+        this.parseError = Object.getPrototypeOf(this).parseError;
+    }
+    function popStack(n) {
+        stack.length = stack.length - 2 * n;
+        vstack.length = vstack.length - n;
+        lstack.length = lstack.length - n;
+    }
+            function lex() {
+            var token;
+            token = tstack.pop() || lexer.lex() || EOF;
+            if (typeof token !== 'number') {
+                if (token instanceof Array) {
+                    tstack = token;
+                    token = tstack.pop();
+                }
+                token = self.symbols_[token] || token;
+            }
+            return token;
+        }
+    var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+    while (true) {
+        state = stack[stack.length - 1];
+        if (this.defaultActions[state]) {
+            action = this.defaultActions[state];
+        } else {
+            if (symbol === null || typeof symbol == 'undefined') {
+                symbol = lex();
+            }
+            action = table[state] && table[state][symbol];
+        }
+        if (typeof action === 'undefined' || !action.length || !action[0]) {
+            var errStr = '';
+            expected = [];
+            for (p in table[state]) {
+                if (this.terminals_[p] && p > TERROR) {
+                    expected.push('\'' + this.terminals_[p] + '\'');
+                }
+            }
+            if (lexer.showPosition) {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
+            } else {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
+            }
+            this.parseError(errStr, {
+                text: lexer.match,
+                token: this.terminals_[symbol] || symbol,
+                line: lexer.yylineno,
+                loc: yyloc,
+                expected: expected
+            });
+        }
+        if (action[0] instanceof Array && action.length > 1) {
+            throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+        }
+        switch (action[0]) {
+        case 1:
+            stack.push(symbol);
+            vstack.push(lexer.yytext);
+            lstack.push(lexer.yylloc);
+            stack.push(action[1]);
+            symbol = null;
+            if (!preErrorSymbol) {
+                yyleng = lexer.yyleng;
+                yytext = lexer.yytext;
+                yylineno = lexer.yylineno;
+                yyloc = lexer.yylloc;
+                if (recovering > 0) {
+                    recovering--;
+                }
+            } else {
+                symbol = preErrorSymbol;
+                preErrorSymbol = null;
+            }
+            break;
+        case 2:
+            len = this.productions_[action[1]][1];
+            yyval.$ = vstack[vstack.length - len];
+            yyval._$ = {
+                first_line: lstack[lstack.length - (len || 1)].first_line,
+                last_line: lstack[lstack.length - 1].last_line,
+                first_column: lstack[lstack.length - (len || 1)].first_column,
+                last_column: lstack[lstack.length - 1].last_column
+            };
+            if (ranges) {
+                yyval._$.range = [
+                    lstack[lstack.length - (len || 1)].range[0],
+                    lstack[lstack.length - 1].range[1]
+                ];
+            }
+            r = this.performAction.apply(yyval, [
+                yytext,
+                yyleng,
+                yylineno,
+                sharedState.yy,
+                action[1],
+                vstack,
+                lstack
+            ].concat(args));
+            if (typeof r !== 'undefined') {
+                return r;
+            }
+            if (len) {
+                stack = stack.slice(0, -1 * len * 2);
+                vstack = vstack.slice(0, -1 * len);
+                lstack = lstack.slice(0, -1 * len);
+            }
+            stack.push(this.productions_[action[1]][0]);
+            vstack.push(yyval.$);
+            lstack.push(yyval._$);
+            newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+            stack.push(newState);
+            break;
+        case 3:
+            return true;
+        }
+    }
+    return true;
+}};
+/* generated by jison-lex 0.3.4 */
+var lexer = (function(){
+var lexer = ({
+
+EOF:1,
+
+parseError:function parseError(str, hash) {
+        if (this.yy.parser) {
+            this.yy.parser.parseError(str, hash);
+        } else {
+            throw new Error(str);
+        }
+    },
+
+// resets the lexer, sets new input
+setInput:function (input, yy) {
+        this.yy = yy || this.yy || {};
+        this._input = input;
+        this._more = this._backtrack = this.done = false;
+        this.yylineno = this.yyleng = 0;
+        this.yytext = this.matched = this.match = '';
+        this.conditionStack = ['INITIAL'];
+        this.yylloc = {
+            first_line: 1,
+            first_column: 0,
+            last_line: 1,
+            last_column: 0
+        };
+        if (this.options.ranges) {
+            this.yylloc.range = [0,0];
+        }
+        this.offset = 0;
+        return this;
+    },
+
+// consumes and returns one char from the input
+input:function () {
+        var ch = this._input[0];
+        this.yytext += ch;
+        this.yyleng++;
+        this.offset++;
+        this.match += ch;
+        this.matched += ch;
+        var lines = ch.match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno++;
+            this.yylloc.last_line++;
+        } else {
+            this.yylloc.last_column++;
+        }
+        if (this.options.ranges) {
+            this.yylloc.range[1]++;
+        }
+
+        this._input = this._input.slice(1);
+        return ch;
+    },
+
+// unshifts one char (or a string) into the input
+unput:function (ch) {
+        var len = ch.length;
+        var lines = ch.split(/(?:\r\n?|\n)/g);
+
+        this._input = ch + this._input;
+        this.yytext = this.yytext.substr(0, this.yytext.length - len);
+        //this.yyleng -= len;
+        this.offset -= len;
+        var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+        this.match = this.match.substr(0, this.match.length - 1);
+        this.matched = this.matched.substr(0, this.matched.length - 1);
+
+        if (lines.length - 1) {
+            this.yylineno -= lines.length - 1;
+        }
+        var r = this.yylloc.range;
+
+        this.yylloc = {
+            first_line: this.yylloc.first_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.first_column,
+            last_column: lines ?
+                (lines.length === oldLines.length ? this.yylloc.first_column : 0)
+                 + oldLines[oldLines.length - lines.length].length - lines[0].length :
+              this.yylloc.first_column - len
+        };
+
+        if (this.options.ranges) {
+            this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+        }
+        this.yyleng = this.yytext.length;
+        return this;
+    },
+
+// When called from action, caches matched text and appends it on next action
+more:function () {
+        this._more = true;
+        return this;
+    },
+
+// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
+reject:function () {
+        if (this.options.backtrack_lexer) {
+            this._backtrack = true;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+
+        }
+        return this;
+    },
+
+// retain first n characters of the match
+less:function (n) {
+        this.unput(this.match.slice(n));
+    },
+
+// displays already matched input, i.e. for error messages
+pastInput:function () {
+        var past = this.matched.substr(0, this.matched.length - this.match.length);
+        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+    },
+
+// displays upcoming input, i.e. for error messages
+upcomingInput:function () {
+        var next = this.match;
+        if (next.length < 20) {
+            next += this._input.substr(0, 20-next.length);
+        }
+        return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+    },
+
+// displays the character position where the lexing error occurred, i.e. for error messages
+showPosition:function () {
+        var pre = this.pastInput();
+        var c = new Array(pre.length + 1).join("-");
+        return pre + this.upcomingInput() + "\n" + c + "^";
+    },
+
+// test the lexed token: return FALSE when not a match, otherwise return token
+test_match:function (match, indexed_rule) {
+        var token,
+            lines,
+            backup;
+
+        if (this.options.backtrack_lexer) {
+            // save context
+            backup = {
+                yylineno: this.yylineno,
+                yylloc: {
+                    first_line: this.yylloc.first_line,
+                    last_line: this.last_line,
+                    first_column: this.yylloc.first_column,
+                    last_column: this.yylloc.last_column
+                },
+                yytext: this.yytext,
+                match: this.match,
+                matches: this.matches,
+                matched: this.matched,
+                yyleng: this.yyleng,
+                offset: this.offset,
+                _more: this._more,
+                _input: this._input,
+                yy: this.yy,
+                conditionStack: this.conditionStack.slice(0),
+                done: this.done
+            };
+            if (this.options.ranges) {
+                backup.yylloc.range = this.yylloc.range.slice(0);
+            }
+        }
+
+        lines = match[0].match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno += lines.length;
+        }
+        this.yylloc = {
+            first_line: this.yylloc.last_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.last_column,
+            last_column: lines ?
+                         lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
+                         this.yylloc.last_column + match[0].length
+        };
+        this.yytext += match[0];
+        this.match += match[0];
+        this.matches = match;
+        this.yyleng = this.yytext.length;
+        if (this.options.ranges) {
+            this.yylloc.range = [this.offset, this.offset += this.yyleng];
+        }
+        this._more = false;
+        this._backtrack = false;
+        this._input = this._input.slice(match[0].length);
+        this.matched += match[0];
+        token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
+        if (this.done && this._input) {
+            this.done = false;
+        }
+        if (token) {
+            return token;
+        } else if (this._backtrack) {
+            // recover context
+            for (var k in backup) {
+                this[k] = backup[k];
+            }
+            return false; // rule action called reject() implying the next rule should be tested instead.
+        }
+        return false;
+    },
+
+// return next match in input
+next:function () {
+        if (this.done) {
+            return this.EOF;
+        }
+        if (!this._input) {
+            this.done = true;
+        }
+
+        var token,
+            match,
+            tempMatch,
+            index;
+        if (!this._more) {
+            this.yytext = '';
+            this.match = '';
+        }
+        var rules = this._currentRules();
+        for (var i = 0; i < rules.length; i++) {
+            tempMatch = this._input.match(this.rules[rules[i]]);
+            if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+                match = tempMatch;
+                index = i;
+                if (this.options.backtrack_lexer) {
+                    token = this.test_match(tempMatch, rules[i]);
+                    if (token !== false) {
+                        return token;
+                    } else if (this._backtrack) {
+                        match = false;
+                        continue; // rule action called reject() implying a rule MISmatch.
+                    } else {
+                        // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+                        return false;
+                    }
+                } else if (!this.options.flex) {
+                    break;
+                }
+            }
+        }
+        if (match) {
+            token = this.test_match(match, rules[index]);
+            if (token !== false) {
+                return token;
+            }
+            // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+            return false;
+        }
+        if (this._input === "") {
+            return this.EOF;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+        }
+    },
+
+// return next match that has a token
+lex:function lex() {
+        var r = this.next();
+        if (r) {
+            return r;
+        } else {
+            return this.lex();
+        }
+    },
+
+// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
+begin:function begin(condition) {
+        this.conditionStack.push(condition);
+    },
+
+// pop the previously active lexer condition state off the condition stack
+popState:function popState() {
+        var n = this.conditionStack.length - 1;
+        if (n > 0) {
+            return this.conditionStack.pop();
+        } else {
+            return this.conditionStack[0];
+        }
+    },
+
+// produce the lexer rule set which is active for the currently active lexer condition state
+_currentRules:function _currentRules() {
+        if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
+            return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+        } else {
+            return this.conditions["INITIAL"].rules;
+        }
+    },
+
+// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
+topState:function topState(n) {
+        n = this.conditionStack.length - 1 - Math.abs(n || 0);
+        if (n >= 0) {
+            return this.conditionStack[n];
+        } else {
+            return "INITIAL";
+        }
+    },
+
+// alias for begin(condition)
+pushState:function pushState(condition) {
+        this.begin(condition);
+    },
+
+// return the number of states currently on the stack
+stateStackSize:function stateStackSize() {
+        return this.conditionStack.length;
+    },
+options: {"case-insensitive":true},
+performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0:return 12;
+break;
+case 1:/* skip all whitespace */
+break;
+case 2:/* skip comments */
+break;
+case 3:/* skip comments */
+break;
+case 4:return 4;
+break;
+case 5:return 15;
+break;
+case 6:return 17;
+break;
+case 7:return 20;
+break;
+case 8:return 21;
+break;
+case 9:return 19;
+break;
+case 10:return 8;
+break;
+case 11:return 8;
+break;
+case 12:return 5;
+break;
+case 13:return 26
+break;
+case 14:this.begin("options");
+break;
+case 15:this.popState();
+break;
+case 16:return 11;
+break;
+case 17:this.begin("string");
+break;
+case 18:this.popState();
+break;
+case 19:return 23;
+break;
+case 20:return 18;
+break;
+case 21:return 7;
+break;
+}
+},
+rules: [/^(?:(\r?\n)+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:gitGraph\b)/i,/^(?:commit\b)/i,/^(?:branch\b)/i,/^(?:merge\b)/i,/^(?:reset\b)/i,/^(?:checkout\b)/i,/^(?:LR\b)/i,/^(?:BT\b)/i,/^(?::)/i,/^(?:\^)/i,/^(?:options\r?\n)/i,/^(?:end\r?\n)/i,/^(?:[^\n]+\r?\n)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[a-zA-Z][a-zA-Z0-9_]+)/i,/^(?:$)/i],
+conditions: {"options":{"rules":[15,16],"inclusive":false},"string":{"rules":[18,19],"inclusive":false},"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,20,21],"inclusive":true}}
+});
+return lexer;
+})();
+parser.lexer = lexer;
+function Parser () {
+  this.yy = {};
+}
+Parser.prototype = parser;parser.Parser = Parser;
+return new Parser;
+})();
+
+
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+exports.parser = parser;
+exports.Parser = parser.Parser;
+exports.parse = function () { return parser.parse.apply(parser, arguments); };
+exports.main = function commonjsMain(args) {
+    if (!args[1]) {
+        console.log('Usage: '+args[0]+' FILE');
+        process.exit(1);
+    }
+    var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8");
+    return exports.parser.parse(source);
+};
+if (typeof module !== 'undefined' && require.main === module) {
+  exports.main(process.argv.slice(1));
+}
+}
\ No newline at end of file
diff --git a/_submodules/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison b/_submodules/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison
new file mode 100644
index 0000000000000000000000000000000000000000..507956ee49ad978027602a9350f8b53a4039c295
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison
@@ -0,0 +1,192 @@
+/** mermaid
+ *  https://mermaidjs.github.io/
+ *  (c) 2014-2015 Knut Sveidqvist
+ *  MIT license.
+ *
+ *  Based on js sequence diagrams jison grammr
+ *  http://bramp.github.io/js-sequence-diagrams/
+ *  (c) 2012-2013 Andrew Brampton (bramp.net)
+ *  Simplified BSD license.
+ */
+%lex
+
+%options case-insensitive
+
+// Special states for recognizing aliases
+%x ID
+%x ALIAS
+
+// A special state for grabbing text up to the first comment/newline
+%x LINE
+
+%%
+
+[\n]+                            return 'NL';
+\s+                              /* skip all whitespace */
+<ID,ALIAS,LINE>((?!\n)\s)+       /* skip same-line whitespace */
+<INITIAL,ID,ALIAS,LINE>\#[^\n]*  /* skip comments */
+\%%[^\n]*                        /* skip comments */
+"participant"     { this.begin('ID'); return 'participant'; }
+<ID>[^\->:\n,;]+?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$)  { this.begin('ALIAS'); return 'ACTOR'; }
+<ALIAS>"as"       { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
+<ALIAS>(?:)       { this.popState(); this.popState(); return 'NL'; }
+"loop"            { this.begin('LINE'); return 'loop'; }
+"opt"             { this.begin('LINE'); return 'opt'; }
+"alt"             { this.begin('LINE'); return 'alt'; }
+"else"            { this.begin('LINE'); return 'else'; }
+"par"             { this.begin('LINE'); return 'par'; }
+"and"             { this.begin('LINE'); return 'and'; }
+<LINE>[^#\n;]*    { this.popState(); return 'restOfLine'; }
+"end"             return 'end';
+"left of"         return 'left_of';
+"right of"        return 'right_of';
+"over"            return 'over';
+"note"            return 'note';
+"activate"        { this.begin('ID'); return 'activate'; }
+"deactivate"      { this.begin('ID'); return 'deactivate'; }
+"title"           return 'title';
+"sequenceDiagram" return 'SD';
+","               return ',';
+";"               return 'NL';
+[^\+\->:\n,;]+      { yytext = yytext.trim(); return 'ACTOR'; }
+"->>"             return 'SOLID_ARROW';
+"-->>"            return 'DOTTED_ARROW';
+"->"              return 'SOLID_OPEN_ARROW';
+"-->"             return 'DOTTED_OPEN_ARROW';
+\-[x]             return 'SOLID_CROSS';
+\-\-[x]           return 'DOTTED_CROSS';
+":"[^#\n;]+       return 'TXT';
+"+"               return '+';
+"-"               return '-';
+<<EOF>>           return 'NL';
+.                 return 'INVALID';
+
+/lex
+
+%left '^'
+
+%start start
+
+%% /* language grammar */
+
+start
+	: SPACE start
+	| NL start
+	| SD document { yy.apply($2);return $2; }
+	;
+
+document
+	: /* empty */ { $$ = [] }
+	| document line {$1.push($2);$$ = $1}
+	;
+
+line
+	: SPACE statement { $$ = $2 }
+	| statement { $$ = $1 }
+	| NL { $$=[];}
+	;
+
+statement
+	: 'participant' actor 'AS' restOfLine 'NL' {$2.description=$4; $$=$2;}
+	| 'participant' actor 'NL' {$$=$2;}
+	| signal 'NL'
+	| 'activate' actor 'NL' {$$={type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $2};}
+	| 'deactivate' actor 'NL' {$$={type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $2};}
+	| note_statement 'NL'
+	| title text2 'NL' {$$=[{type:'setTitle', text:$2}]}
+	| 'loop' restOfLine document end
+	{
+		$3.unshift({type: 'loopStart', loopText:$2, signalType: yy.LINETYPE.LOOP_START});
+		$3.push({type: 'loopEnd', loopText:$2, signalType: yy.LINETYPE.LOOP_END});
+		$$=$3;}
+	| opt restOfLine document end
+	{
+		$3.unshift({type: 'optStart', optText:$2, signalType: yy.LINETYPE.OPT_START});
+		$3.push({type: 'optEnd', optText:$2, signalType: yy.LINETYPE.OPT_END});
+		$$=$3;}
+	| alt restOfLine else_sections end
+	{
+		// Alt start
+		$3.unshift({type: 'altStart', altText:$2, signalType: yy.LINETYPE.ALT_START});
+		// Content in alt is already in $3
+		// End
+		$3.push({type: 'altEnd', signalType: yy.LINETYPE.ALT_END});
+		$$=$3;}
+	| par restOfLine par_sections end
+	{
+		// Parallel start
+		$3.unshift({type: 'parStart', parText:$2, signalType: yy.LINETYPE.PAR_START});
+		// Content in par is already in $3
+		// End
+		$3.push({type: 'parEnd', signalType: yy.LINETYPE.PAR_END});
+		$$=$3;}
+	;
+
+par_sections
+	: document
+	| document and restOfLine par_sections
+	{ $$ = $1.concat([{type: 'and', parText:$3, signalType: yy.LINETYPE.PAR_AND}, $4]); }
+	;
+
+else_sections
+	: document
+	| document else restOfLine else_sections
+	{ $$ = $1.concat([{type: 'else', altText:$3, signalType: yy.LINETYPE.ALT_ELSE}, $4]); }
+	;
+
+note_statement
+	: 'note' placement actor text2
+	{
+		$$ = [$3, {type:'addNote', placement:$2, actor:$3.actor, text:$4}];}
+	| 'note' 'over' actor_pair text2
+	{
+		// Coerce actor_pair into a [to, from, ...] array
+		$2 = [].concat($3, $3).slice(0, 2);
+		$2[0] = $2[0].actor;
+		$2[1] = $2[1].actor;
+		$$ = [$3, {type:'addNote', placement:yy.PLACEMENT.OVER, actor:$2.slice(0, 2), text:$4}];}
+	;
+
+spaceList
+    : SPACE spaceList
+    | SPACE
+    ;
+actor_pair
+	: actor ',' actor   { $$ = [$1, $3]; }
+	| actor             { $$ = $1; }
+	;
+
+placement
+	: 'left_of'   { $$ = yy.PLACEMENT.LEFTOF; }
+	| 'right_of'  { $$ = yy.PLACEMENT.RIGHTOF; }
+	;
+
+signal
+	: actor signaltype '+' actor text2
+	{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
+	              {type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $4}
+	             ]}
+	| actor signaltype '-' actor text2
+	{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
+	             {type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $1}
+	             ]}
+	| actor signaltype actor text2
+	{ $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
+	;
+
+actor
+	: ACTOR {$$={type: 'addActor', actor:$1}}
+	;
+
+signaltype
+	: SOLID_OPEN_ARROW  { $$ = yy.LINETYPE.SOLID_OPEN; }
+	| DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; }
+	| SOLID_ARROW       { $$ = yy.LINETYPE.SOLID; }
+	| DOTTED_ARROW      { $$ = yy.LINETYPE.DOTTED; }
+	| SOLID_CROSS       { $$ = yy.LINETYPE.SOLID_CROSS; }
+	| DOTTED_CROSS      { $$ = yy.LINETYPE.DOTTED_CROSS; }
+	;
+
+text2: TXT {$$ = $1.substring(1).trim().replace(/\\n/gm, "\n");} ;
+
+%%
diff --git a/_submodules/mermaid/src/diagrams/sequence/parser/sequenceDiagram.js b/_submodules/mermaid/src/diagrams/sequence/parser/sequenceDiagram.js
new file mode 100644
index 0000000000000000000000000000000000000000..aa94350990f6cfb09cfe3160f6e4bf2040307fd0
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/sequence/parser/sequenceDiagram.js
@@ -0,0 +1,804 @@
+/* parser generated by jison 0.4.18 */
+/*
+  Returns a Parser object of the following structure:
+
+  Parser: {
+    yy: {}
+  }
+
+  Parser.prototype: {
+    yy: {},
+    trace: function(),
+    symbols_: {associative list: name ==> number},
+    terminals_: {associative list: number ==> name},
+    productions_: [...],
+    performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
+    table: [...],
+    defaultActions: {...},
+    parseError: function(str, hash),
+    parse: function(input),
+
+    lexer: {
+        EOF: 1,
+        parseError: function(str, hash),
+        setInput: function(input),
+        input: function(),
+        unput: function(str),
+        more: function(),
+        less: function(n),
+        pastInput: function(),
+        upcomingInput: function(),
+        showPosition: function(),
+        test_match: function(regex_match_array, rule_index),
+        next: function(),
+        lex: function(),
+        begin: function(condition),
+        popState: function(),
+        _currentRules: function(),
+        topState: function(),
+        pushState: function(condition),
+
+        options: {
+            ranges: boolean           (optional: true ==> token location info will include a .range[] member)
+            flex: boolean             (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
+            backtrack_lexer: boolean  (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
+        },
+
+        performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
+        rules: [...],
+        conditions: {associative list: name ==> set},
+    }
+  }
+
+
+  token location info (@$, _$, etc.): {
+    first_line: n,
+    last_line: n,
+    first_column: n,
+    last_column: n,
+    range: [start_number, end_number]       (where the numbers are indexes into the input string, regular zero-based)
+  }
+
+
+  the parseError function receives a 'hash' object with these members for lexer and parser errors: {
+    text:        (matched text)
+    token:       (the produced terminal token, if any)
+    line:        (yylineno)
+  }
+  while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
+    loc:         (yylloc)
+    expected:    (string describing the set of expected tokens)
+    recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
+  }
+*/
+var parser = (function(){
+var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,2],$V1=[1,3],$V2=[1,4],$V3=[2,4],$V4=[1,9],$V5=[1,11],$V6=[1,12],$V7=[1,14],$V8=[1,15],$V9=[1,17],$Va=[1,18],$Vb=[1,19],$Vc=[1,20],$Vd=[1,21],$Ve=[1,23],$Vf=[1,24],$Vg=[1,4,5,10,15,16,18,20,21,22,23,25,27,28,29,40],$Vh=[1,32],$Vi=[4,5,10,15,16,18,20,21,22,23,25,29,40],$Vj=[4,5,10,15,16,18,20,21,22,23,25,28,29,40],$Vk=[4,5,10,15,16,18,20,21,22,23,25,27,29,40],$Vl=[38,39,40];
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"start":3,"SPACE":4,"NL":5,"SD":6,"document":7,"line":8,"statement":9,"participant":10,"actor":11,"AS":12,"restOfLine":13,"signal":14,"activate":15,"deactivate":16,"note_statement":17,"title":18,"text2":19,"loop":20,"end":21,"opt":22,"alt":23,"else_sections":24,"par":25,"par_sections":26,"and":27,"else":28,"note":29,"placement":30,"over":31,"actor_pair":32,"spaceList":33,",":34,"left_of":35,"right_of":36,"signaltype":37,"+":38,"-":39,"ACTOR":40,"SOLID_OPEN_ARROW":41,"DOTTED_OPEN_ARROW":42,"SOLID_ARROW":43,"DOTTED_ARROW":44,"SOLID_CROSS":45,"DOTTED_CROSS":46,"TXT":47,"$accept":0,"$end":1},
+terminals_: {2:"error",4:"SPACE",5:"NL",6:"SD",10:"participant",12:"AS",13:"restOfLine",15:"activate",16:"deactivate",18:"title",20:"loop",21:"end",22:"opt",23:"alt",25:"par",27:"and",28:"else",29:"note",31:"over",34:",",35:"left_of",36:"right_of",38:"+",39:"-",40:"ACTOR",41:"SOLID_OPEN_ARROW",42:"DOTTED_OPEN_ARROW",43:"SOLID_ARROW",44:"DOTTED_ARROW",45:"SOLID_CROSS",46:"DOTTED_CROSS",47:"TXT"},
+productions_: [0,[3,2],[3,2],[3,2],[7,0],[7,2],[8,2],[8,1],[8,1],[9,5],[9,3],[9,2],[9,3],[9,3],[9,2],[9,3],[9,4],[9,4],[9,4],[9,4],[26,1],[26,4],[24,1],[24,4],[17,4],[17,4],[33,2],[33,1],[32,3],[32,1],[30,1],[30,1],[14,5],[14,5],[14,4],[11,1],[37,1],[37,1],[37,1],[37,1],[37,1],[37,1],[19,1]],
+performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
+/* this == yyval */
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 3:
+ yy.apply($$[$0]);return $$[$0]; 
+break;
+case 4:
+ this.$ = [] 
+break;
+case 5:
+$$[$0-1].push($$[$0]);this.$ = $$[$0-1]
+break;
+case 6: case 7:
+ this.$ = $$[$0] 
+break;
+case 8:
+ this.$=[];
+break;
+case 9:
+$$[$0-3].description=$$[$0-1]; this.$=$$[$0-3];
+break;
+case 10:
+this.$=$$[$0-1];
+break;
+case 12:
+this.$={type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $$[$0-1]};
+break;
+case 13:
+this.$={type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $$[$0-1]};
+break;
+case 15:
+this.$=[{type:'setTitle', text:$$[$0-1]}]
+break;
+case 16:
+
+		$$[$0-1].unshift({type: 'loopStart', loopText:$$[$0-2], signalType: yy.LINETYPE.LOOP_START});
+		$$[$0-1].push({type: 'loopEnd', loopText:$$[$0-2], signalType: yy.LINETYPE.LOOP_END});
+		this.$=$$[$0-1];
+break;
+case 17:
+
+		$$[$0-1].unshift({type: 'optStart', optText:$$[$0-2], signalType: yy.LINETYPE.OPT_START});
+		$$[$0-1].push({type: 'optEnd', optText:$$[$0-2], signalType: yy.LINETYPE.OPT_END});
+		this.$=$$[$0-1];
+break;
+case 18:
+
+		// Alt start
+		$$[$0-1].unshift({type: 'altStart', altText:$$[$0-2], signalType: yy.LINETYPE.ALT_START});
+		// Content in alt is already in $$[$0-1]
+		// End
+		$$[$0-1].push({type: 'altEnd', signalType: yy.LINETYPE.ALT_END});
+		this.$=$$[$0-1];
+break;
+case 19:
+
+		// Parallel start
+		$$[$0-1].unshift({type: 'parStart', parText:$$[$0-2], signalType: yy.LINETYPE.PAR_START});
+		// Content in par is already in $$[$0-1]
+		// End
+		$$[$0-1].push({type: 'parEnd', signalType: yy.LINETYPE.PAR_END});
+		this.$=$$[$0-1];
+break;
+case 21:
+ this.$ = $$[$0-3].concat([{type: 'and', parText:$$[$0-1], signalType: yy.LINETYPE.PAR_AND}, $$[$0]]); 
+break;
+case 23:
+ this.$ = $$[$0-3].concat([{type: 'else', altText:$$[$0-1], signalType: yy.LINETYPE.ALT_ELSE}, $$[$0]]); 
+break;
+case 24:
+
+		this.$ = [$$[$0-1], {type:'addNote', placement:$$[$0-2], actor:$$[$0-1].actor, text:$$[$0]}];
+break;
+case 25:
+
+		// Coerce actor_pair into a [to, from, ...] array
+		$$[$0-2] = [].concat($$[$0-1], $$[$0-1]).slice(0, 2);
+		$$[$0-2][0] = $$[$0-2][0].actor;
+		$$[$0-2][1] = $$[$0-2][1].actor;
+		this.$ = [$$[$0-1], {type:'addNote', placement:yy.PLACEMENT.OVER, actor:$$[$0-2].slice(0, 2), text:$$[$0]}];
+break;
+case 28:
+ this.$ = [$$[$0-2], $$[$0]]; 
+break;
+case 29:
+ this.$ = $$[$0]; 
+break;
+case 30:
+ this.$ = yy.PLACEMENT.LEFTOF; 
+break;
+case 31:
+ this.$ = yy.PLACEMENT.RIGHTOF; 
+break;
+case 32:
+ this.$ = [$$[$0-4],$$[$0-1],{type: 'addMessage', from:$$[$0-4].actor, to:$$[$0-1].actor, signalType:$$[$0-3], msg:$$[$0]},
+	              {type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $$[$0-1]}
+	             ]
+break;
+case 33:
+ this.$ = [$$[$0-4],$$[$0-1],{type: 'addMessage', from:$$[$0-4].actor, to:$$[$0-1].actor, signalType:$$[$0-3], msg:$$[$0]},
+	             {type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $$[$0-4]}
+	             ]
+break;
+case 34:
+ this.$ = [$$[$0-3],$$[$0-1],{type: 'addMessage', from:$$[$0-3].actor, to:$$[$0-1].actor, signalType:$$[$0-2], msg:$$[$0]}]
+break;
+case 35:
+this.$={type: 'addActor', actor:$$[$0]}
+break;
+case 36:
+ this.$ = yy.LINETYPE.SOLID_OPEN; 
+break;
+case 37:
+ this.$ = yy.LINETYPE.DOTTED_OPEN; 
+break;
+case 38:
+ this.$ = yy.LINETYPE.SOLID; 
+break;
+case 39:
+ this.$ = yy.LINETYPE.DOTTED; 
+break;
+case 40:
+ this.$ = yy.LINETYPE.SOLID_CROSS; 
+break;
+case 41:
+ this.$ = yy.LINETYPE.DOTTED_CROSS; 
+break;
+case 42:
+this.$ = $$[$0].substring(1).trim().replace(/\\n/gm, "\n");
+break;
+}
+},
+table: [{3:1,4:$V0,5:$V1,6:$V2},{1:[3]},{3:5,4:$V0,5:$V1,6:$V2},{3:6,4:$V0,5:$V1,6:$V2},o([1,4,5,10,15,16,18,20,22,23,25,29,40],$V3,{7:7}),{1:[2,1]},{1:[2,2]},{1:[2,3],4:$V4,5:$V5,8:8,9:10,10:$V6,11:22,14:13,15:$V7,16:$V8,17:16,18:$V9,20:$Va,22:$Vb,23:$Vc,25:$Vd,29:$Ve,40:$Vf},o($Vg,[2,5]),{9:25,10:$V6,11:22,14:13,15:$V7,16:$V8,17:16,18:$V9,20:$Va,22:$Vb,23:$Vc,25:$Vd,29:$Ve,40:$Vf},o($Vg,[2,7]),o($Vg,[2,8]),{11:26,40:$Vf},{5:[1,27]},{11:28,40:$Vf},{11:29,40:$Vf},{5:[1,30]},{19:31,47:$Vh},{13:[1,33]},{13:[1,34]},{13:[1,35]},{13:[1,36]},{37:37,41:[1,38],42:[1,39],43:[1,40],44:[1,41],45:[1,42],46:[1,43]},{30:44,31:[1,45],35:[1,46],36:[1,47]},o([5,12,34,41,42,43,44,45,46,47],[2,35]),o($Vg,[2,6]),{5:[1,49],12:[1,48]},o($Vg,[2,11]),{5:[1,50]},{5:[1,51]},o($Vg,[2,14]),{5:[1,52]},{5:[2,42]},o($Vi,$V3,{7:53}),o($Vi,$V3,{7:54}),o($Vj,$V3,{24:55,7:56}),o($Vk,$V3,{26:57,7:58}),{11:61,38:[1,59],39:[1,60],40:$Vf},o($Vl,[2,36]),o($Vl,[2,37]),o($Vl,[2,38]),o($Vl,[2,39]),o($Vl,[2,40]),o($Vl,[2,41]),{11:62,40:$Vf},{11:64,32:63,40:$Vf},{40:[2,30]},{40:[2,31]},{13:[1,65]},o($Vg,[2,10]),o($Vg,[2,12]),o($Vg,[2,13]),o($Vg,[2,15]),{4:$V4,5:$V5,8:8,9:10,10:$V6,11:22,14:13,15:$V7,16:$V8,17:16,18:$V9,20:$Va,21:[1,66],22:$Vb,23:$Vc,25:$Vd,29:$Ve,40:$Vf},{4:$V4,5:$V5,8:8,9:10,10:$V6,11:22,14:13,15:$V7,16:$V8,17:16,18:$V9,20:$Va,21:[1,67],22:$Vb,23:$Vc,25:$Vd,29:$Ve,40:$Vf},{21:[1,68]},{4:$V4,5:$V5,8:8,9:10,10:$V6,11:22,14:13,15:$V7,16:$V8,17:16,18:$V9,20:$Va,21:[2,22],22:$Vb,23:$Vc,25:$Vd,28:[1,69],29:$Ve,40:$Vf},{21:[1,70]},{4:$V4,5:$V5,8:8,9:10,10:$V6,11:22,14:13,15:$V7,16:$V8,17:16,18:$V9,20:$Va,21:[2,20],22:$Vb,23:$Vc,25:$Vd,27:[1,71],29:$Ve,40:$Vf},{11:72,40:$Vf},{11:73,40:$Vf},{19:74,47:$Vh},{19:75,47:$Vh},{19:76,47:$Vh},{34:[1,77],47:[2,29]},{5:[1,78]},o($Vg,[2,16]),o($Vg,[2,17]),o($Vg,[2,18]),{13:[1,79]},o($Vg,[2,19]),{13:[1,80]},{19:81,47:$Vh},{19:82,47:$Vh},{5:[2,34]},{5:[2,24]},{5:[2,25]},{11:83,40:$Vf},o($Vg,[2,9]),o($Vj,$V3,{7:56,24:84}),o($Vk,$V3,{7:58,26:85}),{5:[2,32]},{5:[2,33]},{47:[2,28]},{21:[2,23]},{21:[2,21]}],
+defaultActions: {5:[2,1],6:[2,2],32:[2,42],46:[2,30],47:[2,31],74:[2,34],75:[2,24],76:[2,25],81:[2,32],82:[2,33],83:[2,28],84:[2,23],85:[2,21]},
+parseError: function parseError(str, hash) {
+    if (hash.recoverable) {
+        this.trace(str);
+    } else {
+        var error = new Error(str);
+        error.hash = hash;
+        throw error;
+    }
+},
+parse: function parse(input) {
+    var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+    var args = lstack.slice.call(arguments, 1);
+    var lexer = Object.create(this.lexer);
+    var sharedState = { yy: {} };
+    for (var k in this.yy) {
+        if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
+            sharedState.yy[k] = this.yy[k];
+        }
+    }
+    lexer.setInput(input, sharedState.yy);
+    sharedState.yy.lexer = lexer;
+    sharedState.yy.parser = this;
+    if (typeof lexer.yylloc == 'undefined') {
+        lexer.yylloc = {};
+    }
+    var yyloc = lexer.yylloc;
+    lstack.push(yyloc);
+    var ranges = lexer.options && lexer.options.ranges;
+    if (typeof sharedState.yy.parseError === 'function') {
+        this.parseError = sharedState.yy.parseError;
+    } else {
+        this.parseError = Object.getPrototypeOf(this).parseError;
+    }
+    function popStack(n) {
+        stack.length = stack.length - 2 * n;
+        vstack.length = vstack.length - n;
+        lstack.length = lstack.length - n;
+    }
+            function lex() {
+            var token;
+            token = tstack.pop() || lexer.lex() || EOF;
+            if (typeof token !== 'number') {
+                if (token instanceof Array) {
+                    tstack = token;
+                    token = tstack.pop();
+                }
+                token = self.symbols_[token] || token;
+            }
+            return token;
+        }
+    var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+    while (true) {
+        state = stack[stack.length - 1];
+        if (this.defaultActions[state]) {
+            action = this.defaultActions[state];
+        } else {
+            if (symbol === null || typeof symbol == 'undefined') {
+                symbol = lex();
+            }
+            action = table[state] && table[state][symbol];
+        }
+        if (typeof action === 'undefined' || !action.length || !action[0]) {
+            var errStr = '';
+            expected = [];
+            for (p in table[state]) {
+                if (this.terminals_[p] && p > TERROR) {
+                    expected.push('\'' + this.terminals_[p] + '\'');
+                }
+            }
+            if (lexer.showPosition) {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
+            } else {
+                errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
+            }
+            this.parseError(errStr, {
+                text: lexer.match,
+                token: this.terminals_[symbol] || symbol,
+                line: lexer.yylineno,
+                loc: yyloc,
+                expected: expected
+            });
+        }
+        if (action[0] instanceof Array && action.length > 1) {
+            throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+        }
+        switch (action[0]) {
+        case 1:
+            stack.push(symbol);
+            vstack.push(lexer.yytext);
+            lstack.push(lexer.yylloc);
+            stack.push(action[1]);
+            symbol = null;
+            if (!preErrorSymbol) {
+                yyleng = lexer.yyleng;
+                yytext = lexer.yytext;
+                yylineno = lexer.yylineno;
+                yyloc = lexer.yylloc;
+                if (recovering > 0) {
+                    recovering--;
+                }
+            } else {
+                symbol = preErrorSymbol;
+                preErrorSymbol = null;
+            }
+            break;
+        case 2:
+            len = this.productions_[action[1]][1];
+            yyval.$ = vstack[vstack.length - len];
+            yyval._$ = {
+                first_line: lstack[lstack.length - (len || 1)].first_line,
+                last_line: lstack[lstack.length - 1].last_line,
+                first_column: lstack[lstack.length - (len || 1)].first_column,
+                last_column: lstack[lstack.length - 1].last_column
+            };
+            if (ranges) {
+                yyval._$.range = [
+                    lstack[lstack.length - (len || 1)].range[0],
+                    lstack[lstack.length - 1].range[1]
+                ];
+            }
+            r = this.performAction.apply(yyval, [
+                yytext,
+                yyleng,
+                yylineno,
+                sharedState.yy,
+                action[1],
+                vstack,
+                lstack
+            ].concat(args));
+            if (typeof r !== 'undefined') {
+                return r;
+            }
+            if (len) {
+                stack = stack.slice(0, -1 * len * 2);
+                vstack = vstack.slice(0, -1 * len);
+                lstack = lstack.slice(0, -1 * len);
+            }
+            stack.push(this.productions_[action[1]][0]);
+            vstack.push(yyval.$);
+            lstack.push(yyval._$);
+            newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+            stack.push(newState);
+            break;
+        case 3:
+            return true;
+        }
+    }
+    return true;
+}};
+
+/* generated by jison-lex 0.3.4 */
+var lexer = (function(){
+var lexer = ({
+
+EOF:1,
+
+parseError:function parseError(str, hash) {
+        if (this.yy.parser) {
+            this.yy.parser.parseError(str, hash);
+        } else {
+            throw new Error(str);
+        }
+    },
+
+// resets the lexer, sets new input
+setInput:function (input, yy) {
+        this.yy = yy || this.yy || {};
+        this._input = input;
+        this._more = this._backtrack = this.done = false;
+        this.yylineno = this.yyleng = 0;
+        this.yytext = this.matched = this.match = '';
+        this.conditionStack = ['INITIAL'];
+        this.yylloc = {
+            first_line: 1,
+            first_column: 0,
+            last_line: 1,
+            last_column: 0
+        };
+        if (this.options.ranges) {
+            this.yylloc.range = [0,0];
+        }
+        this.offset = 0;
+        return this;
+    },
+
+// consumes and returns one char from the input
+input:function () {
+        var ch = this._input[0];
+        this.yytext += ch;
+        this.yyleng++;
+        this.offset++;
+        this.match += ch;
+        this.matched += ch;
+        var lines = ch.match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno++;
+            this.yylloc.last_line++;
+        } else {
+            this.yylloc.last_column++;
+        }
+        if (this.options.ranges) {
+            this.yylloc.range[1]++;
+        }
+
+        this._input = this._input.slice(1);
+        return ch;
+    },
+
+// unshifts one char (or a string) into the input
+unput:function (ch) {
+        var len = ch.length;
+        var lines = ch.split(/(?:\r\n?|\n)/g);
+
+        this._input = ch + this._input;
+        this.yytext = this.yytext.substr(0, this.yytext.length - len);
+        //this.yyleng -= len;
+        this.offset -= len;
+        var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+        this.match = this.match.substr(0, this.match.length - 1);
+        this.matched = this.matched.substr(0, this.matched.length - 1);
+
+        if (lines.length - 1) {
+            this.yylineno -= lines.length - 1;
+        }
+        var r = this.yylloc.range;
+
+        this.yylloc = {
+            first_line: this.yylloc.first_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.first_column,
+            last_column: lines ?
+                (lines.length === oldLines.length ? this.yylloc.first_column : 0)
+                 + oldLines[oldLines.length - lines.length].length - lines[0].length :
+              this.yylloc.first_column - len
+        };
+
+        if (this.options.ranges) {
+            this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+        }
+        this.yyleng = this.yytext.length;
+        return this;
+    },
+
+// When called from action, caches matched text and appends it on next action
+more:function () {
+        this._more = true;
+        return this;
+    },
+
+// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
+reject:function () {
+        if (this.options.backtrack_lexer) {
+            this._backtrack = true;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+
+        }
+        return this;
+    },
+
+// retain first n characters of the match
+less:function (n) {
+        this.unput(this.match.slice(n));
+    },
+
+// displays already matched input, i.e. for error messages
+pastInput:function () {
+        var past = this.matched.substr(0, this.matched.length - this.match.length);
+        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+    },
+
+// displays upcoming input, i.e. for error messages
+upcomingInput:function () {
+        var next = this.match;
+        if (next.length < 20) {
+            next += this._input.substr(0, 20-next.length);
+        }
+        return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+    },
+
+// displays the character position where the lexing error occurred, i.e. for error messages
+showPosition:function () {
+        var pre = this.pastInput();
+        var c = new Array(pre.length + 1).join("-");
+        return pre + this.upcomingInput() + "\n" + c + "^";
+    },
+
+// test the lexed token: return FALSE when not a match, otherwise return token
+test_match:function (match, indexed_rule) {
+        var token,
+            lines,
+            backup;
+
+        if (this.options.backtrack_lexer) {
+            // save context
+            backup = {
+                yylineno: this.yylineno,
+                yylloc: {
+                    first_line: this.yylloc.first_line,
+                    last_line: this.last_line,
+                    first_column: this.yylloc.first_column,
+                    last_column: this.yylloc.last_column
+                },
+                yytext: this.yytext,
+                match: this.match,
+                matches: this.matches,
+                matched: this.matched,
+                yyleng: this.yyleng,
+                offset: this.offset,
+                _more: this._more,
+                _input: this._input,
+                yy: this.yy,
+                conditionStack: this.conditionStack.slice(0),
+                done: this.done
+            };
+            if (this.options.ranges) {
+                backup.yylloc.range = this.yylloc.range.slice(0);
+            }
+        }
+
+        lines = match[0].match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno += lines.length;
+        }
+        this.yylloc = {
+            first_line: this.yylloc.last_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.last_column,
+            last_column: lines ?
+                         lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
+                         this.yylloc.last_column + match[0].length
+        };
+        this.yytext += match[0];
+        this.match += match[0];
+        this.matches = match;
+        this.yyleng = this.yytext.length;
+        if (this.options.ranges) {
+            this.yylloc.range = [this.offset, this.offset += this.yyleng];
+        }
+        this._more = false;
+        this._backtrack = false;
+        this._input = this._input.slice(match[0].length);
+        this.matched += match[0];
+        token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
+        if (this.done && this._input) {
+            this.done = false;
+        }
+        if (token) {
+            return token;
+        } else if (this._backtrack) {
+            // recover context
+            for (var k in backup) {
+                this[k] = backup[k];
+            }
+            return false; // rule action called reject() implying the next rule should be tested instead.
+        }
+        return false;
+    },
+
+// return next match in input
+next:function () {
+        if (this.done) {
+            return this.EOF;
+        }
+        if (!this._input) {
+            this.done = true;
+        }
+
+        var token,
+            match,
+            tempMatch,
+            index;
+        if (!this._more) {
+            this.yytext = '';
+            this.match = '';
+        }
+        var rules = this._currentRules();
+        for (var i = 0; i < rules.length; i++) {
+            tempMatch = this._input.match(this.rules[rules[i]]);
+            if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+                match = tempMatch;
+                index = i;
+                if (this.options.backtrack_lexer) {
+                    token = this.test_match(tempMatch, rules[i]);
+                    if (token !== false) {
+                        return token;
+                    } else if (this._backtrack) {
+                        match = false;
+                        continue; // rule action called reject() implying a rule MISmatch.
+                    } else {
+                        // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+                        return false;
+                    }
+                } else if (!this.options.flex) {
+                    break;
+                }
+            }
+        }
+        if (match) {
+            token = this.test_match(match, rules[index]);
+            if (token !== false) {
+                return token;
+            }
+            // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+            return false;
+        }
+        if (this._input === "") {
+            return this.EOF;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+        }
+    },
+
+// return next match that has a token
+lex:function lex() {
+        var r = this.next();
+        if (r) {
+            return r;
+        } else {
+            return this.lex();
+        }
+    },
+
+// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
+begin:function begin(condition) {
+        this.conditionStack.push(condition);
+    },
+
+// pop the previously active lexer condition state off the condition stack
+popState:function popState() {
+        var n = this.conditionStack.length - 1;
+        if (n > 0) {
+            return this.conditionStack.pop();
+        } else {
+            return this.conditionStack[0];
+        }
+    },
+
+// produce the lexer rule set which is active for the currently active lexer condition state
+_currentRules:function _currentRules() {
+        if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
+            return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+        } else {
+            return this.conditions["INITIAL"].rules;
+        }
+    },
+
+// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
+topState:function topState(n) {
+        n = this.conditionStack.length - 1 - Math.abs(n || 0);
+        if (n >= 0) {
+            return this.conditionStack[n];
+        } else {
+            return "INITIAL";
+        }
+    },
+
+// alias for begin(condition)
+pushState:function pushState(condition) {
+        this.begin(condition);
+    },
+
+// return the number of states currently on the stack
+stateStackSize:function stateStackSize() {
+        return this.conditionStack.length;
+    },
+options: {"case-insensitive":true},
+performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0:return 5;
+break;
+case 1:/* skip all whitespace */
+break;
+case 2:/* skip same-line whitespace */
+break;
+case 3:/* skip comments */
+break;
+case 4:/* skip comments */
+break;
+case 5: this.begin('ID'); return 10; 
+break;
+case 6: this.begin('ALIAS'); return 40; 
+break;
+case 7: this.popState(); this.popState(); this.begin('LINE'); return 12; 
+break;
+case 8: this.popState(); this.popState(); return 5; 
+break;
+case 9: this.begin('LINE'); return 20; 
+break;
+case 10: this.begin('LINE'); return 22; 
+break;
+case 11: this.begin('LINE'); return 23; 
+break;
+case 12: this.begin('LINE'); return 28; 
+break;
+case 13: this.begin('LINE'); return 25; 
+break;
+case 14: this.begin('LINE'); return 27; 
+break;
+case 15: this.popState(); return 13; 
+break;
+case 16:return 21;
+break;
+case 17:return 35;
+break;
+case 18:return 36;
+break;
+case 19:return 31;
+break;
+case 20:return 29;
+break;
+case 21: this.begin('ID'); return 15; 
+break;
+case 22: this.begin('ID'); return 16; 
+break;
+case 23:return 18;
+break;
+case 24:return 6;
+break;
+case 25:return 34;
+break;
+case 26:return 5;
+break;
+case 27: yy_.yytext = yy_.yytext.trim(); return 40; 
+break;
+case 28:return 43;
+break;
+case 29:return 44;
+break;
+case 30:return 41;
+break;
+case 31:return 42;
+break;
+case 32:return 45;
+break;
+case 33:return 46;
+break;
+case 34:return 47;
+break;
+case 35:return 38;
+break;
+case 36:return 39;
+break;
+case 37:return 5;
+break;
+case 38:return 'INVALID';
+break;
+}
+},
+rules: [/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:participant\b)/i,/^(?:[^\->:\n,;]+?(?=((?!\n)\s)+as(?!\n)\s|[#\n;]|$))/i,/^(?:as\b)/i,/^(?:(?:))/i,/^(?:loop\b)/i,/^(?:opt\b)/i,/^(?:alt\b)/i,/^(?:else\b)/i,/^(?:par\b)/i,/^(?:and\b)/i,/^(?:[^#\n;]*)/i,/^(?:end\b)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:over\b)/i,/^(?:note\b)/i,/^(?:activate\b)/i,/^(?:deactivate\b)/i,/^(?:title\b)/i,/^(?:sequenceDiagram\b)/i,/^(?:,)/i,/^(?:;)/i,/^(?:[^\+\->:\n,;]+)/i,/^(?:->>)/i,/^(?:-->>)/i,/^(?:->)/i,/^(?:-->)/i,/^(?:-[x])/i,/^(?:--[x])/i,/^(?::[^#\n;]+)/i,/^(?:\+)/i,/^(?:-)/i,/^(?:$)/i,/^(?:.)/i],
+conditions: {"LINE":{"rules":[2,3,15],"inclusive":false},"ALIAS":{"rules":[2,3,7,8],"inclusive":false},"ID":{"rules":[2,3,6],"inclusive":false},"INITIAL":{"rules":[0,1,3,4,5,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"inclusive":true}}
+});
+return lexer;
+})();
+parser.lexer = lexer;
+function Parser () {
+  this.yy = {};
+}
+Parser.prototype = parser;parser.Parser = Parser;
+return new Parser;
+})();
+
+
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+exports.parser = parser;
+exports.Parser = parser.Parser;
+exports.parse = function () { return parser.parse.apply(parser, arguments); };
+exports.main = function commonjsMain(args) {
+    if (!args[1]) {
+        console.log('Usage: '+args[0]+' FILE');
+        process.exit(1);
+    }
+    var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8");
+    return exports.parser.parse(source);
+};
+if (typeof module !== 'undefined' && require.main === module) {
+  exports.main(process.argv.slice(1));
+}
+}
\ No newline at end of file
diff --git a/_submodules/mermaid/src/diagrams/sequence/sequenceDb.js b/_submodules/mermaid/src/diagrams/sequence/sequenceDb.js
new file mode 100644
index 0000000000000000000000000000000000000000..665ca820d82c9d1d67863896196c76727bcad069
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/sequence/sequenceDb.js
@@ -0,0 +1,172 @@
+import { logger } from '../../logger'
+
+let actors = {}
+let messages = []
+const notes = []
+let title = ''
+
+export const addActor = function (id, name, description) {
+  // Don't allow description nulling
+  const old = actors[id]
+  if (old && name === old.name && description == null) return
+
+  // Don't allow null descriptions, either
+  if (description == null) description = name
+
+  actors[id] = { name: name, description: description }
+}
+
+export const addMessage = function (idFrom, idTo, message, answer) {
+  messages.push({ from: idFrom, to: idTo, message: message, answer: answer })
+}
+
+export const addSignal = function (idFrom, idTo, message, messageType) {
+  logger.debug('Adding message from=' + idFrom + ' to=' + idTo + ' message=' + message + ' type=' + messageType)
+  messages.push({ from: idFrom, to: idTo, message: message, type: messageType })
+}
+
+export const getMessages = function () {
+  return messages
+}
+
+export const getActors = function () {
+  return actors
+}
+export const getActor = function (id) {
+  return actors[id]
+}
+export const getActorKeys = function () {
+  return Object.keys(actors)
+}
+export const getTitle = function () {
+  return title
+}
+
+export const clear = function () {
+  actors = {}
+  messages = []
+}
+
+export const LINETYPE = {
+  SOLID: 0,
+  DOTTED: 1,
+  NOTE: 2,
+  SOLID_CROSS: 3,
+  DOTTED_CROSS: 4,
+  SOLID_OPEN: 5,
+  DOTTED_OPEN: 6,
+  LOOP_START: 10,
+  LOOP_END: 11,
+  ALT_START: 12,
+  ALT_ELSE: 13,
+  ALT_END: 14,
+  OPT_START: 15,
+  OPT_END: 16,
+  ACTIVE_START: 17,
+  ACTIVE_END: 18,
+  PAR_START: 19,
+  PAR_AND: 20,
+  PAR_END: 21
+}
+
+export const ARROWTYPE = {
+  FILLED: 0,
+  OPEN: 1
+}
+
+export const PLACEMENT = {
+  LEFTOF: 0,
+  RIGHTOF: 1,
+  OVER: 2
+}
+
+export const addNote = function (actor, placement, message) {
+  const note = { actor: actor, placement: placement, message: message }
+
+  // Coerce actor into a [to, from, ...] array
+  const actors = [].concat(actor, actor)
+
+  notes.push(note)
+  messages.push({ from: actors[0], to: actors[1], message: message, type: LINETYPE.NOTE, placement: placement })
+}
+
+export const setTitle = function (titleText) {
+  title = titleText
+}
+
+export const apply = function (param) {
+  if (param instanceof Array) {
+    param.forEach(function (item) {
+      apply(item)
+    })
+  } else {
+    switch (param.type) {
+      case 'addActor':
+        addActor(param.actor, param.actor, param.description)
+        break
+      case 'activeStart':
+        addSignal(param.actor, undefined, undefined, param.signalType)
+        break
+      case 'activeEnd':
+        addSignal(param.actor, undefined, undefined, param.signalType)
+        break
+      case 'addNote':
+        addNote(param.actor, param.placement, param.text)
+        break
+      case 'addMessage':
+        addSignal(param.from, param.to, param.msg, param.signalType)
+        break
+      case 'loopStart':
+        addSignal(undefined, undefined, param.loopText, param.signalType)
+        break
+      case 'loopEnd':
+        addSignal(undefined, undefined, undefined, param.signalType)
+        break
+      case 'optStart':
+        addSignal(undefined, undefined, param.optText, param.signalType)
+        break
+      case 'optEnd':
+        addSignal(undefined, undefined, undefined, param.signalType)
+        break
+      case 'altStart':
+        addSignal(undefined, undefined, param.altText, param.signalType)
+        break
+      case 'else':
+        addSignal(undefined, undefined, param.altText, param.signalType)
+        break
+      case 'altEnd':
+        addSignal(undefined, undefined, undefined, param.signalType)
+        break
+      case 'setTitle':
+        setTitle(param.text)
+        break
+      case 'parStart':
+        addSignal(undefined, undefined, param.parText, param.signalType)
+        break
+      case 'and':
+        addSignal(undefined, undefined, param.parText, param.signalType)
+        break
+      case 'parEnd':
+        addSignal(undefined, undefined, undefined, param.signalType)
+        break
+    }
+  }
+}
+
+export default {
+  addActor,
+  addMessage,
+  addSignal,
+  getMessages,
+  getActors,
+  getActor,
+  getActorKeys,
+  getTitle,
+  clear,
+  LINETYPE,
+  ARROWTYPE,
+  PLACEMENT,
+  addNote,
+  setTitle,
+  apply
+}
diff --git a/_submodules/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js b/_submodules/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..0631d1a610f743133fe7005a936ae51979dd35c8
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js
@@ -0,0 +1,962 @@
+/* eslint-env jasmine */
+import { parser } from './parser/sequenceDiagram'
+import sequenceDb from './sequenceDb'
+import renderer from './sequenceRenderer'
+
+function addConf (conf, key, value) {
+  if (value !== undefined) {
+    conf[key] = value
+  }
+  return conf
+}
+
+describe('when parsing a sequenceDiagram', function () {
+  beforeEach(function () {
+    parser.yy = sequenceDb
+    parser.yy.clear()
+  })
+  it('it should handle a sequenceDiagram defintion', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob:Hello Bob, how are you?\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'Bob-->Alice: I am good thanks!'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(3)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[2].from).toBe('Bob')
+  })
+  it('it should handle a sequenceDiagram definition with a title', function () {
+    const str = 'sequenceDiagram\n' +
+      'title: Diagram Title\n' +
+      'Alice->Bob:Hello Bob, how are you?\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'Bob-->Alice: I am good thanks!'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+    const title = parser.yy.getTitle()
+
+    expect(messages.length).toBe(3)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[2].from).toBe('Bob')
+    expect(title).toBe('Diagram Title')
+  })
+  it('it should space in actor names', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob:Hello Bob, how are - you?\n' +
+      'Bob-->Alice: I am good thanks!'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(2)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[1].from).toBe('Bob')
+  })
+  it('it should alias participants', function () {
+    const str = 'sequenceDiagram\n' +
+      'participant A as Alice\n' +
+      'participant B as Bob\n' +
+      'A->B:Hello Bob, how are you?\n' +
+      'B-->A: I am good thanks!'
+
+    parser.parse(str)
+
+    const actors = parser.yy.getActors()
+    expect(Object.keys(actors)).toEqual(['A', 'B'])
+    expect(actors.A.description).toBe('Alice')
+    expect(actors.B.description).toBe('Bob')
+
+    const messages = parser.yy.getMessages()
+    expect(messages.length).toBe(2)
+    expect(messages[0].from).toBe('A')
+    expect(messages[1].from).toBe('B')
+  })
+  it('it should handle in async messages', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice-xBob:Hello Bob, how are you?'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    expect(actors.Bob.description).toBe('Bob')
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(1)
+    expect(messages[0].type).toBe(parser.yy.LINETYPE.SOLID_CROSS)
+  })
+  it('it should handle in async dotted messages', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice--xBob:Hello Bob, how are you?'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    expect(actors.Bob.description).toBe('Bob')
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(1)
+    expect(messages[0].type).toBe(parser.yy.LINETYPE.DOTTED_CROSS)
+  })
+  it('it should handle in arrow messages', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->>Bob:Hello Bob, how are you?'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    expect(actors.Bob.description).toBe('Bob')
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(1)
+    expect(messages[0].type).toBe(parser.yy.LINETYPE.SOLID)
+  })
+  it('it should handle in arrow messages', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice-->>Bob:Hello Bob, how are you?'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    expect(actors.Bob.description).toBe('Bob')
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(1)
+    expect(messages[0].type).toBe(parser.yy.LINETYPE.DOTTED)
+  })
+  it('it should handle actor activation', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice-->>Bob:Hello Bob, how are you?\n' +
+      'activate Bob\n' +
+      'Bob-->>Alice:Hello Alice, I\'m fine and  you?\n' +
+      'deactivate Bob'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    expect(actors.Bob.description).toBe('Bob')
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(4)
+    expect(messages[0].type).toBe(parser.yy.LINETYPE.DOTTED)
+    expect(messages[1].type).toBe(parser.yy.LINETYPE.ACTIVE_START)
+    expect(messages[1].from.actor).toBe('Bob')
+    expect(messages[2].type).toBe(parser.yy.LINETYPE.DOTTED)
+    expect(messages[3].type).toBe(parser.yy.LINETYPE.ACTIVE_END)
+    expect(messages[3].from.actor).toBe('Bob')
+  })
+  it('it should handle actor one line notation activation', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice-->>+Bob:Hello Bob, how are you?\n' +
+      'Bob-->>- Alice:Hello Alice, I\'m fine and  you?'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    expect(actors.Bob.description).toBe('Bob')
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(4)
+    expect(messages[0].type).toBe(parser.yy.LINETYPE.DOTTED)
+    expect(messages[1].type).toBe(parser.yy.LINETYPE.ACTIVE_START)
+    expect(messages[1].from.actor).toBe('Bob')
+    expect(messages[2].type).toBe(parser.yy.LINETYPE.DOTTED)
+    expect(messages[3].type).toBe(parser.yy.LINETYPE.ACTIVE_END)
+    expect(messages[3].from.actor).toBe('Bob')
+  })
+  it('it should handle stacked activations', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice-->>+Bob:Hello Bob, how are you?\n' +
+      'Bob-->>+Carol:Carol, let me introduce Alice?\n' +
+      'Bob-->>- Alice:Hello Alice, please meet Carol?\n' +
+      'Carol->>- Bob:Oh Bob, I\'m so happy to be here!'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    expect(actors.Bob.description).toBe('Bob')
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(8)
+    expect(messages[0].type).toBe(parser.yy.LINETYPE.DOTTED)
+    expect(messages[1].type).toBe(parser.yy.LINETYPE.ACTIVE_START)
+    expect(messages[1].from.actor).toBe('Bob')
+    expect(messages[2].type).toBe(parser.yy.LINETYPE.DOTTED)
+    expect(messages[3].type).toBe(parser.yy.LINETYPE.ACTIVE_START)
+    expect(messages[3].from.actor).toBe('Carol')
+    expect(messages[5].type).toBe(parser.yy.LINETYPE.ACTIVE_END)
+    expect(messages[5].from.actor).toBe('Bob')
+    expect(messages[7].type).toBe(parser.yy.LINETYPE.ACTIVE_END)
+    expect(messages[7].from.actor).toBe('Carol')
+  })
+  it('it should handle comments in a sequenceDiagram', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      '%% Comment\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'Bob-->Alice: I am good thanks!'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(3)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[2].from).toBe('Bob')
+  })
+  it('it should handle new lines in a sequenceDiagram', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n\n' +
+      '%% Comment\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'Bob-->Alice: I am good thanks!\n'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(3)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[2].from).toBe('Bob')
+  })
+  it('it should handle semicolons', function () {
+    const str = 'sequenceDiagram;' +
+      'Alice->Bob: Hello Bob, how are you?;' +
+      'Note right of Bob: Bob thinks;' +
+      'Bob-->Alice: I am good thanks!;'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(3)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[2].from).toBe('Bob')
+  })
+  it('it should handle one leading space in lines in a sequenceDiagram', function () {
+    const str = 'sequenceDiagram\n' +
+      ' Alice->Bob: Hello Bob, how are you?\n\n' +
+      '%% Comment\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'Bob-->Alice: I am good thanks!'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(3)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[2].from).toBe('Bob')
+  })
+  it('it should handle several leading spaces in lines in a sequenceDiagram', function () {
+    const str = 'sequenceDiagram\n' +
+      '   Alice->Bob: Hello Bob, how are you?\n\n' +
+      '%% Comment\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'Bob-->Alice: I am good thanks!'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(3)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[2].from).toBe('Bob')
+  })
+  it('it should handle several leading spaces in lines in a sequenceDiagram', function () {
+    const str = 'sequenceDiagram\n' +
+      'participant Alice\n' +
+      'participant Bob\n' +
+      'Alice->John: Hello John, how are you?\n' +
+      '    loop Healthcheck\n' +
+      'John->John: Fight against hypochondria\n' +
+      ' end\n' +
+      'Note right of John: Rational thoughts<br/>prevail...\n' +
+      '    John-->Alice: Great!\n' +
+      '    John->Bob: How about you?\n' +
+      'Bob-->John: Jolly good!'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(8)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[2].from).toBe('John')
+  })
+  it('it should handle notes over a single actor', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'Note over Bob: Bob thinks\n'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].from).toBe('Bob')
+    expect(messages[1].to).toBe('Bob')
+  })
+  it('it should handle notes over multiple actors', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'Note over Alice,Bob: confusion\n' +
+      'Note over Bob,Alice: resolution\n'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].from).toBe('Alice')
+    expect(messages[1].to).toBe('Bob')
+    expect(messages[2].from).toBe('Bob')
+    expect(messages[2].to).toBe('Alice')
+  })
+  it('it should handle loop statements', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n\n' +
+      '%% Comment\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'loop Multiple happy responses\n\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(5)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[1].from).toBe('Bob')
+  })
+  it('it should handle opt statements', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n\n' +
+      '%% Comment\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'opt Perhaps a happy response\n\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(5)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[1].from).toBe('Bob')
+  })
+  it('it should handle alt statements', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n\n' +
+      '%% Comment\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'alt isWell\n\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'else isSick\n' +
+      'Bob-->Alice: Feel sick...\n' +
+      'end'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+
+    expect(actors.Alice.description).toBe('Alice')
+    actors.Bob.description = 'Bob'
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(7)
+    expect(messages[0].from).toBe('Alice')
+    expect(messages[1].from).toBe('Bob')
+  })
+  it('it should handle alt statements with multiple elses', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n\n' +
+      '%% Comment\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'alt isWell\n\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'else isSick\n' +
+      'Bob-->Alice: Feel sick...\n' +
+      'else default\n' +
+      'Bob-->Alice: :-)\n' +
+      'end'
+    parser.parse(str)
+    const messages = parser.yy.getMessages()
+    expect(messages.length).toBe(9)
+    expect(messages[1].from).toBe('Bob')
+    expect(messages[2].type).toBe(parser.yy.LINETYPE.ALT_START)
+    expect(messages[3].from).toBe('Bob')
+    expect(messages[4].type).toBe(parser.yy.LINETYPE.ALT_ELSE)
+    expect(messages[5].from).toBe('Bob')
+    expect(messages[6].type).toBe(parser.yy.LINETYPE.ALT_ELSE)
+    expect(messages[7].from).toBe('Bob')
+    expect(messages[8].type).toBe(parser.yy.LINETYPE.ALT_END)
+  })
+  it('it should handle par statements a sequenceDiagram', function () {
+    const str = 'sequenceDiagram\n' +
+      'par Parallel one\n' +
+      'Alice->>Bob: Hello Bob, how are you?\n' +
+      'Bob-->>Alice: I am good thanks!\n' +
+      'and Parallel two\n' +
+      'Alice->>Bob: Are you OK?\n' +
+      'Bob-->>Alice: Fine!\n' +
+      'and Parallel three\n' +
+      'Alice->>Bob: What do you think about it?\n' +
+      'Bob-->>Alice: It\'s good!\n' +
+      'end'
+
+    parser.parse(str)
+    const actors = parser.yy.getActors()
+
+    expect(actors.Alice.description).toBe('Alice')
+    expect(actors.Bob.description).toBe('Bob')
+
+    const messages = parser.yy.getMessages()
+
+    expect(messages.length).toBe(10)
+    expect(messages[0].message).toBe('Parallel one')
+    expect(messages[1].from).toBe('Alice')
+    expect(messages[2].from).toBe('Bob')
+  })
+  it('it should handle special characters in signals', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: -:<>,;# comment'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[0].message).toBe('-:<>,')
+  })
+  it('it should handle special characters in notes', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'Note right of Bob: -:<>,;# comment'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].message).toBe('-:<>,')
+  })
+  it('it should handle special characters in loop', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'loop -:<>,;# comment\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].message).toBe('-:<>,')
+  })
+  it('it should handle special characters in opt', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'opt -:<>,;# comment\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].message).toBe('-:<>,')
+  })
+  it('it should handle special characters in alt', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'alt -:<>,;# comment\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'else ,<>:-#; comment\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].message).toBe('-:<>,')
+    expect(messages[3].message).toBe(',<>:-')
+  })
+  it('it should handle special characters in par', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'par -:<>,;# comment\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'and ,<>:-#; comment\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].message).toBe('-:<>,')
+    expect(messages[3].message).toBe(',<>:-')
+  })
+  it('it should handle no-label loop', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'loop\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].message).toBe('')
+    expect(messages[2].message).toBe('I am good thanks!')
+  })
+  it('it should handle no-label opt', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'opt # comment\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].message).toBe('')
+    expect(messages[2].message).toBe('I am good thanks!')
+  })
+  it('it should handle no-label alt', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'alt;' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'else # comment\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].message).toBe('')
+    expect(messages[2].message).toBe('I am good thanks!')
+    expect(messages[3].message).toBe('')
+    expect(messages[4].message).toBe('I am good thanks!')
+  })
+  it('it should handle no-label par', function () {
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'par;' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'and # comment\n' +
+      'Bob-->Alice: I am good thanks!\n' +
+      'end'
+
+    parser.parse(str)
+
+    const messages = parser.yy.getMessages()
+    expect(messages[1].message).toBe('')
+    expect(messages[2].message).toBe('I am good thanks!')
+    expect(messages[3].message).toBe('')
+    expect(messages[4].message).toBe('I am good thanks!')
+  })
+})
+
+describe('when checking the bounds in a sequenceDiagram', function () {
+  let conf
+  beforeEach(function () {
+    parser.yy = sequenceDb
+    parser.yy.clear()
+    conf = {
+      diagramMarginX: 50,
+      diagramMarginY: 10,
+      actorMargin: 50,
+      width: 150,
+      // Height of actor boxes
+      height: 65,
+      boxMargin: 10,
+      messageMargin: 40,
+      boxTextMargin: 15,
+      noteMargin: 25
+    }
+    renderer.setConf(conf)
+  })
+  it('it should handle a simple bound call', function () {
+    renderer.bounds.init()
+
+    renderer.bounds.insert(100, 100, 200, 200)
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(100)
+    expect(bounds.starty).toBe(100)
+    expect(bounds.stopx).toBe(200)
+    expect(bounds.stopy).toBe(200)
+  })
+  it('it should handle an expanding bound', function () {
+    renderer.bounds.init()
+
+    renderer.bounds.insert(100, 100, 200, 200)
+    renderer.bounds.insert(25, 50, 300, 400)
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(25)
+    expect(bounds.starty).toBe(50)
+    expect(bounds.stopx).toBe(300)
+    expect(bounds.stopy).toBe(400)
+  })
+  it('it should handle inserts within the bound without changing the outer bounds', function () {
+    renderer.bounds.init()
+
+    renderer.bounds.insert(100, 100, 200, 200)
+    renderer.bounds.insert(25, 50, 300, 400)
+    renderer.bounds.insert(125, 150, 150, 200)
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(25)
+    expect(bounds.starty).toBe(50)
+    expect(bounds.stopx).toBe(300)
+    expect(bounds.stopy).toBe(400)
+  })
+  it('it should handle a loop without expanding the area', function () {
+    renderer.bounds.init()
+
+    renderer.bounds.insert(25, 50, 300, 400)
+    renderer.bounds.verticalPos = 150
+    renderer.bounds.newLoop()
+    renderer.bounds.insert(125, 150, 150, 200)
+
+    const loop = renderer.bounds.endLoop()
+
+    expect(loop.startx).toBe(125 - conf.boxMargin)
+    expect(loop.starty).toBe(150 - conf.boxMargin)
+    expect(loop.stopx).toBe(150 + conf.boxMargin)
+    expect(loop.stopy).toBe(200 + conf.boxMargin)
+
+    // Check bounds of first loop
+    const bounds = renderer.bounds.getBounds()
+
+    expect(bounds.startx).toBe(25)
+    expect(bounds.starty).toBe(50)
+    expect(bounds.stopx).toBe(300)
+    expect(bounds.stopy).toBe(400)
+  })
+  it('it should handle multiple loops withtout expanding the bounds', function () {
+    renderer.bounds.init()
+
+    renderer.bounds.insert(100, 100, 1000, 1000)
+    renderer.bounds.verticalPos = 200
+    renderer.bounds.newLoop()
+    renderer.bounds.newLoop()
+    renderer.bounds.insert(200, 200, 300, 300)
+
+    // Check bounds of first loop
+    let loop = renderer.bounds.endLoop()
+
+    expect(loop.startx).toBe(200 - conf.boxMargin)
+    expect(loop.starty).toBe(200 - conf.boxMargin)
+    expect(loop.stopx).toBe(300 + conf.boxMargin)
+    expect(loop.stopy).toBe(300 + conf.boxMargin)
+
+    // Check bounds of second loop
+    loop = renderer.bounds.endLoop()
+
+    expect(loop.startx).toBe(200 - 2 * conf.boxMargin)
+    expect(loop.starty).toBe(200 - 2 * conf.boxMargin)
+    expect(loop.stopx).toBe(300 + 2 * conf.boxMargin)
+    expect(loop.stopy).toBe(300 + 2 * conf.boxMargin)
+
+    // Check bounds of first loop
+    const bounds = renderer.bounds.getBounds()
+
+    expect(bounds.startx).toBe(100)
+    expect(bounds.starty).toBe(100)
+    expect(bounds.stopx).toBe(1000)
+    expect(bounds.stopy).toBe(1000)
+  })
+  it('it should handle a loop that expands the area', function () {
+    renderer.bounds.init()
+
+    renderer.bounds.insert(100, 100, 200, 200)
+    renderer.bounds.verticalPos = 200
+    renderer.bounds.newLoop()
+    renderer.bounds.insert(50, 50, 300, 300)
+
+    const loop = renderer.bounds.endLoop()
+
+    expect(loop.startx).toBe(50 - conf.boxMargin)
+    expect(loop.starty).toBe(50 - conf.boxMargin)
+    expect(loop.stopx).toBe(300 + conf.boxMargin)
+    expect(loop.stopy).toBe(300 + conf.boxMargin)
+
+    // Check bounds after the loop
+    const bounds = renderer.bounds.getBounds()
+
+    expect(bounds.startx).toBe(loop.startx)
+    expect(bounds.starty).toBe(loop.starty)
+    expect(bounds.stopx).toBe(loop.stopx)
+    expect(bounds.stopy).toBe(loop.stopy)
+  })
+})
+
+describe('when rendering a sequenceDiagram', function () {
+  let conf
+  beforeEach(function () {
+    parser.yy = sequenceDb
+    parser.yy.clear()
+
+    conf = {
+      diagramMarginX: 50,
+      diagramMarginY: 10,
+      actorMargin: 50,
+      width: 150,
+      // Height of actor boxes
+      height: 65,
+      boxMargin: 10,
+      messageMargin: 40,
+      boxTextMargin: 15,
+      noteMargin: 25
+    }
+    renderer.setConf(conf)
+  });
+  ['tspan', 'fo', 'old', undefined].forEach(function (textPlacement) {
+    it('it should handle one actor, when textPlacement is ' + textPlacement, function () {
+      renderer.setConf(addConf(conf, 'textPlacement', textPlacement))
+      renderer.bounds.init()
+      const str = 'sequenceDiagram\n' +
+        'participant Alice'
+
+      parser.parse(str)
+      renderer.draw(str, 'tst')
+
+      const bounds = renderer.bounds.getBounds()
+      expect(bounds.startx).toBe(0)
+      expect(bounds.starty).toBe(0)
+      expect(bounds.stopx).toBe(conf.width)
+      expect(bounds.stopy).toBe(conf.height)
+    })
+  })
+  it('it should handle one actor and a centered note', function () {
+    renderer.bounds.init()
+    const str = 'sequenceDiagram\n' +
+      'participant Alice\n' +
+      'Note over Alice: Alice thinks\n'
+
+    parser.parse(str)
+    renderer.draw(str, 'tst')
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(0)
+    expect(bounds.starty).toBe(0)
+    expect(bounds.stopx).toBe(conf.width)
+    // 10 comes from mock of text height
+    expect(bounds.stopy).toBe(conf.height + conf.boxMargin + 2 * conf.noteMargin + 10)
+  })
+  it('it should handle one actor and a note to the left', function () {
+    renderer.bounds.init()
+    const str = 'sequenceDiagram\n' +
+      'participant Alice\n' +
+      'Note left of Alice: Alice thinks'
+
+    parser.parse(str)
+    renderer.draw(str, 'tst')
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(-(conf.width / 2) - (conf.actorMargin / 2))
+    expect(bounds.starty).toBe(0)
+    expect(bounds.stopx).toBe(conf.width)
+    // 10 comes from mock of text height
+    expect(bounds.stopy).toBe(conf.height + conf.boxMargin + 2 * conf.noteMargin + 10)
+  })
+  it('it should handle one actor and a note to the right', function () {
+    renderer.bounds.init()
+    const str = 'sequenceDiagram\n' +
+      'participant Alice\n' +
+      'Note right of Alice: Alice thinks'
+
+    parser.parse(str)
+    renderer.draw(str, 'tst')
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(0)
+    expect(bounds.starty).toBe(0)
+    expect(bounds.stopx).toBe((conf.width / 2) + (conf.actorMargin / 2) + conf.width)
+    // 10 comes from mock of text height
+    expect(bounds.stopy).toBe(conf.height + conf.boxMargin + 2 * conf.noteMargin + 10)
+  })
+  it('it should handle two actors', function () {
+    renderer.bounds.init()
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?'
+
+    parser.parse(str)
+    renderer.draw(str, 'tst')
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(0)
+    expect(bounds.starty).toBe(0)
+    expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin)
+    expect(bounds.stopy).toBe(0 + conf.messageMargin + conf.height)
+  })
+  it('it should handle two actors and two centered shared notes', function () {
+    renderer.bounds.init()
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'Note over Alice,Bob: Looks\n' +
+      'Note over Bob,Alice: Looks back\n'
+
+    parser.parse(str)
+    renderer.draw(str, 'tst')
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(0)
+    expect(bounds.starty).toBe(0)
+    expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin)
+    expect(bounds.stopy).toBe(conf.height + conf.messageMargin + 2 * (conf.boxMargin + 2 * conf.noteMargin + 10))
+  })
+  it('it should draw two actors and two messages', function () {
+    renderer.bounds.init()
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'Bob->Alice: Fine!'
+
+    parser.parse(str)
+    renderer.draw(str, 'tst')
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(0)
+    expect(bounds.starty).toBe(0)
+    expect(bounds.stopx).toBe(0 + conf.width * 2 + conf.actorMargin)
+    expect(bounds.stopy).toBe(0 + 2 * conf.messageMargin + conf.height)
+  })
+  it('it should draw two actors notes to the right', function () {
+    renderer.bounds.init()
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'Note right of Bob: Bob thinks\n' +
+      'Bob->Alice: Fine!'
+
+    parser.parse(str)
+    renderer.draw(str, 'tst')
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(0)
+    expect(bounds.starty).toBe(0)
+
+    const expStopX = conf.actorMargin + conf.width + (conf.width / 2) + conf.noteMargin + conf.width
+
+    expect(bounds.stopx).toBe(expStopX)
+    expect(bounds.stopy).toBe(2 * conf.messageMargin + conf.height + conf.boxMargin + 10 + 2 * conf.noteMargin)
+  })
+  it('it should draw two actors notes to the left', function () {
+    renderer.bounds.init()
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'Note left of Alice: Bob thinks\n' +
+      'Bob->Alice: Fine!'
+
+    parser.parse(str)
+    renderer.draw(str, 'tst')
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(-(conf.width / 2) - (conf.actorMargin / 2))
+    expect(bounds.starty).toBe(0)
+
+    expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin)
+    expect(bounds.stopy).toBe(2 * conf.messageMargin + conf.height + conf.boxMargin + 10 + 2 * conf.noteMargin)
+  })
+  it('it should draw two loops', function () {
+    renderer.bounds.init()
+    const str = 'sequenceDiagram\n' +
+      'Alice->Bob: Hello Bob, how are you?\n' +
+      'loop Cheers\n' +
+      'Bob->Alice: Fine!\n' +
+      'end'
+    parser.parse(str)
+    renderer.draw(str, 'tst')
+
+    const bounds = renderer.bounds.getBounds()
+    expect(bounds.startx).toBe(0)
+    expect(bounds.starty).toBe(0)
+
+    expect(bounds.stopx).toBe(0 + conf.width * 2 + conf.actorMargin)
+    expect(bounds.stopy).toBe(0 + 2 * conf.messageMargin + conf.height + 3 * conf.boxMargin + conf.boxTextMargin)
+  })
+})
+
+describe('when rendering a sequenceDiagram with actor mirror activated', function () {
+  let conf
+  beforeEach(function () {
+    parser.yy = sequenceDb
+    parser.yy.clear()
+
+    conf = {
+      diagramMarginX: 50,
+      diagramMarginY: 10,
+      actorMargin: 50,
+      width: 150,
+      // Height of actor boxes
+      height: 65,
+      boxMargin: 10,
+      messageMargin: 40,
+      boxTextMargin: 15,
+      noteMargin: 25,
+      mirrorActors: true,
+      // Depending on css styling this might need adjustment
+      // Prolongs the edge of the diagram downwards
+      bottomMarginAdj: 1
+    }
+    renderer.setConf(conf)
+  });
+  ['tspan', 'fo', 'old', undefined].forEach(function (textPlacement) {
+    it('it should handle one actor, when textPlacement is' + textPlacement, function () {
+      renderer.setConf(addConf(conf, 'textPlacement', textPlacement))
+      renderer.bounds.init()
+      const str = 'sequenceDiagram\n' +
+        'participant Alice'
+
+      parser.parse(str)
+      renderer.draw(str, 'tst')
+
+      const bounds = renderer.bounds.getBounds()
+      expect(bounds.startx).toBe(0)
+      expect(bounds.starty).toBe(0)
+      expect(bounds.stopx).toBe(conf.width)
+      expect(bounds.stopy).toBe(2 * conf.height + 2 * conf.boxMargin)
+    })
+  })
+})
diff --git a/_submodules/mermaid/src/diagrams/sequence/sequenceRenderer.js b/_submodules/mermaid/src/diagrams/sequence/sequenceRenderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..fcf41d28eabb88c092f29264c50e067cd6c28109
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/sequence/sequenceRenderer.js
@@ -0,0 +1,496 @@
+import * as d3 from 'd3'
+
+import svgDraw from './svgDraw'
+import { logger } from '../../logger'
+import { parser } from './parser/sequenceDiagram'
+import sequenceDb from './sequenceDb'
+
+parser.yy = sequenceDb
+
+const conf = {
+
+  diagramMarginX: 50,
+  diagramMarginY: 30,
+  // Margin between actors
+  actorMargin: 50,
+  // Width of actor boxes
+  width: 150,
+  // Height of actor boxes
+  height: 65,
+  // Margin around loop boxes
+  boxMargin: 10,
+  boxTextMargin: 5,
+  noteMargin: 10,
+  // Space between messages
+  messageMargin: 35,
+  // mirror actors under diagram
+  mirrorActors: false,
+  // Depending on css styling this might need adjustment
+  // Prolongs the edge of the diagram downwards
+  bottomMarginAdj: 1,
+
+  // width of activation box
+  activationWidth: 10,
+
+  // text placement as: tspan | fo | old only text as before
+  textPlacement: 'tspan'
+}
+
+export const bounds = {
+  data: {
+    startx: undefined,
+    stopx: undefined,
+    starty: undefined,
+    stopy: undefined
+  },
+  verticalPos: 0,
+
+  sequenceItems: [],
+  activations: [],
+  init: function () {
+    this.sequenceItems = []
+    this.activations = []
+    this.data = {
+      startx: undefined,
+      stopx: undefined,
+      starty: undefined,
+      stopy: undefined
+    }
+    this.verticalPos = 0
+  },
+  updateVal: function (obj, key, val, fun) {
+    if (typeof obj[key] === 'undefined') {
+      obj[key] = val
+    } else {
+      obj[key] = fun(val, obj[key])
+    }
+  },
+  updateBounds: function (startx, starty, stopx, stopy) {
+    const _self = this
+    let cnt = 0
+    function updateFn (type) {
+      return function updateItemBounds (item) {
+        cnt++
+        // The loop sequenceItems is a stack so the biggest margins in the beginning of the sequenceItems
+        const n = _self.sequenceItems.length - cnt + 1
+
+        _self.updateVal(item, 'starty', starty - n * conf.boxMargin, Math.min)
+        _self.updateVal(item, 'stopy', stopy + n * conf.boxMargin, Math.max)
+
+        _self.updateVal(bounds.data, 'startx', startx - n * conf.boxMargin, Math.min)
+        _self.updateVal(bounds.data, 'stopx', stopx + n * conf.boxMargin, Math.max)
+
+        if (!(type === 'activation')) {
+          _self.updateVal(item, 'startx', startx - n * conf.boxMargin, Math.min)
+          _self.updateVal(item, 'stopx', stopx + n * conf.boxMargin, Math.max)
+
+          _self.updateVal(bounds.data, 'starty', starty - n * conf.boxMargin, Math.min)
+          _self.updateVal(bounds.data, 'stopy', stopy + n * conf.boxMargin, Math.max)
+        }
+      }
+    }
+
+    this.sequenceItems.forEach(updateFn())
+    this.activations.forEach(updateFn('activation'))
+  },
+  insert: function (startx, starty, stopx, stopy) {
+    const _startx = Math.min(startx, stopx)
+    const _stopx = Math.max(startx, stopx)
+    const _starty = Math.min(starty, stopy)
+    const _stopy = Math.max(starty, stopy)
+
+    this.updateVal(bounds.data, 'startx', _startx, Math.min)
+    this.updateVal(bounds.data, 'starty', _starty, Math.min)
+    this.updateVal(bounds.data, 'stopx', _stopx, Math.max)
+    this.updateVal(bounds.data, 'stopy', _stopy, Math.max)
+
+    this.updateBounds(_startx, _starty, _stopx, _stopy)
+  },
+  newActivation: function (message, diagram) {
+    const actorRect = parser.yy.getActors()[message.from.actor]
+    const stackedSize = actorActivations(message.from.actor).length
+    const x = actorRect.x + conf.width / 2 + (stackedSize - 1) * conf.activationWidth / 2
+    this.activations.push({
+      startx: x,
+      starty: this.verticalPos + 2,
+      stopx: x + conf.activationWidth,
+      stopy: undefined,
+      actor: message.from.actor,
+      anchored: svgDraw.anchorElement(diagram)
+    })
+  },
+  endActivation: function (message) {
+    // find most recent activation for given actor
+    const lastActorActivationIdx = this.activations
+      .map(function (activation) { return activation.actor })
+      .lastIndexOf(message.from.actor)
+    const activation = this.activations.splice(lastActorActivationIdx, 1)[0]
+    return activation
+  },
+  newLoop: function (title) {
+    this.sequenceItems.push({ startx: undefined, starty: this.verticalPos, stopx: undefined, stopy: undefined, title: title })
+  },
+  endLoop: function () {
+    const loop = this.sequenceItems.pop()
+    return loop
+  },
+  addSectionToLoop: function (message) {
+    const loop = this.sequenceItems.pop()
+    loop.sections = loop.sections || []
+    loop.sectionTitles = loop.sectionTitles || []
+    loop.sections.push(bounds.getVerticalPos())
+    loop.sectionTitles.push(message)
+    this.sequenceItems.push(loop)
+  },
+  bumpVerticalPos: function (bump) {
+    this.verticalPos = this.verticalPos + bump
+    this.data.stopy = this.verticalPos
+  },
+  getVerticalPos: function () {
+    return this.verticalPos
+  },
+  getBounds: function () {
+    return this.data
+  }
+}
+
+const _drawLongText = (text, x, y, g, width) => {
+  let textHeight = 0
+  const lines = text.split(/<br\/?>/ig)
+  for (const line of lines) {
+    const textObj = svgDraw.getTextObj()
+    textObj.x = x
+    textObj.y = y + textHeight
+    textObj.textMargin = conf.noteMargin
+    textObj.dy = '1em'
+    textObj.text = line
+    textObj.class = 'noteText'
+    const textElem = svgDraw.drawText(g, textObj, width)
+    textHeight += (textElem._groups || textElem)[0][0].getBBox().height
+  }
+  return textHeight
+}
+
+/**
+ * Draws an actor in the diagram with the attaced line
+ * @param center - The center of the the actor
+ * @param pos The position if the actor in the liost of actors
+ * @param description The text in the box
+ */
+const drawNote = function (elem, startx, verticalPos, msg, forceWidth) {
+  const rect = svgDraw.getNoteRect()
+  rect.x = startx
+  rect.y = verticalPos
+  rect.width = forceWidth || conf.width
+  rect.class = 'note'
+
+  let g = elem.append('g')
+  const rectElem = svgDraw.drawRect(g, rect)
+
+  const textHeight = _drawLongText(msg.message, startx - 4, verticalPos + 24, g, rect.width - conf.noteMargin)
+
+  bounds.insert(startx, verticalPos, startx + rect.width, verticalPos + 2 * conf.noteMargin + textHeight)
+  rectElem.attr('height', textHeight + 2 * conf.noteMargin)
+  bounds.bumpVerticalPos(textHeight + 2 * conf.noteMargin)
+}
+
+/**
+ * Draws a message
+ * @param elem
+ * @param startx
+ * @param stopx
+ * @param verticalPos
+ * @param txtCenter
+ * @param msg
+ */
+const drawMessage = function (elem, startx, stopx, verticalPos, msg) {
+  const g = elem.append('g')
+  const txtCenter = startx + (stopx - startx) / 2
+
+  const textElem = g.append('text') // text label for the x axis
+    .attr('x', txtCenter)
+    .attr('y', verticalPos - 7)
+    .style('text-anchor', 'middle')
+    .attr('class', 'messageText')
+    .text(msg.message)
+
+  let textWidth = (textElem._groups || textElem)[0][0].getBBox().width
+
+  let line
+  if (startx === stopx) {
+    line = g.append('path')
+      .attr('d', 'M ' + startx + ',' + verticalPos + ' C ' + (startx + 60) + ',' + (verticalPos - 10) + ' ' + (startx + 60) + ',' +
+      (verticalPos + 30) + ' ' + startx + ',' + (verticalPos + 20))
+
+    bounds.bumpVerticalPos(30)
+    const dx = Math.max(textWidth / 2, 100)
+    bounds.insert(startx - dx, bounds.getVerticalPos() - 10, stopx + dx, bounds.getVerticalPos())
+  } else {
+    line = g.append('line')
+    line.attr('x1', startx)
+    line.attr('y1', verticalPos)
+    line.attr('x2', stopx)
+    line.attr('y2', verticalPos)
+    bounds.insert(startx, bounds.getVerticalPos() - 10, stopx, bounds.getVerticalPos())
+  }
+  // Make an SVG Container
+  // Draw the line
+  if (msg.type === parser.yy.LINETYPE.DOTTED || msg.type === parser.yy.LINETYPE.DOTTED_CROSS || msg.type === parser.yy.LINETYPE.DOTTED_OPEN) {
+    line.style('stroke-dasharray', ('3, 3'))
+    line.attr('class', 'messageLine1')
+  } else {
+    line.attr('class', 'messageLine0')
+  }
+
+  let url = ''
+  if (conf.arrowMarkerAbsolute) {
+    url = window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search
+    url = url.replace(/\(/g, '\\(')
+    url = url.replace(/\)/g, '\\)')
+  }
+
+  line.attr('stroke-width', 2)
+  line.attr('stroke', 'black')
+  line.style('fill', 'none') // remove any fill colour
+  if (msg.type === parser.yy.LINETYPE.SOLID || msg.type === parser.yy.LINETYPE.DOTTED) {
+    line.attr('marker-end', 'url(' + url + '#arrowhead)')
+  }
+
+  if (msg.type === parser.yy.LINETYPE.SOLID_CROSS || msg.type === parser.yy.LINETYPE.DOTTED_CROSS) {
+    line.attr('marker-end', 'url(' + url + '#crosshead)')
+  }
+}
+
+export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
+  // Draw the actors
+  for (let i = 0; i < actorKeys.length; i++) {
+    const key = actorKeys[i]
+
+    // Add some rendering data to the object
+    actors[key].x = i * conf.actorMargin + i * conf.width
+    actors[key].y = verticalPos
+    actors[key].width = conf.diagramMarginX
+    actors[key].height = conf.diagramMarginY
+
+    // Draw the box with the attached line
+    svgDraw.drawActor(diagram, actors[key].x, verticalPos, actors[key].description, conf)
+    bounds.insert(actors[key].x, verticalPos, actors[key].x + conf.width, conf.height)
+  }
+
+  // Add a margin between the actor boxes and the first arrow
+  bounds.bumpVerticalPos(conf.height)
+}
+
+export const setConf = function (cnf) {
+  const keys = Object.keys(cnf)
+
+  keys.forEach(function (key) {
+    conf[key] = cnf[key]
+  })
+}
+
+const actorActivations = function (actor) {
+  return bounds.activations.filter(function (activation) {
+    return activation.actor === actor
+  })
+}
+
+const actorFlowVerticaBounds = function (actor) {
+  // handle multiple stacked activations for same actor
+  const actors = parser.yy.getActors()
+  const activations = actorActivations(actor)
+
+  const left = activations.reduce(function (acc, activation) { return Math.min(acc, activation.startx) }, actors[actor].x + conf.width / 2)
+  const right = activations.reduce(function (acc, activation) { return Math.max(acc, activation.stopx) }, actors[actor].x + conf.width / 2)
+  return [left, right]
+}
+
+/**
+ * Draws a flowchart in the tag with id: id based on the graph definition in text.
+ * @param text
+ * @param id
+ */
+export const draw = function (text, id) {
+  parser.yy.clear()
+  parser.parse(text + '\n')
+
+  bounds.init()
+  const diagram = d3.select(`[id="${id}"]`)
+
+  let startx
+  let stopx
+  let forceWidth
+
+  // Fetch data from the parsing
+  const actors = parser.yy.getActors()
+  const actorKeys = parser.yy.getActorKeys()
+  const messages = parser.yy.getMessages()
+  const title = parser.yy.getTitle()
+  drawActors(diagram, actors, actorKeys, 0)
+
+  // The arrow head definition is attached to the svg once
+  svgDraw.insertArrowHead(diagram)
+  svgDraw.insertArrowCrossHead(diagram)
+
+  function activeEnd (msg, verticalPos) {
+    const activationData = bounds.endActivation(msg)
+    if (activationData.starty + 18 > verticalPos) {
+      activationData.starty = verticalPos - 6
+      verticalPos += 12
+    }
+    svgDraw.drawActivation(diagram, activationData, verticalPos, conf)
+
+    bounds.insert(activationData.startx, verticalPos - 10, activationData.stopx, verticalPos)
+  }
+
+  // const lastMsg
+
+  // Draw the messages/signals
+  messages.forEach(function (msg) {
+    let loopData
+    switch (msg.type) {
+      case parser.yy.LINETYPE.NOTE:
+        bounds.bumpVerticalPos(conf.boxMargin)
+
+        startx = actors[msg.from].x
+        stopx = actors[msg.to].x
+
+        if (msg.placement === parser.yy.PLACEMENT.RIGHTOF) {
+          drawNote(diagram, startx + (conf.width + conf.actorMargin) / 2, bounds.getVerticalPos(), msg)
+        } else if (msg.placement === parser.yy.PLACEMENT.LEFTOF) {
+          drawNote(diagram, startx - (conf.width + conf.actorMargin) / 2, bounds.getVerticalPos(), msg)
+        } else if (msg.to === msg.from) {
+          // Single-actor over
+          drawNote(diagram, startx, bounds.getVerticalPos(), msg)
+        } else {
+          // Multi-actor over
+          forceWidth = Math.abs(startx - stopx) + conf.actorMargin
+          drawNote(diagram, (startx + stopx + conf.width - forceWidth) / 2, bounds.getVerticalPos(), msg,
+            forceWidth)
+        }
+        break
+      case parser.yy.LINETYPE.ACTIVE_START:
+        bounds.newActivation(msg, diagram)
+        break
+      case parser.yy.LINETYPE.ACTIVE_END:
+        activeEnd(msg, bounds.getVerticalPos())
+        break
+      case parser.yy.LINETYPE.LOOP_START:
+        bounds.bumpVerticalPos(conf.boxMargin)
+        bounds.newLoop(msg.message)
+        bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
+        break
+      case parser.yy.LINETYPE.LOOP_END:
+        loopData = bounds.endLoop()
+
+        svgDraw.drawLoop(diagram, loopData, 'loop', conf)
+        bounds.bumpVerticalPos(conf.boxMargin)
+        break
+      case parser.yy.LINETYPE.OPT_START:
+        bounds.bumpVerticalPos(conf.boxMargin)
+        bounds.newLoop(msg.message)
+        bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
+        break
+      case parser.yy.LINETYPE.OPT_END:
+        loopData = bounds.endLoop()
+
+        svgDraw.drawLoop(diagram, loopData, 'opt', conf)
+        bounds.bumpVerticalPos(conf.boxMargin)
+        break
+      case parser.yy.LINETYPE.ALT_START:
+        bounds.bumpVerticalPos(conf.boxMargin)
+        bounds.newLoop(msg.message)
+        bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
+        break
+      case parser.yy.LINETYPE.ALT_ELSE:
+        bounds.bumpVerticalPos(conf.boxMargin)
+        loopData = bounds.addSectionToLoop(msg.message)
+        bounds.bumpVerticalPos(conf.boxMargin)
+        break
+      case parser.yy.LINETYPE.ALT_END:
+        loopData = bounds.endLoop()
+
+        svgDraw.drawLoop(diagram, loopData, 'alt', conf)
+        bounds.bumpVerticalPos(conf.boxMargin)
+        break
+      case parser.yy.LINETYPE.PAR_START:
+        bounds.bumpVerticalPos(conf.boxMargin)
+        bounds.newLoop(msg.message)
+        bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
+        break
+      case parser.yy.LINETYPE.PAR_AND:
+        bounds.bumpVerticalPos(conf.boxMargin)
+        loopData = bounds.addSectionToLoop(msg.message)
+        bounds.bumpVerticalPos(conf.boxMargin)
+        break
+      case parser.yy.LINETYPE.PAR_END:
+        loopData = bounds.endLoop()
+        svgDraw.drawLoop(diagram, loopData, 'par', conf)
+        bounds.bumpVerticalPos(conf.boxMargin)
+        break
+      default:
+        try {
+          // lastMsg = msg
+          bounds.bumpVerticalPos(conf.messageMargin)
+          const fromBounds = actorFlowVerticaBounds(msg.from)
+          const toBounds = actorFlowVerticaBounds(msg.to)
+          const fromIdx = fromBounds[0] <= toBounds[0] ? 1 : 0
+          const toIdx = fromBounds[0] < toBounds[0] ? 0 : 1
+          startx = fromBounds[fromIdx]
+          stopx = toBounds[toIdx]
+
+          const verticalPos = bounds.getVerticalPos()
+          drawMessage(diagram, startx, stopx, verticalPos, msg)
+          const allBounds = fromBounds.concat(toBounds)
+          bounds.insert(Math.min.apply(null, allBounds), verticalPos, Math.max.apply(null, allBounds), verticalPos)
+        } catch (e) {
+          logger.error('error while drawing message', e)
+        }
+    }
+  })
+
+  if (conf.mirrorActors) {
+    // Draw actors below diagram
+    bounds.bumpVerticalPos(conf.boxMargin * 2)
+    drawActors(diagram, actors, actorKeys, bounds.getVerticalPos())
+  }
+
+  const box = bounds.getBounds()
+
+  // Adjust line height of actor lines now that the height of the diagram is known
+  logger.debug('For line height fix Querying: #' + id + ' .actor-line')
+  const actorLines = d3.selectAll('#' + id + ' .actor-line')
+  actorLines.attr('y2', box.stopy)
+
+  let height = box.stopy - box.starty + 2 * conf.diagramMarginY
+  if (conf.mirrorActors) {
+    height = height - conf.boxMargin + conf.bottomMarginAdj
+  }
+
+  const width = (box.stopx - box.startx) + (2 * conf.diagramMarginX)
+
+  if (title) {
+    diagram.append('text')
+      .text(title)
+      .attr('x', ((box.stopx - box.startx) / 2) - (2 * conf.diagramMarginX))
+      .attr('y', -25)
+  }
+
+  if (conf.useMaxWidth) {
+    diagram.attr('height', '100%')
+    diagram.attr('width', '100%')
+    diagram.attr('style', 'max-width:' + (width) + 'px;')
+  } else {
+    diagram.attr('height', height)
+    diagram.attr('width', width)
+  }
+  const extraVertForTitle = title ? 40 : 0
+  diagram.attr('viewBox', (box.startx - conf.diagramMarginX) + ' -' + (conf.diagramMarginY + extraVertForTitle) + ' ' + width + ' ' + (height + extraVertForTitle))
+}
+
+export default {
+  bounds,
+  drawActors,
+  setConf,
+  draw
+}
diff --git a/_submodules/mermaid/src/diagrams/sequence/svgDraw.js b/_submodules/mermaid/src/diagrams/sequence/svgDraw.js
new file mode 100644
index 0000000000000000000000000000000000000000..29dbbafba57237f9ec987edc7c030c2df474dca8
--- /dev/null
+++ b/_submodules/mermaid/src/diagrams/sequence/svgDraw.js
@@ -0,0 +1,313 @@
+export const drawRect = function (elem, rectData) {
+  const rectElem = elem.append('rect')
+  rectElem.attr('x', rectData.x)
+  rectElem.attr('y', rectData.y)
+  rectElem.attr('fill', rectData.fill)
+  rectElem.attr('stroke', rectData.stroke)
+  rectElem.attr('width', rectData.width)
+  rectElem.attr('height', rectData.height)
+  rectElem.attr('rx', rectData.rx)
+  rectElem.attr('ry', rectData.ry)
+
+  if (typeof rectData.class !== 'undefined') {
+    rectElem.attr('class', rectData.class)
+  }
+
+  return rectElem
+}
+
+export const drawText = function (elem, textData, width) {
+  // Remove and ignore br:s
+  const nText = textData.text.replace(/<br\/?>/ig, ' ')
+
+  const textElem = elem.append('text')
+  textElem.attr('x', textData.x)
+  textElem.attr('y', textData.y)
+  textElem.style('text-anchor', textData.anchor)
+  textElem.attr('fill', textData.fill)
+  if (typeof textData.class !== 'undefined') {
+    textElem.attr('class', textData.class)
+  }
+
+  const span = textElem.append('tspan')
+  span.attr('x', textData.x + textData.textMargin * 2)
+  span.attr('fill', textData.fill)
+  span.text(nText)
+
+  return textElem
+}
+
+export const drawLabel = function (elem, txtObject) {
+  function genPoints (x, y, width, height, cut) {
+    return x + ',' + y + ' ' +
+      (x + width) + ',' + y + ' ' +
+      (x + width) + ',' + (y + height - cut) + ' ' +
+      (x + width - cut * 1.2) + ',' + (y + height) + ' ' +
+      (x) + ',' + (y + height)
+  }
+  const polygon = elem.append('polygon')
+  polygon.attr('points', genPoints(txtObject.x, txtObject.y, 50, 20, 7))
+  polygon.attr('class', 'labelBox')
+
+  txtObject.y = txtObject.y + txtObject.labelMargin
+  txtObject.x = txtObject.x + 0.5 * txtObject.labelMargin
+  drawText(elem, txtObject)
+}
+
+let actorCnt = -1
+/**
+ * Draws an actor in the diagram with the attaced line
+ * @param center - The center of the the actor
+ * @param pos The position if the actor in the liost of actors
+ * @param description The text in the box
+ */
+export const drawActor = function (elem, left, verticalPos, description, conf) {
+  const center = left + (conf.width / 2)
+  const g = elem.append('g')
+  if (verticalPos === 0) {
+    actorCnt++
+    g.append('line')
+      .attr('id', 'actor' + actorCnt)
+      .attr('x1', center)
+      .attr('y1', 5)
+      .attr('x2', center)
+      .attr('y2', 2000)
+      .attr('class', 'actor-line')
+      .attr('stroke-width', '0.5px')
+      .attr('stroke', '#999')
+  }
+
+  const rect = getNoteRect()
+  rect.x = left
+  rect.y = verticalPos
+  rect.fill = '#eaeaea'
+  rect.width = conf.width
+  rect.height = conf.height
+  rect.class = 'actor'
+  rect.rx = 3
+  rect.ry = 3
+  drawRect(g, rect)
+
+  _drawTextCandidateFunc(conf)(description, g,
+    rect.x, rect.y, rect.width, rect.height, { 'class': 'actor' })
+}
+
+export const anchorElement = function (elem) {
+  return elem.append('g')
+}
+/**
+ * Draws an actor in the diagram with the attaced line
+ * @param elem - element to append activation rect
+ * @param bounds - activation box bounds
+ * @param verticalPos - precise y cooridnate of bottom activation box edge
+ */
+export const drawActivation = function (elem, bounds, verticalPos) {
+  const rect = getNoteRect()
+  const g = bounds.anchored
+  rect.x = bounds.startx
+  rect.y = bounds.starty
+  rect.fill = '#f4f4f4'
+  rect.width = bounds.stopx - bounds.startx
+  rect.height = verticalPos - bounds.starty
+  drawRect(g, rect)
+}
+
+/**
+ * Draws an actor in the diagram with the attaced line
+ * @param center - The center of the the actor
+ * @param pos The position if the actor in the list of actors
+ * @param description The text in the box
+ */
+export const drawLoop = function (elem, bounds, labelText, conf) {
+  const g = elem.append('g')
+  const drawLoopLine = function (startx, starty, stopx, stopy) {
+    return g.append('line')
+      .attr('x1', startx)
+      .attr('y1', starty)
+      .attr('x2', stopx)
+      .attr('y2', stopy)
+      .attr('class', 'loopLine')
+  }
+  drawLoopLine(bounds.startx, bounds.starty, bounds.stopx, bounds.starty)
+  drawLoopLine(bounds.stopx, bounds.starty, bounds.stopx, bounds.stopy)
+  drawLoopLine(bounds.startx, bounds.stopy, bounds.stopx, bounds.stopy)
+  drawLoopLine(bounds.startx, bounds.starty, bounds.startx, bounds.stopy)
+  if (typeof bounds.sections !== 'undefined') {
+    bounds.sections.forEach(function (item) {
+      drawLoopLine(bounds.startx, item, bounds.stopx, item).style('stroke-dasharray', '3, 3')
+    })
+  }
+
+  let txt = getTextObj()
+  txt.text = labelText
+  txt.x = bounds.startx
+  txt.y = bounds.starty
+  txt.labelMargin = 1.5 * 10 // This is the small box that says "loop"
+  txt.class = 'labelText' // Its size & position are fixed.
+
+  drawLabel(g, txt)
+
+  txt = getTextObj()
+  txt.text = '[ ' + bounds.title + ' ]'
+  txt.x = bounds.startx + (bounds.stopx - bounds.startx) / 2
+  txt.y = bounds.starty + 1.5 * conf.boxMargin
+  txt.anchor = 'middle'
+  txt.class = 'loopText'
+
+  drawText(g, txt)
+
+  if (typeof bounds.sectionTitles !== 'undefined') {
+    bounds.sectionTitles.forEach(function (item, idx) {
+      if (item !== '') {
+        txt.text = '[ ' + item + ' ]'
+        txt.y = bounds.sections[idx] + 1.5 * conf.boxMargin
+        drawText(g, txt)
+      }
+    })
+  }
+}
+
+/**
+ * Setup arrow head and define the marker. The result is appended to the svg.
+ */
+export const insertArrowHead = function (elem) {
+  elem.append('defs').append('marker')
+    .attr('id', 'arrowhead')
+    .attr('refX', 5)
+    .attr('refY', 2)
+    .attr('markerWidth', 6)
+    .attr('markerHeight', 4)
+    .attr('orient', 'auto')
+    .append('path')
+    .attr('d', 'M 0,0 V 4 L6,2 Z') // this is actual shape for arrowhead
+}
+/**
+ * Setup arrow head and define the marker. The result is appended to the svg.
+ */
+export const insertArrowCrossHead = function (elem) {
+  const defs = elem.append('defs')
+  const marker = defs.append('marker')
+    .attr('id', 'crosshead')
+    .attr('markerWidth', 15)
+    .attr('markerHeight', 8)
+    .attr('orient', 'auto')
+    .attr('refX', 16)
+    .attr('refY', 4)
+
+  // The arrow
+  marker.append('path')
+    .attr('fill', 'black')
+    .attr('stroke', '#000000')
+    .style('stroke-dasharray', ('0, 0'))
+    .attr('stroke-width', '1px')
+    .attr('d', 'M 9,2 V 6 L16,4 Z')
+
+  // The cross
+  marker.append('path')
+    .attr('fill', 'none')
+    .attr('stroke', '#000000')
+    .style('stroke-dasharray', ('0, 0'))
+    .attr('stroke-width', '1px')
+    .attr('d', 'M 0,1 L 6,7 M 6,1 L 0,7')
+  // this is actual shape for arrowhead
+}
+
+export const getTextObj = function () {
+  const txt = {
+    x: 0,
+    y: 0,
+    'fill': 'black',
+    'text-anchor': 'start',
+    style: '#666',
+    width: 100,
+    height: 100,
+    textMargin: 0,
+    rx: 0,
+    ry: 0
+  }
+  return txt
+}
+
+export const getNoteRect = function () {
+  const rect = {
+    x: 0,
+    y: 0,
+    fill: '#EDF2AE',
+    stroke: '#666',
+    width: 100,
+    anchor: 'start',
+    height: 100,
+    rx: 0,
+    ry: 0
+  }
+  return rect
+}
+
+const _drawTextCandidateFunc = (function () {
+  function byText (content, g, x, y, width, height, textAttrs) {
+    const text = g.append('text')
+      .attr('x', x + width / 2).attr('y', y + height / 2 + 5)
+      .style('text-anchor', 'middle')
+      .text(content)
+    _setTextAttrs(text, textAttrs)
+  }
+
+  function byTspan (content, g, x, y, width, height, textAttrs) {
+    const text = g.append('text')
+      .attr('x', x + width / 2).attr('y', y)
+      .style('text-anchor', 'middle')
+    text.append('tspan')
+      .attr('x', x + width / 2).attr('dy', '0')
+      .text(content)
+
+    text.attr('y', y + height / 2.0)
+      .attr('dominant-baseline', 'central')
+      .attr('alignment-baseline', 'central')
+
+    _setTextAttrs(text, textAttrs)
+  }
+
+  function byFo (content, g, x, y, width, height, textAttrs) {
+    const s = g.append('switch')
+    const f = s.append('foreignObject')
+      .attr('x', x).attr('y', y)
+      .attr('width', width).attr('height', height)
+
+    const text = f.append('div').style('display', 'table')
+      .style('height', '100%').style('width', '100%')
+
+    text.append('div').style('display', 'table-cell')
+      .style('text-align', 'center').style('vertical-align', 'middle')
+      .text(content)
+
+    byTspan(content, s, x, y, width, height, textAttrs)
+    _setTextAttrs(text, textAttrs)
+  }
+
+  function _setTextAttrs (toText, fromTextAttrsDict) {
+    for (const key in fromTextAttrsDict) {
+      if (fromTextAttrsDict.hasOwnProperty(key)) {
+        toText.attr(key, fromTextAttrsDict[key])
+      }
+    }
+  }
+
+  return function (conf) {
+    return conf.textPlacement === 'fo' ? byFo : (
+      conf.textPlacement === 'old' ? byText : byTspan)
+  }
+})()
+
+export default {
+  drawRect,
+  drawText,
+  drawLabel,
+  drawActor,
+  anchorElement,
+  drawActivation,
+  drawLoop,
+  insertArrowHead,
+  insertArrowCrossHead,
+  getTextObj,
+  getNoteRect
+}
diff --git a/_submodules/mermaid/src/logger.js b/_submodules/mermaid/src/logger.js
new file mode 100644
index 0000000000000000000000000000000000000000..adaadf31d34554bec1e13a0aacda57cfbf3703b3
--- /dev/null
+++ b/_submodules/mermaid/src/logger.js
@@ -0,0 +1,45 @@
+import moment from 'moment'
+
+export const LEVELS = {
+  debug: 1,
+  info: 2,
+  warn: 3,
+  error: 4,
+  fatal: 5
+}
+
+export const logger = {
+  debug: () => {},
+  info: () => {},
+  warn: () => {},
+  error: () => {},
+  fatal: () => {}
+}
+
+export const setLogLevel = function (level) {
+  logger.debug = () => {}
+  logger.info = () => {}
+  logger.warn = () => {}
+  logger.error = () => {}
+  logger.fatal = () => {}
+  if (level <= LEVELS.fatal) {
+    logger.fatal = console.log.bind(console, '\x1b[35m', format('FATAL'))
+  }
+  if (level <= LEVELS.error) {
+    logger.error = console.log.bind(console, '\x1b[31m', format('ERROR'))
+  }
+  if (level <= LEVELS.warn) {
+    logger.warn = console.log.bind(console, `\x1b[33m`, format('WARN'))
+  }
+  if (level <= LEVELS.info) {
+    logger.info = console.log.bind(console, '\x1b[34m', format('INFO'))
+  }
+  if (level <= LEVELS.debug) {
+    logger.debug = console.log.bind(console, '\x1b[32m', format('DEBUG'))
+  }
+}
+
+const format = (level) => {
+  const time = moment().format('HH:mm:ss.SSS')
+  return `${time} : ${level} : `
+}
diff --git a/_submodules/mermaid/src/mermaid.js b/_submodules/mermaid/src/mermaid.js
new file mode 100644
index 0000000000000000000000000000000000000000..a8f05ffea1df32ef3a8f80bf9112b3fe30a199b8
--- /dev/null
+++ b/_submodules/mermaid/src/mermaid.js
@@ -0,0 +1,167 @@
+/**
+ * Web page integration module for the mermaid framework. It uses the mermaidAPI for mermaid functionality and to render
+ * the diagrams to svg code.
+ */
+import he from 'he'
+
+import mermaidAPI from './mermaidAPI'
+import { logger } from './logger'
+
+/**
+ * ## init
+ * Function that goes through the document to find the chart definitions in there and render them.
+ *
+ * The function tags the processed attributes with the attribute data-processed and ignores found elements with the
+ * attribute already set. This way the init function can be triggered several times.
+ *
+ * Optionally, `init` can accept in the second argument one of the following:
+ * - a DOM Node
+ * - an array of DOM nodes (as would come from a jQuery selector)
+ * - a W3C selector, a la `.mermaid`
+ *
+ * ```mermaid
+ * graph LR;
+ *  a(Find elements)-->b{Processed}
+ *  b-->|Yes|c(Leave element)
+ *  b-->|No |d(Transform)
+ * ```
+ * Renders the mermaid diagrams
+ * @param nodes a css selector or an array of nodes
+ */
+const init = function () {
+  const conf = mermaidAPI.getConfig()
+  logger.debug('Starting rendering diagrams')
+  let nodes
+  if (arguments.length >= 2) {
+    /*! sequence config was passed as #1 */
+    if (typeof arguments[0] !== 'undefined') {
+      mermaid.sequenceConfig = arguments[0]
+    }
+
+    nodes = arguments[1]
+  } else {
+    nodes = arguments[0]
+  }
+
+  // if last argument is a function this is the callback function
+  let callback
+  if (typeof arguments[arguments.length - 1] === 'function') {
+    callback = arguments[arguments.length - 1]
+    logger.debug('Callback function found')
+  } else {
+    if (typeof conf.mermaid !== 'undefined') {
+      if (typeof conf.mermaid.callback === 'function') {
+        callback = conf.mermaid.callback
+        logger.debug('Callback function found')
+      } else {
+        logger.debug('No Callback function found')
+      }
+    }
+  }
+  nodes = nodes === undefined ? document.querySelectorAll('.mermaid')
+    : typeof nodes === 'string' ? document.querySelectorAll(nodes)
+      : nodes instanceof window.Node ? [nodes]
+        : nodes // Last case  - sequence config was passed pick next
+
+  logger.debug('Start On Load before: ' + mermaid.startOnLoad)
+  if (typeof mermaid.startOnLoad !== 'undefined') {
+    logger.debug('Start On Load inner: ' + mermaid.startOnLoad)
+    mermaidAPI.initialize({ startOnLoad: mermaid.startOnLoad })
+  }
+
+  if (typeof mermaid.ganttConfig !== 'undefined') {
+    mermaidAPI.initialize({ gantt: mermaid.ganttConfig })
+  }
+
+  let txt
+
+  for (let i = 0; i < nodes.length; i++) {
+    const element = nodes[i]
+
+    /*! Check if previously processed */
+    if (!element.getAttribute('data-processed')) {
+      element.setAttribute('data-processed', true)
+    } else {
+      continue
+    }
+
+    const id = `mermaid-${Date.now()}`
+
+    // Fetch the graph definition including tags
+    txt = element.innerHTML
+
+    // transforms the html to pure text
+    txt = he.decode(txt).trim().replace(/<br>/ig, '<br/>')
+
+    mermaidAPI.render(id, txt, (svgCode, bindFunctions) => {
+      element.innerHTML = svgCode
+      if (typeof callback !== 'undefined') {
+        callback(id)
+      }
+      bindFunctions(element)
+    }, element)
+  }
+}
+
+const initialize = function (config) {
+  logger.debug('Initializing mermaid')
+  if (typeof config.mermaid !== 'undefined') {
+    if (typeof config.mermaid.startOnLoad !== 'undefined') {
+      mermaid.startOnLoad = config.mermaid.startOnLoad
+    }
+    if (typeof config.mermaid.htmlLabels !== 'undefined') {
+      mermaid.htmlLabels = config.mermaid.htmlLabels
+    }
+  }
+  mermaidAPI.initialize(config)
+}
+
+/**
+ * ##contentLoaded
+ * Callback function that is called when page is loaded. This functions fetches configuration for mermaid rendering and
+ * calls init for rendering the mermaid diagrams on the page.
+ */
+const contentLoaded = function () {
+  let config
+
+  if (mermaid.startOnLoad) {
+    // No config found, do check API config
+    config = mermaidAPI.getConfig()
+    if (config.startOnLoad) {
+      mermaid.init()
+    }
+  } else {
+    if (typeof mermaid.startOnLoad === 'undefined') {
+      logger.debug('In start, no config')
+      config = mermaidAPI.getConfig()
+      if (config.startOnLoad) {
+        mermaid.init()
+      }
+    }
+  }
+}
+
+if (typeof document !== 'undefined') {
+  /*!
+   * Wait for document loaded before starting the execution
+   */
+  window.addEventListener('load', function () {
+    contentLoaded()
+  }, false)
+}
+
+const mermaid = {
+  startOnLoad: true,
+  htmlLabels: true,
+
+  mermaidAPI,
+  parse: mermaidAPI.parse,
+  render: mermaidAPI.render,
+
+  init,
+  initialize,
+
+  contentLoaded
+}
+
+export default mermaid
diff --git a/_submodules/mermaid/src/mermaid.spec.js b/_submodules/mermaid/src/mermaid.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..996ad21f56078261dad2752e45bd629f1f159569
--- /dev/null
+++ b/_submodules/mermaid/src/mermaid.spec.js
@@ -0,0 +1,217 @@
+/* eslint-env jasmine */
+import mermaid from './mermaid'
+import flowDb from './diagrams/flowchart/flowDb'
+import flowParser from './diagrams/flowchart/parser/flow'
+import flowRenderer from './diagrams/flowchart/flowRenderer'
+
+describe('when using mermaid and ', function () {
+  describe('when detecting chart type ', function () {
+    it('should not start rendering with mermaid.startOnLoad set to false', function () {
+      mermaid.startOnLoad = false
+      document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'
+      spyOn(mermaid, 'init')
+      mermaid.contentLoaded()
+      expect(mermaid.init).not.toHaveBeenCalled()
+    })
+
+    it('should start rendering with both startOnLoad set', function () {
+      mermaid.startOnLoad = true
+      document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'
+      spyOn(mermaid, 'init')
+      mermaid.contentLoaded()
+      expect(mermaid.init).toHaveBeenCalled()
+    })
+
+    it('should start rendering with mermaid.startOnLoad', function () {
+      mermaid.startOnLoad = true
+      document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'
+      spyOn(mermaid, 'init')
+      mermaid.contentLoaded()
+      expect(mermaid.init).toHaveBeenCalled()
+    })
+
+    it('should start rendering as a default with no changes performed', function () {
+      document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'
+      spyOn(mermaid, 'init')
+      mermaid.contentLoaded()
+      expect(mermaid.init).toHaveBeenCalled()
+    })
+  })
+
+  describe('when calling addEdges ', function () {
+    beforeEach(function () {
+      flowParser.parser.yy = flowDb
+      flowDb.clear()
+    })
+    it('it should handle edges with text', function () {
+      flowParser.parser.parse('graph TD;A-->|text ex|B;')
+      flowParser.parser.yy.getVertices()
+      const edges = flowParser.parser.yy.getEdges()
+
+      const mockG = {
+        setEdge: function (start, end, options) {
+          expect(start).toBe('A')
+          expect(end).toBe('B')
+          expect(options.arrowhead).toBe('normal')
+          expect(options.label.match('text ex')).toBeTruthy()
+        }
+      }
+
+      flowRenderer.addEdges(edges, mockG)
+    })
+
+    it('should handle edges without text', function () {
+      flowParser.parser.parse('graph TD;A-->B;')
+      flowParser.parser.yy.getVertices()
+      const edges = flowParser.parser.yy.getEdges()
+
+      const mockG = {
+        setEdge: function (start, end, options) {
+          expect(start).toBe('A')
+          expect(end).toBe('B')
+          expect(options.arrowhead).toBe('normal')
+        }
+      }
+
+      flowRenderer.addEdges(edges, mockG)
+    })
+
+    it('should handle open-ended edges', function () {
+      flowParser.parser.parse('graph TD;A---B;')
+      flowParser.parser.yy.getVertices()
+      const edges = flowParser.parser.yy.getEdges()
+
+      const mockG = {
+        setEdge: function (start, end, options) {
+          expect(start).toBe('A')
+          expect(end).toBe('B')
+          expect(options.arrowhead).toBe('none')
+        }
+      }
+
+      flowRenderer.addEdges(edges, mockG)
+    })
+
+    it('should handle edges with styles defined', function () {
+      flowParser.parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;')
+      flowParser.parser.yy.getVertices()
+      const edges = flowParser.parser.yy.getEdges()
+
+      const mockG = {
+        setEdge: function (start, end, options) {
+          expect(start).toBe('A')
+          expect(end).toBe('B')
+          expect(options.arrowhead).toBe('none')
+          expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;')
+        }
+      }
+
+      flowRenderer.addEdges(edges, mockG)
+    })
+    it('should handle edges with interpolation defined', function () {
+      flowParser.parser.parse('graph TD;A---B; linkStyle 0 interpolate basis')
+      flowParser.parser.yy.getVertices()
+      const edges = flowParser.parser.yy.getEdges()
+
+      const mockG = {
+        setEdge: function (start, end, options) {
+          expect(start).toBe('A')
+          expect(end).toBe('B')
+          expect(options.arrowhead).toBe('none')
+          expect(options.curve).toBe('basis') // mocked as string
+        }
+      }
+
+      flowRenderer.addEdges(edges, mockG)
+    })
+    it('should handle edges with text and styles defined', function () {
+      flowParser.parser.parse('graph TD;A---|the text|B; linkStyle 0 stroke:val1,stroke-width:val2;')
+      flowParser.parser.yy.getVertices()
+      const edges = flowParser.parser.yy.getEdges()
+
+      const mockG = {
+        setEdge: function (start, end, options) {
+          expect(start).toBe('A')
+          expect(end).toBe('B')
+          expect(options.arrowhead).toBe('none')
+          expect(options.label.match('the text')).toBeTruthy()
+          expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;')
+        }
+      }
+
+      flowRenderer.addEdges(edges, mockG)
+    })
+
+    it('should set fill to "none" by default when handling edges', function () {
+      flowParser.parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;')
+      flowParser.parser.yy.getVertices()
+      const edges = flowParser.parser.yy.getEdges()
+
+      const mockG = {
+        setEdge: function (start, end, options) {
+          expect(start).toBe('A')
+          expect(end).toBe('B')
+          expect(options.arrowhead).toBe('none')
+          expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;')
+        }
+      }
+
+      flowRenderer.addEdges(edges, mockG)
+    })
+
+    it('should not set fill to none if fill is set in linkStyle', function () {
+      flowParser.parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2,fill:blue;')
+      flowParser.parser.yy.getVertices()
+      const edges = flowParser.parser.yy.getEdges()
+      const mockG = {
+        setEdge: function (start, end, options) {
+          expect(start).toBe('A')
+          expect(end).toBe('B')
+          expect(options.arrowhead).toBe('none')
+          expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:blue;')
+        }
+      }
+
+      flowRenderer.addEdges(edges, mockG)
+    })
+  })
+
+  describe('checking validity of input ', function () {
+    it('it should throw for an invalid definiton', function () {
+      expect(() => mermaid.parse('this is not a mermaid diagram definition')).toThrow()
+    })
+
+    it('it should not throw for a valid flow definition', function () {
+      expect(() => mermaid.parse('graph TD;A--x|text including URL space|B;')).not.toThrow()
+    })
+    it('it should throw for an invalid flow definition', function () {
+      expect(() => mermaid.parse('graph TQ;A--x|text including URL space|B;')).toThrow()
+    })
+
+    it('it should not throw for a valid sequenceDiagram definition', function () {
+      const text = 'sequenceDiagram\n' +
+        'Alice->Bob: Hello Bob, how are you?\n\n' +
+        '%% Comment\n' +
+        'Note right of Bob: Bob thinks\n' +
+        'alt isWell\n\n' +
+        'Bob-->Alice: I am good thanks!\n' +
+        'else isSick\n' +
+        'Bob-->Alice: Feel sick...\n' +
+        'end'
+      expect(() => mermaid.parse(text)).not.toThrow()
+    })
+
+    it('it should throw for an invalid sequenceDiagram definition', function () {
+      const text = 'sequenceDiagram\n' +
+        'Alice:->Bob: Hello Bob, how are you?\n\n' +
+        '%% Comment\n' +
+        'Note right of Bob: Bob thinks\n' +
+        'alt isWell\n\n' +
+        'Bob-->Alice: I am good thanks!\n' +
+        'else isSick\n' +
+        'Bob-->Alice: Feel sick...\n' +
+        'end'
+      expect(() => mermaid.parse(text)).toThrow()
+    })
+  })
+})
diff --git a/_submodules/mermaid/src/mermaidAPI.js b/_submodules/mermaid/src/mermaidAPI.js
new file mode 100644
index 0000000000000000000000000000000000000000..d40c336c4290e95d9b84bd817e910fb68028fc8f
--- /dev/null
+++ b/_submodules/mermaid/src/mermaidAPI.js
@@ -0,0 +1,487 @@
+/**
+ * ---
+ * title: mermaidAPI
+ * order: 5
+ * ---
+ * # mermaidAPI
+ * This is the api to be used when handling the integration with the web page instead of using the default integration
+ * (mermaid.js).
+ *
+ * The core of this api is the **render** function that given a graph definitionas text renders the graph/diagram and
+ * returns a svg element for the graph. It is is then up to the user of the API to make use of the svg, either insert it
+ * somewhere in the page or something completely different.
+*/
+import * as d3 from 'd3'
+import scope from 'scope-css'
+
+import { logger, setLogLevel } from './logger'
+import utils from './utils'
+import flowRenderer from './diagrams/flowchart/flowRenderer'
+import flowParser from './diagrams/flowchart/parser/flow'
+import flowDb from './diagrams/flowchart/flowDb'
+import sequenceRenderer from './diagrams/sequence/sequenceRenderer'
+import sequenceParser from './diagrams/sequence/parser/sequenceDiagram'
+import sequenceDb from './diagrams/sequence/sequenceDb'
+import ganttRenderer from './diagrams/gantt/ganttRenderer'
+import ganttParser from './diagrams/gantt/parser/gantt'
+import ganttDb from './diagrams/gantt/ganttDb'
+import classRenderer from './diagrams/class/classRenderer'
+import classParser from './diagrams/class/parser/classDiagram'
+import classDb from './diagrams/class/classDb'
+import gitGraphRenderer from './diagrams/git/gitGraphRenderer'
+import gitGraphParser from './diagrams/git/parser/gitGraph'
+import gitGraphAst from './diagrams/git/gitGraphAst'
+
+const themes = {}
+for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
+  themes[themeName] = require(`./themes/${themeName}/index.scss`)
+}
+
+/**
+ * ## Configuration
+ * These are the default options which can be overridden with the initialization call as in the example below:
+ * ```
+ * mermaid.initialize({
+ *   flowchart:{
+ *      htmlLabels: false
+ *   }
+ * });
+ * ```
+ */
+const config = {
+  theme: 'default',
+  themeCSS: undefined,
+
+  /**
+   * logLevel , decides the amount of logging to be used.
+   *    * debug: 1
+   *    * info: 2
+   *    * warn: 3
+   *    * error: 4
+   *    * fatal: 5
+   */
+  logLevel: 5,
+
+  /**
+   * **startOnLoad** - This options controls whether or mermaid starts when the page loads
+   */
+  startOnLoad: true,
+
+  /**
+   * **arrowMarkerAbsolute** - This options controls whether or arrow markers in html code will be absolute paths or
+   * an anchor, #. This matters if you are using base tag settings.
+   */
+  arrowMarkerAbsolute: false,
+
+  /**
+   * ### flowchart
+   * *The object containing configurations specific for flowcharts*
+   */
+  flowchart: {
+    /**
+     * **htmlLabels** - Flag for setting whether or not a html tag should be used for rendering labels
+     * on the edges
+     */
+    htmlLabels: true,
+
+    curve: 'linear'
+  },
+
+  /**
+   * ###  sequenceDiagram
+   * The object containing configurations specific for sequence diagrams
+   */
+  sequence: {
+
+    /**
+     * **diagramMarginX** - margin to the right and left of the sequence diagram
+     */
+    diagramMarginX: 50,
+
+    /**
+     * **diagramMarginY** - margin to the over and under the sequence diagram
+     */
+    diagramMarginY: 10,
+
+    /**
+     * **actorMargin** - Margin between actors
+     */
+    actorMargin: 50,
+
+    /**
+     * **width** - Width of actor boxes
+     */
+    width: 150,
+
+    /**
+     * **height** - Height of actor boxes
+     */
+    height: 65,
+
+    /**
+     * **boxMargin** - Margin around loop boxes
+     */
+    boxMargin: 10,
+
+    /**
+     * **boxTextMargin** - margin around the text in loop/alt/opt boxes
+     */
+    boxTextMargin: 5,
+
+    /**
+     * **noteMargin** - margin around notes
+     */
+    noteMargin: 10,
+
+    /**
+     * **messageMargin** - Space between messages
+     */
+    messageMargin: 35,
+
+    /**
+     * **mirrorActors** - mirror actors under diagram
+     */
+    mirrorActors: true,
+
+    /**
+     * **bottomMarginAdj** - Depending on css styling this might need adjustment.
+     * Prolongs the edge of the diagram downwards
+     */
+    bottomMarginAdj: 1,
+
+    /**
+     * **useMaxWidth** - when this flag is set the height and width is set to 100% and is then scaling with the
+     * available space if not the absolute space required is used
+     */
+    useMaxWidth: true
+  },
+
+  /** ### gantt
+   * The object containing configurations specific for gantt diagrams*
+   */
+  gantt: {
+    /**
+     * **titleTopMargin** - margin top for the text over the gantt diagram
+     */
+    titleTopMargin: 25,
+
+    /**
+     * **barHeight** - the height of the bars in the graph
+     */
+    barHeight: 20,
+
+    /**
+     * **barGap** - the margin between the different activities in the gantt diagram
+     */
+    barGap: 4,
+
+    /**
+     *  **topPadding** - margin between title and gantt diagram and between axis and gantt diagram.
+     */
+    topPadding: 50,
+
+    /**
+     *  **leftPadding** - the space allocated for the section name to the left of the activities.
+     */
+    leftPadding: 75,
+
+    /**
+     *  **gridLineStartPadding** - Vertical starting position of the grid lines
+     */
+    gridLineStartPadding: 35,
+
+    /**
+     *  **fontSize** - font size ...
+     */
+    fontSize: 11,
+
+    /**
+     * **fontFamily** - font family ...
+     */
+    fontFamily: '"Open-Sans", "sans-serif"',
+
+    /**
+     * **numberSectionStyles** - the number of alternating section styles
+     */
+    numberSectionStyles: 4,
+
+    /**
+     * **axisFormat** - datetime format of the axis, this might need adjustment to match your locale and preferences
+     */
+    axisFormat: '%Y-%m-%d'
+  },
+  class: {},
+  git: {}
+}
+
+setLogLevel(config.logLevel)
+
+function parse (text) {
+  const graphType = utils.detectType(text)
+  let parser
+
+  switch (graphType) {
+    case 'git':
+      parser = gitGraphParser
+      parser.parser.yy = gitGraphAst
+      break
+    case 'flowchart':
+      parser = flowParser
+      parser.parser.yy = flowDb
+      break
+    case 'sequence':
+      parser = sequenceParser
+      parser.parser.yy = sequenceDb
+      break
+    case 'gantt':
+      parser = ganttParser
+      parser.parser.yy = ganttDb
+      break
+    case 'class':
+      parser = classParser
+      parser.parser.yy = classDb
+      break
+  }
+
+  parser.parser.yy.parseError = (str, hash) => {
+    const error = { str, hash }
+    throw error
+  }
+
+  parser.parse(text)
+}
+
+export const encodeEntities = function (text) {
+  let txt = text
+
+  txt = txt.replace(/style.*:\S*#.*;/g, function (s) {
+    const innerTxt = s.substring(0, s.length - 1)
+    return innerTxt
+  })
+  txt = txt.replace(/classDef.*:\S*#.*;/g, function (s) {
+    const innerTxt = s.substring(0, s.length - 1)
+    return innerTxt
+  })
+
+  txt = txt.replace(/#\w+;/g, function (s) {
+    const innerTxt = s.substring(1, s.length - 1)
+
+    const isInt = /^\+?\d+$/.test(innerTxt)
+    if (isInt) {
+      return 'fl°°' + innerTxt + '¶ß'
+    } else {
+      return 'fl°' + innerTxt + '¶ß'
+    }
+  })
+
+  return txt
+}
+
+export const decodeEntities = function (text) {
+  let txt = text
+
+  txt = txt.replace(/fl°°/g, function () {
+    return '&#'
+  })
+  txt = txt.replace(/fl°/g, function () {
+    return '&'
+  })
+  txt = txt.replace(/¶ß/g, function () {
+    return ';'
+  })
+
+  return txt
+}
+/**
+ * ##render
+ * Function that renders an svg with a graph from a chart definition. Usage example below.
+ *
+ * ```
+ * mermaidAPI.initialize({
+ *      startOnLoad:true
+ *  });
+ *  $(function(){
+ *      const graphDefinition = 'graph TB\na-->b';
+ *      const cb = function(svgGraph){
+ *          console.log(svgGraph);
+ *      };
+ *      mermaidAPI.render('id1',graphDefinition,cb);
+ *  });
+ *```
+ * @param id the id of the element to be rendered
+ * @param txt the graph definition
+ * @param cb callback which is called after rendering is finished with the svg code as inparam.
+ * @param container selector to element in which a div with the graph temporarily will be inserted. In one is
+ * provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
+ * completed.
+ */
+const render = function (id, txt, cb, container) {
+  if (typeof container !== 'undefined') {
+    container.innerHTML = ''
+
+    d3.select(container).append('div')
+      .attr('id', 'd' + id)
+      .append('svg')
+      .attr('id', id)
+      .attr('width', '100%')
+      .attr('xmlns', 'http://www.w3.org/2000/svg')
+      .append('g')
+  } else {
+    const element = document.querySelector('#' + 'd' + id)
+    if (element) {
+      element.innerHTML = ''
+    }
+
+    d3.select('body').append('div')
+      .attr('id', 'd' + id)
+      .append('svg')
+      .attr('id', id)
+      .attr('width', '100%')
+      .attr('xmlns', 'http://www.w3.org/2000/svg')
+      .append('g')
+  }
+
+  window.txt = txt
+  txt = encodeEntities(txt)
+
+  const element = d3.select('#d' + id).node()
+  const graphType = utils.detectType(txt)
+
+  // insert inline style into svg
+  const svg = element.firstChild
+  const firstChild = svg.firstChild
+
+  // pre-defined theme
+  let style = themes[config.theme]
+  if (style === undefined) {
+    style = ''
+  }
+
+  // user provided theme CSS
+  if (config.themeCSS !== undefined) {
+    style += `\n${config.themeCSS}`
+  }
+
+  // classDef
+  if (graphType === 'flowchart') {
+    const classes = flowRenderer.getClasses(txt)
+    for (const className in classes) {
+      style += `\n.${className} > * { ${classes[className].styles.join(' !important; ')} !important; }`
+    }
+  }
+
+  const style1 = document.createElement('style')
+  style1.innerHTML = scope(style, `#${id}`)
+  svg.insertBefore(style1, firstChild)
+
+  const style2 = document.createElement('style')
+  const cs = window.getComputedStyle(svg)
+  style2.innerHTML = `#${id} {
+    color: ${cs.color};
+    font: ${cs.font};
+  }`
+  svg.insertBefore(style2, firstChild)
+
+  switch (graphType) {
+    case 'git':
+      config.flowchart.arrowMarkerAbsolute = config.arrowMarkerAbsolute
+      gitGraphRenderer.setConf(config.git)
+      gitGraphRenderer.draw(txt, id, false)
+      break
+    case 'flowchart':
+      config.flowchart.arrowMarkerAbsolute = config.arrowMarkerAbsolute
+      flowRenderer.setConf(config.flowchart)
+      flowRenderer.draw(txt, id, false)
+      break
+    case 'sequence':
+      config.sequence.arrowMarkerAbsolute = config.arrowMarkerAbsolute
+      if (config.sequenceDiagram) { // backwards compatibility
+        sequenceRenderer.setConf(Object.assign(config.sequence, config.sequenceDiagram))
+        console.error('`mermaid config.sequenceDiagram` has been renamed to `config.sequence`. Please update your mermaid config.')
+      } else {
+        sequenceRenderer.setConf(config.sequence)
+      }
+      sequenceRenderer.draw(txt, id)
+      break
+    case 'gantt':
+      config.gantt.arrowMarkerAbsolute = config.arrowMarkerAbsolute
+      ganttRenderer.setConf(config.gantt)
+      ganttRenderer.draw(txt, id)
+      break
+    case 'class':
+      config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute
+      classRenderer.setConf(config.class)
+      classRenderer.draw(txt, id)
+      break
+  }
+
+  d3.select(`[id="${id}"]`).selectAll('foreignobject > *').attr('xmlns', 'http://www.w3.org/1999/xhtml')
+
+  let url = ''
+  if (config.arrowMarkerAbsolute) {
+    url = window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search
+    url = url.replace(/\(/g, '\\(')
+    url = url.replace(/\)/g, '\\)')
+  }
+
+  // Fix for when the base tag is used
+  let svgCode = d3.select('#d' + id).node().innerHTML.replace(/url\(#arrowhead/g, 'url(' + url + '#arrowhead', 'g')
+
+  svgCode = decodeEntities(svgCode)
+
+  if (typeof cb !== 'undefined') {
+    cb(svgCode, flowDb.bindFunctions)
+  } else {
+    logger.warn('CB = undefined!')
+  }
+
+  const node = d3.select('#d' + id).node()
+  if (node !== null && typeof node.remove === 'function') {
+    d3.select('#d' + id).node().remove()
+  }
+
+  return svgCode
+}
+
+const setConf = function (cnf) {
+  // Top level initially mermaid, gflow, sequenceDiagram and gantt
+  const lvl1Keys = Object.keys(cnf)
+  for (let i = 0; i < lvl1Keys.length; i++) {
+    if (typeof cnf[lvl1Keys[i]] === 'object' && cnf[lvl1Keys[i]] != null) {
+      const lvl2Keys = Object.keys(cnf[lvl1Keys[i]])
+
+      for (let j = 0; j < lvl2Keys.length; j++) {
+        logger.debug('Setting conf ', lvl1Keys[i], '-', lvl2Keys[j])
+        if (typeof config[lvl1Keys[i]] === 'undefined') {
+          config[lvl1Keys[i]] = {}
+        }
+        logger.debug('Setting config: ' + lvl1Keys[i] + ' ' + lvl2Keys[j] + ' to ' + cnf[lvl1Keys[i]][lvl2Keys[j]])
+        config[lvl1Keys[i]][lvl2Keys[j]] = cnf[lvl1Keys[i]][lvl2Keys[j]]
+      }
+    } else {
+      config[lvl1Keys[i]] = cnf[lvl1Keys[i]]
+    }
+  }
+}
+
+function initialize (options) {
+  logger.debug('Initializing mermaidAPI')
+  // Update default config with options supplied at initialization
+  if (typeof options === 'object') {
+    setConf(options)
+  }
+  setLogLevel(config.logLevel)
+}
+
+function getConfig () {
+  return config
+}
+
+const mermaidAPI = {
+  render,
+  parse,
+  initialize,
+  getConfig
+}
+
+export default mermaidAPI
diff --git a/_submodules/mermaid/src/mermaidAPI.spec.js b/_submodules/mermaid/src/mermaidAPI.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..51749677cd7090172b2dec138d96e71f5d365d73
--- /dev/null
+++ b/_submodules/mermaid/src/mermaidAPI.spec.js
@@ -0,0 +1,45 @@
+/* eslint-env jasmine */
+import mermaidAPI from './mermaidAPI'
+
+describe('when using mermaidAPI and ', function () {
+  describe('doing initialize ', function () {
+    beforeEach(function () {
+      document.body.innerHTML = ''
+    })
+
+    it('should copy a literal into the configuration', function () {
+      const orgConfig = mermaidAPI.getConfig()
+      expect(orgConfig.testLiteral).toBe(undefined)
+
+      mermaidAPI.initialize({ 'testLiteral': true })
+      const config = mermaidAPI.getConfig()
+
+      expect(config.testLiteral).toBe(true)
+    })
+    it('should copy a an object into the configuration', function () {
+      const orgConfig = mermaidAPI.getConfig()
+      expect(orgConfig.testObject).toBe(undefined)
+
+      const object = {
+        test1: 1,
+        test2: false
+      }
+
+      mermaidAPI.initialize({ 'testObject': object })
+      mermaidAPI.initialize({ 'testObject': { 'test3': true } })
+      const config = mermaidAPI.getConfig()
+
+      expect(config.testObject.test1).toBe(1)
+      expect(config.testObject.test2).toBe(false)
+      expect(config.testObject.test3).toBe(true)
+    })
+  })
+  describe('checking validity of input ', function () {
+    it('it should throw for an invalid definiton', function () {
+      expect(() => mermaidAPI.parse('this is not a mermaid diagram definition')).toThrow()
+    })
+    it('it should not throw for a valid definiton', function () {
+      expect(() => mermaidAPI.parse('graph TD;A--x|text including URL space|B;')).not.toThrow()
+    })
+  })
+})
diff --git a/_submodules/mermaid/src/themes/class.scss b/_submodules/mermaid/src/themes/class.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5e7de91503d085c62ad225d75c6edf8633eb00ed
--- /dev/null
+++ b/_submodules/mermaid/src/themes/class.scss
@@ -0,0 +1,78 @@
+g.classGroup text {
+  fill: $nodeBorder;
+  stroke: none;
+  font-family: 'trebuchet ms', verdana, arial;
+  font-size: 10px;
+}
+
+g.classGroup rect {
+  fill: $nodeBkg;
+  stroke: $nodeBorder;
+}
+
+g.classGroup line {
+  stroke: $nodeBorder;
+  stroke-width: 1;
+}
+
+.classLabel .box {
+  stroke: none;
+  stroke-width: 0;
+  fill: $nodeBkg;
+  opacity: 0.5;
+}
+
+.classLabel .label {
+  fill: $nodeBorder;
+  font-size: 10px;
+}
+
+.relation {
+  stroke: $nodeBorder;
+  stroke-width: 1;
+  fill: none;
+}
+
+@mixin composition {
+  fill: $nodeBorder;
+  stroke: $nodeBorder;
+  stroke-width: 1;
+}
+
+#compositionStart {
+  @include composition;
+}
+
+#compositionEnd {
+  @include composition;
+}
+
+@mixin aggregation {
+  fill: $nodeBkg;
+  stroke: $nodeBorder;
+  stroke-width: 1;
+}
+
+#aggregationStart {
+  @include aggregation;
+}
+
+#aggregationEnd {
+  @include aggregation;
+}
+
+#dependencyStart {
+  @include composition;
+}
+
+#dependencyEnd {
+  @include composition;
+}
+
+#extensionStart {
+  @include composition;
+}
+
+#extensionEnd {
+  @include composition;
+}
diff --git a/_submodules/mermaid/src/themes/default/index.scss b/_submodules/mermaid/src/themes/default/index.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e98fc0741e34cc34363934f6db1489a5bfc2db4a
--- /dev/null
+++ b/_submodules/mermaid/src/themes/default/index.scss
@@ -0,0 +1,52 @@
+$mainBkg: #ECECFF;
+$secondBkg: #ffffde;
+$lineColor: #333333;
+$border1: #CCCCFF;
+$border2: #aaaa33;
+$arrowheadColor: #333333;
+
+/* Flowchart variables */
+
+$nodeBkg: $mainBkg;
+$nodeBorder: #9370DB;
+$clusterBkg: $secondBkg;
+$clusterBorder: $border2;
+$defaultLinkColor: $lineColor;
+$titleColor: #333;
+$edgeLabelBackground: #e8e8e8;
+
+/* Sequence Diagram variables */
+
+$actorBorder: $border1;
+$actorBkg: $mainBkg;
+$actorTextColor: black;
+$actorLineColor: grey;
+$signalColor: #333;
+$signalTextColor: #333;
+$labelBoxBkgColor: $actorBkg;
+$labelBoxBorderColor: $actorBorder;
+$labelTextColor: $actorTextColor;
+$noteBorderColor: $border2;
+$noteBkgColor: #fff5ad;
+
+/* Gantt chart variables */
+
+$sectionBkgColor: rgba(102, 102, 255, 0.49);
+$altSectionBkgColor: white;
+$sectionBkgColor2: #fff400;
+$taskBorderColor: #534fbc;
+$taskBkgColor: #8a90dd;
+$taskTextLightColor: white;
+$taskTextColor: $taskTextLightColor;
+$taskTextDarkColor: black;
+$taskTextOutsideColor: $taskTextDarkColor;
+$activeTaskBorderColor: #534fbc;
+$activeTaskBkgColor: #bfc7ff;
+$gridColor: lightgrey;
+$doneTaskBkgColor: lightgrey;
+$doneTaskBorderColor: grey;
+$critBorderColor: #ff8888;
+$critBkgColor: red;
+$todayLineColor: red;
+
+@import '../mermaid';
diff --git a/_submodules/mermaid/src/themes/flowchart.scss b/_submodules/mermaid/src/themes/flowchart.scss
new file mode 100644
index 0000000000000000000000000000000000000000..edafcf5e75e4d52906c291cce0703878ec92454e
--- /dev/null
+++ b/_submodules/mermaid/src/themes/flowchart.scss
@@ -0,0 +1,54 @@
+.label {
+  font-family: 'trebuchet ms', verdana, arial;
+  color: #333;
+}
+
+.node rect,
+.node circle,
+.node ellipse,
+.node polygon {
+  fill: $mainBkg;
+  stroke: $nodeBorder;
+  stroke-width: 1px;
+}
+
+.node.clickable {
+  cursor: pointer;
+}
+
+.arrowheadPath {
+  fill: $arrowheadColor;
+}
+
+.edgePath .path {
+  stroke: $lineColor;
+  stroke-width: 1.5px;
+}
+
+.edgeLabel {
+  background-color: $edgeLabelBackground;
+}
+
+.cluster rect {
+  fill: $secondBkg !important;
+  stroke: $clusterBorder !important;
+  stroke-width: 1px !important;
+}
+
+.cluster text {
+  fill: $titleColor;
+}
+
+div.mermaidTooltip {
+  position: absolute;
+  text-align: center;
+  max-width: 200px;
+  padding: 2px;
+  font-family: 'trebuchet ms', verdana, arial;
+  font-size: 12px;
+  background: $secondBkg;
+  border: 1px solid $border2;
+  border-radius: 2px;
+  pointer-events: none;
+  z-index: 100;
+}
diff --git a/_submodules/mermaid/src/themes/forest/index.scss b/_submodules/mermaid/src/themes/forest/index.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f6e1a1dae7fd6c12d6533d8e7fa8914c764d943a
--- /dev/null
+++ b/_submodules/mermaid/src/themes/forest/index.scss
@@ -0,0 +1,53 @@
+$mainBkg: #cde498;
+$secondBkg: #cdffb2;
+$lineColor: #1a3318;
+$lineColor: green;
+$border1: #13540c;
+$border2: #6eaa49;
+$arrowheadColor: green;
+
+/* Flowchart variables */
+
+$nodeBkg: $mainBkg;
+$nodeBorder: $border1;
+$clusterBkg: $secondBkg;
+$clusterBorder: $border2;
+$defaultLinkColor: $lineColor;
+$titleColor: #333;
+$edgeLabelBackground: #e8e8e8;
+
+/* Sequence Diagram variables */
+
+$actorBorder: $border1;
+$actorBkg: $mainBkg;
+$actorTextColor: black;
+$actorLineColor: grey;
+$signalColor: #333;
+$signalTextColor: #333;
+$labelBoxBkgColor: $actorBkg;
+$labelBoxBorderColor: #326932;
+$labelTextColor: $actorTextColor;
+$noteBorderColor: $border2;
+$noteBkgColor: #fff5ad;
+
+/* Gantt chart variables */
+
+$sectionBkgColor: #6eaa49;
+$altSectionBkgColor: white;
+$sectionBkgColor2: #6eaa49;
+$taskBorderColor: $border1;
+$taskBkgColor: #487e3a;
+$taskTextLightColor: white;
+$taskTextColor: $taskTextLightColor;
+$taskTextDarkColor: black;
+$taskTextOutsideColor: $taskTextDarkColor;
+$activeTaskBorderColor: $taskBorderColor;
+$activeTaskBkgColor: $mainBkg;
+$gridColor: lightgrey;
+$doneTaskBkgColor: lightgrey;
+$doneTaskBorderColor: grey;
+$critBorderColor: #ff8888;
+$critBkgColor: red;
+$todayLineColor: red;
+
+@import '../mermaid';
diff --git a/_submodules/mermaid/src/themes/gantt.scss b/_submodules/mermaid/src/themes/gantt.scss
new file mode 100644
index 0000000000000000000000000000000000000000..6793135ec854b44e26f15d006c1a5cb151f5d1f8
--- /dev/null
+++ b/_submodules/mermaid/src/themes/gantt.scss
@@ -0,0 +1,209 @@
+/** Section styling */
+
+.section {
+  stroke: none;
+  opacity: 0.2;
+}
+
+.section0 {
+  fill: $sectionBkgColor;
+}
+
+.section2 {
+  fill: $sectionBkgColor2;
+}
+
+.section1,
+.section3 {
+  fill: $altSectionBkgColor;
+  opacity: 0.2;
+}
+
+.sectionTitle0 {
+  fill: $titleColor;
+}
+
+.sectionTitle1 {
+  fill: $titleColor;
+}
+
+.sectionTitle2 {
+  fill: $titleColor;
+}
+
+.sectionTitle3 {
+  fill: $titleColor;
+}
+
+.sectionTitle {
+  text-anchor: start;
+  font-size: 11px;
+  text-height: 14px;
+}
+
+
+/* Grid and axis */
+
+.grid .tick {
+  stroke: $gridColor;
+  opacity: 0.3;
+  shape-rendering: crispEdges;
+}
+
+.grid path {
+  stroke-width: 0;
+}
+
+
+/* Today line */
+
+.today {
+  fill: none;
+  stroke: $todayLineColor;
+  stroke-width: 2px;
+}
+
+
+/* Task styling */
+
+
+/* Default task */
+
+.task {
+  stroke-width: 2;
+}
+
+.taskText {
+  text-anchor: middle;
+  font-size: 11px;
+}
+
+.taskTextOutsideRight {
+  fill: $taskTextDarkColor;
+  text-anchor: start;
+  font-size: 11px;
+}
+
+.taskTextOutsideLeft {
+  fill: $taskTextDarkColor;
+  text-anchor: end;
+  font-size: 11px;
+}
+
+
+/* Specific task settings for the sections*/
+
+.taskText0,
+.taskText1,
+.taskText2,
+.taskText3 {
+  fill: $taskTextColor;
+}
+
+.task0,
+.task1,
+.task2,
+.task3 {
+  fill: $taskBkgColor;
+  stroke: $taskBorderColor;
+}
+
+.taskTextOutside0,
+.taskTextOutside2,
+{
+  fill: $taskTextOutsideColor;
+}
+
+.taskTextOutside1,
+.taskTextOutside3 {
+  fill: $taskTextOutsideColor;
+}
+
+
+/* Active task */
+
+.active0,
+.active1,
+.active2,
+.active3 {
+  fill: $activeTaskBkgColor;
+  stroke: $activeTaskBorderColor;
+}
+
+.activeText0,
+.activeText1,
+.activeText2,
+.activeText3 {
+  fill: $taskTextDarkColor !important;
+}
+
+
+/* Completed task */
+
+.done0,
+.done1,
+.done2,
+.done3 {
+  stroke: $doneTaskBorderColor;
+  fill: $doneTaskBkgColor;
+  stroke-width: 2;
+}
+
+.doneText0,
+.doneText1,
+.doneText2,
+.doneText3 {
+  fill: $taskTextDarkColor !important;
+}
+
+
+/* Tasks on the critical line */
+
+.crit0,
+.crit1,
+.crit2,
+.crit3 {
+  stroke: $critBorderColor;
+  fill: $critBkgColor;
+  stroke-width: 2;
+}
+
+.activeCrit0,
+.activeCrit1,
+.activeCrit2,
+.activeCrit3 {
+  stroke: $critBorderColor;
+  fill: $activeTaskBkgColor;
+  stroke-width: 2;
+}
+
+.doneCrit0,
+.doneCrit1,
+.doneCrit2,
+.doneCrit3 {
+  stroke: $critBorderColor;
+  fill: $doneTaskBkgColor;
+  stroke-width: 2;
+  cursor: pointer;
+  shape-rendering: crispEdges;
+}
+
+.doneCritText0,
+.doneCritText1,
+.doneCritText2,
+.doneCritText3 {
+  fill: $taskTextDarkColor !important;
+}
+
+.activeCritText0,
+.activeCritText1,
+.activeCritText2,
+.activeCritText3 {
+  fill: $taskTextDarkColor !important;
+}
+
+.titleText {
+  text-anchor: middle;
+  font-size: 18px;
+  fill: $taskTextDarkColor;
+}
diff --git a/_submodules/mermaid/src/themes/git.scss b/_submodules/mermaid/src/themes/git.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f4072ef6526819fb162af3b02146ceb4f79f8dcb
--- /dev/null
+++ b/_submodules/mermaid/src/themes/git.scss
@@ -0,0 +1,6 @@
+.commit-id,
+.commit-msg,
+.branch-label {
+  fill: lightgrey;
+  color: lightgrey;
+}
diff --git a/_submodules/mermaid/src/themes/mermaid.scss b/_submodules/mermaid/src/themes/mermaid.scss
new file mode 100644
index 0000000000000000000000000000000000000000..9a46f5142e06ce354a2dad513acc5a678360c314
--- /dev/null
+++ b/_submodules/mermaid/src/themes/mermaid.scss
@@ -0,0 +1,5 @@
+@import 'flowchart';
+@import 'sequence';
+@import 'gantt';
+@import 'class';
+@import 'git';
diff --git a/_submodules/mermaid/src/themes/neutral/index.scss b/_submodules/mermaid/src/themes/neutral/index.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f0560142f0d87358ce74356362e49fd805f818b1
--- /dev/null
+++ b/_submodules/mermaid/src/themes/neutral/index.scss
@@ -0,0 +1,57 @@
+$mainBkg: #eee;
+$contrast: #26a;
+$secondBkg: lighten($contrast, 55%);
+$lineColor: #666;
+$border1: #999;
+$border2: $contrast;
+$note: #ffa;
+$text: #333;
+$critical: #d42;
+$done: #bbb;
+$arrowheadColor: #333333;
+
+/* Flowchart variables */
+
+$nodeBkg: $mainBkg;
+$nodeBorder: $border1;
+$clusterBkg: $secondBkg;
+$clusterBorder: $border2;
+$defaultLinkColor: $lineColor;
+$titleColor: $text;
+$edgeLabelBackground: white;
+
+/* Sequence Diagram variables */
+
+$actorBorder: $border1;
+$actorBkg: $mainBkg;
+$actorTextColor: $text;
+$actorLineColor: $lineColor;
+$signalColor: $text;
+$signalTextColor: $text;
+$labelBoxBkgColor: $actorBkg;
+$labelBoxBorderColor: $actorBorder;
+$labelTextColor: white;
+$noteBorderColor: darken($note, 60%);
+$noteBkgColor: $note;
+
+/* Gantt chart variables */
+
+$sectionBkgColor: lighten($contrast, 30%);
+$altSectionBkgColor: white;
+$sectionBkgColor2: lighten($contrast, 30%);
+$taskBorderColor: darken($contrast, 10%);
+$taskBkgColor: $contrast;
+$taskTextLightColor: white;
+$taskTextColor: $taskTextLightColor;
+$taskTextDarkColor: $text;
+$taskTextOutsideColor: $taskTextDarkColor;
+$activeTaskBorderColor: $taskBorderColor;
+$activeTaskBkgColor: $mainBkg;
+$gridColor: lighten($border1, 30%);
+$doneTaskBkgColor: $done;
+$doneTaskBorderColor: $lineColor;
+$critBkgColor: $critical;
+$critBorderColor: darken($critBkgColor, 10%);
+$todayLineColor: $critBkgColor;
+
+@import '../mermaid';
diff --git a/_submodules/mermaid/src/themes/sequence.scss b/_submodules/mermaid/src/themes/sequence.scss
new file mode 100644
index 0000000000000000000000000000000000000000..780e8ecf5dc74059d24730fe46a01937667c99f8
--- /dev/null
+++ b/_submodules/mermaid/src/themes/sequence.scss
@@ -0,0 +1,75 @@
+.actor {
+  stroke: $actorBorder;
+  fill: $actorBkg;
+}
+
+text.actor {
+  fill: $actorTextColor;
+  stroke: none;
+}
+
+.actor-line {
+  stroke: $actorLineColor;
+}
+
+.messageLine0 {
+  stroke-width: 1.5;
+  stroke-dasharray: '2 2';
+  marker-end: 'url(#arrowhead)';
+  stroke: $signalColor;
+}
+
+.messageLine1 {
+  stroke-width: 1.5;
+  stroke-dasharray: '2 2';
+  stroke: $signalColor;
+}
+
+#arrowhead {
+  fill: $signalColor;
+}
+
+#crosshead path {
+  fill: $signalColor !important;
+  stroke: $signalColor !important;
+}
+
+.messageText {
+  fill: $signalTextColor;
+  stroke: none;
+}
+
+.labelBox {
+  stroke: $labelBoxBorderColor;
+  fill: $labelBoxBkgColor;
+}
+
+.labelText {
+  fill: $labelTextColor;
+  stroke: none;
+}
+
+.loopText {
+  fill: $labelTextColor;
+  stroke: none;
+}
+
+.loopLine {
+  stroke-width: 2;
+  stroke-dasharray: '2 2';
+  marker-end: 'url(#arrowhead)';
+  stroke: $labelBoxBorderColor;
+}
+
+.note {
+  //stroke: #decc93;
+  stroke: $noteBorderColor;
+  fill: $noteBkgColor;
+}
+
+.noteText {
+  fill: black;
+  stroke: none;
+  font-family: 'trebuchet ms', verdana, arial;
+  font-size: 14px;
+}
diff --git a/_submodules/mermaid/src/utils.js b/_submodules/mermaid/src/utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..a18bed14a61f89aef49dc423976d83cc0dfa59bf
--- /dev/null
+++ b/_submodules/mermaid/src/utils.js
@@ -0,0 +1,66 @@
+import * as d3 from 'd3'
+
+/**
+ * @function detectType
+ * Detects the type of the graph text.
+ * ```mermaid
+ * graph LR
+ *  a-->b
+ *  b-->c
+ *  c-->d
+ *  d-->e
+ *  e-->f
+ *  f-->g
+ *  g-->h
+ * ```
+ *
+ * @param {string} text The text defining the graph
+ * @returns {string} A graph definition key
+ */
+export const detectType = function (text) {
+  text = text.replace(/^\s*%%.*\n/g, '\n')
+  if (text.match(/^\s*sequenceDiagram/)) {
+    return 'sequence'
+  }
+
+  if (text.match(/^\s*gantt/)) {
+    return 'gantt'
+  }
+
+  if (text.match(/^\s*classDiagram/)) {
+    return 'class'
+  }
+
+  if (text.match(/^\s*gitGraph/)) {
+    return 'git'
+  }
+  return 'flowchart'
+}
+
+/**
+ * @function isSubstringInArray
+ * Detects whether a substring in present in a given array
+ * @param {string} str The substring to detect
+ * @param {array} arr The array to search
+ * @returns {number} the array index containing the substring or -1 if not present
+ **/
+export const isSubstringInArray = function (str, arr) {
+  for (let i = 0; i < arr.length; i++) {
+    if (arr[i].match(str)) return i
+  }
+  return -1
+}
+
+export const interpolateToCurve = (interpolate, defaultCurve) => {
+  if (!interpolate) {
+    return defaultCurve
+  }
+  const curveName = `curve${interpolate.charAt(0).toUpperCase() + interpolate.slice(1)}`
+  return d3[curveName] || defaultCurve
+}
+
+export default {
+  detectType,
+  isSubstringInArray,
+  interpolateToCurve
+}
diff --git a/_submodules/mermaid/src/utils.spec.js b/_submodules/mermaid/src/utils.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..43341fb6d116b0a8f9af03ee6a3b33b9964c1d59
--- /dev/null
+++ b/_submodules/mermaid/src/utils.spec.js
@@ -0,0 +1,39 @@
+/* eslint-env jasmine */
+import utils from './utils'
+
+describe('when detecting chart type ', function () {
+  it('should handle a graph defintion', function () {
+    const str = 'graph TB\nbfs1:queue'
+    const type = utils.detectType(str)
+    expect(type).toBe('flowchart')
+  })
+  it('should handle a graph defintion with leading spaces', function () {
+    const str = '    graph TB\nbfs1:queue'
+    const type = utils.detectType(str)
+    expect(type).toBe('flowchart')
+  })
+
+  it('should handle a graph defintion with leading spaces and newline', function () {
+    const str = '  \n  graph TB\nbfs1:queue'
+    const type = utils.detectType(str)
+    expect(type).toBe('flowchart')
+  })
+  it('should handle a graph defintion for gitGraph', function () {
+    const str = '  \n  gitGraph TB:\nbfs1:queue'
+    const type = utils.detectType(str)
+    expect(type).toBe('git')
+  })
+})
+
+describe('when finding substring in array ', function () {
+  it('should return the array index that contains the substring', function () {
+    const arr = ['stroke:val1', 'fill:val2']
+    const result = utils.isSubstringInArray('fill', arr)
+    expect(result).toEqual(1)
+  })
+  it('should return -1 if the substring is not found in the array', function () {
+    const arr = ['stroke:val1', 'stroke-width:val2']
+    const result = utils.isSubstringInArray('fill', arr)
+    expect(result).toEqual(-1)
+  })
+})
diff --git a/_submodules/mermaid/todo.md b/_submodules/mermaid/todo.md
new file mode 100644
index 0000000000000000000000000000000000000000..615856d5a1626e59b1974b30ef2d904717e9fa93
--- /dev/null
+++ b/_submodules/mermaid/todo.md
@@ -0,0 +1,4 @@
+- Get familar with jison
+- git graph requires a blank line at the end. why?
+- Create a desktop client
+- Do the rendering in an iframe to avoid global CSS to affect rendering.
diff --git a/_submodules/mermaid/webpack.config.babel.js b/_submodules/mermaid/webpack.config.babel.js
new file mode 100644
index 0000000000000000000000000000000000000000..73faab7840d7fcfa6826a556c55aa9319c270c60
--- /dev/null
+++ b/_submodules/mermaid/webpack.config.babel.js
@@ -0,0 +1,11 @@
+import nodeExternals from 'webpack-node-externals'
+
+import { jsConfig } from './webpack.config.base'
+
+const config = jsConfig()
+
+const coreConfig = jsConfig()
+coreConfig.externals = [nodeExternals()]
+coreConfig.output.filename = '[name].core.js'
+
+export default [config, coreConfig]
diff --git a/_submodules/mermaid/webpack.config.base.js b/_submodules/mermaid/webpack.config.base.js
new file mode 100644
index 0000000000000000000000000000000000000000..727880fd64d4c649df1bb8bd6b3ac0b4aff3663f
--- /dev/null
+++ b/_submodules/mermaid/webpack.config.base.js
@@ -0,0 +1,48 @@
+import path from 'path'
+
+const amdRule = {
+  parser: {
+    amd: false // https://github.com/lodash/lodash/issues/3052
+  }
+}
+
+const jsRule = {
+  test: /\.js$/,
+  exclude: /node_modules/,
+  use: {
+    loader: 'babel-loader'
+  }
+}
+
+const scssRule = { // load scss to string
+  test: /\.scss$/,
+  use: [
+    { loader: 'css-to-string-loader' },
+    { loader: 'css-loader' },
+    { loader: 'sass-loader' }
+  ]
+}
+
+export const jsConfig = () => {
+  return {
+    mode: 'development',
+    target: 'web',
+    entry: {
+      mermaid: './src/mermaid.js'
+    },
+    node: {
+      fs: 'empty' // jison generated code requires 'fs'
+    },
+    output: {
+      path: path.join(__dirname, './dist/'),
+      filename: '[name].js',
+      library: 'mermaid',
+      libraryTarget: 'umd',
+      libraryExport: 'default'
+    },
+    module: {
+      rules: [amdRule, jsRule, scssRule]
+    },
+    devtool: 'source-map'
+  }
+}
diff --git a/_submodules/mermaid/webpack.config.prod.babel.js b/_submodules/mermaid/webpack.config.prod.babel.js
new file mode 100644
index 0000000000000000000000000000000000000000..9ae00d575982af91b85916bc0c9a73df49bf4d36
--- /dev/null
+++ b/_submodules/mermaid/webpack.config.prod.babel.js
@@ -0,0 +1,7 @@
+import { jsConfig } from './webpack.config.base'
+
+const minConfig = jsConfig()
+minConfig.mode = 'production'
+minConfig.output.filename = '[name].min.js'
+
+export default [minConfig]
diff --git a/assets/img/favicon.ico b/assets/img/favicon.ico
index 3de0ab46720fce42c4729feab729cefe27aa7dcf..ab8b4f414a5efddf26f976d9ce62347ade1d9567 100644
Binary files a/assets/img/favicon.ico and b/assets/img/favicon.ico differ
diff --git a/slides/unused/img/georgia-1.png b/slides/unused/img/georgia-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..56e7e858a0a813f8a34fd16feab46ba3f4ee072e
Binary files /dev/null and b/slides/unused/img/georgia-1.png differ
diff --git a/slides/unused/img/georgia-2.png b/slides/unused/img/georgia-2.png
new file mode 100644
index 0000000000000000000000000000000000000000..a373894d49636bf80f364e46d11bf32209670719
Binary files /dev/null and b/slides/unused/img/georgia-2.png differ
diff --git a/slides/wk01/hci-intro.html b/slides/wk01/hci-intro.html
new file mode 100644
index 0000000000000000000000000000000000000000..ec15a5fa39ec71cde5c490d9625fc46a02cc66c9
--- /dev/null
+++ b/slides/wk01/hci-intro.html
@@ -0,0 +1,228 @@
+---
+layout: presentation
+title: Introduction to HCI --Week 1, Monday--
+description: High Level Introduction to HCI for Interaction Programming, CSE 340
+class: middle, center, inverse
+---
+name: inverse
+layout: true
+class: center, middle, inverse
+---
+
+# Introduction to Human Computer Interaction
+
+CSE 340 {{site.quarter}}
+
+---
+layout: false
+
+#  Human-Computer Interaction (HCI)
+
+A science of the artificial
+- Human
+ - Anyone impacted by the existence of the program
+ - End users
+- Computer
+ - Artificial thing human is interacting with
+- Interaction
+ - How the artificial stuff is actually used
+- 48% of app code [Myers & Rosson, CHI'92]; probably more now!
+
+---
+.left-column-half[
+![:img Couple with cordless telegraph apparatus,100%](img/hci-intro/mobile-telegraph.png)
+]
+.right-column-half[
+Robida's vision of a cordless telegraph (1906)
+]
+---
+.left-column-half[
+![:img Two women using video phones,100%](img/hci-intro/videophones.png)
+]
+.right-column-half[
+Commercial vision of a wireless private video phone  (1929)
+]
+---
+.left-column-half[
+![:img Vannevar Bush's imagined Memex--a desk with mechanical and digital innards,100%](img/hci-intro/memex.png)
+]
+
+.right-column-half[
+His article: [As we may think](http://web.mit.edu/STS.035/www/PDFs/think.pdf)
+
+.quote[The MEMEX 'is a desk that can instantly bring files and material on
+any subject to the operators fingertips....' (Bush, 1945, 'As We May
+Think', Atlantic Monthly)]
+]
+
+---
+.left-column-half[
+![:img A prototype of a wearable wristwatch/cellphone and Dick Tracy using it in a stamp based on the comic strip, 100%](img/hci-intro/watches.png)
+]
+.right-column-half[
+A wrist-watch cellphone prototype (1947), which captured popular imagination as shown in this Dick Tracy stamp
+]
+---
+.left-column-half[
+![:img A young man sitting at a complex machine covered in buttons--drawing on the screen with a pen, 100%](img/hci-intro/sutherland.png)
+]
+.right-column-half[
+Ivan Sutherland. 1964. The sketchpad graphical communication system in action.
+.font-small[In Proceedings of the SHARE design automation workshop (DAC '64). ACM, New York, NY, USA, 6.329-6.346.]
+]
+---
+# Sutherland  Inspires Engelbart
+
+.left-column-half[
+![:img Two images -- a man giving a live demo of a text based
+    interface; and a wooden computer mouse.,
+80%](img/hci-intro/engelbart.png)
+![:youtube Engelbart's *Mother of all Demos*. His presentation included windows;
+hypertext; graphics; video conferencing; the computer mouse; word
+processing; dynamic file linking; revision control; collaborative
+real-time editing; all new inventions!, B6rKUf9DWRI]
+]
+.right-column-half[
+Engelbart's *Mother of all Demos* His presentation included many all new inventions:
+- windows
+- hypertext
+- graphics
+- video conferencing
+- the computer mouse
+- word processing
+- dynamic file linking
+- revision control
+- collaborative real-time editing
+]
+.footnote[
+December 9 1968 at the
+Fall Joint Computer Conference.
+]
+---
+# [1970s](https://www.computerhistory.org/timeline/1970/) starts the computer revolution 
+
+.left-column-half[
+![:img Picture of two people with long hair and glasses interacting with a computer kiosk in Lepold's Records, 80%](img/hci-intro/community-memory.jpg)
+]
+.right-column-half[
+- 1971: Email is invented by Ray Tomlinson.
+- 1973: The first commercial graphical user interface is introduced in 1973 on the Xerox Alto. The modern GUI is later popularized by the Xerox Star and Apple Lisa (but Xerox never made money on this)
+- 1973: The first capacitive touchscreen is developed at CERN.
+]
+---
+# [1980s](https://www.computerhistory.org/timeline/1980/): Wave 1 of HCI: Personal Computing (mobile and desktops) 
+
+.left-column-half[
+![:img LEFT: Picture of peter lewis holding a large cellphone with Harry Brock - President of Metrocell in 1982. RIGHT: Close-up of Peter T. Lewis in 2015 - a smiling African-American man in a pink shirt holding his glasses,60%](img/hci-intro/Lewis.png)<br>
+![:img A desktop personal computer showing a WYSIWYG
+word processor and graphical window system, 25%](img/hci-intro/smalltalk-interface.png)
+![:img A picture of Adele Goldberg inventor of Smalltalk-80, 30%](img/hci-intro/goldberg.jpg)
+]
+.right-column-half[
+
+- 1984: The first commercially available cell phone, the DynaTAC 8000X, is created by Motorola.
+- 1985: "The term 'Internet of Things' was first conceptualized, coined, and published by Peter T. Lewis in a speech to the Congressional Black Caucus ([Chetan Sharma](http://www.chetansharma.com/correcting-the-iot-history/))<sup>1</sup>
+- Personal computers hit the mass market
+  - User interface toolkits were invented (Smalltalk 80, invented by Adele Goldberg, is the first)
+]
+.footnote[<sup>1</sup> This history was erased and instead attributed to a white man until very recently]
+
+
+---
+# [1990s](https://www.computerhistory.org/timeline/1990/): Wave 2: Collaboration & communication 
+
+.left-column-half[
+![:img A picture of Hiroshi Ishii in a suit and tie and Roz Picard in a black pantsuit touching a physical computing device hooked up to circuit boards with wires, 30%](img/hci-intro/ishii.jpg)
+<br>.font-small[Hiroshi Ishii and Roz Picard at the MIT Media lab in 1998, from [Ben Shneiderman's archive of HCI Pioneers](https://hcipioneers.wordpress.com/portfolio/ishii-hiroshi/)]
+]
+.right-column-half[
+- 1990: The World Wide Web is first introduced to the public by English engineer and computer scientist Sir Tim Berners-Lee, echoing the MEMEX proposed in 1945
+- 1993: Mosaic, the first popular web browser is introduced
+- 1996: Palm pilot is introduced
+- 1998: The first portable MP3 player is released by SaeHan Information Systems.
+21st century
+- 1998: Ishii pioneers tangible computing
+]
+.footnote[[HCI/UX timeline](https://miro.medium.com/max/2000/1*_c9Sy7zXyBMlmuGH_orf2Q.png)]
+---
+# [2000s](https://www.computerhistory.org/timeline/2000/): Wave 2: Collaboration & communication 
+
+.left-column-half[
+![:img A picture of Deborah Estrin talking about mobile health, 45%](img/hci-intro/estrin.jpg) ![:img A picture of an App store full of mobile apps, 40%](img/hci-intro/mobile-apps.jpg)<br>
+
+
+.font-small[(left) Deborah Estrin, who won a MacArthur Genius award for her pioneering work in Mobile Health (right) "The iPhone’s phenomenal popularity creates a new computing platform that brings mobile Web browsing to a large audience. Google’s Android mobile platform soon makes that audience even larger." ([Computer History Museum](https://www.computerhistory.org/timeline/2009/))
+]
+]
+
+.right-column-half[
+- 2007: Apple Inc. launches the iPhone, the first touchscreen smart phone.[411]
+- 2009: Mobile apps hit mass market
+- 2011: Deborah Estrin pioneers the use of mobile technology for health
+]
+
+---
+# [2010s](https://www.computerhistory.org/timeline/2010/): Wave 3: Self-expression, social change 
+
+.left-column-half[
+![:img A picture of a large crowd gathered in Tunis, 40%](img/hci-intro/arab-spring.jpg)
+.font-small["Starting in late 2010 and continuing through 2011, protests in North Africa and the Middle East lead to regime change, and in some cases, free elections for the first time in history. Many of these protests were organized or promoted on sites such as Twitter and Facebook, and commentary appearing on popular blogs helped get the news out to the rest of the world while official, government-run media outlets were often silent." ([Computer History Museum](https://www.computerhistory.org/timeline/2011/))
+]]
+
+.right-column-half[
+- Diversification of focus and impact
+ - Study of computers in many contexts
+ - Diversification of apps
+- Diversification of input and output 
+ - 2015 first SmartWatch released 
+ - Mobile interaction options proliferate
+ - Introduction of new mobile interaction techniques, sensors 
+ - Accessibility of mobile devices increases and through the access to navigation, information through the camera and more
+- [Rise of HCI](https://blog.prototypr.io/the-rise-of-human-computer-interaction-hci-823dd6286e1d)
+]
+
+---
+.left-column-half[
+## HCI's more shameful moments
+![:img A picture of an alert stating: Emergency Alert. Ballistic missile threat inbound to Hawaii. Seek immediate shelter. This is not a drill, 50%](img/hci-intro/hawaii.jpg)
+<br>.font-small[2017 [Hawaii missile gaffe](https://theconversation.com/hawaiis-missile-alert-gaffe-why-good-human-machine-design-is-critical-90144) warns incorrectly of incoming nuclear missile
+]
+]
+.right-column-half[
+- 1979-1989 over 1000 people die due to computer-related accidents, many involving HCI interfaces. [1994 report](https://www.cs.uic.edu/~i377/MacKenzie-Computer-related-accidental-death-an-empirical-exploration.pdf). This data still isn't tracked...
+- 2015 [Google's image recognition code tags a Black person as a gorilla](https://muse-jhu-edu.offcampus.lib.washington.edu/article/645268/pdf). The fix [as of 2018](https://www.wired.com/story/when-it-comes-to-gorillas-google-photos-remains-blind/) was still a hard coded solution
+- 2016 [Did fake news change the outcome of an election?](https://www.npr.org/2018/04/11/601323233/6-facts-we-know-about-fake-news-in-the-2016-election) 
+- 2019 Discussions increase about the need to address [algorithmic bias in health care](https://www.healthaffairs.org/do/10.1377/hblog20191031.373615/full/). Articles show how [race is baked into health care decision making algorithms](https://www.statnews.com/2020/06/17/racial-bias-skews-algorithms-widely-used-to-guide-patient-care/) while patients do not have the right to know the basis 
+- 2020 [Algorithm leads to false arrest of Black man](https://www.npr.org/2020/06/24/882683463/the-computer-got-it-wrong-how-facial-recognition-led-to-a-false-arrest-in-michig) 
+]
+
+---
+#  HCI in the Future
+
+How do we invent a preferable future? an inclusive future?
+- What will the future look like?
+- Who needs to help design the future for us to achieve this?
+- Who needs to use technology in the future for us to achieve this?
+
+---
+
+#  <span class="fa-stack fa-lg"> <i class="fa fa-comment fa-stack-2x"></i> <i class="fa fa-cloud fa-stack-2x"></i> </span> Think Ahead
+
+Question we will discuss in lecture: How is it changing us as individuals and a society?
+
+???
+- Social networking has been implicated in revolutions and elections.
+- Interfaces designs have impacted health and safety.
+
+
+---
+# Summary (somewhat tongue in cheek)
+
+
+Welcome to the class!
+
+All of HCI was already invented (sort of!)
+
+HCI has a huge influence on individuals and society
+
+HCI is really all of computer science
diff --git a/slides/wk01/img/hci-intro/Lewis.png b/slides/wk01/img/hci-intro/Lewis.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c5d43add5041afb5946baa2225de6dc7ce10031
Binary files /dev/null and b/slides/wk01/img/hci-intro/Lewis.png differ
diff --git a/slides/wk01/img/hci-intro/alto.jpg b/slides/wk01/img/hci-intro/alto.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b4ca583b8540653a19898578653e4287e6d045b0
Binary files /dev/null and b/slides/wk01/img/hci-intro/alto.jpg differ
diff --git a/slides/wk01/img/hci-intro/arab-spring.jpg b/slides/wk01/img/hci-intro/arab-spring.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..ac79e327acb07ca70a878c8c84b76b04754097e3
Binary files /dev/null and b/slides/wk01/img/hci-intro/arab-spring.jpg differ
diff --git a/slides/wk01/img/hci-intro/community-memory.jpg b/slides/wk01/img/hci-intro/community-memory.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..4651c035dae94869fee908b331676b39d4b0c530
Binary files /dev/null and b/slides/wk01/img/hci-intro/community-memory.jpg differ
diff --git a/slides/wk01/img/hci-intro/cronkite.png b/slides/wk01/img/hci-intro/cronkite.png
new file mode 100644
index 0000000000000000000000000000000000000000..85d3eebdaacbde2c235f083343dc30dd629864d4
Binary files /dev/null and b/slides/wk01/img/hci-intro/cronkite.png differ
diff --git a/slides/wk01/img/hci-intro/engelbart.png b/slides/wk01/img/hci-intro/engelbart.png
new file mode 100644
index 0000000000000000000000000000000000000000..25df0b9821e1057ce4c3dba78bc2e3633ef39f3f
Binary files /dev/null and b/slides/wk01/img/hci-intro/engelbart.png differ
diff --git a/slides/wk01/img/hci-intro/estrin.jpg b/slides/wk01/img/hci-intro/estrin.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..0ecfe09f6b14e5fb8e502075d67d8fd376a4caab
Binary files /dev/null and b/slides/wk01/img/hci-intro/estrin.jpg differ
diff --git a/slides/wk01/img/hci-intro/goldberg.jpg b/slides/wk01/img/hci-intro/goldberg.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..e391a0edd5e7e70d2638cb5acbc3304f23ac80f2
Binary files /dev/null and b/slides/wk01/img/hci-intro/goldberg.jpg differ
diff --git a/slides/wk01/img/hci-intro/google-blooper.jpeg b/slides/wk01/img/hci-intro/google-blooper.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..0fb9f6a15edc02069dddf011c0a7e45644222d49
Binary files /dev/null and b/slides/wk01/img/hci-intro/google-blooper.jpeg differ
diff --git a/slides/wk01/img/hci-intro/hawaii.jpg b/slides/wk01/img/hci-intro/hawaii.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2f8abde6ee1c5e18fe62e71c1a68c7149b138988
Binary files /dev/null and b/slides/wk01/img/hci-intro/hawaii.jpg differ
diff --git a/slides/wk01/img/hci-intro/ishii.jpg b/slides/wk01/img/hci-intro/ishii.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9179cfeab5f2c5ea8b0153257df2869a2c85f751
Binary files /dev/null and b/slides/wk01/img/hci-intro/ishii.jpg differ
diff --git a/slides/wk01/img/hci-intro/memex.png b/slides/wk01/img/hci-intro/memex.png
new file mode 100644
index 0000000000000000000000000000000000000000..094de66dc1e71babd0e2159d440b227f757cd87c
Binary files /dev/null and b/slides/wk01/img/hci-intro/memex.png differ
diff --git a/slides/wk01/img/hci-intro/mobile-apps.jpg b/slides/wk01/img/hci-intro/mobile-apps.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..04cbd780353972f0b098c2eaf4626d90d4b64e48
Binary files /dev/null and b/slides/wk01/img/hci-intro/mobile-apps.jpg differ
diff --git a/slides/wk01/img/hci-intro/mobile-telegraph.png b/slides/wk01/img/hci-intro/mobile-telegraph.png
new file mode 100644
index 0000000000000000000000000000000000000000..80467c73d558996e3bef98d15ab75f7edf4747d7
Binary files /dev/null and b/slides/wk01/img/hci-intro/mobile-telegraph.png differ
diff --git a/slides/wk01/img/hci-intro/palm-pilot.jpg b/slides/wk01/img/hci-intro/palm-pilot.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..f00d70b065afc837a6f6e7ce3cab95f86809aa1c
Binary files /dev/null and b/slides/wk01/img/hci-intro/palm-pilot.jpg differ
diff --git a/slides/wk01/img/hci-intro/sketchpad-screens.png b/slides/wk01/img/hci-intro/sketchpad-screens.png
new file mode 100644
index 0000000000000000000000000000000000000000..54869734fb21241e285a9da40967771f30565685
Binary files /dev/null and b/slides/wk01/img/hci-intro/sketchpad-screens.png differ
diff --git a/slides/wk01/img/hci-intro/smalltalk-interface.png b/slides/wk01/img/hci-intro/smalltalk-interface.png
new file mode 100644
index 0000000000000000000000000000000000000000..e5bb5cc43560722e13ce84477f0717a0d04d0502
Binary files /dev/null and b/slides/wk01/img/hci-intro/smalltalk-interface.png differ
diff --git a/slides/wk01/img/hci-intro/sutherland.png b/slides/wk01/img/hci-intro/sutherland.png
new file mode 100644
index 0000000000000000000000000000000000000000..5f3d71b7c3fa008288e99202f9ccc567257f8544
Binary files /dev/null and b/slides/wk01/img/hci-intro/sutherland.png differ
diff --git a/slides/wk01/img/hci-intro/videophones.png b/slides/wk01/img/hci-intro/videophones.png
new file mode 100644
index 0000000000000000000000000000000000000000..6cad4552510a46a7c25e7598ad73b6b025f50257
Binary files /dev/null and b/slides/wk01/img/hci-intro/videophones.png differ
diff --git a/slides/wk01/img/hci-intro/watches.png b/slides/wk01/img/hci-intro/watches.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc3dd12e5b373e6067bfc0699f0fcb61d49628b4
Binary files /dev/null and b/slides/wk01/img/hci-intro/watches.png differ
diff --git a/slides/wk01/intro.html b/slides/wk01/intro.html
index e32c9759e5f8b07a180dedb5ff72450cca6fa9d4..45722dfbbe46c9d5cac56a7db0f4f7b0e412e840 100644
--- a/slides/wk01/intro.html
+++ b/slides/wk01/intro.html
@@ -114,9 +114,112 @@ layout: false
 - **Course staff**
 - How we will teach this course
 - Learning goals
-- Assignments & Grading
-- Relevance of HCI to all of Computer Science
+- Course staff
+- Syllabus
+
+
+---
+#  HCI in the Future
+
+Small Group Discussion: How is it changing us as individuals and a society?
+
+<iframe src="https://us.edstem.org/courses/114/sway/host/1153" width="800" height="600" frameBorder="0"></iframe>
+
+???
+- Social networking has been implicated in revolutions and elections.
+- Interfaces designs have impacted health and safety.
+---
+# Return to discuss as a class
+
+---
+# Summary (somewhat tongue in cheek)
+
+
+Welcome to the class!
+
+All of HCI was already invented (sort of!)
+
+HCI has a huge influence on individuals and society
+
+HCI is really all of computer science
+
+---
+layout: false
+
+<!-- (Outline Slide) -->
+
+# Today's goals
+
+- HCI: Designing the Future
+- **Learning goals**
+- Course staff
+- Syllabus
+---
+.left-column[
+# This **week's** learning goals
+]
+.right-column[
+- What is HCI? Past, present and future
+- Get up to speed with Android basics
+- Learn about basic abstractions for UI implementation
+- Names for common interactors
+]
+---
+.left-column[
+# Course learning goals:
+]
+.right-column[
+# Building Interfaces
+- Deep understanding of **how to build user interfaces**
+- Basic abstractions (layout, event handling, *etc.*)
+- Implementing best practices: Undo, Accessibility, Feedback, Errors,
+*etc.*
+- Exploration of advanced UI concepts: Ubicomp, Sensing, AR, *etc.*
+]
+---
+
+.left-column[
+# Course learning goals:
+]
+.right-column[
+#  Iterative Design
+
+- Basic understanding of **Iterative Design**
+- Why designers are valuable
+- Iterative design process
+- How designers get data from users
+]
+---
+layout: false
+
+<!-- (Outline Slide) -->
+
+# Today's goals
+
+- HCI: Designing the Future
+- Learning goals
+- **Course staff**
+- Syllabus
+---
+.left-column-half[
+# Jennifer Mankoff
+
+Pronouns: She/her
+
+[Make4All Lab](http://make4all.org)
 
+I use techonology to improving inclusion in
+and accessibility of our digital future.
+
+- Assistive and health technology
+- Fabrication/Physical computing
+- Improve inclusion and accessibility
+]
+
+.right-column-half[
+![:img Pictures of projects from the make4all lab including experiments with 3D printing materials; toolkits; capabilities; application; and understanding socio-technical system, 80%](img/intro/mankoff-projects.png)
+
+]
 
 ---
 
@@ -345,56 +448,22 @@ GitGrade Inventor
 ---
 # Let's get to know you!
 
-<iframe src="https://embed.polleverywhere.com/free_text_polls/fyXGV5NwPUnzsZ4Bbpso2?controls=none&short_poll=true" width="800" height="600" frameBorder="0"></iframe>
+Breakout rooms, with questions:
 
----
-# Aside: Accessibility in this course
-
-- Our goal is to make this course inclusive and accessible.
-
-- If you use a screen reader, or just want to see it there is a link to the slide markdown for every slide (demo)
+- Where in the world are you?
+- What is one thing that helped you most through quarantine?
+- What is your favorite food? Make a [wordcloud](https://us.edstem.org/courses/114/sway/host/1156)
 
-- There are live (but automated captions) on every slide deck.
-
-- The answers to slides can be viewed by pressing P.
 
 ---
 
 [//]: # (Outline Slide)
 # Today's  goals
 
-- Course staff
-- **How we will teach this course**
+- HCI: Designing the Future
 - Learning goals
-- Assignments & Grading
-- Relevance of HCI to all of Computer Science
-
----
-# Class Structure
-
-Lectures
-- Readings before lectures to give you context about what we'll discuss (aiming for 20 minutes)
-- Discussion of theory and practice
-- Emphasis on active learning including exercises in lectures
-- Class attendance is **STRONGLY encouraged**. Recordings won't give you the chance do in-class work
-
-Labs
-- Support for synthesizing the theory and the practice through programming and data analysis
-
-~~Exams~~ Examlets: Four mini assessments to test your understanding on Weeks 3, 5, 7, 9
-- Practice quizzes (for participation) on Weeks 2, 4, 6, 8, 10
-- No midterm, no final
-
-Please post (privately) on Ed if you are in a time zone that does not allow you
-to partipate during class synchronously.
-
----
-# Getting help
-
-- Questions during class
-- Instructor Office Hours
-- TA Office Hours
-- Ed Message Board
+- Course staff
+- **Syllabus**
 
 ---
 # Where to find what
@@ -412,28 +481,69 @@ either in office hours or through [Ed]({{site.piazza}})
 starter code and commit your changes.
 ---
 
-#  Expectations
+#  Development Environement
 
-- **Background**: Lots of programming expected
-- **Sharing**:
- - Documentation is required ("Jenny helped me figure out how to do
-   slow in/slow out animations")
- - No direct copying, using your eyes or cut and paste
- - Fundementally we want to understand as assess YOUR learning.
+**Background**: Lots of programming expected. Need 143 or equivalent
 
-See our [Academic Conduct]({{site.baseurl}}/academic-conduct) page for more details
+Java is our primary language
+- Comfortable with Java; basic software engineering; some Data Structures
+ - Fast-paced introduction to git & Android Studio IDE
+ - Advanced Java use (e.g. anonymous inner classes)
+ - Must be comfortable with reading documentation (not just Stack Overflow)
+ - We will use trees, state machines, etc.
+ - Math computations (trig) for later assignments
+
+---
+# Platform: Android
+
+Most commonly used
+interface development platform for Java
+
+--
+  - Open source
+  - Around 75% market share
+  - Thousands of supported devices
+
+--
+- Exposes Android SDK
+  - Framework for building apps on mobile devices
+--
+
+  - Written in __Java__ and E__X__tensible __M__arkup __L__anguage (__XML__)
 
-???
-talk about this a little deeply. What does direct copying mean? Copy paste? Watching someone else
-type it and then typing it into your code (that’s a no no too). Watching a video and typing in that code (also a no no).
 ---
+.left-column-half[
+## Android Versioning
 
-#  Expectations and Values
+![:img Android Versioning and Distribution, 100%](img/intro/android-versions-may2019.png)
+]
+.right-column-half[
+## We're going to target  .red[Nougat] :)
+
+That's .red[__19%__] of devices. (Looking into upgrading to Oreo)
+
+]
+
+---
+# Android Versioning
+
+- We encourage you to buy an android phone (no tablets)  ~$150
+- ~~Support will have 10 Android phones on loan for 24hrs or less~~
+- Laptop will need sufficient memory/disk space to run <br/>
+Android Studio & emulators
+- Ask questions on Ed if you need more guidance
 
+---
+# [Syllabus]({{site.baseurl}}) Scavenger Hunt 
+
+---
+#  Summary of expectations and values
+
+- **Sharing**: Yes, but don't copy
 - **Accessibility**: This course is designed to be accessible
 - **Inclusivity**: An important value in this class, and in HCI!
-- **Academic Integrity**: A course value and requirement
-- **Language**: I am Lauren, or Dr. Bricker
+- **Academic Integrity**: A course value and requirement See our [Academic Conduct]({{site.baseurl}}/academic-conduct) page for more details
+- **Language**: I am Jen, or Dr. Mankoff
 - **Respect**: This class is a compact between us based on respect
 - **Healthy Environment**: Your health/mental health are important and we have tried to
 structure the class to support you (e.g. up to 3 late days without questions asked).
@@ -459,7 +569,7 @@ So how's this Distance Learning thing going to work? With shared expections of t
 - If you do type in the chat, ensure what you are typing is school appropriate and inclusive.
 - There will be individual and group based activities which you are expected to
 participate in. (Contact us if you are not able to participate in real time)
-
+- Please post (privately) on Ed if you are in a time zone that does not allow you to participate during class synchronously so we can accomodate that for exams, etc.
 
 ---
 # Lecture: Instructor Expectations
@@ -495,116 +605,6 @@ There will be 3 and sometimes 4 TAs in each section. TAs will
 
 A TA who is not actively presenting during a section will act as a moderator.
 
----
-
-#  Development Environement
-
-Java is our primary language
-- Need 143 or equivalent
-- Comfortable with Java; basic software engineering; some Data Structures
- - Fast-paced introduction to git & Android Studio IDE
- - Advanced Java use (e.g. anonymous inner classes)
- - Must be comfortable with reading documentation (not just Stack Overflow)
- - We will use trees, state machines, etc.
- - Math computations (trig) for later assignments
-
----
-# Platform: Android
-
-Most commonly used
-interface development platform for Java
-
---
-  - Open source
-  - Around 75% market share
-  - Thousands of supported devices
-
---
-- Exposes Android SDK
-  - Framework for building apps on mobile devices
---
-
-  - Written in __Java__ and E__X__tensible __M__arkup __L__anguage (__XML__)
-
----
-.left-column-half[
-## Android Versioning
-
-![:img Android Versioning and Distribution, 100%](img/intro/android-versions-may2019.png)
-]
-.right-column-half[
-## We're going to target  .red[Nougat] :)
-
-That's .red[__19%__] of devices. (Looking into upgrading to Oreo)
-
-]
-
----
-# Android Versioning
-
-- We encourage you to buy an android phone (no tablets)  ~$150
-- ~~Support will have 10 Android phones on loan for 24hrs or less~~
-- Laptop will need sufficient memory/disk space to run <br/>
-Android Studio & emulators
-- Ask questions on Ed if you need more guidance
-
----
-
-[//]: # (Outline Slide)
-# Today's  goals
-
-- Course staff
-- How we will teach this course
-- **Learning goals**
-- Assignments & Grading
-- Relevance of HCI to all of Computer Science
-
----
-.left-column[
-# This **week's** learning goals
-]
-.right-column[
-- What is HCI? Past, present and future
-- Get up to speed with Android basics
-- Learn about basic abstractions for UI implementation
-- Names for common interactors
-]
----
-.left-column[
-# Course learning goals:
-]
-.right-column[
-# Building Interfaces
-- Deep understanding of **how to build user interfaces**
-- Basic abstractions (layout, event handling, *etc.*)
-- Implementing best practices: Undo, Accessibility, Feedback, Errors,
-*etc.*
-- Exploration of advanced UI concepts: Ubicomp, Sensing, AR, *etc.*
-]
----
-
-.left-column[
-# Course learning goals:
-]
-.right-column[
-#  Iterative Design
-
-- Basic understanding of **Iterative Design**
-- Why designers are valuable
-- Iterative design process
-- How designers get data from users
-]
----
-
-[//]: # (Outline Slide)
-# Today's goals
-
-- Course staff
-- How we will teach this course
-- Learning goals
-- **Assignments & Grading**
-- Relevance of HCI to all of Computer Science
-
 ---
 # Assignment Structure
 
@@ -646,170 +646,3 @@ Projects: Implementation of Interfaces
 .right-column[
 # [Assignments]({{site.baseurl}}/assignments)
 ]
----
-[//]: # (Outline Slide)
-# Today's  goals
-
-- Course staff
-- How we will teach this course
-- Learning goals
-- Assignments & Grading
-- **Relevance of HCI to all of Computer Science**
-
----
-#  Human-Computer Interaction (HCI)
-
-A science of the artificial
-- Human
- - Anyone impacted by the existence of the program
- - end users
-- Computer
- - Artificial thing human is interacting with
-- Interaction
- - How the artificial stuff is actually used
-- 48% of app code [Myers & Rosson, CHI'92]; probably more now!
-
----
-# Pre-work check
-
-<iframe src="https://embed.polleverywhere.com/multiple_choice_polls/Cbv53A5TsXjCnG5QCzHJc?controls=none&short_poll=true" width="800" height="600" frameBorder="0"></iframe>
-
----
-
-.left-column-half[
-![:img Couple with cordless telegraph apparatus,100%](img/intro/mobile-telegraph.png)
-]
-.right-column-half[
-Robida's vision of a cordless telegraph (1906)
-]
----
-.left-column-half[
-![:img Two women using video phones,100%](img/intro/videophones.png)
-]
-.right-column-half[
-Commercial vision of a wireless private video phone  (1929)
-]
----
-.left-column-half[
-![:img Vannevar Bush's imagined Memex--a desk with mechanical and digital innards,100%](img/intro/memex.png)
-]
-.right-column-half[
-.quote[The MEMEX 'is a desk that can instantly bring files and material on
-any subject to the operators fingertips....' (Bush, 1945, 'As We May
-Think', Atlantic Monthly)]
-]
-
----
-.left-column-half[
-![:img A prototype of a wearable wristwatch/cellphone and Dick Tracy using it in a stamp based on the comic strip, 100%](img/intro/watches.png)
-]
-.right-column-half[
-A wrist-watch cellphone prototype (1947), which captured popular imagination as shown in this Dick Tracy stamp
-]
----
-.left-column-half[
-![:img A young man sitting at a complex machine covered in buttons--drawing on the screen with a pen, 100%](img/intro/sutherland.png)
-]
-.right-column-half[
-Ivan Sutherland. 1964. The sketchpad graphical communication system in action.
-<small> In Proceedings of the SHARE design automation workshop (DAC '64). ACM, New York, NY, USA, 6.329-6.346.</small>
-]
----
-.left-column-half[
-![:img Four screen shots of a vector-based diagram being created
-within Sketchpad, 100%](img/intro/sketchpad-screens.png)
-]
-.right-column-half[
-Sketchpad introduced direct manipulation, constraints, and chorded input.
-]
----
-
-
-# Sketchpad Inspires Engelbart
-
-.left-column-half[
-![:img Two images -- a man giving a live demo of a text based
-    interface; and a wooden computer mouse.,
-80%](img/intro/engelbart.png)
-![:youtube Engelbart's *Mother of all Demos*. His presentation included windows;
-hypertext; graphics; video conferencing; the computer mouse; word
-processing; dynamic file linking; revision control; collaborative
-real-time editing; all new inventions!, B6rKUf9DWRI]
-]
-.right-column-half[
-Engelbart's *Mother of all Demos* His presentation included many all new inventions:
-- windows
-- hypertext
-- graphics
-- video conferencing
-- the computer mouse
-- word processing
-- dynamic file linking
-- revision control
-- collaborative real-time editing
-]
-.footnote[
-December 9 1968 at the
-Fall Joint Computer Conference.
-]
----
-.left-column-half[
-![:img A desktop personal computer showing a WYSIWYG
-word processor and graphical window system, 42%](img/intro/smalltalk-interface.png)
-![:img A picture of a woman interacting with the Xerox
-Alto computer, 50%](img/intro/alto.jpg)
-]
-.right-column-half[Engelbart inspires many things, including
-- the Xerox Star
-- the first
-graphical personal computer
-- Smalltalk 80 the first UI Toolkit
-]
----
-# HCI in the Future
-
-"The Future" is alway relative...
-
-[![:img Walter Cronkit in Short clip from the March 12 1967
-episode of the CBS show "The 21st Century", 25%](img/intro/cronkite.png)](https://www.youtube.com/watch?v=V6DSu3IfRlo)
-
----
-
-#  HCI in the Future
-
-How do we invent a preferable future? an inclusive future?
-- What will the future look like?
-- Who needs to help design the future for us to achieve this?
-- Who needs to use technology in the future for us to achieve this?
-
----
-
-#  HCI in the Future
-
-Discussion: How is it changing us as individuals and a society?
-
-???
-- Social networking has been implicated in revolutions and elections.
-- Interfaces designs have impacted health and safety.
----
-
-#  HCI in the Future
-
-Watch videos and discuss:
-- [Victoria Belotti](https://www.youtube.com/watch?v=IK3rW1dSWoE)
-- [Tovi Grossman](https://www.youtube.com/watch?v=DFPnwaoxSQU)
-- [Jon Froehlich](https://www.youtube.com/watch?v=qRlIuWWdkHY) (UW)
-- [Leysia Palen](https://www.youtube.com/watch?v=jmXjaMs2tUE)
-- [Many more](http://www.id-book.com/talkingheads.php) if you want to keep exploring.
-
----
-# Summary (somewhat tongue in cheek)
-
-
-Welcome to the class!
-
-All of HCI was already invented (sort of!)
-
-HCI has a huge influence on individuals and society
-
-HCI is really all of computer science