Compare commits

..

780 Commits
0.2.3 ... 7.0.5

Author SHA1 Message Date
Tyler Long
f6b5c704e8 Make build faster 2017-09-01 18:34:10 +08:00
Tyler Long
6fe62060c6 Update config 2017-09-01 18:05:46 +08:00
Tyler Long
51fe2e8a9b Remove useless gulp scripts 2017-09-01 17:45:01 +08:00
Tyler Long
a18179e6db Remove editor 2017-09-01 17:35:13 +08:00
Tyler Long
89e1da4780 Update build scripts 2017-09-01 16:15:41 +08:00
Tyler Long
cb9a7f4cd1 Remove dist/www 2017-09-01 16:04:22 +08:00
Tyler Long
dcad2e9a17 Add screenshot for gantt diagram 2017-09-01 16:01:03 +08:00
Tyler Long
75eb6b1b6a Update screenshots 2017-09-01 15:53:20 +08:00
Tyler Long
47be3df96a Remove docs 2017-09-01 15:46:40 +08:00
Tyler Long
b077918832 Update library 2017-09-01 15:33:20 +08:00
Tyler Long
73a66b8206 Update libries 2017-09-01 14:52:27 +08:00
Tyler Long
89179ff5d1 Update badge position 2017-08-17 00:03:06 +08:00
Tyler Long
12a4882efe Update README 2017-08-17 00:02:13 +08:00
Tyler Long
4017628630 Release 7.0.4 2017-08-17 00:00:26 +08:00
Tyler Long
d596b58895 Update dependencies 2017-08-16 23:57:43 +08:00
Tyler Long
398f4bf56d Update readme 2017-08-16 23:19:44 +08:00
Tyler Long
e2b4fd4b20 New documentation website: https://mermaidjs.github.io/ 2017-08-16 22:02:28 +08:00
Tyler Long
e0c732ad47 Update readme 2017-06-04 15:26:45 +08:00
Tyler Long
3a0e665c13 Add installation guide to readme 2017-06-04 12:21:13 +08:00
Tyler Long
1076d67137 Disable amd parse for lodash 2017-06-04 12:13:22 +08:00
Tyler Long
b023892c57 Format demo code 2017-06-02 14:42:04 +08:00
Tyler Long
5f5c8a2df0 package.json specify files 2017-06-01 13:38:08 +08:00
Tyler Long
0c4bab2966 Release 7.0.1 2017-06-01 13:19:03 +08:00
Tyler Long
054950ef16 webpack target node '6.9' 2017-05-31 23:17:24 +08:00
Tyler Long
e08496d84c Merge pull request #536 from gibson042/2017-05-cli-output
Improve CLI output
2017-05-31 22:51:58 +08:00
Tyler Long
ef07012359 Merge branch 'master' into 2017-05-cli-output 2017-05-31 22:43:19 +08:00
Tyler Long
952fe4923c Don't use google CDN because we cannot access it from China 2017-05-31 22:35:17 +08:00
Tyler Long
fb94892ca6 Merge pull request #537 from ctruelson/fix-spelling
Fix spelling of 'you' in sequenceDiagram docs
2017-05-31 22:22:08 +08:00
Tyler Long
b81b3bb118 Merge pull request #538 from tylingsoft/master
Update Travis config
2017-05-31 22:16:05 +08:00
Tyler Long
5496f790bf Update Travis to remove node 6 2017-05-31 21:58:22 +08:00
Tyler Long
a6ab0a5742 Update libraries 2017-05-31 21:46:09 +08:00
Tyler Long
ef053a813d Merge branch 'master' of github.com:tylingsoft/mermaid 2017-05-31 21:11:55 +08:00
Tyler Long
550e7beb61 Try to fix Chrome issue 2017-05-31 21:11:12 +08:00
Tyler Long
e8930e7ded Merge branch 'master' into master 2017-05-31 21:04:23 +08:00
Tyler Long
22ad977349 Disable Chrome headless mode because it's not supported by stable version of Chrome 2017-05-31 20:58:50 +08:00
Charles Truelson
c223c2be5c Fix spelling of 'you' 2017-05-26 15:04:56 -04:00
Richard Gibson
e3585a7d66 Use valid values in .travis.yml 2017-05-25 12:53:28 -04:00
Richard Gibson
905e0c20d9 Let Travis CI use headless chrome 2017-05-25 12:40:22 -04:00
Richard Gibson
981c3e0ce4 Inherit environment variables when testing 2017-05-25 12:29:20 -04:00
Richard Gibson
de2db39161 Include file paths in CLI log output 2017-05-25 10:14:11 -04:00
Richard Gibson
ef25bfee14 Always use verbosity-aware CLI logging 2017-05-25 10:04:50 -04:00
Richard Gibson
4cfd18e25f Output all CLI log messages in the same way 2017-05-25 09:57:30 -04:00
Richard Gibson
e0f3656629 Fix phantomjs invocation for Ubuntu 16.04 2017-05-25 09:52:06 -04:00
Richard Gibson
2ddad07fc7 Fix typo 2017-05-25 09:51:02 -04:00
Tyler Long
1dcb6cd390 Update travis config 2017-04-26 10:45:12 +08:00
Tyler Long
1a3d37d8a5 Merge pull request #524 from tylingsoft/master
Modernize mermaid
2017-04-24 23:56:45 +08:00
Tyler Long
5cd8ac1fac Try to fix travis chrome headless issue 2017-04-24 23:11:55 +08:00
Tyler Long
7889e35340 Run Chrome with --no-sandbox 2017-04-24 23:00:51 +08:00
Tyler Long
dcffde3e98 Update travis config 2017-04-24 22:42:07 +08:00
Tyler Long
d38b9be891 Update todo list 2017-04-22 23:37:23 +08:00
Tyler Long
42846cce50 Fix class diagram in dark theme issue 2017-04-22 23:33:54 +08:00
Tyler Long
2134dbb2fd Fix git graph dark theme text color issue 2017-04-22 23:27:19 +08:00
Tyler Long
ccec3810df Fix neutral theme sequence diagram issue 2017-04-22 23:15:50 +08:00
Tyler Long
12af9b0b66 Fix class diagram css style in other themes 2017-04-22 23:10:22 +08:00
Tyler Long
97b9edf7c3 Adjust class diagram label size 2017-04-22 22:31:18 +08:00
Tyler Long
c63d556cf8 Fix class diagram display issue 2017-04-22 22:25:07 +08:00
Tyler Long
b4e8cffd0a Fix git graph default style issue 2017-04-22 22:12:10 +08:00
Tyler Long
4ba3ff6769 Fix class diagram default theme color issue 2017-04-22 21:43:07 +08:00
Tyler Long
c56ac30d39 Fix labelBox style issue 2017-04-22 21:24:21 +08:00
Tyler Long
7072979cfc beautify less code 2017-04-22 20:43:18 +08:00
Tyler Long
727f6c0e28 Format less code 2017-04-22 20:26:53 +08:00
Tyler Long
1c7c109960 Refactor or remove some file 2017-04-22 17:13:05 +08:00
Tyler Long
391149dbd8 Refactor webpack config code 2017-04-22 17:12:12 +08:00
Tyler Long
7bb988c48b Update contributing.md 2017-04-22 17:10:37 +08:00
Tyler Long
1927ed47ff Update some config files 2017-04-22 17:09:51 +08:00
Tyler Long
73027d5cec yarn dist generates minified css files 2017-04-22 16:04:16 +08:00
Tyler Long
c52a8fc442 Webpack compile less to css 2017-04-22 15:40:35 +08:00
Tyler Long
acab3a5ee1 Remove css files from dist folder 2017-04-22 10:46:42 +08:00
Tyler Long
0bd8aabd7c Add instructions for manual test 2017-04-21 20:49:35 +08:00
Tyler Long
99de658093 Add documentation for contributors 2017-04-21 20:31:49 +08:00
Tyler Long
a4d06742c3 Remove jshint because we are using standard style based on ESLint 2017-04-21 11:21:42 +08:00
Tyler Long
3ebae8f331 Use Chrome stable instead of Chrome Canary 2017-04-21 10:25:16 +08:00
Tyler Long
7fed3b408e Refactor git graph code 2017-04-19 20:17:57 +08:00
Tyler Long
95712ea357 Update package.json 2017-04-19 18:37:55 +08:00
Tyler Long
d5178f61f1 Remove gulp-uglify 2017-04-19 00:02:38 +08:00
Tyler Long
941a7f9ff5 Get rid of browserify 2017-04-18 23:35:52 +08:00
Tyler Long
0ccc216a2c Replace karma-browserify with karma-webpack 2017-04-18 23:20:38 +08:00
Tyler Long
f0dc7f89e4 Replace phantomjs with phantomjs-prebuilt 2017-04-18 22:20:49 +08:00
Tyler Long
332ceb0da6 Replace karma + PhantomJS with karma + Chrome headless 2017-04-18 22:14:02 +08:00
Knut Sveidqvist
189758cca4 Merge pull request #519 from tylingsoft/master
Modernize mermaid
2017-04-17 19:45:59 +02:00
Tyler Long
2fa4df2830 yarn doesn't support node 4 2017-04-17 23:11:51 +08:00
Tyler Long
a8ff7a65f4 Fix Travis CI env issue 2017-04-17 22:58:54 +08:00
Tyler Long
5d68359eb6 First working version of babel 2017-04-17 22:34:25 +08:00
Tyler Long
4032824c79 Format code 2017-04-17 17:13:37 +08:00
Tyler Long
0ebf162673 Try babel 2017-04-17 16:47:26 +08:00
Tyler Long
166acad749 Remove some dependencies 2017-04-17 00:52:23 +08:00
Tyler Long
15bfc12cbe Remove useless dependencies 2017-04-17 00:37:01 +08:00
Tyler Long
3ac23c21d9 Refactor code 2017-04-17 00:12:30 +08:00
Tyler Long
8b44401b1b Update some configuration files 2017-04-17 00:12:05 +08:00
Tyler Long
5624453c87 Refactor code 2017-04-16 23:48:36 +08:00
Tyler Long
87c7f73245 Refactor code 2017-04-16 23:24:47 +08:00
Tyler Long
110e4ee586 Refactor code 2017-04-16 23:08:37 +08:00
Tyler Long
6a7a5b9249 Format code of src/d3.js 2017-04-16 22:49:59 +08:00
Tyler Long
8832aa61fe Remove useless jasmine config file 2017-04-16 21:44:31 +08:00
Tyler Long
e16e501b93 mermaidAPI should target node 2017-04-16 21:38:48 +08:00
Tyler Long
8536c975bf Refactor code of gulp scripts 2017-04-16 18:36:42 +08:00
Tyler Long
3d500800f1 Refactor code 2017-04-16 18:27:48 +08:00
Tyler Long
b9b991c5fe Refactor code in lib/ folder 2017-04-16 17:52:09 +08:00
Tyler Long
701e638907 Add demo for all the 5 diagram types 2017-04-16 17:30:40 +08:00
Tyler Long
52d03f3ce3 Remove useless file 2017-04-16 16:00:38 +08:00
Tyler Long
ebf00eecaa Remove useless file 2017-04-16 15:57:56 +08:00
Tyler Long
262a917952 Test mermaidAPI 2017-04-16 15:37:32 +08:00
Tyler Long
a730225700 Replace window.console with console in order to support node 2017-04-16 09:37:05 +08:00
Tyler Long
538d2c2b83 Remove getTransformToElement polyfill because it's fixed in dagre-d3 2017-04-16 09:30:04 +08:00
Tyler Long
736d57f3f2 Create demo folder 2017-04-16 09:17:25 +08:00
Tyler Long
053388a2d3 Add todo.md 2017-04-15 16:46:10 +08:00
Tyler Long
e17ec3e299 Don't use d3-textwrap before it's for d3 v4 only 2017-04-15 16:44:25 +08:00
Tyler Long
869a64e457 Generate all the dist files using webpack 2017-04-15 16:09:30 +08:00
Tyler Long
9d56595d48 Update libraries to latest version 2017-04-15 15:34:23 +08:00
Tyler Long
ea11f8bff1 Use external d3-textwrap 2017-04-15 14:49:35 +08:00
Tyler Long
fd5b3083dd Update d3 to 3.5.17 2017-04-15 13:26:58 +08:00
Tyler Long
58a787b660 Upgrade D3 to 3.5.12 2017-04-15 13:22:26 +08:00
Tyler Long
dffc53b7b1 Minor changes 2017-04-15 13:13:54 +08:00
Tyler Long
173899fc76 First fully working version of webpack 2017-04-15 12:38:24 +08:00
Tyler Long
7166ade310 Debug flowchart 2017-04-15 12:28:16 +08:00
Tyler Long
7b935823da First working version of webpack 2017-04-15 10:28:37 +08:00
Tyler Long
e945bf884e First try of webpack 2017-04-14 17:43:53 +08:00
Tyler Long
d87da684a9 eslint watch 2017-04-13 22:35:27 +08:00
Tyler Long
15bdaca2f9 Use yarn install of npm 2017-04-13 22:21:09 +08:00
Tyler Long
df8085bee0 Fix all code style issues 2017-04-13 21:53:23 +08:00
Tyler Long
f04d415b0a Fix gulpfile.js style issue 2017-04-13 21:08:51 +08:00
Tyler Long
ab398c64a8 Fix code style issues 2017-04-13 20:50:22 +08:00
Tyler Long
a2ce9e6a54 Fix code style issues 2017-04-13 20:16:38 +08:00
Tyler Long
dc4edf775d Fix style issue of logger.js 2017-04-11 23:46:11 +08:00
Tyler Long
e91229f69f Fix style issue of mermaid.js and mermaidAPI.js 2017-04-11 23:31:59 +08:00
Tyler Long
f136a7fd8d Add .ackrc 2017-04-11 23:11:12 +08:00
Tyler Long
413162dfad Fix some code style issues 2017-04-11 22:57:57 +08:00
Tyler Long
7d1c1e3845 Add index.html to dist folder 2017-04-11 22:24:55 +08:00
Tyler Long
620f3e8734 Auto fix standard style voilations 2017-04-11 22:14:25 +08:00
Tyler Long
d46fda537d Use JavaScript Standard Style for lint 2017-04-11 22:02:50 +08:00
Tyler Long
fc96360e17 Get all tape tests correct 2017-04-11 21:52:19 +08:00
Tyler Long
bc94fc3d6d Convert test/cli_test-sample.js to ES6 2017-04-10 22:24:20 +08:00
Tyler Long
c1e5fe3281 Change cli_test-parser.js to ES6 2017-04-10 21:59:40 +08:00
Tyler Long
69ceab9f44 Change cli_test-output.js to ES6 2017-04-10 21:26:38 +08:00
Tyler Long
546e766fd9 Add JavaScript Standard Style 2017-04-10 21:15:12 +08:00
Tyler Long
82220638a0 package.json scripts test should do dist 2017-04-10 20:51:01 +08:00
Tyler Long
432f3446b0 Minor change to d3 version 2017-04-10 20:28:50 +08:00
Tyler Long
19212eb4aa Upgrade all libraries except d3 2017-04-10 18:10:30 +08:00
Tyler Long
176ffa8e7c Update phantomjs 2017-04-10 17:52:19 +08:00
Tyler Long
3caa755b28 Update some libraries 2017-04-10 17:46:52 +08:00
Tyler Long
e48ff4c7e9 Update chalk, he and lodash 2017-04-10 17:43:02 +08:00
Tyler Long
e1e7be07f7 Remove auto generated js file from dist folder 2017-04-10 17:09:21 +08:00
Tyler Long
23dd96be97 Update .gitignore 2017-04-10 16:53:32 +08:00
Tyler Long
2525929e8d Add yarn.lock file 2017-04-10 16:45:22 +08:00
Tyler Long
2deacb8e97 Format files in root folder 2017-04-10 16:36:54 +08:00
Tyler Long
3b2b0da3a1 Add .editorconfig 2017-04-10 16:33:31 +08:00
Knut Sveidqvist
ca5a96f53a Merge pull request #509 from filipedeschamps/patch-1
Update CLI instructions
2017-04-08 08:45:40 +02:00
Filipe Deschamps
c2227e1a48 Update CLI instructions
Documentation `Usage` section wasn't reflecting CLI `--help` instructions anymore.
2017-04-07 23:11:16 -03:00
Knut Sveidqvist
dff8027be6 Merge pull request #503 from yudenzel/master
Add style for classDiagram to dark/default theme
2017-04-07 07:27:11 +02:00
YuDenzel
8f7f1ee0b5 Add style for classDiagram to dark/default theme
Copy classDiagram.less in forest theme to dark and default theme.

Issue: #454
2017-04-06 00:40:14 +08:00
Knut Sveidqvist
c7c077c21c Fix for issue #501
Added heko request to readme
2017-04-01 13:51:03 +02:00
Knut Sveidqvist
d7b1eb74e5 Upgraded version of node 2017-04-01 12:04:02 +02:00
Knut Sveidqvist
8772a5bba0 Merge pull request #498 from gomlgs/patch-1
Fix documentation for git graph.
2017-04-01 11:45:07 +02:00
Knut Sveidqvist
c6ccfa8b6a Merge pull request #497 from saveman71/master
Fix links in documentations
2017-04-01 11:43:59 +02:00
Knut Sveidqvist
63057c2f60 Merge pull request #470 from u-minor/support_parallel
add par statement to sequenceDiagram
2017-04-01 11:41:25 +02:00
Minoru Nakata
3b9baeb866 add generated parser 2017-03-30 18:39:48 +09:00
Minoru Nakata
38c5e65c35 add par statement to sequenceDiagram 2017-03-30 18:28:44 +09:00
gomlgs
fa7570efd8 Fix documentation for git graph. 2017-03-24 13:27:26 -07:00
Antoine Bolvy
b50c15e278 Fix links in documentations
* The main change (in term of importance) is the fix of two broken links that were
pointing to outdated sections ids.
* Added a link to mermaidCLI in the sequenceDiagram section, to reflect the one in
the gantt section.

* Converted some links to https.
2017-03-23 20:17:01 -04:00
Knut Sveidqvist
96be32225e Merge pull request #473 from gnkm/Add-viewbox-to-class-diagram
Add viewbox attr to class diagram
2017-03-19 13:37:39 +01:00
Knut Sveidqvist
dab3bc6ce4 Merge pull request #481 from raghur/gitgraph-doc-updates
Update README.md with git graph sample
2017-03-19 13:34:33 +01:00
Knut Sveidqvist
b0e81bf188 Merge pull request #479 from stevenschobert/ss-fix-typo
Fix misspelling of “another”
2017-03-19 13:34:14 +01:00
Knut Sveidqvist
acbbfbe419 Merge pull request #477 from brookhong/sequenceDiagram
Fixed #456 sequenceDiagram: dotted line for alt and empty bracket sho…
2017-03-19 13:33:52 +01:00
Raghu Rajagopalan
c1478418ae Add documenation for git graph 2017-02-21 12:32:19 +05:30
Steven Schobert
37f9655714 fix misspelling of “another” 2017-02-20 11:56:57 -06:00
brook hong
4f12596e29 Update color for label to make it align with loop line 2017-02-16 23:11:53 +08:00
brook hong
c714a7a28c Fixed #456 sequenceDiagram: dotted line for alt and empty bracket should be hidden
And draw label with right bottom corner cut
2017-02-16 22:50:39 +08:00
Genki Matsunaga
b935108ed8 Add viewbox attr to class diagram 2017-02-07 18:29:49 +09:00
Knut Sveidqvist
fb70b9742a New release 2017-01-29 12:14:02 +01:00
Knut Sveidqvist
8d91a42fe7 Fix for merge error 2017-01-09 21:36:01 +01:00
Knut Sveidqvist
0be8d62f7d Fix for merge error 2017-01-09 21:35:53 +01:00
Knut Sveidqvist
cac960c6b8 Merge pull request #452 from joshuacolvin/issue416_link_style_fill_none
Fix for #416, customizing link style with any color sets `fill` property to `black` instead of `none`
2017-01-09 21:30:03 +01:00
Knut Sveidqvist
8596eb6091 Merge branch 'master' into issue416_link_style_fill_none 2017-01-09 08:02:08 +01:00
Knut Sveidqvist
7ff87fdac9 Merge pull request #447 from jawn/patch-1
Fix spelling
2017-01-09 07:58:36 +01:00
Knut Sveidqvist
4c04001637 Merge pull request #449 from bfriedz/circle-styles
Allow .node>circle to receive css styles
2017-01-09 07:58:07 +01:00
Joshua Colvin
8c6cfebc1c Add unit tests to check that 'fill:none' is being set by default.
Updated unit tests to include 'fill:none' in expect style array values.
2017-01-07 12:58:47 -05:00
Joshua Colvin
ff0ab048df Add unit tests for isSubstringInArray utility function 2017-01-07 12:57:32 -05:00
Joshua Colvin
9b66b114cc Add conditional to add 'fill:none' to styles array if fill not present 2017-01-07 12:54:49 -05:00
Joshua Colvin
957244583c Add utilty function to check for substring in array 2017-01-07 12:53:49 -05:00
Rebecca Dreezer
a124be71b1 Allow .node>circle to receive css styles 2017-01-05 22:02:38 -05:00
Bernard Vander Beken
345d460643 Fix spelling 2017-01-04 11:19:14 +01:00
Knut Sveidqvist
7f0d4fff1b Merge pull request #445 from whyzdev/flowchat_text
added tests and fix cli css style selector lowercase problem
2017-01-03 19:28:06 +01:00
Knut Sveidqvist
1903154d01 Merge pull request #441 from hetz/hetz-patch-gulp-uglify
Update d3.js
2017-01-03 19:27:18 +01:00
whyzdev
9558ebcb0b remove dups 2017-01-01 23:41:38 -05:00
whyzdev
d63934637f remove dups 2017-01-01 23:36:42 -05:00
whyzdev
256c529de8 fix cli css style selector text lowercase problem by setting svg style content to commandline css content, if available -- this overwrites cloneCssStyles 2017-01-01 22:47:09 -05:00
whyzdev
a56fe7a8fe add more tests in cli_test-samples: flowchart, sequence, gantt, gitgraph, load html in phantomjs and save screenshot png 2017-01-01 22:47:09 -05:00
贺天卓
be4bbed687 Update d3.js
fix gulp-uglify error.
2016-12-20 01:45:23 +08:00
Knut Sveidqvist
2d2f0b9281 Update .codeclimate.yml 2016-12-19 13:07:43 +01:00
Knut Sveidqvist
dfd9622ce7 Merge pull request #439 from whyzdev/flowchat_text
adde tests to reproduce #434 in flowchart
2016-12-19 13:04:36 +01:00
whyzdev
71125e7b86 change " to ' per code climate 2016-12-19 00:40:29 -05:00
whyzdev
4a5bbe1791 added a flowchart cli test to reproduce #434 2016-12-18 23:17:24 -05:00
whyzdev
0ecbbf8ddf add tests 2016-12-18 22:52:41 -05:00
whyzdev
b73698849d rename tests 2016-12-18 18:05:37 -05:00
whyzdev
e13c939583 added cli_test_run-samples to test samples from commandline; added --outputSuffix option 2016-12-18 12:30:37 -05:00
whyzdev
96a25935ac fix warnings per code climate 2016-12-17 18:03:11 -05:00
Knut Sveidqvist
7d5c3144dc Merge pull request #437 from larkinscott/master
Code Climate config
2016-12-15 12:02:13 +01:00
Scott Larkin
2cf9a07bb2 Update .codeclimate.yml 2016-12-14 15:11:07 -05:00
Knut Sveidqvist
72718c14d1 Merge pull request #435 from whyzdev/issue428_fix_gantt_cli_cfg
fix gantt and sequence digram cli cfg
2016-12-14 12:43:50 +01:00
Knut Sveidqvist
fbe64b21bc Fix for issue #311, mermaid will clear div used for rendering initially in the redner function. 2016-12-14 12:41:29 +01:00
Knut Sveidqvist
d65741177a Fix for issue #311, mermaid will clear div used for rendering initially in the redner function. 2016-12-14 12:41:22 +01:00
Knut Sveidqvist
046b4fdae4 Fix for issue #311, mermaid will clear div used for rendering initially in the redner function. 2016-12-14 12:41:12 +01:00
Knut Sveidqvist
a179212c60 Fix for issue #311, mermaid will clear div used for rendering initially in the redner function. 2016-12-14 12:40:44 +01:00
whyzdev
5d99e9c0b8 changed tspan as default of sequence actor text placement 2016-12-13 21:07:32 -05:00
whyzdev
b426e600ce fix gantt chart cli configuration one more time missing }; make sequenceConfig cli configuration work; added guantt and sequence files in tape cli output tests 2016-12-13 21:07:32 -05:00
Knut Sveidqvist
66d7c00e84 Merge pull request #433 from whyzdev/issue428_fix_gantt_cli_cfg
fix gantt chart cli configuration broken
2016-12-13 09:28:37 +01:00
whyzdev
293c23ec52 fix gantt chart cli configuration broken #430; remove json quote around function in the config e.g. axisFormatter 2016-12-13 00:19:35 -05:00
Knut Sveidqvist
9e701b0fb3 Merge pull request #430 from whyzdev/issue428_fix_gantt_cli_cfg
fix gantt chart cli configuration parsing including functions
2016-12-11 14:01:56 +01:00
whyzdev
95a0cb53d3 fix gantt chart cli configuration parsing including functions 2016-12-10 16:21:21 -05:00
Knut Sveidqvist
3c03c84782 Merge pull request #427 from whyzdev/issue422_seq_actor_text_placement
use tspan via d3.textwrap to place actor text in sequence diagram
2016-12-10 10:05:18 +01:00
Knut Sveidqvist
a3fa318edc Merge pull request #429 from daveaglick/master
Uses an empty text node instead of a string for svg group labels
2016-12-10 10:05:01 +01:00
Dave Glick
0187407cff Uses an empty text node instead of a string for svg group labels 2016-12-09 15:47:26 -05:00
whyzdev
1424ff7d22 fix karma tests where text.textwrap is undefined; added textPlacement parameters to some test cases 2016-12-07 21:46:58 -05:00
whyzdev
db62447c98 fix logger to display exception stack includig message not just {} 2016-12-07 20:26:22 -05:00
whyzdev
b96ae68aee added byTspan in _drawText candidate function, which uses d3textwrap and align middle vertically by setting text.y 2016-12-07 20:26:22 -05:00
Scott Larkin
7d3f0da75c Create .codeclimate.yml
Created a Code Climate config that uses your existing ESLint configuration (also, enabled our Duplication engine).
2016-12-05 16:06:17 -05:00
Knut Sveidqvist
4984f9e041 Merge pull request #423 from whyzdev/issue422_seq_actor_text_placement_by_fo
#422 use foreignObject/div to place actor label in sequence diagram
2016-12-02 13:25:59 +01:00
whyzdev
1e27238083 fix non-fo y position as before 2016-12-01 20:09:57 -05:00
whyzdev
1e41cea107 use foreignObject/div to place actor label in sequence diagram, enabled by config.textPlacement:fo 2016-12-01 01:00:53 -05:00
Knut Sveidqvist
da99d3b15e Merge pull request #413 from sifb/patch-1
Clarify the need for a CSS stylesheet
2016-11-25 18:55:40 +01:00
sifb
a0dafb4093 Clarify the need for a CSS stylesheet
Issue #273 says "the need for mermaid.css should be mentioned explicitly in the intro docs" - I ran into this problem as well, because I read `mermaid.min.js This bundle contains everything you need to run mermaid` very literally, but it doesn't contain everything and it needs a stylesheet as well.

This edit tries to make that more clear.
2016-11-24 21:26:07 +00:00
Knut Sveidqvist
f5485c1265 Merge pull request #412 from sinedied/master
Added hads downstream project
2016-11-20 10:22:33 +01:00
Yohan Lasorsa
24da004bd6 Added hads downstream project 2016-11-17 17:10:50 +01:00
Knut Sveidqvist
e8f28b71f7 Merge pull request #406 from jinntrance/master
update usage and fix #273
2016-11-06 22:42:10 +01:00
Knut Sveidqvist
fa91653beb Merge pull request #404 from raghur/docs-downstream-pandoc-filter
Add https://github.com/raghur/mermaid-filter to downstream projects docs page
2016-11-06 22:40:56 +01:00
Joseph Jin-Chuan Tang
aa93860466 update usage and fix #273 2016-11-07 01:00:13 +08:00
Raghu Rajagopalan
ea26bd0ff2 Add https://github.com/raghur/mermaid-filter to downstream projects documentation page 2016-11-03 09:15:46 +05:30
Knut Sveidqvist
214fc1d8c4 Fix for issue #400, added unix new line at the end of generated svg 2016-10-31 20:34:34 +01:00
Knut Sveidqvist
09558e6e9a Merge branch 'master' of https://github.com/knsv/mermaid 2016-10-30 16:13:11 +01:00
Knut Sveidqvist
cc069d3987 Some change 2016-10-30 16:13:06 +01:00
Knut Sveidqvist
db3dc0ffab Merge pull request #395 from sinedied/master
New neutral theme
2016-10-05 20:05:13 +02:00
Yohan Lasorsa
6a6d6b6f85 Added neutral style 2016-10-05 15:20:58 +02:00
Knut Sveidqvist
605d74dd0d Merge pull request #390 from ben-page/cli-fix
fix cli issues
2016-09-29 07:34:07 +02:00
Ben Page
0c62c08abd fixed css, sequenceConfig, and ganttConfig being sent as buffer to phantomjs
made verbose cli argument a boolean
2016-09-28 15:08:46 -05:00
Knut Sveidqvist
aa6e15e3ac Merge pull request #386 from The-Alchemist/patch-1
Add missing space for 'Labels out of bounds' section
2016-09-02 07:43:17 +02:00
Knut Sveidqvist
537cba4bda Merge pull request #382 from swhgoon/patch-1
Fix typo: `pats` -> `paths`
2016-09-02 07:42:24 +02:00
The Alchemist
7e9419f299 Add missing space for 'Labels out of bounds' section
If you go to http://knsv.github.io/mermaid/#simple-usage-on-a-web-page, you'll see that the '###' did not get translated, probably because there needs to be a space between '###' and the text.
2016-09-01 15:48:49 -04:00
SwhGo_oN
d59ac53f64 Fix typo: pats -> paths 2016-08-29 20:15:04 +08:00
Knut Sveidqvist
92c7fe9bf6 Merge pull request #379 from HustLion/master
Added class diagram example to README.md
2016-07-27 18:31:34 +02:00
HustLion
3f2cd65159 made a better shot 2016-07-27 20:01:01 +08:00
HustLion
da717b5a91 reindent loop 2016-07-27 19:57:50 +08:00
HustLion
e277f286c0 td style not accepted, so use p style 2016-07-27 19:55:39 +08:00
HustLion
d45e21a786 Adjust img to make it look better 2016-07-27 19:49:35 +08:00
HustLion
22fc99d521 Reformat code block 2016-07-27 19:31:21 +08:00
HustLion
1026ad7011 Add Class-diagram example 2016-07-27 19:29:15 +08:00
Knut Sveidqvist
f9491ef193 Merge pull request #376 from dodoinblue/enhance-arrowhead-style
override normal flowchart arrowhead to allow css styling
2016-07-20 12:25:29 +02:00
Charles Liu
5302ca38da override normal flowchart arrowhead to allow css styling 2016-07-20 13:19:27 +08:00
Knut Sveidqvist
2830c70140 Merge pull request #371 from mgaitan/patch-1
added sphinx extension
2016-07-10 21:49:49 +02:00
Knut Sveidqvist
5e2d8d1c90 Merge pull request #369 from ggpasqualino/master
Fix typo in the sequence diagram documentation
2016-07-10 21:44:09 +02:00
Martín Gaitán
65797742ef added sphinx extension 2016-07-09 12:04:41 -03:00
Guilherme Pasqualino
1a5a52358e Merge pull request #1 from ggpasqualino/ggpasqualino-patch-1
Fix typo in sequence diagram documentation
2016-07-07 11:10:26 +02:00
Guilherme Pasqualino
5852fc2d68 Fix typo in sequence diagram documentation 2016-07-07 11:08:26 +02:00
Knut Sveidqvist
b8d3b01f64 New release 2016-05-29 19:25:02 +02:00
Knut Sveidqvist
165d43b0cd New release 2016-05-29 19:22:10 +02:00
Knut Sveidqvist
d8929db208 New release 2016-05-29 19:18:04 +02:00
Knut Sveidqvist
0ab0be01c2 Merge pull request #353 from zeroyonichihachi/gantt-width
The option of gantt for the spaces for the section names.
2016-05-25 13:59:27 +02:00
tkfm.yamaguchi
f2e4b922a9 Fix tests 2016-05-23 11:45:47 +09:00
tkfm.yamaguchi
a7e5db6e19 Fix API: gantt's side paddings 2016-05-23 11:44:46 +09:00
tkfm.yamaguchi
e8be61e062 Add example for side span of gantt 2016-05-23 11:23:49 +09:00
tkfm.yamaguchi
0cd04af6a4 Replace sidePadding with left/rightPadding 2016-05-23 11:21:38 +09:00
Knut Sveidqvist
23105211f8 Merge pull request #349 from AsaAyers/master
Remove the text-shadows that make the text look blurry
2016-05-08 23:02:42 +02:00
Knut Sveidqvist
618bc4fbc2 Merge pull request #350 from raghur/gitgraph-updates
Gitgraph: Make reset work with parent ref carets
2016-05-08 20:05:19 +02:00
Raghu Rajagopalan
cbefb716e3 Make reset work with parent ref carets 2016-05-07 11:31:05 +05:30
Asa Ayers
d2cecca297 Remove the text-shadows that make the text look blurry 2016-05-04 13:55:50 -04:00
Knut Sveidqvist
8f8856b767 Merge pull request #346 from AlanHohn/line-interpolation
add line interpolation to linkStyle in flowchart
2016-04-29 07:39:32 +02:00
Alan Hohn
00682160b6 add line interpolation to linkStyle in flowchart 2016-04-28 20:59:09 -04:00
Knut Sveidqvist
6ffef61afc Merge pull request #344 from raghur/options
Support git graph diagrams in mermaid
2016-04-28 15:48:35 +02:00
Raghu Rajagopalan
75c9debb5e fix branching lines 2016-04-28 11:27:52 +05:30
Raghu Rajagopalan
6521cc81fe formatting clean up 2016-04-28 11:27:24 +05:30
Raghu Rajagopalan
b634439765 infer svg height, width from # of nodes 2016-04-27 10:05:50 +05:30
Raghu Rajagopalan
1d8d42ad08 Show commit msg if available in BT orientation 2016-04-27 09:47:39 +05:30
Raghu Rajagopalan
4e26b8e645 Support different colors for each branch 2016-04-27 09:27:56 +05:30
Raghu Rajagopalan
6a674a0a40 add support for BT rendering 2016-04-26 22:53:38 +05:30
Raghu Rajagopalan
a5a3bc8b7c fix logging output in prettyprint 2016-04-26 22:52:27 +05:30
Raghu Rajagopalan
6ff536fca7 fix eslintrc so eslint doesn't barf - duplicated keys 2016-04-25 16:35:20 +05:30
Raghu Rajagopalan
719ee2e001 fix more eslint issues 2016-04-25 16:34:45 +05:30
Raghu Rajagopalan
6c28c55ea7 Update node label distance. 2016-04-25 13:01:17 +05:30
Raghu Rajagopalan
6032e05581 fix eslint issues. 2016-04-25 12:38:48 +05:30
Raghu Rajagopalan
0538048f24 add case block for mermaid api parser. 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
89307d3f13 foreignObject works. labels work! Yeah! 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
5dcd3e6f35 Apply commit and branch labels using foreignObjects.
Only works in browser.
2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
59ef2b0d4f Options are working. 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
a92e116a21 remove magic nos in renderer 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
d8805ae4d7 WIP - prettier curves. 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
942bd02add handle multiple branches; handle unmerged branches 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
0d6d848325 WIP - code simplified. Draw arrows later. still ugly 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
c6494f92fa draw bezier curves - still butt ugly 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
9cb9407e1c working but ugly 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
43ab44d8ba fix LR layout - node position and arrow direction. 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
c2bc3317c3 WIP - basic arrows. 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
413c6e12f5 WIP - Bug - shows only 5 of 6 nodes with test file. 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
65140f6b3f WIP - able to draw circles now. 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
2fee6e62aa WIP - scaffolding for rendering gitgraph. 2016-04-25 11:36:36 +05:30
Raghu Rajagopalan
4b91dbfed6 gitgraph.jison - handle LF & CRLF as line endings 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
e17b35a96a WIP-hook up a renderer 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
44dc30c02c add pretty print function 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
038062b3c3 fix bug in tracking parent commits 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
5cb44d55ff logger improvements - allow multiple args 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
8befe916fb WIP - pretty print commit on console 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
d3caa9b97d handle merge commits in history walk 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
2d99ccfcda Handle merge commit with two parents 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
92be61dc48 make logger print object instead of string concatentation 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
2fecb4d945 handle case when merge is a noop 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
462c3d3a6c handle fast forward merges 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
422d78cbcd basic reset command 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
7b0e2513bd refactor fns in ast 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
f8f0454f33 add another test for checkout 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
d937a3c552 support orientation, commit msg, branch checkout 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
dbd2855bfc first couple of tests for the half baked parser 2016-04-25 11:35:44 +05:30
Raghu Rajagopalan
8084d80d64 WIP - very basic grammar 2016-04-25 11:35:44 +05:30
Knut Sveidqvist
d585121bdb Merge pull request #338 from ssbarnea/master
Build and test execution changes
2016-03-29 20:00:34 +02:00
Knut Sveidqvist
9e496175d9 Merge pull request #331 from Jmuccigr/master
Reformatting of css files
2016-03-29 19:59:00 +02:00
Sorin Sbarnea
e6fcc2b3e3 Added LTS and current STABLE versions of nodejs to Travis test matrix. 2016-03-29 14:16:25 +01:00
Sorin Sbarnea
f748969186 Removed dist/ directory from source as is not supposed to be tracked here. 2016-03-29 14:15:48 +01:00
Sorin Sbarnea
4750769c63 Configured travis to use an osx_image which has nvm preinstalled. 2016-03-29 12:54:58 +01:00
Sorin Sbarnea
8c5c2b0fe1 Attempt to convince Travis to install the missing nvm 2016-03-29 12:49:17 +01:00
Sorin Sbarnea
1b04dbf7f9 Fixed .gitignore 2016-03-29 12:41:57 +01:00
Sorin Sbarnea
97e208f081 Enabled OS X build on Travis. 2016-03-29 12:37:46 +01:00
Knut Sveidqvist
2de974f71d Merge remote-tracking branch 'origin/master'
# Conflicts:
#	dist/mermaid.js
#	dist/mermaid.min.js
#	dist/mermaid.slim.js
#	dist/mermaid.slim.min.js
#	dist/mermaidAPI.js
#	dist/mermaidAPI.min.js
#	dist/mermaidAPI.slim.js
#	dist/mermaidAPI.slim.min.js
2016-03-23 20:35:34 +01:00
Knut Sveidqvist
bda1aaa5db Draft fix for issue #304 2016-03-23 20:34:44 +01:00
John Muccigrosso
b69010d83b Merge pull request #2 from Jmuccigr/patch-2
Reformat with no changes to content.
2016-03-21 19:16:14 -04:00
John Muccigrosso
2e3bc443ff Merge pull request #1 from Jmuccigr/patch-1
Reformat with no changes to content.
2016-03-21 19:15:14 -04:00
John Muccigrosso
d2e3517c78 Reformat with no changes to content. 2016-03-21 19:11:38 -04:00
John Muccigrosso
e930c5a30c Reformat with no changes to content. 2016-03-21 19:10:33 -04:00
Knut Sveidqvist
fb500ace8a Merge pull request #320 from bronsoja/seq-diagram-title-support
(WIP) Sequence Diagram Title Support
2016-03-16 19:40:59 +01:00
Josh Bronson
4f9a2a8083 Update dist files 2016-03-14 18:09:27 -04:00
Josh Bronson
2acaef67b1 Support title in parser, add to renderer, with naive positioning logic 2016-03-14 17:51:03 -04:00
Knut Sveidqvist
1ad9e75a86 Merge pull request #318 from ciekawy/activation_draft
activations doc + few fixes
2016-03-14 07:20:15 +01:00
Knut Sveidqvist
e9c6db4d8c Merge pull request #317 from crodriguez1a/feature/dark-theme
Dark theme for better contrast on darker backgrounds
2016-03-14 07:14:37 +01:00
Szymon Stasik
d070029856 activations doc 2016-03-12 19:54:09 +01:00
Szymon Stasik
68b65ba5d4 adjust activation after message to self 2016-03-12 19:40:45 +01:00
Szymon Stasik
c3f840126c fix loop/block bounds with activations through them 2016-03-12 19:33:35 +01:00
Carlos Rodriguez
7404de6776 fix bad spacing 2016-03-12 13:24:53 -05:00
Carlos Rodriguez
d763fffd7a some clean up, fix mix.html 404 2016-03-12 13:23:31 -05:00
Szymon Stasik
0435509952 fix message to self with activation 2016-03-12 18:39:49 +01:00
Carlos Rodriguez
bba1050e5e Use mix as test file instead 2016-03-12 12:36:37 -05:00
Carlos Rodriguez
28f9e6f17b Starting with gantt theme 2016-03-12 12:00:47 -05:00
Knut Sveidqvist
b3d147b97a Merge pull request #316 from ciekawy/activation_draft
Activations
2016-03-12 16:41:43 +01:00
Szymon Stasik
3f85391bde better align activation box start 2016-03-12 15:50:11 +01:00
Szymon Stasik
513c35d0ef handle one line arrow activation notation 2016-03-12 15:27:37 +01:00
Szymon Stasik
499a2582e6 fix deactivating proper actor from activation stack 2016-03-12 13:58:22 +01:00
knsv
d6363e908d Fix for issue with rednering in IE as described in #303 2016-03-12 11:28:14 +01:00
knsv
325d0dd075 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	dist/mermaid.js
2016-03-12 11:24:22 +01:00
knsv
2c90034d39 Fix for issue with rednering in IE as described in #303 2016-03-12 11:23:57 +01:00
Szymon Stasik
614e9f4b87 draw activation boxes 2016-03-11 16:44:45 +01:00
Szymon Stasik
2452532557 draft of activation boxes handling 2016-03-08 10:40:52 +01:00
Knut Sveidqvist
6c621f243d Merge pull request #312 from ashsearle/master
Support leading comments for sequenceDiagrams
2016-03-04 07:16:02 +01:00
Ash Searle
9cedbea2cf Support leading comments for sequenceDiagrams 2016-03-02 17:26:21 +00:00
Knut Sveidqvist
2168cce020 Merge pull request #309 from ashsearle/master
Show a little lenience for white-space around names
2016-02-29 07:55:48 +01:00
Ash Searle
7858186ecd Show a little lenience for white-space around names 2016-02-28 18:44:14 +00:00
Knut Sveidqvist
d2e9918e5f Merge pull request #307 from maxArturo/add-to-downstream-projs
Update list of downstream projects
2016-02-27 16:21:10 +01:00
Max Arturo AS
9a7b498818 Update list of downstream projects
Add [Previm](https://github.com/kannokanno/previm), allows live viewing of mermaid diagrams + .md from vim
2016-02-24 11:27:24 -06:00
Knut Sveidqvist
3ff23b3c45 Merge pull request #300 from LarryKlugerDS/master
Issue 299: Sequence diagram Loops: changing boxMargin spoils the "loop" notation
2016-02-10 16:51:16 +01:00
LarryKlugerDS
0f7dda9dfc #296 Further edit of build instructions 2016-02-10 09:10:15 +02:00
LarryKlugerDS
eef6eb93f8 Issue #296 -- better build instructions 2016-02-10 09:08:19 +02:00
LarryKlugerDS
31f78d1aa9 Issue 299: Sequence diagram Loops: changing boxMargin spoils the "loop" notation
In Sequence diagrams, a loop section draws a box with the word "loop"
in the top left corner. This is all good. In addition, the title of the
loop is in the center of the box, near the top of the box.

To add a bit of margin between the Loop title and the line at the top
of the box, change the configuration field boxMargin

Problem: by error, the boxMargin is also used for positioning the
"loop" label. SInce the loop label is in a fixed small place, changing
boxMargin causes the word "loop" to move out of its box.
2016-02-10 08:33:12 +02:00
Knut Sveidqvist
193ee657e6 Merge pull request #298 from LarryKlugerDS/master
Issue 297 - src/mermaid.js generates bad code
2016-02-10 07:22:17 +01:00
LarryKlugerDS
b5a030c78d Issue 297 - src/mermaid.js generates bad code 2016-02-10 08:16:03 +02:00
Knut Sveidqvist
5e447e8938 Merge pull request #295 from LarryKlugerDS/master
Updated instructions for running tests
2016-02-09 19:14:41 +01:00
Knut Sveidqvist
b495a7d834 Merge pull request #288 from tylerlong/master
Add Markdown Plus to Downstream projects
2016-02-09 19:13:56 +01:00
LarryKlugerDS
9aa4a79d6e Updated instructions for running tests 2016-02-09 20:11:10 +02:00
Tyler Long
cabd0769a8 Add Markdown Plus to Downstream projects
Markdown Plus is a markdown editor with extra features. The frontend is open source under MIT license. It provides apps for OS X and Windows for purchase.
2016-02-02 09:39:50 +08:00
Knut Sveidqvist
803916c99b Merge pull request #286 from raghur/master
Quote phantomPath so that it doesn't fail on window
2016-02-01 20:31:17 +01:00
Raghu
54433ee555 Quote phantomPath so that it doesn't fail on window
Quote phantomPath so that it doesn't fail on windows when path has spaces. Fixes #236
2016-02-01 11:02:01 +05:30
Knut Sveidqvist
6ee298dc7d New release 2016-01-27 15:02:41 +01:00
knsv
9bd140ce42 Fix for issue windows builds as described in #284 2016-01-26 18:03:58 +01:00
knsv
f005c72669 Fix for issue #281, support for Chrome v 48 2016-01-25 18:59:03 +01:00
knsv
d56af022c1 Fix for issue #262 2015-12-27 15:57:11 +01:00
knsv
cc8c5aecaf Fix for issue #271 2015-12-27 14:18:21 +01:00
knsv
8c8f5928f5 Changes to build environment for windows compatability 2015-12-26 10:59:44 +01:00
Knut Sveidqvist
ab38e17936 Merge pull request #267 from Anoia/master
gh-50 Allow styling of edge labels in css
2015-12-08 22:45:45 +01:00
esther.machata
52fcd18e6a gh-50 Allow styling of edge labels in css 2015-12-08 17:26:17 +01:00
Knut Sveidqvist
9fb980ab2c Merge pull request #265 from gibson042/sequenceDiagram-alias
Allow sequenceDiagram participant aliasing
2015-12-06 13:42:53 +01:00
Richard Gibson
b42939796c Allow sequenceDiagram participant aliasing
Fixes gh-263
2015-12-06 05:53:08 -05:00
Richard Gibson
69ce29841d Test for sequenceDiagram participant aliasing 2015-12-06 05:51:02 -05:00
knsv
425a83c0da Fix for issue #259 2015-11-22 18:55:37 +01:00
knsv
a9f4f8e06c Fix for issue #259 2015-11-22 18:53:09 +01:00
knsv
989ff03e43 Set version to 0.5.6
Removed logging
One more iteration in handling of parser API
2015-11-22 18:10:38 +01:00
knsv
64b0517bac Added missing karma dependencies 2015-11-21 12:20:37 +01:00
knsv
f52fa46326 Added missing karma dependencies 2015-11-21 11:58:16 +01:00
knsv
51858c02eb Fix for issues #249. Adding configuration option for when to use absolute references for arrow heads. Default is off.
Jasmine tests running in browser via karma.
2015-11-21 11:51:15 +01:00
knsv
4eb38e4cfd Fix for issues #239 and #257. 2015-11-15 15:06:24 +01:00
knsv
4a5828c732 Version change of d3 to 3.5.6 2015-11-11 20:15:02 +01:00
knsv
774ebfe671 Version change of babelify 2015-11-11 18:37:53 +01:00
knsv
6e2534aa77 Labels for relations between classes in classdiagrams 2015-11-11 16:19:06 +01:00
knsv
ad4e0a2ad5 Labels for relations between classes in classdiagrams 2015-11-11 13:16:38 +01:00
knsv
24d1afd40f Redering arrow heads depending on relation defined in class diagram. 2015-11-07 10:00:40 +01:00
Knut Sveidqvist
2278325822 Merge pull request #253 from gibson042/optional-terminal-newline
Make sequenceDiagram terminal newline optional
2015-11-06 20:17:19 +01:00
Knut Sveidqvist
49c13f19ff Merge pull request #252 from gibson042/sequenceDiagram-over
Support sequenceDiagram "over" notes
2015-11-06 20:16:24 +01:00
Richard Gibson
dbd63ff797 Make sequenceDiagram terminal newline optional
Fixes gh-229
2015-11-06 10:47:59 -05:00
Richard Gibson
a59a5a2402 Update documentation 2015-11-06 10:24:51 -05:00
Richard Gibson
26ebbf82ee Support sequenceDiagram "over" notes 2015-11-06 03:00:02 -05:00
knsv
76130bc485 Updated distribution after PR #251 2015-11-06 07:36:14 +01:00
knsv
f79577c816 Merge remote-tracking branch 'origin/master' 2015-11-06 07:23:44 +01:00
Knut Sveidqvist
ecca3588bc Merge pull request #251 from gibson042/gh-247
Properly handle "rest of line" statements
2015-11-06 07:23:23 +01:00
Knut Sveidqvist
34209db57b Merge pull request #250 from gibson042/exit-code
CLI: Propagate exit code from lib (i.e., phantomjs)
2015-11-06 07:11:26 +01:00
Richard Gibson
ec37fc897b Clean up lexer definitions 2015-11-06 00:05:16 -05:00
Richard Gibson
5666d5f298 Properly handle "rest of line" statements
Closes gh-247
2015-11-06 00:05:00 -05:00
Richard Gibson
6ac0c00e30 Add no-label test cases 2015-11-06 00:04:01 -05:00
Richard Gibson
990cab53a9 Add special-character test cases 2015-11-06 00:04:01 -05:00
Richard Gibson
18766466c6 Minor test cleanup 2015-11-06 00:04:01 -05:00
Richard Gibson
73e1957c62 Be explicit about testing semicolons 2015-11-06 00:04:00 -05:00
Richard Gibson
e0a31feaec Make testing quieter 2015-11-06 00:04:00 -05:00
Richard Gibson
c62061090a CLI: Propagate exit code from lib (i.e., phantomjs) 2015-11-05 15:50:59 -05:00
Tomasz Szczęśniak-Szlagowski
0f689c2ea1 Commit built files 2015-11-05 19:40:42 +00:00
Tomasz Szczęśniak-Szlagowski
ef290796e0 Fix a bug in CSS cloning error handler
Firefox throws SecurityError when `cssRules` is accessed, which exposes
this bug.

Fixes #245
2015-11-05 19:38:36 +00:00
knsv
c8fef693fa Added extension markers for classDiagrams
Added styling (forest) for classDiagrams
2015-11-04 20:38:14 +01:00
knsv
a9ac654040 Class diagram example 2015-11-04 16:36:19 +01:00
Knut Sveidqvist
17be68f5d9 Merge pull request #244 from ma-zal/master
flowRender.js - Fix FontAwesome icon insert
2015-11-04 16:21:18 +01:00
Martin Zaloudek
5d7815c98a flowRender.js - Fix fontAwesome icon insert
Missing ending tag </i> in fontAwesome icon inserting.
2015-11-04 15:46:31 +01:00
knsv
7fe5b5af7a Rendering of class diagrams with methods and members represented 2015-11-01 19:00:14 +01:00
knsv
068b7ce6a9 Simple rendering of class diagrams 2015-10-30 11:34:24 +01:00
knsv
eec89e0442 Merge remote-tracking branch 'origin/master' 2015-10-30 10:47:51 +01:00
knsv
7a58e8261f Simple rendering of class diagrams 2015-10-30 10:47:25 +01:00
Knut Sveidqvist
eec0aced7f Merge pull request #240 from r-a-v-a-s/patch-1
updated links in README.md
2015-10-30 08:47:27 +01:00
ravas
74eb2e4493 updated links in README.md 2015-10-29 23:06:36 -07:00
knsv
01ddfea478 1st version parsing for class diagrams, parsing draft completed, starting rendering work 2015-10-29 07:49:08 +01:00
knsv
a694f61003 1st version parsing for class diagrams, fetching data from parsing 2015-10-28 08:12:47 +01:00
Knut Sveidqvist
d3a025acd6 Merge pull request #237 from spect88/ellipse-syntax
Ellipse syntax
2015-10-27 07:23:40 +01:00
Tomasz Szczęśniak-Szlagowski
51d5c90c9f Add build with new ellipse syntax 2015-10-26 23:03:07 +00:00
Tomasz Szczęśniak-Szlagowski
e4d25ed9cd Add support for node(-ellipse shape-) in flow charts 2015-10-26 23:03:07 +00:00
knsv
1a60b6e412 1st version parsing for class diagrams 2015-10-26 08:03:30 +01:00
knsv
dd6495abf6 1st version parsing for class diagrams 2015-10-25 18:09:58 +01:00
knsv
35865ba70e Starting work with addition of class diagrams 2015-10-25 11:35:26 +01:00
Knut Sveidqvist
cee267193f Merge pull request #235 from spect88/fix-keyword-suffix
Allow keywords as suffixes of node ids
2015-10-25 07:38:14 +01:00
Tomasz Szczęśniak-Szlagowski
30149df88e Allow node ids with keywords as suffixes
I've extracted alpha and punctuation characters from the huge regex and
made multiple occurences of alpha characters be recognised as a single
ALPHA token.

This way everything should work just like before, with the
difference that alpha strings will swallow keywords, ie. `spend` is
`ALPHA`, while previously it would have been `ALPHA ALPHA end`.
2015-10-24 22:25:13 +01:00
Tomasz Szczęśniak-Szlagowski
841aea9d97 Add changes in generated files after jison update 2015-10-24 21:33:19 +01:00
knsv
d68323d7bb Using the latest jison in build 2015-10-24 20:45:42 +02:00
knsv
a583daf712 Using the latest jison in build 2015-10-24 20:33:52 +02:00
knsv
cf1912523b Using the latest jison in build 2015-10-24 20:27:54 +02:00
knsv
eecd56d6e0 Fix for issue #229, poor handling of activities in a gantt diagram where one activities follows a yet undefined activity. 2015-10-24 12:44:47 +02:00
Knut Sveidqvist
1cd1423780 Merge pull request #230 from gitter-badger/gitter-badge
Add a Gitter chat badge to README.md
2015-10-23 20:13:16 +02:00
The Gitter Badger
68812b0f04 Add Gitter badge 2015-10-23 18:12:08 +00:00
knsv
c9d29c16e2 New release 2015-10-21 21:14:41 +02:00
Knut Sveidqvist
185233c971 Merge pull request #228 from tylerlong/master
Fix a typo: crosshead --> arrowhead
2015-10-21 16:19:00 +02:00
Tyler Long
ca42976c62 Merge pull request #1 from tylerlong/tylerlong-patch-1
Fix a typo: crosshead --> arrowhead
2015-10-21 03:08:29 -07:00
Tyler Long
9ff2b35ae0 Fix a typo: crosshead --> arrowhead
Fix https://github.com/knsv/mermaid/issues/227
2015-10-21 18:07:51 +08:00
knsv
ecb12ae2a8 New release 2015-10-19 22:08:17 +02:00
knsv
b43e695da2 Fix for issue #204, added width option to the CLI. Default value for width is 1200.
Added logger using es6 syntax
2015-10-19 21:36:55 +02:00
knsv
57b731842b Fixed uglify command 2015-10-17 15:21:34 +02:00
knsv
a8ef091cd6 Fixed uglify command 2015-10-17 15:12:59 +02:00
knsv
315923d1d3 Modernization of build environment, better linting, adjustment after stricter static rules, cleanup of package.json 2015-10-17 12:46:36 +02:00
knsv
632a564158 Modernization, better linting, adjustment after stricter static rules 2015-10-17 10:39:20 +02:00
knsv
8365fcc2f9 Modernization, better linting, adjustment after stricter static rules 2015-10-17 10:38:05 +02:00
knsv
2417741283 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	dist/mermaid.js
#	dist/mermaid.min.js
#	dist/mermaid.slim.js
#	dist/mermaid.slim.min.js
#	dist/mermaidAPI.js
#	dist/mermaidAPI.slim.js
#	dist/mermaidAPI.slim.min.js
#	package.json
2015-10-15 07:21:30 +02:00
Knut Sveidqvist
e0033965bc Merge pull request #224 from spect88/fix-end-substring
Allow `end` as a substring of vertex id
2015-10-15 07:16:59 +02:00
Knut Sveidqvist
ae73583d53 Merge pull request #223 from spect88/remove-duplicate-dependencies
Remove duplicate npm dependencies: d3 and he
2015-10-15 07:14:49 +02:00
Tomasz Szczęśniak-Szlagowski
b87764ed94 Allow end as a substring of vertex id
Jison adds \b (word boundary) to literal string patterns by default.
It does so, because it doesn't follow traditional match-longest
approach, but does match-first instead. Without including word
boundaries, it'd be hard to distinguish between a keyword and
identifier.

The pattern for `end` keyword is not a simple string literal - it
swallows trailing whitespace, so we have to add \b manually.

This partially fixes #184 - at least now `end` behaves the same as other
keywords: it can be used as a prefix and infix, but not as a suffix.

To solve this issue completely, ALPHA pattern would have to match
multiple letters, which is a much bigger change.
2015-10-15 00:31:18 +01:00
Tomasz Szczęśniak-Szlagowski
7e9e9cc98c Remove duplicate npm dependencies: d3 and he
These are already defined as dependencies (same versions), so there's
no point keeping them as devDependencies. npm complains about that.
2015-10-14 21:52:41 +01:00
knsv
658ed3d790 Modernization of build environment. Less gulp, more npm. Eslint. 2015-10-12 07:37:02 +02:00
knsv
ed65e6df3b Draft of font-awesome support as described in issue #49. 2015-10-07 18:35:30 +02:00
knsv
16906ee242 New version 2015-10-04 23:28:37 +02:00
knsv
a611ff3abd New version 2015-10-04 23:09:00 +02:00
knsv
8258fb059c Fix for defect #180, SVG foreignObject rendering 2015-10-04 21:18:05 +02:00
knsv
122274bf52 Fix of broken tests 2015-10-04 19:30:53 +02:00
knsv
c5d41c5a21 Added support for entity codes for sequence diagrams so that it for instance is possible to represent a " with #quot; and a heart with #9829;. This as referenced in issue #74 and issue #193. 2015-10-04 14:05:53 +02:00
knsv
a2b6bc5213 Added support for entity codes so that it for instance is possible to represent a " with #quot; and a heart with #9829; This differs from the regular html codes in that the leading & isreplaced with a dsh and for dec codes dropped. This as referenced in issue #219. 2015-10-03 21:50:32 +02:00
knsv
cb5e88c2f1 Documentation of usage of the mermaidAPI and binding user interaction events 2015-10-03 16:53:27 +02:00
knsv
8bc3bdd300 Added styling for mermaid tooltips
Adjustments of tooltip positioning for large documents

Documentation of tooltips
2015-10-03 16:30:50 +02:00
knsv
b3fa6378bd Reenabled all tests.
Subgraph issue with ids written as title resolved.
2015-10-02 00:21:38 +02:00
knsv
e406fda9cd Draft implementation of tooltips and hyperlinks as described in issue #34. More tests and documentation to follow. 2015-10-02 00:18:47 +02:00
knsv
30a755221b Fix for broken build 2015-09-27 20:51:22 +02:00
knsv
40efbc5b84 Fix for issue #210, classDef broken also added some documentation on how to predefine classes in the css and set them from the graph definition. 2015-09-26 18:32:35 +02:00
knsv
dc1a6ba8b5 Wit 2015-09-26 18:30:13 +02:00
knsv
709ebe524d New docs and updated dist 2015-09-26 13:00:30 +02:00
knsv
fb94aaaa6f Fix for issue #209, missing links when baste tag is used
Fix for issue #195, text wrap in sequence diagrams drops last word
Documentation
2015-09-26 12:09:47 +02:00
knsv
bae1e80ac0 Updated package.json 2015-09-24 18:41:38 +02:00
knsv
4536930e17 New style docs 2015-09-24 08:04:06 +02:00
knsv
189325508e Usage of path.join for adding styles as per suggestions from @fardog 2015-09-16 07:30:25 +02:00
Knut Sveidqvist
c81e933dc3 Merge pull request #205 from gillesdemey/dev-default-style
Default style when using the CLI
2015-09-16 07:23:39 +02:00
Gilles De Mey
85481b2310 Added test for using a default style 2015-09-15 19:22:07 +02:00
Gilles De Mey
b04f198f3e auto width for the default style 2015-09-15 19:11:05 +02:00
Gilles De Mey
a989b5ccde fall back to using the default mermaid style 2015-09-15 19:10:53 +02:00
Knut Sveidqvist
4ee0f9b6be Merge pull request #198 from dbrans/master
Gantt chart - add minutes and seconds durations
2015-08-29 18:50:34 +02:00
Derek Brans
1d371e0a1e Gantt chart - add minutes and seconds durations 2015-08-25 14:49:56 -07:00
knsv
3ec7c6d18b Fix for being able to use the character v in node ids as described in issue #192 2015-07-23 15:33:26 +02:00
Knut Sveidqvist
80912ffaee Merge pull request #190 from bollwyvl/amd
Using QUnit for AMD testing
2015-07-22 08:46:51 +02:00
Nicholas Bollweg
4777fe0d91 doing build before test 2015-07-22 01:15:05 -04:00
Nicholas Bollweg
feddb6d048 fixing weird sort, travis bait 2015-07-22 01:00:45 -04:00
Nicholas Bollweg
b148ec0850 restoring standalone 2015-07-22 00:47:03 -04:00
Nicholas Bollweg
1a36ed9786 adding failing test for requirejs 2015-07-22 00:36:31 -04:00
knsv
1b7ad1fc38 Beta fix for binding of click events when using the render function as discussed in issue #188. (Try num 2) 2015-07-18 00:40:42 +02:00
knsv
71d4113ecf Beta fix for binding of click events when using the render function as discussed in issue #188. 2015-07-17 23:13:40 +02:00
knsv
ed712fa673 Fix for startOnLoad issues 2015-07-15 11:59:01 +02:00
knsv
fcb2af780d Added callback to be called after each diagram is rendered as described in issue #176 2015-07-15 11:39:46 +02:00
knsv
a8c8f9a472 Set height of actor line height in sequenceDiagrams as discussed in issue #181 2015-07-04 10:00:14 +02:00
knsv
ab1dd1e549 Fix of broken logger 2015-07-04 09:30:26 +02:00
knsv
145983fc5d Merge remote-tracking branch 'origin/master' 2015-07-03 08:53:10 +02:00
knsv
9fb45a3ab7 Cleanup 2015-07-03 08:52:55 +02:00
knsv
b97e5ff5b0 Cleanup 2015-07-03 08:52:34 +02:00
knsv
0ca04671ed New version of docs 2015-07-03 08:52:01 +02:00
knsv
6e062b241e New version of docs 2015-07-03 08:51:10 +02:00
knsv
df77276805 New version of docs 2015-07-03 08:50:32 +02:00
Knut Sveidqvist
a85a694cc1 Merge pull request #182 from phairow/master
Update phantomscript.js
2015-07-03 08:39:29 +02:00
phairow
590fa0894b Update phantomscript.js
clean up
2015-07-02 17:52:37 -07:00
phairow
4d7f7d0756 Update phantomscript.js
attempt two at fixing the rendering issue
2015-07-02 17:32:13 -07:00
phairow
af0d40e907 Update phantomscript.js
Computing the display size seems to cause the svg rendering to complete bofore saving the image. Referencing issue #181 https://github.com/knsv/mermaid/issues/181
2015-07-02 14:53:35 -07:00
knsv
6f96b5dd14 Added logger for dealing with issue #179
Added markdown files for the documentation.
parseError exposed from the mermaidAPI
2015-06-30 14:23:32 +02:00
knsv
0dc983d04a New release 2015-06-21 17:26:19 +02:00
knsv
50219cf2d9 Fix for issues with generating diagrams using the cli: default style for clusters and fixes for autoscaling side effects 2015-06-21 17:25:58 +02:00
knsv
4ed345101a Fix for issue #178, auto-line wrap of notes in sequence diagrams 2015-06-20 20:58:58 +02:00
knsv
e5a701d04d Fix for issue #175 2015-06-16 10:40:08 +02:00
knsv
342b83a010 Fix for issue #174 2015-06-14 14:46:01 +02:00
knsv
eb555f7da6 Deleted dead code and unused code 2015-06-14 09:51:42 +02:00
knsv
8a0c857a92 Merge remote-tracking branch 'origin/master' 2015-06-14 09:39:00 +02:00
knsv
31d5899011 Fix for issue #170 2015-06-14 09:37:59 +02:00
knsv
14520e8bd5 Path fix for jison parser generation 2015-06-14 09:37:25 +02:00
knsv
efb4e464b2 Fix for issue #170 2015-06-14 09:33:48 +02:00
Knut Sveidqvist
6042ca9c56 Merge pull request #172 from kkirsche/patch-1
Remove moot `version` property from bower.json
2015-06-14 09:32:44 +02:00
knsv
e5c16fd960 Added subgraphs in new grammar 2015-06-13 18:34:45 +02:00
knsv
6eb018489e Fix issue with new line in the lexer 2015-06-13 08:19:31 +02:00
knsv
5447a88c34 Cleanup of bundles 2015-06-11 08:22:01 +02:00
Kevin Kirsche
4fbebcfab4 Remove moot version property from bower.json
Per bower/bower.json-spec@a325da3
2015-06-10 18:51:14 -04:00
Knut Sveidqvist
35e4cd7c18 Update README.md 2015-06-07 21:09:09 +02:00
knsv
999430f9e0 New release 2015-06-07 17:05:57 +02:00
knsv
be535604ab Cleanup of bundles 2015-06-07 16:56:09 +02:00
knsv
0703292fb9 Bumped up version and removed logging 2015-06-07 16:51:56 +02:00
Knut Sveidqvist
c0ca932ccf Merge pull request #168 from knsv/dev-0.5.0
Merge of changes in dev-0.5.0 branch into master before release
2015-06-07 09:32:51 +02:00
knsv
08fa19bc83 Updated conf handling
Added initialize function
Moved genric configuration as clonseCssStyles till conf root
Added parse funtion to the mermaidAPI api
2015-06-07 09:21:19 +02:00
knsv
55fa62d9c0 Chineese example 2015-06-06 12:13:55 +02:00
knsv
94e5177bb1 Fixed build issue 2015-05-31 08:19:26 +02:00
knsv
45f34d871b Added option for whether or not to cloneCssStyles as mentioned in issue #157. 2015-05-30 17:20:24 +02:00
knsv
173a2b12aa Fix for issue #150. 2015-05-30 13:00:04 +02:00
knsv
3f0ead3e0a Fix for issue #150 this change makes gant diagram autoscale. 2015-05-30 12:59:20 +02:00
knsv
6864652b80 Initial fix for defect #162. 2015-05-29 08:23:13 +02:00
knsv
ca99a30b71 Example page for using the slim bundle and with some nested subgraphs. 2015-05-28 23:05:49 +02:00
knsv
b2e489b689 Fix for issue #161 concering nested subgraphs and correct labels for each subgraph. 2015-05-28 23:02:41 +02:00
knsv
cf67307711 Fix for build scripts, mermaid full was not including d3. 2015-05-26 21:10:33 +02:00
knsv
2d1f5aa6cb Adding missing files 2015-05-26 20:59:23 +02:00
Knut Sveidqvist
56375faa65 Update README.md 2015-05-26 20:45:30 +02:00
Knut Sveidqvist
1236c0c272 Update README.md 2015-05-26 20:44:44 +02:00
knsv
b4a96c9b21 Render function as mentioned in issue #146, only works in browser context
Updated build scripts
New way for bundling content in dist, tobe tested, currently to be considered beta
2015-05-26 20:41:53 +02:00
knsv
05f3982632 Fix for defect #161, nested subgraphs. 2015-05-15 12:11:36 +02:00
knsv
9face45357 Merge remote-tracking branch 'origin/master'
Conflicts:
	dist/mermaid.full.js
	dist/mermaid.full.min.js
	dist/mermaid.slim.js
	dist/mermaid.slim.min.js
	src/diagrams/gantt/ganttRenderer.js
	test/gantt.html
2015-05-09 19:18:12 +02:00
knsv
824a43f537 Added support for nested subgraphs in grammar, part 1 of issue #161 2015-05-09 19:08:04 +02:00
knsv
ae6bb57cf5 Added support for nested subgraphs in grammar, part 1 of issue #161 2015-05-09 19:05:47 +02:00
knsv
82dffe0a1d Fix for defect #158 2015-04-20 21:22:37 +02:00
knsv
22cee7e4b0 Fix for defect #158 2015-04-20 21:22:05 +02:00
knsv
c65f6aaed3 Merge remote-tracking branch 'origin/master' 2015-04-20 21:21:37 +02:00
knsv
2204d46ce1 Fix for defect #158 2015-04-20 21:21:17 +02:00
Knut Sveidqvist
11a30dd505 Merge pull request #155 from tylerlong/master
IE, local html, cssRules access is denied
2015-04-19 10:14:59 +02:00
Tyler Long
e0d90def93 IE, local html, cssRules access is denied
IE, open html file directly from hard disk(insead of from a http server), cssRules access is denied.

Thus an expection is thrown. But in the catch statement, another one is thrown again thus caused the whole mermaid code break. 

In this line `console.warn('Invalid CSS selector "' + rule.selectorText + '"', err);`  rule is undefined, so exception will be thrown if we don't check.
2015-04-19 15:58:09 +08:00
knsv
f310eb0574 Fix for default arguments to init method 2015-04-11 16:47:14 +02:00
knsv
2bd7dee3c7 Fix for init not running by default 2015-04-11 16:36:04 +02:00
knsv
594e69dd93 Adjusted cli tool for new init functionality 2015-03-29 12:26:47 +02:00
knsv
1fd94f9cf1 Remoed a console.log 2015-03-29 12:08:07 +02:00
knsv
78d8ee01ac #Adjustments of init - **could break some integrations!!**
* Configuration are picked up from the mermaid object and is not passed as arguments. Same handling for all diagram types, sequenceDiagrams were handled in a different way before this commit.

When init is called with:

* 0 arguments - all mermaid divs are processed
* 1 argument - this is interpreted as a definition of what nodes to process
* 2 arguments - for (some) backwards compatability the second argument is interpreted as the definition of nodes to process. The first argument (prrobably a sequence config is ignored)

A definition of nodes to process can be

* a css selector for what elements to be processed
* a list of nodes as in the result of a command like the one below

```
document.querySelectorAll('.tbProcessed');
```
2015-03-29 11:54:54 +02:00
knsv
c966aad496 Fix for subgraphs 2015-03-22 18:36:17 +01:00
knsv
0ed5a01756 Fix fir defect #141 regarding comment characters 2015-03-22 18:02:45 +01:00
knsv
22b9ee4919 Comment handling 2015-03-22 17:51:13 +01:00
knsv
3781dea498 Removed duplicate code 2015-03-15 14:55:16 +01:00
Knut Sveidqvist
60392424dc Merge pull request #139 from skywinder/add-change-log-file
Add automatically generated change log file.
2015-03-14 09:20:35 +01:00
Petr Korolev
23bd06c2d3 Added automatically generated change log file 2015-03-13 11:36:49 +02:00
Knut Sveidqvist
1644f06ec8 Merge pull request #137 from bollwyvl/patch-4
Adding init argument to the global API
2015-03-10 20:44:01 +01:00
bollwyvl
f591fedb77 Adding init argument to the global API
Will also handle future arguments :)
2015-03-10 15:38:53 -04:00
Knut Sveidqvist
b0cf9836af Merge pull request #135 from bollwyvl/patch-2
Allow other forms of node selection for init()
2015-03-10 20:07:58 +01:00
Knut Sveidqvist
34ee8a0e21 Merge pull request #134 from bollwyvl/patch-1
Use a library-level variable for assigning ids
2015-03-10 20:06:43 +01:00
bollwyvl
d61aac362c Allow other forms of node selection for init()
The existing behavior of init will always re-render the whole page, and requires that a chart be classed `mermaid`.

This change allows the user to specify:
- a DOM Node (as from getQuerySelector)
- a DOM NodeList  (as from getQuerySelectorAll)
- an array of nodes (as from jQuery.find)
- a string (to be handed to getQuerySelectorAll)
2015-03-10 13:25:16 -04:00
bollwyvl
6aff481e9b Use a library-level variable for assigning ids
The current behavior is unexpected when re-running `init()`, as every run through will start with the first `.mermaid` it finds, and start re-indexing at 0, irrespective if the original `id=mermaidChart0` is still around.

This change ensures that successive runs will not clobber old ones.
2015-03-10 13:06:39 -04:00
knsv
3ae1b5f1a3 Fix for issue #129 - Possibility to set the width of the generated flowchart 2015-03-07 14:52:18 +01:00
knsv
cbebf126ce Fix for issue #128 - flowchart - styling of edges via css overrides specific styles set in the graph definition 2015-03-07 13:51:47 +01:00
knsv
3fedc1263c Fix for issue #126 2015-03-07 13:37:24 +01:00
knsv
f4b2211f8f Fix for issue #37
Updated dependencies to dagre/dagre-d3
2015-03-07 12:30:16 +01:00
knsv
bc5f73daa2 Fix for issue #37
Updated dependencies to dagre/dagre-d3
2015-03-07 12:28:52 +01:00
knsv
ec9094696b Modified the number of week for which the x-axis stays in week mode as discussed in issue #125 2015-03-05 08:12:17 +01:00
knsv
85cdfbaaf8 Modified the number of week for which the x-axis stays in week mode as discussed in issue #125 2015-03-05 08:11:29 +01:00
Knut Sveidqvist
421409181d Added the css files 2015-03-02 09:53:56 +01:00
knsv
cc1a4be15a Added default stylesheet and less structure to create new themes as described in issue #122 2015-03-01 16:48:16 +01:00
knsv
ead74558ea Added default stylesheet and less structure to create new themes 2015-03-01 16:29:41 +01:00
knsv
3c5a19e5a7 Gantt: Configurable format for dates on x-axis
Flowchart: Default styles for links as discussed in issue #31
2015-02-28 23:50:23 +01:00
knsv
8598a7d712 More forgiving flowcharts, space at the end of the line allowed 2015-02-25 07:57:26 +01:00
knsv
0955c4c428 Today line for gantt chart 2015-02-25 00:07:13 +01:00
knsv
182729e015 Small fixes for linting 2015-02-24 23:03:09 +01:00
Knut Sveidqvist
a7dd6a6614 Better width calculations for task texts 2015-02-24 15:48:41 +01:00
knsv
e5657d028d Better styling for gantt diagrams and addition of critical path, active and completed tasks 2015-02-22 20:18:44 +01:00
knsv
4078cf3aed Removed semantic-ui form package 2015-02-20 20:42:59 +01:00
knsv
a7339eaf1c Experimental support for gantt diagrams 2015-02-20 19:34:18 +01:00
knsv
2877501ff5 Experimental support for gantt diagrams 2015-02-20 19:06:15 +01:00
knsv
1b016bd412 First rendering draft 2015-02-20 16:22:37 +01:00
knsv
2512666f49 New release 2015-02-15 19:34:18 +01:00
knsv
074a819ca8 New release 2015-02-15 18:11:46 +01:00
knsv
c211434c38 Draft parser for gantt diagrams. 2015-02-08 20:07:15 +01:00
Knut Sveidqvist
7896a7152e Merge pull request #116 from fardog/cli-css
Adds CSS option to the CLI
2015-02-03 22:17:48 +01:00
fardog
3a96682b9c Adds CSS option to the CLI 2015-02-02 19:32:15 -08:00
Knut Sveidqvist
c4639d11c8 Merge pull request #112 from jasonbellamy/bower
Ignore all files except the license and dist/ folder when installing with Bower.
2015-01-29 20:59:19 +01:00
Jason Bellamy
10b47b5785 ignore all files except the license and dist/ folder 2015-01-28 12:03:19 -05:00
knsv
5452be9fed New release 2015-01-25 16:41:43 +01:00
knsv
4562a811fc Better margin calculation when there are several diagrams on one page. 2015-01-25 16:40:12 +01:00
knsv
910b90b79c Better margin calclulation when there are several diagrams on one page. 2015-01-25 16:39:34 +01:00
knsv
a38a156d3b Mirror actors below sequence diagram possible as described in issue #106 2015-01-25 14:24:58 +01:00
knsv
d3d44ec806 Api for validating the syntax without rendering 2015-01-25 13:06:25 +01:00
knsv
6612b3e01e Fix relatwed to issue number #54 - % in text 2015-01-24 19:33:10 +01:00
knsv
d0428d492b Fix for issue number #108 - v in text 2015-01-20 20:23:02 +01:00
knsv
ee6ad01209 Merge branch 'master' of https://github.com/knsv/mermaid
Conflicts:
	dist/mermaid.full.js
	dist/mermaid.full.min.js
	dist/mermaid.slim.js
	dist/mermaid.slim.min.js
2015-01-20 20:04:16 +01:00
knsv
33973354ed * Removed logging 2015-01-20 19:53:38 +01:00
knsv
2af4a1f9f8 * Removed logging 2015-01-20 19:52:13 +01:00
knsv
44a2e0472a * Draft implementation of info diagram
* Fix for issue #109
2015-01-20 19:48:33 +01:00
Knut Sveidqvist
699d3bca52 Merge pull request #107 from markdalgleish/require-d3
Require d3 directly to better support Node usage
2015-01-20 07:28:03 +01:00
Mark Dalgleish
50d285bc49 Require d3 directly to better support Node usage 2015-01-20 16:59:58 +11:00
knsv
48500652b3 Allow overriding sequence diagram configuration (SVG properties) - handling of non existent configuration 2015-01-14 19:10:39 +01:00
knsv
8e050d4223 Merge branch 'jjmr-sequence_config_file' 2015-01-14 18:11:14 +01:00
knsv
73f567f3e2 Merge branch 'sequence_config_file' of https://github.com/jjmr/mermaid into jjmr-sequence_config_file
Conflicts:
	dist/mermaid.full.js
	dist/mermaid.full.min.js
	dist/mermaid.slim.js
	dist/mermaid.slim.min.js
	src/main.js
2015-01-14 18:10:21 +01:00
Knut Sveidqvist
1033a74970 Merge pull request #101 from bjowes/text-labels
Text based labels, new shape
2015-01-14 08:13:49 +01:00
Björn Weström
595fe1fd32 New build 2015-01-14 00:06:38 +01:00
Björn Weström
9095b2df97 Corrected example syntax 2015-01-14 00:06:14 +01:00
Björn Weström
8e45e7284b Removed extraction of dagre-d3
Pending resolution of a issue with globals
2015-01-14 00:05:58 +01:00
Björn Weström
fe437550fc Test cases for styling 2015-01-14 00:05:06 +01:00
Björn Weström
84124c9427 Improved CSS cloning
Better separation of styles between multiple mermaid SVGs on the same
page
Added support for text label styling
2015-01-14 00:04:38 +01:00
Björn Weström
3fe38237e6 New config attribute, htmlLabels 2015-01-14 00:03:41 +01:00
Björn Weström
9c8e36ea95 Syntax suggestion for reverse asymmetric shape 2015-01-14 00:03:19 +01:00
Björn Weström
8e9890d6e1 New shape, text labels
Added support to configure mermaid to use plain text labels for better
SVG compatibility
Added a reverse asymmetric shape, not in the graph syntax yet
2015-01-14 00:02:58 +01:00
Björn Weström
1abb53ce00 Merge branch 'master' into text-labels 2015-01-13 22:13:51 +01:00
Björn Weström
fec3101f37 Merge pull request #5 from knsv/master
Update README.md
2015-01-13 22:13:30 +01:00
Björn Weström
a23fce6c2d Cleanup after merge 2015-01-13 21:55:10 +01:00
Björn Weström
9f970ed953 Merge branch 'master' into text-labels
Conflicts:
	dist/mermaid.full.js
	dist/mermaid.full.min.js
	dist/mermaid.slim.js
	dist/mermaid.slim.min.js
	src/diagrams/flowchart/flowRenderer.js
	src/utils.js
	test/web_style.html
2015-01-13 21:50:05 +01:00
jjmr
9b892ef128 Add new parameter to the console client to override the svg configuration in sequence diagrams 2015-01-13 16:17:30 +01:00
Knut Sveidqvist
0b2afb8e71 Update README.md 2015-01-12 07:31:51 +01:00
Björn Weström
cbd7393d36 Additional tests for labels 2015-01-11 16:17:02 +01:00
Björn Weström
a27b3d8fcb Added edge labels 2015-01-11 16:16:50 +01:00
Björn Weström
9246df4410 Break out dagre-d3 dependency 2015-01-11 16:16:18 +01:00
Björn Weström
5ad30c86c5 Improved style handling 2015-01-11 16:16:07 +01:00
Björn Weström
43d2b0b9d7 Improved style handling 2015-01-11 16:15:57 +01:00
Björn Weström
367d620a82 Break out dagre-d3, new label styling 2015-01-11 16:15:38 +01:00
Björn Weström
1b76160a7d Break out dagre-d3 dependency 2015-01-11 16:15:19 +01:00
Björn Weström
d9f8ef6d57 Generated 2015-01-11 16:14:34 +01:00
Björn Weström
58659c4d85 Merge pull request #4 from knsv/master
Merge from master
2015-01-11 16:12:13 +01:00
knsv
911cd09dd8 New release 2015-01-11 15:02:35 +01:00
knsv
27687fc742 Added notation for titles on subgraphs 2015-01-10 19:23:57 +01:00
knsv
3027882847 Added notation for dotted links as described in issue #26 and support for thicker links 2015-01-10 14:33:50 +01:00
Knut Sveidqvist
a7fd52237a Merge pull request #96 from gkchic/master
Merge pull request #1 from knsv/master
2015-01-10 09:46:04 +01:00
knsv
36b389b1fd Added arrow head to async arrow with cross 2015-01-09 08:21:48 +01:00
knsv
952723706b Making it possible to have style/% in class definitions as described in issue #54 2015-01-08 08:20:07 +01:00
knsv
5a720b6d63 Introducing subgraphs 2015-01-07 21:02:58 +01:00
gkchic
0ae130de0a Merge pull request #1 from knsv/master
Update from original
2015-01-07 13:23:17 +01:00
knsv
273eb63efb Added link to demos page 2015-01-07 11:30:08 +01:00
knsv
c5c995c916 Added the option to disable auto rendering via the mermaid namespace as described in issue #91
Added optional ; as statement separator equal to newline for sequence diagrams (help for tidy users)
2015-01-06 19:33:00 +01:00
knsv
222a6e0682 New release 0.3.1 2015-01-05 15:32:39 +01:00
knsv
7741f49ed8 Move of drawLoop to svgDraw 2015-01-05 15:06:01 +01:00
knsv
786d9ddb54 Move of drawLoop to svgDraw 2015-01-05 14:41:00 +01:00
knsv
03a711d82d Cleanup of code 2015-01-05 14:12:49 +01:00
knsv
758a9d7254 Cleanup of code 2015-01-05 13:41:32 +01:00
knsv
9bc23129d5 Html file for testing sequence diagrams 2015-01-05 13:26:15 +01:00
knsv
343bd52c17 Support for alt & opt statements. 2015-01-05 13:25:37 +01:00
knsv
f9943b6b82 Support for different arrow types in sequence diagrams: no arrow head, cross head and regular arrow head. 2015-01-04 17:56:58 +01:00
knsv
c7e2c2b3c6 Fix for issue #84 2015-01-03 15:34:49 +01:00
Knut Sveidqvist
c10b4553ae Update README.md
Referring further reading to new documentation site
2014-12-31 11:32:25 +01:00
Knut Sveidqvist
c8d1b388d8 Merge pull request #79 from it0a/master
Fix for issue #73
2014-12-31 09:57:50 +01:00
it0a
da37b4dee5 Fix for issue #73 2014-12-31 02:02:39 -05:00
Knut Sveidqvist
9f904da7b3 Merge pull request #75 from fardog/enhancement-71
Show help and version even if phantom isn't present. Fixes #71
2014-12-28 12:06:14 +01:00
fardog
7bcbe6f469 Show help and version even if phantom isn't present. Fixes #71 2014-12-28 00:26:37 -08:00
Knut Sveidqvist
439cca926d Merge pull request #72 from sudodoki/modify/contibuting
Add apostrophe & 'and'
2014-12-28 09:21:54 +01:00
Oleksandr Lapshyn
5bc20a4b08 Add apostrophe & 'and' 2014-12-27 20:05:15 -05:00
knsv
26d0908b27 Fix for issue #53 2014-12-27 09:46:28 +01:00
knsv
385e823c17 Fix for issue #70 2014-12-23 18:06:07 +01:00
knsv
83d9a3269e Merge branch 'master' of https://github.com/knsv/mermaid
Conflicts:
	README.md
2014-12-22 13:58:30 +01:00
knsv
c50178c640 Merge branch 'master' of https://github.com/knsv/mermaid
Conflicts:
	README.md
2014-12-22 13:52:00 +01:00
knsv
96b12e876e New release 2014-12-22 13:49:38 +01:00
knsv
9a8d7a8c57 Handling of min size of messages to self 2014-12-22 13:47:21 +01:00
Knut Sveidqvist
d4b35b5594 Update README.md
Added an image example of a sequence diagram
2014-12-22 13:22:45 +01:00
knsv
0af92a1626 Support messages to self in sequence diagrams 2014-12-22 09:07:28 +01:00
knsv
c3eb62255f Support for backslash in nodes 2014-12-22 08:11:49 +01:00
knsv
5fc873468d Styling of sequence diagrams from css classes 2014-12-21 20:59:11 +01:00
knsv
307c599a0e Inclusion of tape tests in new test target for travis 2014-12-21 11:25:31 +01:00
Knut Sveidqvist
a83639addd Merge pull request #69 from fardog/master
Adds Command Line Interface for generating PNGs from mermaid description files
2014-12-21 09:18:18 +01:00
fardog
8920411b3c Make obvious that README known issues section is for the CLI 2014-12-20 17:50:45 -08:00
fardog
46a7eaea7d Better information around CLI's phantomjs requirement 2014-12-20 17:49:06 -08:00
fardog
d2513e9b50 Adds CLI information to the README. 2014-12-20 17:46:23 -08:00
fardog
f349ddd796 Adds CLI tests 2014-12-20 17:40:58 -08:00
fardog
c703c9a0d3 Adds CLI for rendering mermaid files 2014-12-20 17:18:38 -08:00
Björn Weström
6a1550fbdf Merge pull request #3 from knsv/master
Merge from master
2014-12-20 19:02:32 +01:00
knsv
9458bfb24f Sequence diagram loop margins
Handling of arrows to the left in loops in sequence diagrams
Addition of labels
2014-12-20 18:41:20 +01:00
knsv
7bb75c16da Work with loop rendering for sequence diagrams 2014-12-20 09:19:56 +01:00
knsv
69c84df367 Box rendering 2014-12-20 09:18:12 +01:00
knsv
000ffbb622 Modifications of sequence diagram rendering + tests 2014-12-20 08:40:48 +01:00
knsv
53d5221aed Minor changes 2014-12-18 23:47:30 +01:00
knsv
0dc8aa32c6 Added additional tests for sequence diagram rendering. Removed some log output 2014-12-18 23:17:32 +01:00
Knut Sveidqvist
8fa47dac37 Merge pull request #66 from vijay40/master
Allow special symbols for direction along with acronyms
2014-12-17 19:20:10 +01:00
vijay40
cebe033b4b The directions can now be specified with special symbols along with acronyms. So >,<,^,v can be used in place of LR,RL,BT,TB respectively. 2014-12-17 15:24:28 +05:30
knsv
0a3e9ac8e8 Small change to the README reflecting the new syntax without semicolons for the flowcharts 2014-12-16 23:23:27 +01:00
knsv
a1e415686c Adding support for loops to the sequence diagram grammar. 2014-12-16 20:51:48 +01:00
knsv
21daaf3e29 Adding support for notes to the left of the actor in sequence diagrams. 2014-12-16 20:12:24 +01:00
knsv
5712c6de7b Release 0.2.16 2014-12-15 19:35:09 +01:00
Knut Sveidqvist
e32cbe3004 Merge pull request #63 from vijay40/master
New grammar will allow statements ending without semicolon as disccused in Issue #38
2014-12-15 19:00:28 +01:00
vijay40
3279edab33 Flow grammer is modified to allow each line ending without semicolon to improve readablity. The old declarations also work. One test is also included in flow.spec.js to test the changes. 2014-12-15 15:32:47 +05:30
Knut Sveidqvist
13a65db40e Merge pull request #62 from bjowes/master
Class based styling
2014-12-14 22:49:32 +01:00
Björn Weström
e550ef9e25 Merge branch 'pr/2'
Conflicts:
	.gitignore
	dist/mermaid.full.min.js
	dist/mermaid.slim.min.js
2014-12-14 22:30:42 +01:00
Björn Weström
429b901e7a Removed unused variables 2014-12-14 22:18:16 +01:00
Björn Weström
f656269a50 Improved class styling for nodes
Added unit testing
classDefs are now exported to the common style of the SVG
Styling is no longer copied from other mermaid SVGs on the same webpage
2014-12-14 22:15:47 +01:00
knsv
eb7c341046 Removed logging from sequence diagram test 2014-12-14 19:34:14 +01:00
knsv
71b6a664f7 Support for comments to the right of the Actor 2014-12-14 19:30:58 +01:00
knsv
7de9687911 Support for comments to the right of the Actor 2014-12-14 19:30:58 +01:00
Knut Sveidqvist
7e3bd2e574 Merge pull request #60 from sublimino/patch-1
Fix typos
2014-12-14 17:46:52 +01:00
Andrew Martin
764907ce34 Fix typos 2014-12-14 16:09:12 +00:00
Knut Sveidqvist
ae76dc957a Merge pull request #57 from alvynmcq/master
Included .DS_Store in gitignore
2014-12-14 12:35:16 +01:00
Björn Weström
6ef6d79b48 Added proper SVG namespace attribute 2014-12-14 00:46:20 +01:00
Björn Weström
3fcbc7ae56 Added class definitions to nodes
Removed the inline styling of nodes with classes, replaced with class
attributes and a common CSS Style section at the start of the SVG.
2014-12-14 00:32:27 +01:00
Alvyn McQuitty
bb9e36d5fa Included .DS_Store in gitignore 2014-12-13 22:57:16 +00:00
Knut Sveidqvist
8a129d7384 Moved main bulk of documentation into the wiki 2014-12-13 21:34:39 +01:00
Björn Weström
71fd8e1b70 Merge pull request #1 from knsv/master
Update from master
2014-12-13 21:24:14 +01:00
knsv
28594138e0 Fix for issue #46, slashes in text 2014-12-13 21:10:50 +01:00
knsv
7fc2a0a544 Increased scope of lint check, removed some issues 2014-12-13 20:58:53 +01:00
knsv
48af3b3590 Refactoring/cleanup of flowchart grammar 2014-12-13 18:04:55 +01:00
knsv
8a13c4cc06 Refactoring/cleanup of flowchart grammar 2014-12-13 18:01:25 +01:00
Knut Sveidqvist
bfd93627a0 Merge pull request #56 from vijay40/master
Improves readablity discussed in issue #38
2014-12-13 17:01:05 +01:00
Vijay Jain
d4451ef8a1 flow.jison is modified to include a single spaces between vertices and link to improve readablity for issue #38. There should not be any space between vertex and its text and link and its text. flow.spec.js is modified to include three new tests for testing new graphs. 2014-12-13 17:09:03 +05:30
knsv
3f3e23b419 Fixes for issues #47 and #55 including new test cases. 2014-12-13 08:43:25 +01:00
Knut Sveidqvist
728bd656d7 Added link to live editor 2014-12-12 19:05:27 +01:00
knsv
1cc3f16d7a Refactoring, split of diagrams into different folders for easier additions of more diagram types 2014-12-11 21:02:11 +01:00
knsv
92439fbdf3 Merge branch 'master' of https://github.com/knsv/mermaid
Conflicts:
	gulpfile.js
2014-12-11 19:35:03 +01:00
knsv
0955305076 * Support for comments pointed out in issue #47 2014-12-11 19:29:40 +01:00
knsv
977df99c80 * Support for comments pointed out in issue #47 2014-12-11 19:23:36 +01:00
Knut Sveidqvist
fcfbce8a97 Merge pull request #43 from serv/chore/refactor-tests
Added a linting task for gulp
2014-12-10 19:44:27 +01:00
Jason Kim
202af046aa Made changes based on jslint 2014-12-09 22:26:42 -08:00
Jason Kim
bd2633acf4 Settig up a linting task for src/ 2014-12-09 22:17:07 -08:00
knsv
9aeff6be2d * Changed the license in package json to the correct license (MIT).
* Changed the link style from the pointed one to a more triangle formed style as default style for links
* Updated the readme file regarding linkStyles to explain the magic number 3a s described in issue #41
2014-12-09 20:42:31 +01:00
knsv
6988dea353 Release 0.2.15 2014-12-05 10:50:47 +01:00
Knut Sveidqvist
afe12b5636 Merge pull request #33 from serv/chore/bower-comp-gitignore
Include bower_components/ to .gitignore
2014-12-05 10:20:34 +01:00
knsv
7b79b0c8e0 Merge remote-tracking branch 'origin/master' 2014-12-05 10:19:47 +01:00
knsv
2b9e464798 Cleanup of sequence diagram rendering code (still experimental)
Better handling of width. Instead uf using width 100% the width is set to the width of the graph. Should help issue #19.
2014-12-05 10:19:07 +01:00
Jason Kim
89780140c8 Include bower_components/ to .gitignore
We should prevent `bower_components` from accidentally getting committed
2014-12-04 13:11:04 -08:00
Knut Sveidqvist
907bd62678 Merge pull request #32 from guyellis/master
Fixed reference to Git repo.
2014-12-04 21:24:28 +01:00
Guy Ellis
76d48db219 Fixed reference to Git repo. 2014-12-04 15:16:40 -05:00
Knut Sveidqvist
9e3253ee70 Update README.md 2014-12-04 18:28:53 +01:00
knsv
8949166a17 Adding experimental new grammars 2014-12-04 18:06:54 +01:00
knsv
4c564ebe9e Adding experimental new grammars 2014-12-04 17:58:05 +01:00
knsv
2a0a2a2269 Adding experimental new grammars 2014-12-04 17:35:07 +01:00
Knut Sveidqvist
53e3b431d5 Update README.md
Added some info about the parser available in the api.
2014-12-03 20:03:15 +01:00
knsv
dd26960852 New release 2014-12-03 19:35:00 +01:00
knsv
fe2f3b403d Patch release 2014-12-03 19:29:44 +01:00
knsv
dabedb379d Added support for more characters as described in issue #25 2014-12-03 18:34:18 +01:00
knsv
1e53b588fc Exposing parser in API as requested in issue #21 2014-12-03 18:12:33 +01:00
knsv
c0e2c86fd8 Merge remote-tracking branch 'origin/master' 2014-12-03 17:43:31 +01:00
knsv
52a78375a4 Added check for previously processed div elements, fix for issue #29, thank to user madebits for fix. 2014-12-03 17:42:39 +01:00
Knut Sveidqvist
c0e9638109 Update CONTRIBUTING.md 2014-12-03 17:28:21 +01:00
knsv
0539b8a74e Added keywords as valid text token, fix for issue #8 2014-12-03 07:49:41 +01:00
knsv
7b09a6d1bf Added DIR element as valid text token, fix for issue #8 2014-12-03 07:42:11 +01:00
knsv
ab78295faf Preparation for npm package 2014-12-02 19:05:09 +01:00
knsv
540c614c88 Preparation for npm package 2014-12-02 18:36:16 +01:00
Knut Sveidqvist
d0ed2d2735 Merge remote-tracking branch 'origin/ImanimalXI-graph_editor_page' 2014-12-02 10:00:07 +01:00
Knut Sveidqvist
fb58204900 Merge remote-tracking branch 'origin/master' 2014-12-02 09:16:07 +01:00
Knut Sveidqvist
240939a7ec Guidelines for contributing 2014-12-02 09:15:58 +01:00
knsv
c067b31a57 Mermaid editor added 2014-12-02 07:49:33 +01:00
knsv
1502346172 Support for cyrillic characters 2014-12-01 21:12:14 +01:00
knsv
9537f60b01 Support for cyrillic characters 2014-12-01 21:10:09 +01:00
knsv
1a7997cdef Support for unicode letters.
Merge branch 'master' of https://github.com/knsv/mermaid

Conflicts:
	src/parser/flow.jison
	src/parser/flow.js
2014-12-01 21:00:47 +01:00
Knut Sveidqvist
3a65cd1df3 Merge pull request #13 from codebeige/unicode_labels
Allow unicode chars in labels
2014-12-01 20:55:13 +01:00
IamanimalXI
225560dcdf initial setup for editor page to generate graph through textarea input 2014-12-01 20:50:58 +01:00
Tibor Claassen
d6766de7be Use ranges to allow unicode chars in labels 2014-12-01 21:48:34 +02:00
knsv
8906ee8097 Merge remote-tracking branch 'origin/master'
Conflicts:
	bower.json
	dist/mermaid.full.js
	dist/mermaid.full.min.js
	dist/mermaid.slim.js
	dist/mermaid.slim.min.js
	src/main.js
2014-12-01 20:43:49 +01:00
knsv
fc4f6e617f Better support for unicode and cyrillic letters 2014-12-01 20:41:08 +01:00
Knut Sveidqvist
8261429afb Update README.md
Added link to jsbin thanks to Christian Genco.
2014-12-01 19:08:55 +01:00
Knut Sveidqvist
733cbc753c Update README.md
Added a link to a live example.
2014-12-01 16:53:20 +01:00
Knut Sveidqvist
1d2327c4c2 Added test for caps in vertices 2014-12-01 09:59:34 +01:00
Knut Sveidqvist
5f8508ab52 Version fix 2014-12-01 09:40:12 +01:00
Knut Sveidqvist
cb7b8a4b98 Version fix 2014-12-01 09:38:49 +01:00
Knut Sveidqvist
cbd41dbb1b Version fix 2014-12-01 09:28:47 +01:00
Knut Sveidqvist
9d5c6d5d1d Update README.md 2014-12-01 09:16:27 +01:00
Knut Sveidqvist
ba0ea20388 Update bower.json 2014-12-01 09:13:01 +01:00
knsv
0c87128902 Release 0.2.6
* Default shape set to rect when none is set
2014-11-28 18:08:36 +01:00
knsv
9a35844731 Release 0.2.5
* Support for new shapes circle,  irregular rectangle
* Support for styling of links
* Support for newlines in texts
* Centered text on links
* Shaded background for text on links
* Support for click on nodes
2014-11-27 21:28:04 +01:00
knsv
18be0e02aa Updated readme and changes to the coverage setting on the ci server 2014-11-27 21:16:17 +01:00
knsv
167431d677 Merge remote-tracking branch 'origin/master' 2014-11-27 21:15:50 +01:00
knsv
9eac1ff9c4 Updated readme and changes to the coverage setting on the ci server 2014-11-27 20:08:27 +01:00
Knut Sveidqvist
1077261a18 Update README.md 2014-11-27 19:42:31 +01:00
knsv
f1bffb5814 Updated readme and changes to the coverage setting on the ci server 2014-11-27 19:34:35 +01:00
knsv
12fdc9f10d Updated readme and changes to the coverage setting on the ci server 2014-11-27 19:04:41 +01:00
knsv
fa46d61ab0 Updated readme and changes to the coverage setting on the ci server 2014-11-27 18:55:15 +01:00
knsv
fe1c78ca29 Updated shape syntax
Coverage instrumentation in tests
2014-11-27 18:21:15 +01:00
Knut Sveidqvist
289329736d Merge pull request #1 from bjowes/master
Added new shapes!
2014-11-27 09:06:53 +01:00
Björn Weström
28f6b93c16 Dists for new shapes 2014-11-27 09:01:54 +01:00
Björn Weström
c7b44679c1 Added shapes
Added UTF-8 tag to test webpage
Added tests for shapes on test webpage
Added odd shape
Added circle shape
Modified diamond shape to always use right angles
2014-11-27 09:00:25 +01:00
knsv
d52224194c Styles on links 2014-11-26 20:47:22 +01:00
knsv
8e591c0494 Background on labels and centering of labels 2014-11-26 19:03:15 +01:00
knsv
d6c26ab2cd Merge remote-tracking branch 'origin/master' 2014-11-26 18:51:06 +01:00
knsv
6757709f45 Small fix, Cleaner add node code 2014-11-26 18:50:12 +01:00
Knut Sveidqvist
62fc161193 Update README.md
Added build status
2014-11-25 23:28:21 +01:00
knsv
a5b9de2c43 Setting up continuous integration using travis 2014-11-25 23:23:40 +01:00
knsv
6382fe07ab Fix for br tags within nodes for new line 2014-11-25 23:16:44 +01:00
knsv
9c31ac8aa6 Fix for br tags within nodes for new line 2014-11-25 22:58:57 +01:00
knsv
2317ea5117 Better text handling for flowcharts 2014-11-25 18:58:47 +01:00
183 changed files with 29963 additions and 8811 deletions

2
.ackrc Normal file
View File

@@ -0,0 +1,2 @@
--ignore-dir=dist
--ignore-file=match:/^yarn\.lock$/

5
.babelrc Normal file
View File

@@ -0,0 +1,5 @@
{
"presets": [
"es2015"
]
}

14
.codeclimate.yml Normal file
View File

@@ -0,0 +1,14 @@
engines:
duplication:
enabled: true
config:
languages:
- javascript
eslint:
enabled: true
ratings:
paths:
- "**.js"
exclude_paths:
- node_modules/
- dist/

11
.editorconfig Normal file
View File

@@ -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

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.js text eol=lf

15
.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
node_modules
bower_components
*.sublime-project
*.sublime-workspace
.DS_Store
.idea
coverage
test/tmp_*
test/fixtures/samples/*.actual*
dist/*.js
dist/*.css

19
.travis.yml Normal file
View File

@@ -0,0 +1,19 @@
sudo: required
dist: trusty
addons:
chrome: stable
code_climate:
repo_token: e87e6bf1c253e0555437ebd23235fdfe2749b889358e7c6d100e4ea5b4f2e091
language: node_js
node_js:
- "8"
before_install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start &
- sleep 3
before_script:
- yarn build
script:
- yarn test
after_script:
- cat coverage/lcov.info | codeclimate

386
CHANGELOG.md Normal file
View File

@@ -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)*

88
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,88 @@
# How to contribute
Great that you want to be involved in this project! Contributing is fun and contributions are GREAT! :)
This page is currently a starting point and is not so rigorous to start with.
Some important guidlines:
* The work will be organized using the issues list
* In the list there will be the bugs/enhancements etc we are working with in the project
* There will be milestones outlineing the roadmap ahead
* There will issues marked with help wanted
The issue list and the items marked with **help wanted** is a good starting point if you want to do some work.
## Guidelines for avoiding duplicate work
Contributing is great. It is not so fun when you are done with your issue and just before you're about to push your
change you can't because someone else just pushed the same fix so you have wasted your time. The guidelines below are in
place to prevent this:
* Comment in the issue that you are working on it. You will then be added as an assignee (eventually).
* When you pick an issue to work on.
* Check that the issue not assigned
* Also check the comments so that no one has started working on it before beeing officially assigned.
## Submitting changes
Please send a GitHub Pull Request with a clear list of what you've done (read more about pull requests). When you send
a pull request, we will love you forever if you include jasmine tests. We can always use more test coverage.
Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this:
$ git commit -m "A brief summary of the commit
>
> A paragraph describing what changed and its impact."
Coding conventions
Start reading our code and you'll get the hang of it. We optimize for readability:
This is open source software. Consider the people who will read your code, and make it look nice for them. It's sort of
like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as
smooth as possible.
So that we can consistently serve images from the CDN, always use image_path or image_tag when referring to images.
Never prepend "/images/" when using image_path or image_tag.
Also for the CDN, always use cwd-relative paths rather than root-relative paths in image URLs in any CSS. So instead of
url('/images/blah.gif'), use url('../images/blah.gif').
# Build instructions
Fork, then:
```
yarn install
```
Then the dependencies will have been installed. You use gulp and yarn calls as build tools.
The following targets are probably interesting:
* jison - compiles the jison grammars to parser files
for instance:
```
gulp jison
```
To build:
```
yarn build
```
To run the tests:
```
yarn test
```
Make sure you have Chrome browser installed. We use Chrome headless for testing.
Manual test:
```
open dist/demo/index.html
```

View File

@@ -19,4 +19,3 @@ 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.

380
README.md
View File

@@ -1,5 +1,10 @@
mermaid
=======
# mermaid
[![Build Status](https://travis-ci.org/knsv/mermaid.svg?branch=master)](https://travis-ci.org/knsv/mermaid)
[![Code Climate](https://codeclimate.com/github/knsv/mermaid/badges/gpa.svg)](https://codeclimate.com/github/knsv/mermaid)
[![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)
![](./img/header.png)
Generation of diagrams and flowcharts from text in a similar manner as markdown.
@@ -8,215 +13,226 @@ Ever wanted to simplify documentation and avoid heavy tools like Visio when expl
This is why mermaid was born, a simple markdown-like script language for generating charts from text via javascript.
The code below would render the following image
```
<table>
<tr><th>Code</th><th>Rendered diagram</th></tr>
<tr>
<td>
<pre>
<code>
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
</code>
</pre>
</td>
<td>
<p align="center">
<img src='./img/flow.png' alt='Flowchart'>
</p>
</td>
</tr>
<tr>
<td>
<pre>
<code>
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 &lt;br/>prevail...
John-->>Alice: Great!
John->>Bob: How about you?
Bob-->>John: Jolly good!
</code>
</pre>
</td>
<td>
<img src='./img/sequence.png' alt='Sequence Diagram'>
</td>
</tr>
<tr>
<td>
<pre>
<code>
gantt
dateFormat YYYY-MM-DD
title Adding GANTT diagram to mermaid
would render this lovely chart:
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
</code>
</pre>
</td>
<td>
<img src='./img/gantt.png' alt='Gantt Diagram'>
</td>
</tr>
<tr>
<td>
<pre>
<code>
classDiagram
Class01 &lt;|-- 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 &lt;--> C2: Cool label
</code>
</pre>
</td>
<td>
<img src='./img/class.png' alt='Class Diagram'>
</td>
</tr>
<tr>
<td>
<pre>
<code>
gitGraph :
options
{
"key": "value",
"nodeWidth": 150,
"nodeSpacing" : 150
}
end
commit
branch newbranch
checkout newbranch
commit
commit
checkout master
commit
commit
merge newbranch
</code>
</pre>
</td>
<td>
<img src='./img/git.png' alt='Git Graph'>
</td>
</tr>
![Example 1](http://www.sveido.com/mermaid/img/ex1.png)
</table>
#Installation
## Installation
Either use the bower package manager as per below:
### CDN
```
bower install mermaid --save-dev
https://unpkg.com/mermaid@<version>/dist/
```
Or download javascript files:
Replace `<version>` with expected version number.
* [mermaid including dependencies](http://www.sveido.com/mermaid/dist/mermaid.full.min.js)
Example: https://unpkg.com/mermaid@7.0.4/dist/
This file bundles mermaid with d3 and dagre-d3.
* [mermaid without dependencies](http://www.sveido.com/mermaid/dist/mermaid.slim.min.js)
With this file you will need to include d3 and dagre-d3 yourself.
# Usage
Include mermaid on your web page:
### Node.js
```
<script src="mermaid.full.min.js"></script>
```
Further down on your page mermaid will look for tags with ```class="mermaid"```. From these tags mermaid will try to
read the chart definiton which will be replaced with the svg chart.
A chart defined like this:
```
<div class="mermaid">
CHART DEFINITION GOES HERE
</div>
```
Would end up like this:
```
<div class="mermaid" id="mermaidChart0">
<svg>
Chart ends up here
</svg>
</div>
```
An id is also added to mermaid tags without id.
# A graph example
```
graph LR;
A[Hard edge]-->|Link text|B(Round edge);
B-->C{Decision};
C-->|One|D[Result one];
C-->|Two|E[Result two];
```
![Example 2](http://www.sveido.com/mermaid/img/ex2.png)
#Syntax
## Graph
This statement declares a new graph and the direction of the graph layout.
```
graph TD
```
This declares a graph oriented from top to bottom.
![Example 3](http://www.sveido.com/mermaid/img/ex3.png)
```
graph LR
```
This declares a graph oriented from left to right.
Possible directions are:
* TB - top bottom
* BT - bottom top
* RL - right left
* LR - left right
* TD - same as TB
![Example 4](http://www.sveido.com/mermaid/img/ex4.png)
## Nodes
### A node (default)
```
id1;
```
![Single node](http://www.sveido.com/mermaid/img/ex5.png)
Note that the id is what is displayed in the box.
### A node with text
It is also possible to set text in the box that differs from the id. If this is done several times, it is the last text
found for the node that will be used. Also if you define edges for the node later on, you can omit text definitions. The
one previously defined will be used when rendering the box.
```
id1[This is the text in the box];
```
![Text in node](http://www.sveido.com/mermaid/img/ex6.png)
### A node with round edges
```
id1(This is the text in the box);
```
![Node with round edges](http://www.sveido.com/mermaid/img/ex7.png)
### A node (rhombus)
```
id1{This is the text in the box};
```
![Decision box](http://www.sveido.com/mermaid/img/ex8.png)
### Styling a node
It is possible to apply specific styles such as a thicker border or a different background color to a node.
```
graph LR;
id1(Start)-->id2(Stop);
style id1 fill:#f9f,stroke:#333,stroke-width:4px;
style id2 fill:#ccf,stroke:#f66,stroke-width:2px,stroke-dasharray: 5, 5;
```
![Node with styles](http://www.sveido.com/mermaid/img/ex9.png)
#### Classes
More convenient then defining the style everytime is to define a class of styles and attach this class to the nodes that
should have a different look.
a class definition looks like the example below:
```
classDef className fill:#f9f,stroke:#333,stroke-width:4px;
```
Attachment of a class to a node is done as per below:
```
class nodeId1 className;
```
It is also possible to attach a class to a list of nodes in one statement:
```
class nodeId1,nodeId2 className;
```
#### Default class
If a class is named default it will be assigned to all classes without specific class definitions.
```
classDef default fill:#f9f,stroke:#333,stroke-width:4px;
yarn add mermaid
```
## Links between nodes
## Further reading
Nodes can be connected with links/edges. It is possible to have different types of links or attach a text string to a link.
* [Usage](https://mermaidjs.github.io/usage.html)
* [Flowchart syntax](https://mermaidjs.github.io/flowchart.html)
* [Sequence diagram syntax](https://mermaidjs.github.io/sequenceDiagram.html)
* [Mermaid CLI](https://mermaidjs.github.io/mermaidCLI.html)
* [Demos](https://mermaidjs.github.io/demos.html)
### A link with arrow head
```
A-->B;
```
![Link with arrowhead](http://www.sveido.com/mermaid/img/ex4.png)
# Request for assistance
### An open link
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.
```
A---B;
```
As part of this team you would get write access to the repository and would
represent the project when answering questions and issues.
![Open link](http://www.sveido.com/mermaid/img/ex10.png)
Together we could continue the work with things like:
* port the code to es6
* adding more typers of diagrams like mindmaps, ert digrams etc
* improving existing diagrams
### Text on links
Don't hesitate to contact me if you want to get involved.
```
A---|This is the text|B;
```
![Text on links](http://www.sveido.com/mermaid/img/ex11.png)
# For contributors
## Setup
Make sure you have Chrome browser installed, this project uses Chrome headless to running tests.
yarn install
## Build
yarn build
If you want real time incremental build:
yarn 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/demo/index.html
Manual test in Node.js:
node dist/demo/index.js
## Release
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!!!
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).

24
bin/mermaid.js Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env node
var chalk = require('chalk')
var cli = require('../lib/cli.js')
var lib = require('../lib')
cli.parse(process.argv.slice(2), function (err, message, options) {
if (err) {
console.error(
chalk.bold.red('\nYou had errors in your syntax. Use --help for further information.')
)
err.forEach(function (e) {
console.error(e.message)
})
return
} else if (message) {
console.log(message)
return
}
lib.process(options.files, options, process.exit)
})

View File

@@ -1,28 +1,38 @@
{
"name": "mermaid",
"version": "0.2.3",
"authors": [
"knsv <knut@sveido.com>"
"knsv <knut@sveido.com>",
"Tyler Long (https://github.com/tylerlong)"
],
"description": "Markdownish syntax for generating flowcharts",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "dist/mermaid.slim.js",
"keywords": [
"diagram",
"markdown",
"flowchart"
"flowchart",
"sequence diagram",
"gantt",
"class diagram",
"git graph"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"vendor",
"test",
"tests"
"*",
"!dist/",
"!dist/*",
"!LICENSE"
],
"devDependencies": {
"jasmine": "~2.0.4",
"dagre": "0.6.2",
"d3": "3.3.8",
"dagre-d3": "0.3.2"
"dependencies": {
"chalk": "^2.1.0",
"d3": "3.5.17",
"dagre": "^0.7.4",
"dagre-d3-renderer": "^0.1.6",
"he": "^1.1.1",
"lodash": "^4.17.4",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"moment": "^2.18.1",
"semver": "^5.4.1",
"which": "^1.3.0"
}
}

View File

@@ -1,60 +0,0 @@
// Karma configuration
// Generated on Mon Nov 03 2014 07:53:38 GMT+0100 (CET)
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'../src/**/*.js'
],
// list of files to exclude
exclude: ['../src/backup/**/*.js'],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_DEBUG,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};

106
dist/demo/index.html vendored Normal file
View File

@@ -0,0 +1,106 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
<link rel="stylesheet" href="../mermaid.css">
</head>
<body>
<div class="mermaid">
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
</div>
<hr/>
<div class="mermaid">
sequenceDiagram
loop every day
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
end
</div>
<hr/>
<div class="mermaid">
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
section Critical tasks
Completed task in the critical line :crit, done, 2014-01-06,24h
Implement parser and jison :crit, done, after des1, 2d
Create tests for parser :crit, active, 3d
Future task in critical line :crit, 5d
Create tests for renderer :2d
Add to mermaid :1d
section Documentation
Describe gantt syntax :active, a1, after des1, 3d
Add gantt diagram to demo page :after a1 , 20h
Add another diagram to demo page :doc1, after a1 , 48h
section Last section
Describe gantt syntax :after doc1, 3d
Add gantt diagram to demo page : 20h
Add another diagram to demo page : 48h
</div>
<hr/>
<div class="mermaid">
gitGraph:
options
{
"nodeSpacing": 150,
"nodeRadius": 10
}
end
commit
branch newbranch
checkout newbranch
commit
commit
checkout master
commit
commit
merge newbranch
</div>
<hr/>
<div class="mermaid">
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
</div>
<script src="../mermaid.js"></script>
<script>
mermaid.initialize({startOnLoad: true});
</script>
</body>
</html>

9
dist/demo/index.js vendored Normal file
View File

@@ -0,0 +1,9 @@
const api = require('../mermaidAPI.js')
const r = api.parse(`sequenceDiagram
loop every day
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
end`)
console.log(r)

2467
dist/mermaid.full.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2435
dist/mermaid.slim.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

26
gulp/tasks/dev.js Normal file
View File

@@ -0,0 +1,26 @@
/**
* Created by knut on 2015-12-26.
*/
var gulp = require('gulp')
var shell = require('gulp-shell')
var liveServer = require('live-server')
var params = {
port: 8080, // Set the server port. Defaults to 8080.
host: '0.0.0.0', // Set the address to bind to. Defaults to 0.0.0.0.
root: './test/examples', // Set root directory that's being server. Defaults to cwd.
open: true, // When false, it won't load your browser by default.
ignore: 'scss,my/templates', // comma-separated string for paths to ignore
// file: "index.html", // When set, serve this file for every 404 (useful for single-page applications)
wait: 1000, // Waits for all changes, before reloading. Defaults to 0 sec.
mount: [['/dist', './dist']] // Mount a directory to a route.
}
gulp.task('live-server', function () {
liveServer.start(params)
})
gulp.task('watch2', ['live-server'], function () {
return shell.task([
'yarn build -- --watch'
])
})

23
gulp/tasks/jison.js Normal file
View File

@@ -0,0 +1,23 @@
var gulp = require('gulp')
var shell = require('gulp-shell')
var jison = require('gulp-jison')
var filelog = require('gulp-filelog')
gulp.task('jison', function () {
return gulp.src('./src/**/*.jison')
.pipe(filelog('Jison file:'))
.pipe(jison({ moduleType: 'commonjs' }))
.pipe(gulp.dest('./src/'))
})
gulp.task('jison_legacy', function () {
shell.task([
'node node_modules/jison/lib/cli.js src/diagrams/classDiagram/parser/classDiagram.jison -o src/diagrams/classDiagram/parser/classDiagram.js',
'node node_modules/jison/lib/cli.js src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison -o src/diagrams/sequenceDiagram/parser/sequenceDiagram.js',
'node node_modules/jison/lib/cli.js src/diagrams/example/parser/example.jison -o src/diagrams/example/parser/example.js',
'node node_modules/jison/lib/cli.js src/diagrams/flowchart/parser/flow.jison -o src/diagrams/flowchart/parser/flow.js',
'node node_modules/jison/lib/cli.js src/diagrams/flowchart/parser/dot.jison -o src/diagrams/flowchart/parser/dot.js',
'node node_modules/jison/lib/cli.js src/diagrams/gitGraph/parser/gitGraph.jison -o src/diagrams/gitGraph/parser/gitGraph.js',
'node node_modules/jison/lib/cli.js src/diagrams/gantt/parser/gantt.jison -o src/diagrams/gantt/parser/gantt.js'
])
})

View File

@@ -1,60 +1,3 @@
var gulp = require('gulp');
var jison = require('gulp-jison');
var shell = require('gulp-shell');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var extReplace = require('gulp-ext-replace');
var rename = require('gulp-rename');
var requireDir = require('require-dir')
gulp.task('jison2', function() {
return gulp.src('./src/*.jison')
.pipe(jison({ moduleType: 'amd' }))
.pipe(gulp.dest('./src/'));
});
gulp.task('jison', shell.task([
'jison src/parser/flow.jison -o src/parser/flow.js'
//'source scripts/compileJison.sh'
// 'jison src/parser/flow.jison -o src/parser/flow.js',
]))
gulp.task('jisonSd', shell.task([
//'jison src/parser/flow.jison -o src/parser/flow.js',
'jison src/parser/sequence.jison -o src/parser/sequence.js'
//'source scripts/compileFlow.sh'
]));
gulp.task('dist', ['slimDist', 'fullDist']);
var jasmine = require('gulp-jasmine');
gulp.task('jasmine',['jison','jisonSd'], function () {
return gulp.src(['src/**/*.spec.js'])
.pipe(jasmine());
});
var browserify = require('gulp-browserify');
// Basic usage
gulp.task('slimDist', function() {
// Single entry point to browserify
return gulp.src('src/main.js')
.pipe(browserify())
.pipe(rename('mermaid.slim.js'))
.pipe(gulp.dest('./dist/'))
.pipe(uglify())
.pipe(extReplace('.min.js'))
.pipe(gulp.dest('./dist/'));
});
// Basic usage
gulp.task('fullDist', ['slimDist'], function() {
// Single entry point to browserify
gulp.src(['node_modules/d3/d3.min.js','node_modules/dagre-d3/dist/dagre-d3.min.js','dist/mermaid.slim.js'])
.pipe(concat('mermaid.full.js'))
.pipe(gulp.dest('./dist/'));
return gulp.src(['node_modules/d3/d3.min.js','node_modules/dagre-d3/dist/dagre-d3.min.js','dist/mermaid.slim.min.js'])
.pipe(concat('mermaid.full.min.js'))
.pipe(gulp.dest('./dist/'));
});
requireDir('./gulp/tasks')

BIN
img/class.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
img/flow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
img/gantt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
img/git.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
img/header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
img/sequence.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1,74 +1,99 @@
// Karma configuration
// Generated on Mon Nov 03 2014 23:31:38 GMT+0100 (CET)
// Generated on Mon Nov 03 2014 07:53:38 GMT+0100 (CET)
module.exports = function(config) {
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
basePath: '.',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine','requirejs'],
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'src/**/*.js',
'test/test-main.js'
'./src/*.spec.js',
'./src/diagrams/flowchart/**/*.spec.js',
'./src/diagrams/example/**/*.spec.js',
'./src/diagrams/sequenceDiagram/**/*.spec.js',
'./src/diagrams/classDiagram/**/*.spec.js',
'./src/diagrams/gantt/**/*.spec.js',
'./src/diagrams/gitGraph/**/*.spec.js'
],
// list of files to exclude
exclude: [
//'src/parser/flow.js'
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'src/**/*.spec.js': ['webpack']
},
webpack: {
externals: ['fs'],
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
['env', {
'targets': {
'browsers': ['last 3 versions']
}
}]
],
plugins: [
'transform-remove-strict-mode'
]
}
}
}
]
}
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
reporters: ['dots'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_DEBUG,
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
customLaunchers: {
ChromeHeadless: {
base: 'Chrome',
flags: [
'--incognito',
// '--headless',
'--disable-gpu',
'--no-sandbox',
// Without a remote debugging port, Google Chrome exits immediately.
'--remote-debugging-port=9222'
]
}
},
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],
browsers: ['ChromeHeadless'],
plugins: [
'karma-chrome-launcher',
'karma-firefox-launcher',
'karma-script-launcher',
'karma-jasmine',
'karma-requirejs'
'karma-chrome-launcher',
'karma-webpack'
],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};
})
}

196
lib/cli.js Normal file
View File

@@ -0,0 +1,196 @@
var fs = require('fs')
var exec = require('child_process').exec
var chalk = require('chalk')
var which = require('which')
var parseArgs = require('minimist')
var semver = require('semver')
var path = require('path')
var PHANTOM_VERSION = '^2.1.0'
var info = chalk.blue.bold
module.exports = (function () {
return new Cli()
}())
function Cli (options) {
this.options = {
alias: {
help: 'h',
png: 'p',
outputDir: 'o',
outputSuffix: 'O',
svg: 's',
verbose: 'v',
phantomPath: 'e',
sequenceConfig: 'c',
ganttConfig: 'g',
css: 't',
width: 'w'
},
'boolean': ['help', 'png', 'svg', 'verbose'],
'string': ['outputDir', 'outputSuffix']
}
this.errors = []
this.message = null
this.helpMessage = [
info('Usage: mermaid [options] <file>...'),
'',
'file The mermaid description file to be rendered',
'',
'Options:',
' -s --svg Output SVG instead of PNG (experimental)',
' -p --png If SVG was selected, and you also want PNG, set this flag',
' -o --outputDir Directory to save files, will be created automatically, defaults to `cwd`',
" -O --outputSuffix Suffix to output filenames in front of '.svg' or '.png', defaults to ''",
' -e --phantomPath Specify the path to the phantomjs executable',
' -t --css Specify the path to a CSS file to be included when processing output',
' -c --sequenceConfig Specify the path to the file with the configuration to be applied in the sequence diagram',
' -g --ganttConfig Specify the path to the file with the configuration to be applied in the gantt diagram',
' -h --help Show this message',
' -v --verbose Show logging',
' -w --width width of the generated png (number)',
' --version Print version and quit'
]
return this
}
Cli.prototype.parse = function (argv, next) {
this.errors = [] // clear errors
var options = parseArgs(argv, this.options)
if (options.version) {
var pkg = require('../package.json')
this.message = '' + pkg.version
next(null, this.message)
} else if (options.help) {
this.message = this.helpMessage.join('\n')
next(null, this.message)
} else {
options.files = options._
if (!options.files.length) {
this.errors.push(new Error('You must specify at least one source file.'))
}
// ensure that parameter-expecting options have parameters
;['outputDir', 'outputSuffix', 'phantomPath', 'sequenceConfig', 'ganttConfig', 'css'].forEach(function (i) {
if (typeof options[i] !== 'undefined') {
if (typeof options[i] !== 'string' || options[i].length < 1) {
this.errors.push(new Error(i + ' expects a value.'))
}
}
}.bind(this))
// set svg/png flags appropriately
if (options.svg && !options.png) {
options.png = false
} else {
options.png = true
}
if (options.sequenceConfig) {
try {
fs.accessSync(options.sequenceConfig, fs.R_OK)
} catch (err) {
this.errors.push(err)
}
} else {
options.sequenceConfig = null
}
if (options.ganttConfig) {
try {
fs.accessSync(options.ganttConfig, fs.R_OK)
} catch (err) {
this.errors.push(err)
}
} else {
options.ganttConfig = null
}
if (options.css) {
try {
fs.accessSync(options.css, fs.R_OK)
} catch (err) {
this.errors.push(err)
}
} else {
options.css = path.join(__dirname, '..', 'dist', 'mermaid.css')
}
// set svg/png flags appropriately
if (!options.width) {
options.width = 1200
}
this.checkPhantom = createCheckPhantom(options.phantomPath)
this.checkPhantom(function (err, path) {
if (err) {
this.errors.push(err)
}
options.phantomPath = path
next(
this.errors.length > 0 ? this.errors : null
, this.message
, options
)
}.bind(this))
}
}
function createCheckPhantom (_phantomPath) {
var phantomPath = _phantomPath
return function checkPhantom (_next) {
var next = _next || function () { }
var err
if (typeof phantomPath === 'undefined') {
try {
var phantom = require('phantomjs')
phantomPath = phantom.path
} catch (e) {
try {
phantomPath = which.sync('phantomjs')
} catch (e) {
if (!phantomPath) {
phantomPath = null
err = new Error(
[
'Cannot find phantomjs in your PATH. If phantomjs is installed',
"you may need to specify its path manually with the '-e' option.",
"Run this executable with '--help' or view the README for more",
'details.'
].join('\n')
)
next(err)
return
}
}
}
}
// If we have phantompath, see if its version satisfies our requirements
exec('"' + phantomPath + '" --version', function (err, stdout, stderr) {
if (err) {
next(new Error('Could not find phantomjs at the specified path.'))
} else if (!semver.satisfies(stdout, PHANTOM_VERSION)) {
next(new Error(
'mermaid requires phantomjs ' +
PHANTOM_VERSION +
' to be installed, found version ' +
stdout
))
} else {
next(null, phantomPath)
}
})
}
}

43
lib/index.js Normal file
View File

@@ -0,0 +1,43 @@
var path = require('path')
var spawn = require('child_process').spawn
var mkdirp = require('mkdirp')
var phantomscript = path.join(__dirname, 'phantomscript.js')
module.exports = { process: processMermaid }
function processMermaid (files, _options, _next) {
var options = _options || {}
var outputDir = options.outputDir || process.cwd()
var outputSuffix = options.outputSuffix || ''
var next = _next || function () { }
var phantomArgs = [
phantomscript,
outputDir,
options.png,
options.svg,
options.css,
options.sequenceConfig,
options.ganttConfig,
options.verbose,
options.width,
outputSuffix
]
files.forEach(function (file) {
phantomArgs.push(file)
})
mkdirp(outputDir, function (err) {
if (err) {
throw err
}
var phantom = spawn(options.phantomPath, phantomArgs)
phantom.on('exit', next)
phantom.stderr.pipe(process.stderr)
phantom.stdout.pipe(process.stdout)
})
}

231
lib/phantomscript.js Normal file
View File

@@ -0,0 +1,231 @@
/**
* Credits:
* - SVG Processing from the NYTimes svg-crowbar, under an MIT license
* https://github.com/NYTimes/svg-crowbar
* - Thanks to the grunticon project for some guidance
* https://github.com/filamentgroup/grunticon
*/
window.phantom.onError = function (msg, trace) {
var msgStack = ['PHANTOM ERROR: ' + msg]
if (trace && trace.length) {
msgStack.push('TRACE:')
trace.forEach(function (t) {
msgStack.push(
' -> ' +
(t.file || t.sourceURL) +
': ' +
t.line +
(t.function ? ' (in function ' + t.function + ')' : '')
)
})
}
system.stderr.write(msgStack.join('\n'))
window.phantom.exit(1)
}
var system = require('system')
var fs = require('fs')
var webpage = require('webpage')
var page = webpage.create()
var files = system.args.slice(10, system.args.length)
var width = system.args[8]
if (typeof width === 'undefined' || width === 'undefined') {
width = 1200
}
var options = {
outputDir: system.args[1],
png: system.args[2] === 'true',
svg: system.args[3] === 'true',
css: fs.read(system.args[4]),
sequenceConfig: system.args[5] !== 'null' ? JSON.parse(fs.read(system.args[5])) : {},
ganttConfig: system.args[6] !== 'null' ? JSON.parse(fs.read(system.args[6])) : {},
verbose: system.args[7] === 'true',
width: width,
outputSuffix: system.args[9]
}
var log = logger(options.verbose)
options.sequenceConfig.useMaxWidth = false
page.content = [
'<html>',
'<head>',
'<style type="text/css">body {background:white;font-family: Arial;}',
options.css,
'</style>',
'</head>',
'<body>',
'</body>',
'</html>'
].join('\n')
page.injectJs('../dist/mermaid.js')
page.onConsoleMessage = function (msg, lineNum, sourceId) {
log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")')
}
log('Num files to execute : ' + files.length)
files.forEach(function (file) {
var contents = fs.read(file)
var filename = file.split(fs.separator).slice(-1)
var oParser = new window.DOMParser()
var oDOM
var svgContent
log('ready to execute: ' + file)
// this JS is executed in this statement is sandboxed, even though it doesn't
// look like it. we need to serialize then unserialize the svgContent that's
// taken from the DOM
svgContent = page.evaluate(executeInPage, {
contents: contents,
ganttConfig: options.ganttConfig,
sequenceConfig: options.sequenceConfig,
confWidth: options.width
})
oDOM = oParser.parseFromString(svgContent, 'text/xml')
resolveSVGElement(oDOM.firstChild)
setSVGStyle(oDOM.firstChild, options.css)
var outputPath = options.outputDir + fs.separator + filename + options.outputSuffix
if (options.png) {
page.viewportSize = {
width: ~~oDOM.documentElement.attributes.getNamedItem('width').value,
height: ~~oDOM.documentElement.attributes.getNamedItem('height').value
}
page.render(outputPath + '.png')
log('saved png: ' + outputPath + '.png')
}
if (options.svg) {
var serialize = new window.XMLSerializer()
fs.write(outputPath + '.svg'
, serialize.serializeToString(oDOM) + '\n'
, 'w'
)
log('saved svg: ' + outputPath + '.svg')
}
})
window.phantom.exit()
function logger (_verbose) {
var verbose = _verbose
return function (_message, _level) {
var level = _level
var message = _message
var log
log = level === 'error' ? system.stderr : system.stdout
if (verbose) {
log.write(message + '\n')
}
}
}
function resolveSVGElement (element) {
var prefix = {
xmlns: 'http://www.w3.org/2000/xmlns/',
xlink: 'http://www.w3.org/1999/xlink',
svg: 'http://www.w3.org/2000/svg'
}
element.setAttribute('version', '1.1')
// removing attributes so they aren't doubled up
element.removeAttribute('xmlns')
element.removeAttribute('xlink')
// These are needed for the svg
if (!element.hasAttributeNS(prefix.xmlns, 'xmlns')) {
element.setAttributeNS(prefix.xmlns, 'xmlns', prefix.svg)
}
if (!element.hasAttributeNS(prefix.xmlns, 'xmlns:xlink')) {
element.setAttributeNS(prefix.xmlns, 'xmlns:xlink', prefix.xlink)
}
}
function setSVGStyle (svg, css) {
if (!css || !svg) { return }
var styles = svg.getElementsByTagName('style')
if (!styles || styles.length === 0) { return }
styles[0].textContent = css
}
// The sandboxed function that's executed in-page by phantom
function executeInPage (data) {
var xmlSerializer = new window.XMLSerializer()
var contents = data.contents
var sequenceConfig = JSON.stringify(data.sequenceConfig)
var ganttConfig = JSON.stringify(data.ganttConfig).replace(/"(function.*})"/, '$1')
var svg
var svgValue
var boundingBox
var width
var height
var confWidth = data.confWidth
var toRemove = document.getElementsByClassName('mermaid')
if (toRemove && toRemove.length) {
for (var i = 0, len = toRemove.length; i < len; i++) {
toRemove[i].parentNode.removeChild(toRemove[i])
}
}
var el = document.createElement('div')
el.className = 'mermaid'
el.appendChild(document.createTextNode(contents))
document.body.appendChild(el)
var config = {
sequenceDiagram: JSON.parse(sequenceConfig),
flowchart: { useMaxWidth: false },
logLevel: 1
}
window.mermaid.initialize(config)
var sc = document.createElement('script')
sc.appendChild(document.createTextNode('mermaid.ganttConfig = ' + ganttConfig + ';'))
document.body.appendChild(sc)
window.mermaid.init()
svg = document.querySelector('svg')
boundingBox = svg.getBoundingClientRect() // the initial bonding box of the svg
width = boundingBox.width * 1.5 // adding the scale factor for consistency with output in chrome browser
height = boundingBox.height * 1.5 // adding the scale factor for consistency with output in chrome browser
var scalefactor = confWidth / (width - 8)
// resizing the body to fit the svg
document.body.setAttribute(
'style'
, 'width: ' + (confWidth - 8) + '; height: ' + (height * scalefactor) + ';'
)
// resizing the svg via css for consistent display
svg.setAttribute(
'style'
, 'width: ' + (confWidth - 8) + '; height: ' + (height * scalefactor) + ';'
)
// set witdth and height attributes used to set the viewport when rending png image
svg.setAttribute(
'width'
, confWidth
)
svg.setAttribute(
'height'
, height * scalefactor
)
svgValue = xmlSerializer.serializeToString(svg) + '\n'
return svgValue
}

View File

@@ -1,629 +0,0 @@
/* parser generated by jison 0.4.11 */
/*
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 ebnf = (function(){
var parser = {trace: function trace() { },
yy: {},
symbols_: {"error":2,"production":3,"handle":4,"EOF":5,"handle_list":6,"|":7,"expression_suffix":8,"expression":9,"suffix":10,"ALIAS":11,"symbol":12,"(":13,")":14,"*":15,"?":16,"+":17,"$accept":0,"$end":1},
terminals_: {2:"error",5:"EOF",7:"|",11:"ALIAS",12:"symbol",13:"(",14:")",15:"*",16:"?",17:"+"},
productions_: [0,[3,2],[6,1],[6,3],[4,0],[4,2],[8,3],[8,2],[9,1],[9,3],[10,0],[10,1],[10,1],[10,1]],
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.$ = [$$[$0]];
break;
case 3: $$[$0-2].push($$[$0]);
break;
case 4: this.$ = [];
break;
case 5: $$[$0-1].push($$[$0]);
break;
case 6: this.$ = ['xalias', $$[$0-1], $$[$0-2], $$[$0]];
break;
case 7: if ($$[$0]) this.$ = [$$[$0], $$[$0-1]]; else this.$ = $$[$0-1];
break;
case 8: this.$ = ['symbol', $$[$0]];
break;
case 9: this.$ = ['()', $$[$0-1]];
break;
}
},
table: [{3:1,4:2,5:[2,4],12:[2,4],13:[2,4]},{1:[3]},{5:[1,3],8:4,9:5,12:[1,6],13:[1,7]},{1:[2,1]},{5:[2,5],7:[2,5],12:[2,5],13:[2,5],14:[2,5]},{5:[2,10],7:[2,10],10:8,11:[2,10],12:[2,10],13:[2,10],14:[2,10],15:[1,9],16:[1,10],17:[1,11]},{5:[2,8],7:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[2,8],17:[2,8]},{4:13,6:12,7:[2,4],12:[2,4],13:[2,4],14:[2,4]},{5:[2,7],7:[2,7],11:[1,14],12:[2,7],13:[2,7],14:[2,7]},{5:[2,11],7:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11]},{5:[2,12],7:[2,12],11:[2,12],12:[2,12],13:[2,12],14:[2,12]},{5:[2,13],7:[2,13],11:[2,13],12:[2,13],13:[2,13],14:[2,13]},{7:[1,16],14:[1,15]},{7:[2,2],8:4,9:5,12:[1,6],13:[1,7],14:[2,2]},{5:[2,6],7:[2,6],12:[2,6],13:[2,6],14:[2,6]},{5:[2,9],7:[2,9],11:[2,9],12:[2,9],13:[2,9],14:[2,9],15:[2,9],16:[2,9],17:[2,9]},{4:17,7:[2,4],12:[2,4],13:[2,4],14:[2,4]},{7:[2,3],8:4,9:5,12:[1,6],13:[1,7],14:[2,3]}],
defaultActions: {3:[2,1]},
parseError: function parseError(str, hash) {
if (hash.recoverable) {
this.trace(str);
} else {
throw new Error(str);
}
},
parse: function parse(input) {
var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
var args = lstack.slice.call(arguments, 1);
this.lexer.setInput(input);
this.lexer.yy = this.yy;
this.yy.lexer = this.lexer;
this.yy.parser = this;
if (typeof this.lexer.yylloc == 'undefined') {
this.lexer.yylloc = {};
}
var yyloc = this.lexer.yylloc;
lstack.push(yyloc);
var ranges = this.lexer.options && this.lexer.options.ranges;
if (typeof this.yy.parseError === 'function') {
this.parseError = this.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 = self.lexer.lex() || EOF;
if (typeof token !== 'number') {
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 (this.lexer.showPosition) {
errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + this.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: this.lexer.match,
token: this.terminals_[symbol] || symbol,
line: this.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(this.lexer.yytext);
lstack.push(this.lexer.yylloc);
stack.push(action[1]);
symbol = null;
if (!preErrorSymbol) {
yyleng = this.lexer.yyleng;
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
yyloc = this.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,
this.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.2.1 */
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) {
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 - 1);
//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:/* skip whitespace */
break;
case 1:return 12;
break;
case 2:yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 11;
break;
case 3:return 12;
break;
case 4:return 12;
break;
case 5:return 'bar';
break;
case 6:return 13;
break;
case 7:return 14;
break;
case 8:return 15;
break;
case 9:return 16;
break;
case 10:return 7;
break;
case 11:return 17;
break;
case 12:return 5;
break;
}
},
rules: [/^(?:\s+)/,/^(?:([a-zA-Z][a-zA-Z0-9_-]*))/,/^(?:\[([a-zA-Z][a-zA-Z0-9_-]*)\])/,/^(?:'[^']*')/,/^(?:\.)/,/^(?:bar\b)/,/^(?:\()/,/^(?:\))/,/^(?:\*)/,/^(?:\?)/,/^(?:\|)/,/^(?:\+)/,/^(?:$)/],
conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12],"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 = ebnf;
exports.Parser = ebnf.Parser;
exports.parse = function () { return ebnf.parse.apply(ebnf, 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));
}
}

View File

@@ -1,41 +1,129 @@
{
"name": "mermaid",
"version": "0.0.0",
"description": "",
"main": "index.js",
"version": "7.0.5",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "src/mermaid.js",
"keywords": [
"diagram",
"markdown",
"flowchart",
"sequence diagram",
"gantt",
"class diagram",
"git graph"
],
"bin": {
"mermaid": "./bin/mermaid.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"build": "node -r babel-register ./node_modules/.bin/webpack --progress --colors",
"watch": "yarn build -- --watch",
"release": "yarn build -- -p --config webpack.config.prod.js",
"upgrade": "yarn-upgrade-all && yarn remove d3 && yarn add d3@3.5.17",
"lint": "node_modules/.bin/standard",
"karma": "node node_modules/karma/bin/karma start karma.conf.js --single-run",
"tape": "node node_modules/tape/bin/tape test/cli_test-*.js",
"test": "yarn lint && yarn tape && yarn karma",
"live": "live-server ./test/examples",
"jison": "gulp jison_legacy",
"live_server": "gulp live-server",
"prepublishOnly": "yarn build && yarn release && yarn test"
},
"repository": {
"type": "git",
"url": "git://github.com/username/repository.git"
"url": "https://github.com/knsv/mermaid"
},
"author": "Knut Sveidqvist",
"license": "MIT",
"standard": {
"ignore": [
"**/parser/*.js",
"dist/**/*.js"
]
},
"dependencies": {
"chalk": "^2.1.0",
"d3": "3.5.17",
"dagre": "^0.7.4",
"dagre-d3-renderer": "^0.1.6",
"he": "^1.1.1",
"lodash": "^4.17.4",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"moment": "^2.18.1",
"semver": "^5.4.1",
"which": "^1.3.0"
},
"author": "",
"license": "BSD-2-Clause",
"devDependencies": {
"browserify": "~6.2.0",
"d3": "~3.4.13",
"dagre-d3": "~0.3.2",
"gulp": "~3.8.9",
"gulp-concat": "~2.4.1",
"gulp-ext-replace": "~0.1.0",
"gulp-jasmine": "~1.0.1",
"gulp-jison": "~1.0.0",
"gulp-rename": "~1.2.0",
"gulp-uglify": "~1.0.1",
"jasmine": "~2.0.1",
"jison": "~0.4.15",
"karma": "~0.12.20",
"karma-chrome-launcher": "~0.1.5",
"karma-jasmine": "~0.2.1",
"karma-requirejs": "~0.2.2",
"lodash": "^2.4.1",
"lodash._escapestringchar": "^2.4.1",
"lodash._objecttypes": "^2.4.1",
"lodash._reinterpolate": "^2.4.1",
"lodash._reunescapedhtml": "^2.4.1",
"lodash.defaults": "^2.4.1",
"lodash.templatesettings": "^2.4.1",
"lodash.values": "^2.4.1"
}
"async": "^2.5.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-transform-remove-strict-mode": "^0.0.2",
"babel-preset-env": "^1.6.0",
"babel-preset-es2015": "^6.24.1",
"clone": "^2.1.1",
"codeclimate-test-reporter": "^0.5.0",
"css-loader": "^0.28.7",
"dox": "^0.9.0",
"event-stream": "^3.3.4",
"extract-text-webpack-plugin": "^3.0.0",
"front-matter": "^2.2.0",
"gulp": "^3.9.1",
"gulp-bower": "^0.0.13",
"gulp-bump": "^2.7.0",
"gulp-concat": "^2.6.1",
"gulp-data": "^1.2.1",
"gulp-dox": "^0.1.6",
"gulp-ext-replace": "^0.3.0",
"gulp-filelog": "^0.4.1",
"gulp-front-matter": "^1.3.0",
"gulp-hogan": "^2.0.0",
"gulp-if": "^2.0.2",
"gulp-insert": "^0.5.0",
"gulp-istanbul": "^1.1.2",
"gulp-jasmine": "^2.4.2",
"gulp-jasmine-browser": "^1.9.0",
"gulp-jison": "^1.2.0",
"gulp-less": "^3.3.2",
"gulp-livereload": "^3.8.1",
"gulp-marked": "^1.0.0",
"gulp-mdvars": "^2.0.0",
"gulp-qunit": "^1.5.0",
"gulp-rename": "^1.2.2",
"gulp-shell": "^0.6.3",
"gulp-tag-version": "^1.3.0",
"gulp-util": "^3.0.8",
"gulp-vartree": "^2.0.1",
"hogan.js": "^3.0.2",
"inject-loader": "^3.0.1",
"jasmine": "^2.8.0",
"jasmine-es6": "^0.4.1",
"jison": "^0.4.17",
"jsdom": "^11.2.0",
"karma": "^1.7.1",
"karma-chrome-launcher": "^2.2.0",
"karma-jasmine": "^1.1.0",
"karma-webpack": "^2.0.4",
"less": "^2.7.2",
"less-loader": "^4.0.5",
"live-server": "^1.2.0",
"map-stream": "^0.0.7",
"marked": "^0.3.6",
"mock-browser": "^0.92.14",
"phantomjs-prebuilt": "^2.1.15",
"require-dir": "^0.3.2",
"rimraf": "^2.6.1",
"standard": "^10.0.3",
"style-loader": "^0.18.2",
"tape": "^4.8.0",
"webpack": "^3.5.5",
"webpack-node-externals": "^1.6.0",
"yarn-upgrade-all": "^0.1.8"
},
"files": [
"bin",
"dist",
"lib",
"src"
]
}

View File

@@ -1,3 +0,0 @@
sed s/this.parseError\(errStr/console.log\(errStr/< src/parser/flow.js > src/parser/flowParser.js
cp src/parser/flowParser.js src/parser/flow.js
rm src/parser/flowParser.js

View File

@@ -1,3 +0,0 @@
#sed s/this.parseError\(errStr/console.log\(errStr/< src/parser/flow.js > src/parser/flowParser.js
#cp src/parser/flowParser.js src/parser/flow.js
#rm src/parser/flowParser.js

7
scripts/jison.sh Normal file
View File

@@ -0,0 +1,7 @@
node node_modules/jison/lib/cli.js src/diagrams/classDiagram/parser/classDiagram.jison -o src/diagrams/classDiagram/parser/classDiagram.js
node node_modules/jison/lib/cli.js src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison -o src/diagrams/sequenceDiagram/parser/sequenceDiagram.js
node node_modules/jison/lib/cli.js src/diagrams/example/parser/example.jison -o src/diagrams/example/parser/example.js
node node_modules/jison/lib/cli.js src/diagrams/flowchart/parser/flow.jison -o src/diagrams/flowchart/parser/flow.js
node node_modules/jison/lib/cli.js src/diagrams/flowchart/parser/dot.jison -o src/diagrams/flowchart/parser/dot.js
node node_modules/jison/lib/cli.js src/diagrams/gantt/parser/gantt.jison -o src/diagrams/gantt/parser/gantt.js
node node_modules/jison/lib/cli.js src/diagrams/gitGraph/parser/gitGraph.jison -o src/diagrams/gitGraph/parser/gitGraph.js

447
src/d3.js vendored Normal file
View File

@@ -0,0 +1,447 @@
const d3 = require('d3')
module.exports = d3;
/*
D3 Text Wrap
By Vijith Assar
http://www.vijithassar.com
http://www.github.com/vijithassar
@vijithassar
Detailed instructions at http://www.github.com/vijithassar/d3textwrap
*/
(function () {
// set this variable to a string value to always force a particular
// wrap method for development purposes, for example to check tspan
// rendering using a foreignobject-enabled browser. set to 'tspan' to
// use tspans and 'foreignobject' to use foreignobject
var forceWrapMethod = false // by default no wrap method is forced
forceWrapMethod = 'tspans' // uncomment this statement to force tspans
// force_wrap_method = 'foreignobjects'; // uncomment this statement to force foreignobjects
// exit immediately if something in this location
// has already been defined; the plugin will defer to whatever
// else you're doing in your code
if (d3.selection.prototype.textwrap) {
return false
}
// double check the force_wrap_method flag
// and reset if someone screwed up the above
// settings
if (typeof forceWrapMethod === 'undefined') {
forceWrapMethod = false
}
// create the plugin method twice, both for regular use
// and again for use inside the enter() selection
d3.selection.prototype.textwrap = d3.selection.enter.prototype.textwrap = function (bounds, padding) {
// default value of padding is zero if it's undefined
padding = parseInt(padding) || 0
// save callee into a variable so we can continue to refer to it
// as the function scope changes
var selection = this
// create a variable to store desired return values in
var returnValue
// extract wrap boundaries from any d3-selected rect and return them
// in a format that matches the simpler object argument option
var extractBounds = function (bounds) {
// discard the nested array wrappers added by d3
var boundingRect = bounds[0][0]
// sanitize the svg element name so we can test against it
var elementType = boundingRect.tagName.toString()
// if it's not a rect, exit
if (elementType !== 'rect') {
return false
// if it's a rect, proceed to extracting the position attributes
} else {
var boundsExtracted = {}
boundsExtracted.x = d3.select(boundingRect).attr('x') || 0
boundsExtracted.y = d3.select(boundingRect).attr('y') || 0
boundsExtracted.width = d3.select(boundingRect).attr('width') || 0
boundsExtracted.height = d3.select(boundingRect).attr('height') || 0
// also pass along the getter function
boundsExtracted.attr = bounds.attr
}
return boundsExtracted
}
// double check the input argument for the wrapping
// boundaries to make sure it actually contains all
// the information we'll need in order to wrap successfully
var verifyBounds = function (bounds) {
// quickly add a simple getter method so you can use either
// bounds.x or bounds.attr('x') as your notation,
// the latter being a common convention among D3
// developers
if (!bounds.attr) {
bounds.attr = function (property) {
if (this[property]) {
return this[property]
}
}
}
// if it's an associative array, make sure it has all the
// necessary properties represented directly
if (
(typeof bounds === 'object') &&
(typeof bounds.x !== 'undefined') &&
(typeof bounds.y !== 'undefined') &&
(typeof bounds.width !== 'undefined') &&
(typeof bounds.height !== 'undefined')
// if that's the case, then the bounds are fine
) {
// return the lightly modified bounds
return bounds
// if it's a numerically indexed array, assume it's a
// d3-selected rect and try to extract the positions
} else if (
// first try to make sure it's an array using Array.isArray
(
(typeof Array.isArray === 'function') &&
(Array.isArray(bounds))
) ||
// but since Array.isArray isn't always supported, fall
// back to casting to the object to string when it's not
(Object.prototype.toString.call(bounds) === '[object Array]')
) {
// once you're sure it's an array, extract the boundaries
// from the rect
var extractedBounds = extractBounds(bounds)
return extractedBounds
} else {
// but if the bounds are neither an object nor a numerical
// array, then the bounds argument is invalid and you'll
// need to fix it
return false
}
}
var applyPadding = function (bounds, padding) {
var paddedBounds = bounds
if (padding !== 0) {
paddedBounds.x = parseInt(paddedBounds.x) + padding
paddedBounds.y = parseInt(paddedBounds.y) + padding
paddedBounds.width -= padding * 2
paddedBounds.height -= padding * 2
}
return paddedBounds
}
// verify bounds
var verifiedBounds = verifyBounds(bounds)
// modify bounds if a padding value is provided
if (padding) {
verifiedBounds = applyPadding(verifiedBounds, padding)
}
// check that we have the necessary conditions for this function to operate properly
if (
// selection it's operating on cannot be not empty
(selection.length === 0) ||
// d3 must be available
(!d3) ||
// desired wrapping bounds must be provided as an input argument
(!bounds) ||
// input bounds must validate
(!verifiedBounds)
) {
// try to return the calling selection if possible
// so as not to interfere with methods downstream in the
// chain
if (selection) {
return selection
// if all else fails, just return false. if you hit this point then you're
// almost certainly trying to call the textwrap() method on something that
// doesn't make sense!
} else {
return false
}
// if we've validated everything then we can finally proceed
// to the meat of this operation
} else {
// reassign the verified bounds as the set we want
// to work with from here on; this ensures that we're
// using the same data structure for our bounds regardless
// of whether the input argument was a simple object or
// a d3 selection
bounds = verifiedBounds
// wrap using html and foreignObjects if they are supported
var wrapWithForeignobjects = function (item) {
// establish variables to quickly reference target nodes later
var parent = d3.select(item[0].parentNode)
var textNode = parent.select('text')
var styledLineHeight = textNode.style('line-height')
// extract our desired content from the single text element
var textToWrap = textNode.text()
// remove the text node and replace with a foreign object
textNode.remove()
var foreignObject = parent.append('foreignObject')
// add foreign object and set dimensions, position, etc
foreignObject
.attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
.attr('x', bounds.x)
.attr('y', bounds.y)
.attr('width', bounds.width)
.attr('height', bounds.height)
// insert an HTML div
var wrapDiv = foreignObject
.append('xhtml:div')
// this class is currently hardcoded
// probably not necessary but easy to
// override using .classed() and for now
// it's nice to avoid a litany of input
// arguments
.attr('class', 'wrapped')
// set div to same dimensions as foreign object
wrapDiv
.style('height', bounds.height)
.style('width', bounds.width)
// insert text content
.html(textToWrap)
if (styledLineHeight) {
wrapDiv.style('line-height', styledLineHeight)
}
returnValue = parent.select('foreignObject')
}
// wrap with tspans if foreignObject is undefined
var wrapWithTspans = function (item) {
// operate on the first text item in the selection
var textNode = item[0]
var parent = textNode.parentNode
var textNodeSelected = d3.select(textNode)
// measure initial size of the text node as rendered
var textNodeHeight = textNode.getBBox().height
var textNodeWidth = textNode.getBBox().width
// figure out the line height, either from rendered height
// of the font or attached styling
var lineHeight
var renderedLineHeight = textNodeHeight
var styledLineHeight = textNodeSelected.style('line-height')
if (
(styledLineHeight) &&
(parseInt(styledLineHeight))
) {
lineHeight = parseInt(styledLineHeight.replace('px', ''))
} else {
lineHeight = renderedLineHeight
}
// only fire the rest of this if the text content
// overflows the desired dimensions
if (textNodeWidth > bounds.width) {
// store whatever is inside the text node
// in a variable and then zero out the
// initial content; we'll reinsert in a moment
// using tspan elements.
var textToWrap = textNodeSelected.text()
textNodeSelected.text('')
if (textToWrap) {
// keep track of whether we are splitting by spaces
// so we know whether to reinsert those spaces later
var breakDelimiter
// split at spaces to create an array of individual words
var textToWrapArray
if (textToWrap.indexOf(' ') !== -1) {
breakDelimiter = ' '
textToWrapArray = textToWrap.split(' ')
} else {
// if there are no spaces, figure out the split
// points by comparing rendered text width against
// bounds and translating that into character position
// cuts
breakDelimiter = ''
var stringLength = textToWrap.length
var numberOfSubstrings = Math.ceil(textNodeWidth / bounds.width)
var spliceInterval = Math.floor(stringLength / numberOfSubstrings)
if (
!(spliceInterval * numberOfSubstrings >= stringLength)
) {
numberOfSubstrings++
}
textToWrapArray = []
var substring
var startPosition
for (var i = 0; i < numberOfSubstrings; i++) {
startPosition = i * spliceInterval
substring = textToWrap.substr(startPosition, spliceInterval)
textToWrapArray.push(substring)
}
}
// new array where we'll store the words re-assembled into
// substrings that have been tested against the desired
// maximum wrapping width
var substrings = []
// computed text length is arguably incorrectly reported for
// all tspans after the first one, in that they will include
// the width of previous separate tspans. to compensate we need
// to manually track the computed text length of all those
// previous tspans and substrings, and then use that to offset
// the miscalculation. this then gives us the actual correct
// position we want to use in rendering the text in the SVG.
var totalOffset = 0
// object for storing the results of text length computations later
var temp = {}
// loop through the words and test the computed text length
// of the string against the maximum desired wrapping width
for (i = 0; i < textToWrapArray.length; i++) {
var word = textToWrapArray[i]
var previousString = textNodeSelected.text()
var previousWidth = textNode.getComputedTextLength()
// initialize the current word as the first word
// or append to the previous string if one exists
var newstring
if (previousString) {
newstring = previousString + breakDelimiter + word
} else {
newstring = word
}
// add the newest substring back to the text node and
// measure the length
textNodeSelected.text(newstring)
var newWidth = textNode.getComputedTextLength()
// adjust the length by the offset we've tracked
// due to the misreported length discussed above
// if our latest version of the string is too
// big for the bounds, use the previous
// version of the string (without the newest word
// added) and use the latest word to restart the
// process with a new tspan
if (newWidth > bounds.width) {
if (
(previousString) &&
(previousString !== '')
) {
totalOffset = totalOffset + previousWidth
temp = { string: previousString, width: previousWidth, offset: totalOffset }
substrings.push(temp)
textNodeSelected.text('')
textNodeSelected.text(word)
// Handle case where there is just one more word to be wrapped
if (i === textToWrapArray.length - 1) {
newstring = word
textNodeSelected.text(newstring)
newWidth = textNode.getComputedTextLength()
}
}
}
// if we're up to the last word in the array,
// get the computed length as is without
// appending anything further to it
if (i === textToWrapArray.length - 1) {
textNodeSelected.text('')
var finalString = newstring
if (
(finalString) &&
(finalString !== '')
) {
if ((newWidth - totalOffset) > 0) { newWidth = newWidth - totalOffset }
temp = { string: finalString, width: newWidth, offset: totalOffset }
substrings.push(temp)
}
}
}
// append each substring as a tspan
var currentTspan
// var tspanCount
// double check that the text content has been removed
// before we start appending tspans
textNodeSelected.text('')
for (i = 0; i < substrings.length; i++) {
substring = substrings[i].string
// only append if we're sure it won't make the tspans
// overflow the bounds.
if ((i) * lineHeight < bounds.height - (lineHeight * 1.5)) {
currentTspan = textNodeSelected.append('tspan')
.text(substring)
// vertical shift to all tspans after the first one
currentTspan
.attr('dy', function (d) {
if (i > 0) {
return lineHeight
}
})
// shift left from default position, which
// is probably based on the full length of the
// text string until we make this adjustment
currentTspan
.attr('x', function () {
var xOffset = bounds.x
if (padding) { xOffset += padding }
return xOffset
})
}
}
}
}
// position the overall text node, whether wrapped or not
textNodeSelected.attr('y', function () {
var yOffset = bounds.y
// shift by line-height to move the baseline into
// the bounds otherwise the text baseline would be
// at the top of the bounds
if (lineHeight) { yOffset += lineHeight }
// shift by padding, if it's there
if (padding) { yOffset += padding }
return yOffset
})
// shift to the right by the padding value
textNodeSelected.attr('x', function () {
var xOffset = bounds.x
if (padding) { xOffset += padding }
return xOffset
})
// assign our modified text node with tspans
// to the return value
returnValue = d3.select(parent).selectAll('text')
}
// variable used to hold the functions that let us
// switch between the wrap methods
var wrapMethod
// if a wrap method if being forced, assign that
// function
if (forceWrapMethod) {
if (forceWrapMethod === 'foreignobjects') {
wrapMethod = wrapWithForeignobjects
} else if (forceWrapMethod === 'tspans') {
wrapMethod = wrapWithTspans
}
}
// if no wrap method is being forced, then instead
// test for browser support of foreignobject and
// use whichever wrap method makes sense accordingly
if (!forceWrapMethod) {
if (typeof SVGForeignObjectElement !== 'undefined') {
wrapMethod = wrapWithForeignobjects
} else {
wrapMethod = wrapWithTspans
}
}
// run the desired wrap function for each item
// in the d3 selection that called .textwrap()
for (var i = 0; i < selection.length; i++) {
var item = selection[i]
wrapMethod(item)
}
// return the modified nodes so we can chain other
// methods to them.
return returnValue
}
}
})()

View File

@@ -0,0 +1,80 @@
var Logger = require('../../logger')
var log = Logger.Log
var relations = []
var classes
classes = {
}
/**
* Function called by parser when a node definition has been found.
* @param id
* @param text
* @param type
* @param style
*/
exports.addClass = function (id) {
if (typeof classes[id] === 'undefined') {
classes[id] = {
id: id,
methods: [],
members: []
}
}
}
exports.clear = function () {
relations = []
classes = {}
}
module.exports.getClass = function (id) {
return classes[id]
}
module.exports.getClasses = function () {
return classes
}
module.exports.getRelations = function () {
return relations
}
exports.addRelation = function (relation) {
log.warn('Adding relation: ' + JSON.stringify(relation))
exports.addClass(relation.id1)
exports.addClass(relation.id2)
relations.push(relation)
}
exports.addMembers = function (className, MembersArr) {
var theClass = classes[className]
if (typeof MembersArr === 'string') {
if (MembersArr.substr(-1) === ')') {
theClass.methods.push(MembersArr)
} else {
theClass.members.push(MembersArr)
}
}
}
exports.cleanupLabel = function (label) {
if (label.substring(0, 1) === ':') {
return label.substr(2).trim()
} else {
return label.trim()
}
}
exports.lineType = {
LINE: 0,
DOTTED_LINE: 1
}
exports.relationType = {
AGGREGATION: 0,
EXTENSION: 1,
COMPOSITION: 2,
DEPENDENCY: 3
}

View File

@@ -0,0 +1,214 @@
/* eslint-env jasmine */
/**
* Created by knut on 14-11-18.
*/
describe('class diagram, ', function () {
describe('when parsing an info graph it', function () {
var cd, cDDb
beforeEach(function () {
cd = require('./parser/classDiagram').parser
cDDb = require('./classDb')
cd.yy = cDDb
})
it('should handle relation definitions', function () {
var str = 'classDiagram\n' +
'Class01 <|-- Class02\n' +
'Class03 *-- Class04\n' +
'Class05 o-- Class06\n' +
'Class07 .. Class08\n' +
'Class09 -- Class1'
cd.parse(str)
})
it('should handle relation definition of different types and directions', function () {
var str = 'classDiagram\n' +
'Class11 <|.. Class12\n' +
'Class13 --> Class14\n' +
'Class15 ..> Class16\n' +
'Class17 ..|> Class18\n' +
'Class19 <--* Class20'
cd.parse(str)
})
it('should handle cardinality and labels', function () {
var str = 'classDiagram\n' +
'Class01 "1" *-- "many" Class02 : contains\n' +
'Class03 o-- Class04 : aggregation\n' +
'Class05 --> "1" Class06'
cd.parse(str)
})
it('should handle class definitions', function () {
var str = 'classDiagram\n' +
'class Car\n' +
'Driver -- Car : drives >\n' +
'Car *-- Wheel : have 4 >\n' +
'Car -- Person : < owns'
cd.parse(str)
})
it('should handle method statements', function () {
var str = 'classDiagram\n' +
'Object <|-- ArrayList\n' +
'Object : equals()\n' +
'ArrayList : Object[] elementData\n' +
'ArrayList : size()'
cd.parse(str)
})
it('should handle parsing of method statements grouped by brackets', function () {
var str = 'classDiagram\n' +
'class Dummy {\n' +
'String data\n' +
' void methods()\n' +
'}\n' +
'\n' +
'class Flight {\n' +
' flightNumber : Integer\n' +
' departureTime : Date\n' +
'}'
cd.parse(str)
})
it('should handle parsing of separators', function () {
var 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' +
'}'
cd.parse(str)
})
})
describe('when fetching data from an classDiagram graph it', function () {
var cd, cDDb
beforeEach(function () {
cd = require('./parser/classDiagram').parser
cDDb = require('./classDb')
cd.yy = cDDb
cd.yy.clear()
})
it('should handle relation definitions EXTENSION', function () {
var str = 'classDiagram\n' +
'Class01 <|-- Class02'
cd.parse(str)
var relations = cd.yy.getRelations()
expect(cd.yy.getClass('Class01').id).toBe('Class01')
expect(cd.yy.getClass('Class02').id).toBe('Class02')
expect(relations[0].relation.type1).toBe(cDDb.relationType.EXTENSION)
expect(relations[0].relation.type2).toBe('none')
expect(relations[0].relation.lineType).toBe(cDDb.lineType.LINE)
})
it('should handle relation definitions AGGREGATION and dotted line', function () {
var str = 'classDiagram\n' +
'Class01 o.. Class02'
cd.parse(str)
var relations = cd.yy.getRelations()
expect(cd.yy.getClass('Class01').id).toBe('Class01')
expect(cd.yy.getClass('Class02').id).toBe('Class02')
expect(relations[0].relation.type1).toBe(cDDb.relationType.AGGREGATION)
expect(relations[0].relation.type2).toBe('none')
expect(relations[0].relation.lineType).toBe(cDDb.lineType.DOTTED_LINE)
})
it('should handle relation definitions COMPOSITION on both sides', function () {
var str = 'classDiagram\n' +
'Class01 *--* Class02'
cd.parse(str)
var relations = cd.yy.getRelations()
expect(cd.yy.getClass('Class01').id).toBe('Class01')
expect(cd.yy.getClass('Class02').id).toBe('Class02')
expect(relations[0].relation.type1).toBe(cDDb.relationType.COMPOSITION)
expect(relations[0].relation.type2).toBe(cDDb.relationType.COMPOSITION)
expect(relations[0].relation.lineType).toBe(cDDb.lineType.LINE)
})
it('should handle relation definitions no types', function () {
var str = 'classDiagram\n' +
'Class01 -- Class02'
cd.parse(str)
var relations = cd.yy.getRelations()
expect(cd.yy.getClass('Class01').id).toBe('Class01')
expect(cd.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(cDDb.lineType.LINE)
})
it('should handle relation definitions with type only on right side', function () {
var str = 'classDiagram\n' +
'Class01 --|> Class02'
cd.parse(str)
var relations = cd.yy.getRelations()
expect(cd.yy.getClass('Class01').id).toBe('Class01')
expect(cd.yy.getClass('Class02').id).toBe('Class02')
expect(relations[0].relation.type1).toBe('none')
expect(relations[0].relation.type2).toBe(cDDb.relationType.EXTENSION)
expect(relations[0].relation.lineType).toBe(cDDb.lineType.LINE)
})
it('should handle multiple classes and relation definitions', function () {
var str = 'classDiagram\n' +
'Class01 <|-- Class02\n' +
'Class03 *-- Class04\n' +
'Class05 o-- Class06\n' +
'Class07 .. Class08\n' +
'Class09 -- Class10'
cd.parse(str)
var relations = cd.yy.getRelations()
expect(cd.yy.getClass('Class01').id).toBe('Class01')
expect(cd.yy.getClass('Class10').id).toBe('Class10')
expect(relations.length).toBe(5)
expect(relations[0].relation.type1).toBe(cDDb.relationType.EXTENSION)
expect(relations[0].relation.type2).toBe('none')
expect(relations[0].relation.lineType).toBe(cDDb.lineType.LINE)
expect(relations[3].relation.type1).toBe('none')
expect(relations[3].relation.type2).toBe('none')
expect(relations[3].relation.lineType).toBe(cDDb.lineType.DOTTED_LINE)
})
})
})

View File

@@ -0,0 +1,366 @@
/**
* Created by knut on 14-11-23.
*/
var cd = require('./parser/classDiagram').parser
var cDDb = require('./classDb')
cd.yy = cDDb
var d3 = require('../../d3')
var Logger = require('../../logger')
var log = Logger.Log
var dagre = require('dagre')
var idCache
idCache = {}
var classCnt = 0
var conf = {
dividerMargin: 10,
padding: 5,
textHeight: 10
}
// Todo optimize
var getGraphId = function (label) {
var keys = Object.keys(idCache)
var i
for (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.
*/
var 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')
}
var edgeCount = 0
var drawEdge = function (elem, path, relation) {
var getRelationType = function (type) {
switch (type) {
case cDDb.relationType.AGGREGATION:
return 'aggregation'
case cDDb.relationType.EXTENSION:
return 'extension'
case cDDb.relationType.COMPOSITION:
return 'composition'
case cDDb.relationType.DEPENDENCY:
return 'dependency'
}
}
// The data for our line
var lineData = path.points
// This is the accessor function we talked about above
var lineFunction = d3.svg.line()
.x(function (d) {
return d.x
})
.y(function (d) {
return d.y
})
.interpolate('basis')
var svgPath = elem.append('path')
.attr('d', lineFunction(lineData))
.attr('id', 'edge' + edgeCount)
.attr('class', 'relation')
var 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' + ')')
}
var x, y
var l = path.points.length
if ((l % 2) !== 0) {
var p1 = path.points[Math.floor(l / 2)]
var p2 = path.points[Math.ceil(l / 2)]
x = (p1.x + p2.x) / 2
y = (p1.y + p2.y) / 2
} else {
var p = path.points[Math.floor(l / 2)]
x = p.x
y = p.y
}
if (typeof relation.title !== 'undefined') {
var g = elem.append('g')
.attr('class', 'classLabel')
var 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
var 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++
}
var drawClass = function (elem, classDef) {
log.info('Rendering class ' + classDef)
var addTspan = function (textEl, txt, isFirst) {
var tSpan = textEl.append('tspan')
.attr('x', conf.padding)
.text(txt)
if (!isFirst) {
tSpan.attr('dy', conf.textHeight)
}
}
var id = 'classId' + classCnt
var classInfo = {
id: id,
label: classDef.id,
width: 0,
height: 0
}
var g = elem.append('g')
.attr('id', id)
.attr('class', 'classGroup')
var title = g.append('text')
.attr('x', conf.padding)
.attr('y', conf.textHeight + conf.padding)
.text(classDef.id)
var titleHeight = title.node().getBBox().height
var 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)
var 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')
var isFirst = true
classDef.members.forEach(function (member) {
addTspan(members, member, isFirst)
isFirst = false
})
var membersBox = members.node().getBBox()
var 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)
var 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
})
var 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
}
module.exports.setConf = function (cnf) {
var 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
*/
module.exports.draw = function (text, id) {
cd.yy.clear()
cd.parse(text)
log.info('Rendering diagram ' + text)
/// / Fetch the default direction, use TD if none was found
var diagram = d3.select('#' + id)
insertMarkers(diagram)
// Layout graph, Create a new directed graph
var g = new dagre.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 {}
})
var classes = cDDb.getClasses()
var keys = Object.keys(classes)
var i
for (i = 0; i < keys.length; i++) {
var classDef = classes[keys[i]]
var 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)
log.info('Org height: ' + node.height)
}
var relations = cDDb.getRelations()
relations.forEach(function (relation) {
log.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') {
log.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) {
log.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))
}

View File

@@ -0,0 +1,28 @@
/* eslint-env jasmine */
/**
* Created by knut on 14-11-18.
*/
describe('class diagram, ', function () {
describe('when rendering a classDiagram', function () {
beforeEach(function () {
Object.defineProperties(window.HTMLElement.prototype, {
getBBox: {
get: function () { return { x: 10, y: 10, width: 100, height: 100 } }
},
offsetLeft: {
get: function () { return parseFloat(window.getComputedStyle(this).marginLeft) || 0 }
},
offsetTop: {
get: function () { return parseFloat(window.getComputedStyle(this).marginTop) || 0 }
},
offsetHeight: {
get: function () { return parseFloat(window.getComputedStyle(this).height) || 0 }
},
offsetWidth: {
get: function () { return parseFloat(window.getComputedStyle(this).width) || 0 }
}
})
})
})
})

View File

@@ -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;
%%

View File

@@ -0,0 +1,736 @@
/* parser generated by jison 0.4.17 */
/*
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 {
function _parseError (msg, hash) {
this.message = msg
this.hash = hash
}
_parseError.prototype = Error
throw new _parseError(str, hash)
}
},
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
}
var lex = function () {
var token
token = lexer.lex() || EOF
if (typeof token !== 'number') {
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: 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))
}
}

View File

@@ -0,0 +1,22 @@
/* eslint-env jasmine */
/**
* Created by knut on 14-11-18.
*/
describe('when parsing an info graph it', function () {
var ex
beforeEach(function () {
ex = require('./parser/example').parser
ex.yy = require('./exampleDb')
})
it('should handle an info definition', function () {
var str = 'info\nsay: hello'
ex.parse(str)
})
it('should handle an showMessage statement definition', function () {
var str = 'info\nshowInfo'
ex.parse(str)
})
})

View File

@@ -0,0 +1,29 @@
/**
* Created by knut on 15-01-14.
*/
var Logger = require('../../logger')
var log = Logger.Log
var message = ''
var info = false
exports.setMessage = function (txt) {
log.debug('Setting message to: ' + txt)
message = txt
}
exports.getMessage = function () {
return message
}
exports.setInfo = function (inf) {
info = inf
}
exports.getInfo = function () {
return info
}
exports.parseError = function (err, hash) {
global.mermaidAPI.parseError(err, hash)
}

View File

@@ -0,0 +1,39 @@
/**
* Created by knut on 14-12-11.
*/
var db = require('./exampleDb')
var exampleParser = require('./parser/example.js')
var d3 = require('../../d3')
var Logger = require('../../logger')
var log = Logger.Log
/**
* Draws a an info picture in the tag with id: id based on the graph definition in text.
* @param text
* @param id
*/
exports.draw = function (txt, id, ver) {
var parser
parser = exampleParser.parser
parser.yy = db
log.debug('Renering example diagram')
// Parse the graph definition
parser.parse(txt)
// Fetch the default direction, use TD if none was found
var svg = d3.select('#' + id)
var g = svg.append('g')
g.append('text') // text label for the x axis
.attr('x', 100)
.attr('y', 40)
.attr('class', 'version')
.attr('font-size', '32px')
.style('text-anchor', 'middle')
.text('mermaid ' + ver)
svg.attr('height', 100)
svg.attr('width', 400)
}

View File

@@ -0,0 +1,53 @@
/** mermaid
* https://mermaidjs.github.io/
* (c) 2015 Knut Sveidqvist
* MIT license.
*/
%lex
%options case-insensitive
%{
// Pre-lexer code can go here
%}
%%
[\n]+ return 'NL';
"showInfo" return 'showInfo';
"info" return 'info';
"say" return 'say';
":"[^#\n;]+ return 'TXT';
<<EOF>> return 'EOF';
. return 'INVALID';
/lex
%start start
%% /* language grammar */
start
: info document 'EOF' { return yy; }
;
document
: /* empty */
| document line
;
line
: statement { }
| 'NL'
;
statement
: showInfo { yy.setInfo(true); }
| message { yy.setMessage($1); }
;
message
: 'say' TXT { $$ = $1.substring(1).trim().replace(/\\n/gm, "\n"); }
;
%%

View File

@@ -0,0 +1,625 @@
/* parser generated by jison 0.4.17 */
/*
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, 9, 10, 12]
var parser = {trace: function trace () { },
yy: {},
symbols_: {'error': 2, 'start': 3, 'info': 4, 'document': 5, 'EOF': 6, 'line': 7, 'statement': 8, 'NL': 9, 'showInfo': 10, 'message': 11, 'say': 12, 'TXT': 13, '$accept': 0, '$end': 1},
terminals_: {2: 'error', 4: 'info', 6: 'EOF', 9: 'NL', 10: 'showInfo', 12: 'say', 13: 'TXT'},
productions_: [0, [3, 3], [5, 0], [5, 2], [7, 1], [7, 1], [8, 1], [8, 1], [11, 2]],
performAction: function anonymous (yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
/* this == yyval */
var $0 = $$.length - 1
switch (yystate) {
case 1:
return yy
break
case 4:
break
case 6:
yy.setInfo(true)
break
case 7:
yy.setMessage($$[$0])
break
case 8:
this.$ = $$[$0 - 1].substring(1).trim().replace(/\\n/gm, '\n')
break
}
},
table: [{3: 1, 4: [1, 2]}, {1: [3]}, o($V0, [2, 2], {5: 3}), {6: [1, 4], 7: 5, 8: 6, 9: [1, 7], 10: [1, 8], 11: 9, 12: [1, 10]}, {1: [2, 1]}, o($V0, [2, 3]), o($V0, [2, 4]), o($V0, [2, 5]), o($V0, [2, 6]), o($V0, [2, 7]), {13: [1, 11]}, o($V0, [2, 8])],
defaultActions: {4: [2, 1]},
parseError: function parseError (str, hash) {
if (hash.recoverable) {
this.trace(str)
} else {
function _parseError (msg, hash) {
this.message = msg
this.hash = hash
}
_parseError.prototype = Error
throw new _parseError(str, hash)
}
},
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
}
var lex = function () {
var token
token = lexer.lex() || EOF
if (typeof token !== 'number') {
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 9
break
case 1:return 10
break
case 2:return 4
break
case 3:return 12
break
case 4:return 13
break
case 5:return 6
break
case 6:return 'INVALID'
break
}
},
rules: [/^(?:[\n]+)/i, /^(?:showInfo\b)/i, /^(?:info\b)/i, /^(?:say\b)/i, /^(?::[^#\n;]+)/i, /^(?:$)/i, /^(?:.)/i],
conditions: {'INITIAL': {'rules': [0, 1, 2, 3, 4, 5, 6], '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))
}
}

View File

@@ -0,0 +1,507 @@
/**
* Created by knut on 14-12-11.
*/
var graph = require('./graphDb')
var flow = require('./parser/flow')
var dot = require('./parser/dot')
var d3 = require('../../d3')
var dagreD3 = require('dagre-d3-renderer')
var Logger = require('../../logger')
var log = Logger.Log
var conf = {
}
module.exports.setConf = function (cnf) {
var keys = Object.keys(cnf)
var i
for (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.
*/
exports.addVertices = function (vert, g) {
var keys = Object.keys(vert)
var styleFromStyleArr = function (styleStr, arr) {
var i
// Create a compound style definition from the style definitions found for the node in the graph definition
for (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) {
var vertice = vert[id]
var verticeText
/**
* Variable for storing the classes for the vertice
* @type {string}
*/
var classStr = ''
if (vertice.classes.length > 0) {
classStr = vertice.classes.join(' ')
}
/**
* Variable for storing the extracted style for the vertice
* @type {string}
*/
var 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
}
var labelTypeStr = ''
if (conf.htmlLabels) {
labelTypeStr = 'html'
verticeText = verticeText.replace(/fa:fa[\w-]+/g, function (s) {
return '<i class="fa ' + s.substring(3) + '"></i>'
})
} else {
var svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text')
var rows = verticeText.split(/<br>/)
var j = 0
for (j = 0; j < rows.length; j++) {
var 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
}
var radious = 0
var _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
*/
exports.addEdges = function (edges, g) {
var cnt = 0
var defaultStyle
if (typeof edges.defaultStyle !== 'undefined') {
defaultStyle = edges.defaultStyle.toString().replace(/,/g, ';')
}
edges.forEach(function (edge) {
cnt++
var edgeData = {}
// Set link type for rendering
if (edge.type === 'arrow_open') {
edgeData.arrowhead = 'none'
} else {
edgeData.arrowhead = 'normal'
}
var 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.lineInterpolate = edge.interpolate
} else {
if (typeof edges.defaultInterpolate !== 'undefined') {
edgeData.lineInterpolate = edges.defaultInterpolate
}
}
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
*/
exports.getClasses = function (text, isDot) {
var parser
graph.clear()
if (isDot) {
parser = dot.parser
} else {
parser = flow.parser
}
parser.yy = graph
// Parse the graph definition
parser.parse(text)
var classes = graph.getClasses()
// Add default class if undefined
if (typeof (classes.default) === 'undefined') {
classes.default = { id: 'default' }
classes.default.styles = []
classes.default.clusterStyles = ['rx:4px', 'fill: rgb(255, 255, 222)', 'rx: 4px', 'stroke: rgb(170, 170, 51)', 'stroke-width: 1px']
classes.default.nodeLabelStyles = ['fill:#000', 'stroke:none', 'font-weight:300', 'font-family:"Helvetica Neue",Helvetica,Arial,sans-serf', 'font-size:14px']
classes.default.edgeLabelStyles = ['fill:#000', 'stroke:none', 'font-weight:300', 'font-family:"Helvetica Neue",Helvetica,Arial,sans-serf', 'font-size:14px']
}
return classes
}
/**
* Draws a flowchart in the tag with id: id based on the graph definition in text.
* @param text
* @param id
*/
exports.draw = function (text, id, isDot) {
log.debug('Drawing flowchart')
var parser
graph.clear()
if (isDot) {
parser = dot.parser
} else {
parser = flow.parser
}
parser.yy = graph
// Parse the graph definition
try {
parser.parse(text)
} catch (err) {
log.debug('Parsing failed')
}
// Fetch the default direction, use TD if none was found
var dir
dir = graph.getDirection()
if (typeof dir === 'undefined') {
dir = 'TD'
}
// Create the input mermaid.graph
var g = new dagreD3.graphlib.Graph({
multigraph: true,
compound: true
})
.setGraph({
rankdir: dir,
marginx: 20,
marginy: 20
})
.setDefaultEdgeLabel(function () {
return {}
})
var subG
var subGraphs = graph.getSubGraphs()
var i = 0
for (i = subGraphs.length - 1; i >= 0; i--) {
subG = subGraphs[i]
graph.addVertex(subG.id, subG.title, 'group', undefined)
}
// Fetch the verices/nodes and edges/links from the parsed graph definition
var vert = graph.getVertices()
var edges = graph.getEdges()
i = 0
var j
for (i = subGraphs.length - 1; i >= 0; i--) {
subG = subGraphs[i]
d3.selectAll('cluster').append('text')
for (j = 0; j < subG.nodes.length; j++) {
g.setParent(subG.nodes[j], subG.id)
}
}
exports.addVertices(vert, g)
exports.addEdges(edges, g)
// Create the renderer
var Render = dagreD3.render
var render = new Render()
// Add custom shape for rhombus type of boc (decision)
render.shapes().question = function (parent, bbox, node) {
var w = bbox.width
var h = bbox.height
var s = (w + h) * 0.8
var points = [
{ x: s / 2, y: 0 },
{ x: s, y: -s / 2 },
{ x: s / 2, y: -s },
{ x: 0, y: -s / 2 }
]
var 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) {
var w = bbox.width
var h = bbox.height
var points = [
{ x: -h / 2, y: 0 },
{ x: w, y: 0 },
{ x: w, y: -h },
{ x: -h / 2, y: -h },
{ x: 0, y: -h / 2 }
]
var 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) {
var w = bbox.width
var h = bbox.height
var 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 }
]
var 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) {
var 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')
var 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) {
var 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.
var svg = d3.select('#' + id)
// Run the renderer. This is what draws the final graph.
var element = d3.select('#' + id + ' g')
render(element, g)
element.selectAll('g.node')
.attr('title', function () {
return graph.getTooltip(this.id)
})
if (conf.useMaxWidth) {
// Center the graph
svg.attr('height', '100%')
svg.attr('width', conf.width)
svg.attr('viewBox', '0 0 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20))
svg.attr('style', 'max-width:' + (g.graph().width + 20) + 'px;')
} else {
// Center the graph
svg.attr('height', g.graph().height)
if (typeof conf.width === 'undefined') {
svg.attr('width', g.graph().width)
} else {
svg.attr('width', conf.width)
}
svg.attr('viewBox', '0 0 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20))
}
// Index nodes
graph.indexNodes('subGraph' + i)
for (i = 0; i < subGraphs.length; i++) {
subG = subGraphs[i]
if (subG.title !== 'undefined') {
var clusterRects = document.querySelectorAll('#' + id + ' #' + subG.id + ' rect')
var clusterEl = document.querySelectorAll('#' + id + ' #' + subG.id)
var xPos = clusterRects[0].x.baseVal.value
var yPos = clusterRects[0].y.baseVal.value
var width = clusterRects[0].width.baseVal.value
var cluster = d3.select(clusterEl[0])
var 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) {
var labels = document.querySelectorAll('#' + id + ' .edgeLabel .label')
var k
for (k = 0; k < labels.length; k++) {
var label = labels[i]
// Get dimensions of label
var dim = label.getBBox()
var 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)
}
}
}

View File

@@ -0,0 +1,406 @@
/**
* Created by knut on 14-11-03.
*/
var Logger = require('../../logger')
var log = Logger.Log
var utils = require('../../utils')
var d3 = require('../../d3')
var vertices = {}
var edges = []
var classes = []
var subGraphs = []
var tooltips = {}
var subCount = 0
var direction
// Functions to be run after graph rendering
var funs = []
/**
* Function called by parser when a node definition has been found
* @param id
* @param text
* @param type
* @param style
*/
exports.addVertex = function (id, text, type, style) {
var 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
*/
exports.addLink = function (start, end, type, linktext) {
log.info('Got edge...', start, end)
var 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
*/
exports.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
*/
exports.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
}
}
exports.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
*/
exports.setDirection = function (dir) {
direction = dir
}
/**
* Called by parser when a graph definition is found, stores the direction of the chart.
* @param dir
*/
exports.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)
}
}
}
var setTooltip = function (id, tooltip) {
if (typeof tooltip !== 'undefined') {
tooltips[id] = tooltip
}
}
var setClickFun = function (id, functionName) {
if (typeof functionName === 'undefined') {
return
}
if (typeof vertices[id] !== 'undefined') {
funs.push(function (element) {
var elem = d3.select(element).select('#' + id)
if (elem !== null) {
elem.on('click', function () {
window[functionName](id)
})
}
})
}
}
var setLink = function (id, linkStr) {
if (typeof linkStr === 'undefined') {
return
}
if (typeof vertices[id] !== 'undefined') {
funs.push(function (element) {
var elem = d3.select(element).select('#' + id)
if (elem !== null) {
elem.on('click', function () {
window.open(linkStr, 'newTab')
})
}
})
}
}
exports.getTooltip = function (id) {
return tooltips[id]
}
/**
* Called by parser when a graph definition is found, stores the direction of the chart.
* @param dir
*/
exports.setClickEvent = function (id, functionName, link, tooltip) {
if (id.indexOf(',') > 0) {
id.split(',').forEach(function (id2) {
setTooltip(id2, tooltip)
setClickFun(id2, functionName)
setLink(id2, link)
})
} else {
setTooltip(id, tooltip)
setClickFun(id, functionName)
setLink(id, link)
}
}
exports.bindFunctions = function (element) {
funs.forEach(function (fun) {
fun(element)
})
}
exports.getDirection = function () {
return direction
}
/**
* Retrieval function for fetching the found nodes after parsing has completed.
* @returns {{}|*|vertices}
*/
exports.getVertices = function () {
return vertices
}
/**
* Retrieval function for fetching the found links after parsing has completed.
* @returns {{}|*|edges}
*/
exports.getEdges = function () {
return edges
}
/**
* Retrieval function for fetching the found class definitions after parsing has completed.
* @returns {{}|*|classes}
*/
exports.getClasses = function () {
return classes
}
var setupToolTips = function (element) {
var tooltipElem = d3.select('.mermaidTooltip')
if (tooltipElem[0][0] === null) {
tooltipElem = d3.select('body')
.append('div')
.attr('class', 'mermaidTooltip')
.style('opacity', 0)
}
var svg = d3.select(element).select('svg')
var nodes = svg.selectAll('g.node')
nodes
.on('mouseover', function () {
var el = d3.select(this)
var title = el.attr('title')
// Dont try to draw a tooltip if no data is provided
if (title === null) {
return
}
var 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)
var el = d3.select(this)
el.classed('hover', false)
})
}
funs.push(setupToolTips)
/**
* Clears the internal graph db so that a new graph can be parsed.
*/
exports.clear = function () {
vertices = {}
classes = {}
edges = []
funs = []
funs.push(setupToolTips)
subGraphs = []
subCount = 0
tooltips = []
}
/**
*
* @returns {string}
*/
exports.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.
*/
exports.addSubGraph = function (list, title) {
function uniq (a) {
var prims = { 'boolean': {}, 'number': {}, 'string': {} }
var objs = []
return a.filter(function (item) {
var type = typeof item
if (item === ' ') {
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) }
})
}
var nodeList = []
nodeList = uniq(nodeList.concat.apply(nodeList, list))
var subGraph = { id: 'subGraph' + subCount, nodes: nodeList, title: title }
subGraphs.push(subGraph)
subCount = subCount + 1
return subGraph.id
}
var getPosForId = function (id) {
var i
for (i = 0; i < subGraphs.length; i++) {
if (subGraphs[i].id === id) {
return i
}
}
return -1
}
var secCount = -1
var posCrossRef = []
var indexNodes = function (id, pos) {
var 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
}
}
var count = 0
var posCount = 1
while (count < nodes.length) {
var childPos = getPosForId(nodes[count])
// Ignore regular nodes (pos will be -1)
if (childPos >= 0) {
var res = indexNodes(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
}
}
exports.getDepthFirstPos = function (pos) {
return posCrossRef[pos]
}
exports.indexNodes = function () {
secCount = -1
if (subGraphs.length > 0) {
indexNodes('none', subGraphs.length - 1, 0)
}
}
exports.getSubGraphs = function () {
return subGraphs
}
exports.parseError = function (err, hash) {
global.mermaidAPI.parseError(err, hash)
}

View File

@@ -0,0 +1,266 @@
/* description: Parses end executes mathematical expressions. */
/* lexical grammar */
%lex
%%
"style" return 'STYLE';
"linkStyle" return 'LINKSTYLE';
"classDef" return 'CLASSDEF';
"class" return 'CLASS';
"click" return 'CLICK';
"graph" return 'GRAPH';
"digraph" return 'DIGRAPH';
"subgraph" return 'SUBGRAPH';
"node" return 'NODE';
"edge" return 'EDGE';
"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 ';';
"," return ',';
"=" return '=';
"*" return 'MULT';
"." return 'DOT';
\-\-[x] return 'ARROW_CROSS';
\-\> return 'ARROW_POINT';
\-\-[o] return 'ARROW_CIRCLE';
\-\- return 'ARROW_OPEN';
\- return 'MINUS';
\+ return 'PLUS';
\= return 'EQUALS';
[\u0021-\u0027\u002A-\u002E\u003F\u0041-\u005A\u0061-\u007A\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 'ALPHA';
"|" return 'PIPE';
"(" return 'PS';
")" return 'PE';
"[" return '[';
"]" return ']';
"{" return '{'
"}" return '}'
"\"" return 'QUOTE';
\s return 'SPACE';
\n return 'NEWLINE';
<<EOF>> return 'EOF';
/lex
/* operator associations and precedence */
%left '^'
%start expressions
%% /* language grammar */
expressions
: graph EOF
{$$=$1;}
;
graph : graphStatement idStatement '{' stmt_list '}'
{$$=$1;}
| strict graphStatement idStatement '{' stmt_list '}'
{$$=$1;}
|graphStatement '{' stmt_list '}'
{$$=$1;}
;
graphStatement: GRAPH
| DIGRAPH
;
idStatement:
textNoTags
;
textNoTags: textNoTagsToken
{$$=$1;}
| textNoTags textNoTagsToken
{$$=$1+''+$2;}
;
textNoTagsToken: ALPHA
{$$=$1;}
| NUM
{$$=$1;}
| COLON
{$$ = $1;}
| PLUS
{$$ = $1;}
| EQUALS
{$$ = $1;}
| MULT
{$$ = $1;}
| DOT
{$$ = $1;}
| BRKT
{$$ = '<br>';}
| SPACE
{$$ = $1;}
| MINUS
{$$ = $1;}
| keywords
{$$ = $1;}
;
stmt_list : stmt
| stmt ';' stmt_list
;
stmt : node_stmt
| edge_stmt
| attr_stmt
| idStatement '=' idStatement
| subgraph
;
attr_stmt : GRAPH attr_list
| NODE attr_list
| EDGE attr_list
;
attr_list
: '[' a_list ']' attr_list
| '[' ']' attr_list
| '[' a_list ']'
| '[' ']'
;
a_list
: idStatement '=' idStatement ';' a_list
| idStatement '=' idStatement ',' a_list
| idStatement '=' idStatement
;
edge_stmt
: subgraph edgeRHS attr_list
| node_id edgeRHS attr_list
{$$='oy';}
| node_id edgeRHS
{
yy.addLink($1,$2.id,$2.op);
$$='oy';}
| subgraph edgeRHS
;
edgeRHS
: edgeop node_id edgeRHS
{
yy.addLink($2,$3.id,$3.op);
$$={op:$1,id:$2};
}
| edgeop subgraph edgeRHS
| edgeop node_id
{
$$={op:$1,id:$2};
}
| edgeop subgraph
;
node_stmt
: node_id attr_list
| node_id
;
node_id
: idStatement port
{yy.addVertex($1);$$=$1;}
| idStatement
{yy.addVertex($1);$$=$1;}
;
port
: ':' idStatement ':' compass_pt
| ':' idStatement
| ':' compass_pt
;
subgraph
: SUBGRAPH idStatement '{' stmt_list '}'
| SUBGRAPH '{' stmt_list '}'
| '{' stmt_list '}'
;
compass_pt
: 'n'
| ne
| e
| se
| s
| sw
| w
| nw
| c
| _
;
edgeop
: ARROW_POINT
{$$='arrow';}
| ARROW_OPEN
{$$='arrow_open';}
;
%%

File diff suppressed because one or more lines are too long

View File

@@ -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 ;
%%

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
```
gantt
dateFormat yyyy-mm-dd
title Adding gantt diagram functionality to mermaid
section Design
Design jison grammar :des1, 2014-01-01, 2014-01-04
Create example text :des2, 2014-01-01, 3d
Bounce gantt example with users :des3, after des2, 5d
section Implementation
update build script :2014-01-02,1h
Implement parser and jison :after des1, 2d
Create tests for parser :3d
Create renderer :5d
Create tests for renderer :2d
Add to mermaid core :1d
section Documentation
Describe gantt syntax :a1, 2014-01-01, 3d
Add gantt diagram to demo page :after a1 , 2h
Add gantt to diagram to demo page :after a1 , 2h
```

View File

@@ -0,0 +1,62 @@
* {
margin: 0;
padding: 0;
}
body {
background: #fff;
font-family: 'Open-Sans',sans-serif;
}
#container{
margin: 0 auto;
position: relative;
width:800px;
overflow: visible;
}
.svg {
width:800px;
height:400px;
overflow: visible;
position:absolute;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.3;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
#tag {
color: white;
background: #FA283D;
width: 150px;
position: absolute;
display: none;
padding:3px 6px;
margin-left: -80px;
font-size: 11px;
}
#tag:before {
border: solid transparent;
content: ' ';
height: 0;
left: 50%;
margin-left: -5px;
position: absolute;
width: 0;
border-width: 10px;
border-bottom-color: #FA283D;
top: -20px;
}

View File

@@ -0,0 +1,52 @@
/* eslint-env jasmine */
/**
* Created by knut on 14-11-18.
*/
describe('when parsing a gantt diagram it', function () {
var gantt
beforeEach(function () {
gantt = require('./parser/gantt').parser
gantt.yy = require('./ganttDb')
})
it('should handle an dateFormat definition', function () {
var str = 'gantt\ndateFormat yyyy-mm-dd'
gantt.parse(str)
})
it('should handle an dateFormat definition', function () {
var str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
gantt.parse(str)
})
it('should handle an dateFormat definition', function () {
var str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
gantt.parse(str)
})
it('should handle an section definition', function () {
var str = 'gantt\ndateFormat yyyy-mm-dd\ntitle Adding gantt diagram functionality to mermaid'
gantt.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 () {
var 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'
gantt.parse(str)
})
})

View File

@@ -0,0 +1,358 @@
/**
* Created by knut on 15-01-14.
*/
var moment = require('moment')
var Logger = require('../../logger')
var log = Logger.Log
var dateFormat = ''
var title = ''
var sections = []
var tasks = []
var currentSection = ''
exports.clear = function () {
sections = []
tasks = []
currentSection = ''
title = ''
taskCnt = 0
lastTask = undefined
lastTaskID = undefined
rawTasks = []
}
exports.setDateFormat = function (txt) {
dateFormat = txt
}
exports.getDateFormat = function () {
return dateFormat
}
exports.setTitle = function (txt) {
title = txt
}
exports.getTitle = function () {
return title
}
exports.addSection = function (txt) {
currentSection = txt
sections.push(txt)
}
exports.getTasks = function () {
var allItemsPricessed = compileTasks()
var maxDepth = 10
var iterationCount = 0
while (!allItemsPricessed && (iterationCount < maxDepth)) {
allItemsPricessed = compileTasks()
iterationCount++
}
tasks = rawTasks
return tasks
}
var getStartDate = function (prevTime, dateFormat, str) {
str = str.trim()
// Test for after
var re = /^after\s+([\d\w-]+)/
var afterStatement = re.exec(str.trim())
if (afterStatement !== null) {
var task = exports.findTaskById(afterStatement[1])
if (typeof task === 'undefined') {
var 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 {
log.debug('Invalid date:' + str)
log.debug('With date format:' + dateFormat.trim())
}
// Default date - now
return new Date()
}
var 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()
}
var d = moment(prevTime)
// Check for length
var re = /^([\d]+)([wdhms])/
var 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()
}
var taskCnt = 0
var 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
var compileData = function (prevTask, dataStr) {
var ds
if (dataStr.substr(0, 1) === ':') {
ds = dataStr.substr(1, dataStr.length)
} else {
ds = dataStr
}
var data = ds.split(',')
var task = {}
var df = exports.getDateFormat()
// Get tags like active, done cand crit
var 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
}
}
var i
for (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, df, data[0])
break
case 2:
task.id = parseId()
task.startTime = getStartDate(undefined, df, data[0])
task.endTime = getEndDate(task.startTime, df, data[1])
break
case 3:
task.id = parseId(data[0])
task.startTime = getStartDate(undefined, df, data[1])
task.endTime = getEndDate(task.startTime, df, data[2])
break
default:
}
return task
}
var parseData = function (prevTaskId, dataStr) {
var ds
if (dataStr.substr(0, 1) === ':') {
ds = dataStr.substr(1, dataStr.length)
} else {
ds = dataStr
}
var data = ds.split(',')
var task = {}
// Get tags like active, done cand crit
var 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
}
}
var i
for (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
}
var lastTask
var lastTaskID
var rawTasks = []
var taskDb = {}
exports.addTask = function (descr, data) {
var rawTask = {
section: currentSection,
type: currentSection,
processed: false,
raw: { data: data },
task: descr
}
var 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
var pos = rawTasks.push(rawTask)
lastTaskID = rawTask.id
// Store cross ref
taskDb[rawTask.id] = pos - 1
}
exports.findTaskById = function (id) {
var pos = taskDb[id]
return rawTasks[pos]
}
exports.addTaskOrg = function (descr, data) {
var newTask = {
section: currentSection,
type: currentSection,
description: descr,
task: descr
}
var 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)
}
var compileTasks = function () {
var df = exports.getDateFormat()
var compileTask = function (pos) {
var task = rawTasks[pos]
var startTime = ''
switch (rawTasks[pos].raw.startTime.type) {
case 'prevTaskEnd':
var prevTask = exports.findTaskById(task.prevTaskId)
task.startTime = prevTask.endTime
break
case 'getStartDate':
startTime = getStartDate(undefined, df, rawTasks[pos].raw.startTime.startData)
if (startTime) {
rawTasks[pos].startTime = startTime
}
break
}
if (rawTasks[pos].startTime) {
rawTasks[pos].endTime = getEndDate(rawTasks[pos].startTime, df, rawTasks[pos].raw.endTime.data)
if (rawTasks[pos].endTime) {
rawTasks[pos].processed = true
}
}
return rawTasks[pos].processed
}
var i
var allProcessed = true
for (i = 0; i < rawTasks.length; i++) {
compileTask(i)
allProcessed = allProcessed && rawTasks[i].processed
}
return allProcessed
}
exports.parseError = function (err, hash) {
global.mermaidAPI.parseError(err, hash)
}

View File

@@ -0,0 +1,179 @@
/* eslint-env jasmine */
/**
* Created by knut on 14-11-18.
*/
describe('when using the ganttDb', function () {
var gDb
var moment = require('moment')
beforeEach(function () {
gDb = require('./ganttDb')
gDb.clear()
})
it('should handle an fixed dates', function () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2013-01-12')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2d')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2h')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2m')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2s')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2w')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2w')
gDb.addTask('test2', 'id2,after id1,1d')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2w')
gDb.addTask('test2', 'id2,after id3,1d')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', '2013-01-01,2013-01-12')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', '2013-01-01,4d')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2w')
gDb.addTask('test2', 'after id1,1d')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2w')
gDb.addTask('test2', '2013-01-26')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2w')
gDb.addTask('test2', '2d')
var tasks = gDb.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 () {
gDb.setDateFormat('YYYY-MM-DD')
gDb.addSection('testa1')
gDb.addTask('test1', 'id1,2013-01-01,2w')
gDb.addTask('test2', 'id2,after id3,1d')
gDb.addSection('testa2')
gDb.addTask('test3', 'id3,after id1,2d')
var tasks = gDb.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())
})
})

View File

@@ -0,0 +1,399 @@
var gantt = require('./parser/gantt').parser
gantt.yy = require('./ganttDb')
var d3 = require('../../d3')
var moment = require('moment')
var daysInChart
var conf = {
titleTopMargin: 25,
barHeight: 20,
barGap: 4,
topPadding: 50,
rightPadding: 75,
leftPadding: 75,
gridLineStartPadding: 35,
fontSize: 11,
fontFamily: '"Open-Sans", "sans-serif"'
}
module.exports.setConf = function (cnf) {
var keys = Object.keys(cnf)
keys.forEach(function (key) {
conf[key] = cnf[key]
})
}
var w
module.exports.draw = function (text, id) {
gantt.yy.clear()
gantt.parse(text)
var elem = document.getElementById(id)
w = elem.parentElement.offsetWidth
if (typeof w === 'undefined') {
w = 1200
}
if (typeof conf.useWidth !== 'undefined') {
w = conf.useWidth
}
var taskArray = gantt.yy.getTasks()
// Set height based on number of tasks
var h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding
elem.setAttribute('height', '100%')
// Set viewBox
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h)
var svg = d3.select('#' + id)
var startDate = d3.min(taskArray, function (d) {
return d.startTime
})
var endDate = d3.max(taskArray, function (d) {
return d.endTime
})
// Set timescale
var timeScale = d3.time.scale()
.domain([d3.min(taskArray, function (d) {
return d.startTime
}),
d3.max(taskArray, function (d) {
return d.endTime
})])
.rangeRound([0, w - conf.leftPadding - conf.rightPadding])
var categories = []
daysInChart = moment.duration(endDate - startDate).asDays()
for (var i = 0; i < taskArray.length; i++) {
categories.push(taskArray[i].type)
}
var 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(gantt.yy.getTitle())
.attr('x', w / 2)
.attr('y', conf.titleTopMargin)
.attr('class', 'titleText')
function makeGant (tasks, pageWidth, pageHeight) {
var barHeight = conf.barHeight
var gap = barHeight + conf.barGap
var topPadding = conf.topPadding
var leftPadding = conf.leftPadding
var colorScale = d3.scale.linear()
.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 (var i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
return 'section section' + (i % conf.numberSectionStyles)
}
}
return 'section section0'
})
var 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) {
var res = 'task '
var secNum = 0
for (var 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) {
var startX = timeScale(d.startTime)
var endX = timeScale(d.endTime)
var 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) {
var startX = timeScale(d.startTime)
var endX = timeScale(d.endTime)
var textWidth = this.getBBox().width
var secNum = 0
for (var i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
secNum = (i % conf.numberSectionStyles)
}
}
var 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) {
var pre = [
['.%L', function (d) {
return d.getMilliseconds()
}],
[':%S', function (d) {
return d.getSeconds()
}],
// Within a hour
['h1 %I:%M', function (d) {
return d.getMinutes()
}]]
var post = [
['%Y', function () {
return true
}]]
var mid = [
// Within a day
['%I:%M', function (d) {
return d.getHours()
}],
// Day within a week (not monday)
['%a %d', function (d) {
return d.getDay() && d.getDate() !== 1
}],
// within a month
['%b %d', function (d) {
return d.getDate() !== 1
}],
// Month
['%B', function (d) {
return d.getMonth()
}]
]
var formatter
if (typeof conf.axisFormatter !== 'undefined') {
mid = []
conf.axisFormatter.forEach(function (item) {
var n = []
n[0] = item[0]
n[1] = item[1]
mid.push(n)
})
}
formatter = pre.concat(mid).concat(post)
var xAxis = d3.svg.axis()
.scale(timeScale)
.orient('bottom')
.tickSize(-h + theTopPad + conf.gridLineStartPadding, 0, 0)
.tickFormat(d3.time.format.multi(formatter))
if (daysInChart > 7 && daysInChart < 230) {
xAxis = xAxis.ticks(d3.time.monday.range)
}
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) {
var numOccurances = []
var prevGap = 0
for (var 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 (var 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 (var 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) {
var todayG = svg.append('g')
.attr('class', 'today')
var 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) {
var hash = {}
var result = []
for (var 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) {
var i = arr.length // var to loop over
var 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
}
}

View File

@@ -0,0 +1,62 @@
/** 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';
\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);}
| 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';}
;
%%

View File

@@ -0,0 +1,653 @@
/* parser generated by jison 0.4.17 */
/*
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], $V1 = [1, 9], $V2 = [1, 10], $V3 = [1, 11], $V4 = [1, 12]
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, 'title': 12, 'section': 13, 'taskTxt': 14, 'taskData': 15, '$accept': 0, '$end': 1 },
terminals_: { 2: 'error', 4: 'gantt', 6: 'EOF', 8: 'SPACE', 10: 'NL', 11: 'dateFormat', 12: 'title', 13: 'section', 14: 'taskTxt', 15: 'taskData' },
productions_: [0, [3, 3], [5, 0], [5, 2], [7, 2], [7, 1], [7, 1], [7, 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.setTitle($$[$0].substr(6)); this.$ = $$[$0].substr(6)
break
case 10:
yy.addSection($$[$0].substr(8)); this.$ = $$[$0].substr(8)
break
case 11:
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 }, o($V0, [2, 7], { 1: [2, 1] }), o($V0, [2, 3]), { 9: 13, 11: $V1, 12: $V2, 13: $V3, 14: $V4 }, o($V0, [2, 5]), o($V0, [2, 6]), o($V0, [2, 8]), o($V0, [2, 9]), o($V0, [2, 10]), { 15: [1, 14] }, o($V0, [2, 4]), o($V0, [2, 11])],
defaultActions: {},
parseError: function parseError (str, hash) {
if (hash.recoverable) {
this.trace(str)
} else {
function _parseError (msg, hash) {
this.message = msg
this.hash = hash
}
_parseError.prototype = Error
throw new _parseError(str, hash)
}
},
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
}
var lex = function () {
var token
token = lexer.lex() || EOF
if (typeof token !== 'number') {
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 'date'
break
case 7: return 12
break
case 8: return 13
break
case 9: return 14
break
case 10: return 15
break
case 11: return ':'
break
case 12: return 6
break
case 13: return 'INVALID'
break
}
},
rules: [/^(?:[\n]+)/i, /^(?:\s+)/i, /^(?:#[^\n]*)/i, /^(?:%[^\n]*)/i, /^(?:gantt\b)/i, /^(?:dateFormat\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], '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))
}
}

View File

@@ -0,0 +1,208 @@
const Logger = require('../../logger')
const log = Logger.Log
const _ = require('lodash')
var commits = {}
var head = null
var branches = { 'master': head }
var curBranch = 'master'
var direction = 'LR'
var seq = 0
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
function getId () {
var pool = '0123456789abcdef'
var id = ''
for (var i = 0; i < 7; i++) {
id += pool[getRandomInt(0, 16)]
}
return id
}
function isfastforwardable (currentCommit, otherCommit) {
log.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)) {
log.debug('In merge commit:', otherCommit.parent)
return isfastforwardable(currentCommit, commits[otherCommit.parent[0]]) ||
isfastforwardable(currentCommit, commits[otherCommit.parent[1]])
} else {
otherCommit = commits[otherCommit.parent]
}
}
log.debug(currentCommit.id, otherCommit.id)
return currentCommit.id === otherCommit.id
}
function isReachableFrom (currentCommit, otherCommit) {
var currentSeq = currentCommit.seq
var otherSeq = otherCommit.seq
if (currentSeq > otherSeq) return isfastforwardable(otherCommit, currentCommit)
return false
}
exports.setDirection = function (dir) {
direction = dir
}
var options = {}
exports.setOptions = function (rawOptString) {
log.debug('options str', rawOptString)
rawOptString = rawOptString && rawOptString.trim()
rawOptString = rawOptString || '{}'
try {
options = JSON.parse(rawOptString)
} catch (e) {
log.error('error while parsing gitGraph options', e.message)
}
}
exports.getOptions = function () {
return options
}
exports.commit = function (msg) {
var commit = {
id: getId(),
message: msg,
seq: seq++,
parent: head == null ? null : head.id
}
head = commit
commits[commit.id] = commit
branches[curBranch] = commit.id
log.debug('in pushCommit ' + commit.id)
}
exports.branch = function (name) {
branches[name] = head != null ? head.id : null
log.debug('in createBranch')
}
exports.merge = function (otherBranch) {
var currentCommit = commits[branches[curBranch]]
var otherCommit = commits[branches[otherBranch]]
if (isReachableFrom(currentCommit, otherCommit)) {
log.debug('Already merged')
return
}
if (isfastforwardable(currentCommit, otherCommit)) {
branches[curBranch] = branches[otherBranch]
head = commits[branches[curBranch]]
} else {
// create merge commit
var 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
}
log.debug(branches)
log.debug('in mergeBranch')
}
exports.checkout = function (branch) {
log.debug('in checkout')
curBranch = branch
var id = branches[curBranch]
head = commits[id]
}
exports.reset = function (commitRef) {
log.debug('in reset', commitRef)
var ref = commitRef.split(':')[0]
var parentCount = parseInt(commitRef.split(':')[1])
var commit = ref === 'HEAD' ? head : commits[branches[ref]]
log.debug(commit, parentCount)
while (parentCount > 0) {
commit = commits[commit.parent]
parentCount--
if (!commit) {
var err = 'Critical error - unique parent commit not found during reset'
log.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) {
var commit = _.maxBy(commitArr, 'seq')
var line = ''
commitArr.forEach(function (c) {
if (c === commit) {
line += '\t*'
} else {
line += '\t|'
}
})
var label = [line, commit.id, commit.seq]
_.each(branches, function (value, key) {
if (value === commit.id) label.push(key)
})
log.debug(label.join(' '))
if (Array.isArray(commit.parent)) {
var newCommit = commits[commit.parent[0]]
upsert(commitArr, commit, newCommit)
commitArr.push(commits[commit.parent[1]])
} else if (commit.parent == null) {
return
} else {
var nextCommit = commits[commit.parent]
upsert(commitArr, commit, nextCommit)
}
commitArr = _.uniqBy(commitArr, 'id')
prettyPrintCommitHistory(commitArr)
}
exports.prettyPrint = function () {
log.debug(commits)
var node = exports.getCommitsArray()[0]
prettyPrintCommitHistory([node])
}
exports.clear = function () {
commits = {}
head = null
branches = { 'master': head }
curBranch = 'master'
seq = 0
}
exports.getBranchesAsObjArray = function () {
const branchArr = _.map(branches, function (value, key) {
return { 'name': key, 'commit': commits[value] }
})
return branchArr
}
exports.getBranches = function () { return branches }
exports.getCommits = function () { return commits }
exports.getCommitsArray = function () {
var commitArr = Object.keys(commits).map(function (key) {
return commits[key]
})
commitArr.forEach(function (o) { log.debug(o.id) })
return _.orderBy(commitArr, ['seq'], ['desc'])
}
exports.getCurrentBranch = function () { return curBranch }
exports.getDirection = function () { return direction }
exports.getHead = function () { return head }

View File

@@ -0,0 +1,243 @@
/* eslint-env jasmine */
var parser = require('./parser/gitGraph').parser
var ast = require('./gitGraphAst.js')
describe('when parsing a gitGraph', function () {
'use strict'
beforeEach(function () {
console.log('ast', ast)
console.log('parser', parser)
parser.yy = ast
parser.yy.clear()
})
it('should handle a gitGraph defintion', function () {
var str = 'gitGraph:\n' +
'commit\n'
parser.parse(str)
var 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 () {
var str = 'gitGraph:\n' +
'options\n' +
'end\n' +
'commit\n'
parser.parse(str)
var 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 () {
var str = 'gitGraph:\n' +
'options\n' +
'{"key": "value"}\n' +
'end\n' +
'commit\n'
parser.parse(str)
var 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 () {
var str = 'gitGraph:\n' +
'options\n' +
'{"key": "value"\n' +
'end\n' +
'commit\n'
parser.parse(str)
var 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 () {
var str = 'gitGraph BT:\n' +
'commit\n'
parser.parse(str)
var 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 () {
var str = 'gitGraph:\n' +
'branch new\n' +
'checkout new\n'
parser.parse(str)
var 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 () {
var str = 'gitGraph:\n' +
'branch new\n' +
'checkout new\n' +
'commit\n' +
'commit\n'
parser.parse(str)
var commits = parser.yy.getCommits()
expect(Object.keys(commits).length).toBe(2)
expect(parser.yy.getCurrentBranch()).toBe('new')
var branchCommit = parser.yy.getBranches()['new']
expect(branchCommit).not.toBeNull()
expect(commits[branchCommit].parent).not.toBeNull()
})
it('should handle commit with args', function () {
var str = 'gitGraph:\n' +
'commit "a commit"\n'
parser.parse(str)
var commits = parser.yy.getCommits()
expect(Object.keys(commits).length).toBe(1)
var 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 () {
var str = 'gitGraph:\n' +
'commit\n' +
'commit\n' +
'branch newbranch\n' +
'checkout newbranch\n' +
'commit\n' +
'reset master\n'
parser.parse(str)
var 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 () {
var str = 'gitGraph:\n' +
'commit\n' +
'commit\n' +
'branch newbranch\n' +
'checkout newbranch\n' +
'commit\n' +
'reset master^\n'
parser.parse(str)
var commits = parser.yy.getCommits()
expect(Object.keys(commits).length).toBe(3)
expect(parser.yy.getCurrentBranch()).toBe('newbranch')
var master = commits[parser.yy.getBranches()['master']]
expect(parser.yy.getHead().id).toEqual(master.parent)
})
it('it should handle fast forwardable merges', function () {
var str = 'gitGraph:\n' +
'commit\n' +
'branch newbranch\n' +
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
'checkout master\n' +
'merge newbranch\n'
parser.parse(str)
var 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 () {
var str = 'gitGraph:\n' +
'commit\n' +
'branch newbranch\n' +
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
'merge master\n'
parser.parse(str)
var 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 () {
var 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)
var 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 () {
var 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)
var 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()
})
})

View File

@@ -0,0 +1,274 @@
const db = require('./gitGraphAst')
const _ = require('lodash')
const gitGraphParser = require('./parser/gitGraph')
const d3 = require('../../d3')
const Logger = require('../../logger')
const log = Logger.Log
var allCommitsDict = {}
var branchNum
var 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
}
}
var apiConfig = {}
exports.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('xhtml:p')
.html('')
}
function svgDrawLine (svg, points, colorIdx, interpolate) {
interpolate = interpolate || 'basis'
var color = config.branchColors[colorIdx % config.branchColors.length]
var lineGen = d3.svg.line()
.x(function (d) {
return Math.round(d.x)
})
.y(function (d) {
return Math.round(d.y)
})
.interpolate(interpolate)
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()
var ctm = element.node().getCTM()
var xn = ctm.e + coords.x * ctm.a
var 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) {
log.debug('svgDrawLineForCommits: ', fromId, toId)
var fromBbox = getElementCoords(svg.select('#node-' + fromId + ' circle'))
var toBbox = getElementCoords(svg.select('#node-' + toId + ' circle'))
switch (direction) {
case 'LR':
// (toBbox)
// +--------
// + (fromBbox)
if (fromBbox.left - toBbox.left > config.nodeSpacing) {
var lineStart = { x: fromBbox.left - config.nodeSpacing, y: toBbox.top + toBbox.height / 2 }
var 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) {
lineStart = { x: toBbox.left + toBbox.width / 2, y: fromBbox.top + fromBbox.height + config.nodeSpacing }
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) {
var commit
var numCommits = Object.keys(allCommitsDict).length
if (_.isString(commitid)) {
do {
commit = allCommitsDict[commitid]
log.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)
var branch = _.find(branches, ['commit', commit])
if (branch) {
log.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)) {
log.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]]
}
}
}
exports.draw = function (txt, id, ver) {
try {
var parser
parser = gitGraphParser.parser
parser.yy = db
log.debug('in gitgraph renderer', txt, id, ver)
// Parse the graph definition
parser.parse(txt + '\n')
config = _.extend(config, apiConfig, db.getOptions())
log.debug('effective options', config)
var direction = db.getDirection()
allCommitsDict = db.getCommits()
var branches = db.getBranchesAsObjArray()
if (direction === 'BT') {
config.nodeLabel.x = branches.length * config.branchOffset
config.nodeLabel.width = '100%'
config.nodeLabel.y = -1 * 2 * config.nodeRadius
}
var svg = d3.select('#' + 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) {
log.error('Error while rendering gitgraph')
log.error(e.message)
}
}

View File

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

View File

@@ -0,0 +1,692 @@
/* parser generated by jison 0.4.17 */
/*
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 {
function _parseError (msg, hash) {
this.message = msg
this.hash = hash
}
_parseError.prototype = Error
throw new _parseError(str, hash)
}
},
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
}
var lex = function () {
var token
token = lexer.lex() || EOF
if (typeof token !== 'number') {
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))
}
}

View File

@@ -0,0 +1,191 @@
/** 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 document else restOfLine document end
{
// Alt start
$3.unshift({type: 'altStart', altText:$2, signalType: yy.LINETYPE.ALT_START});
// Content in alt is already in $3
// Else
$3.push({type: 'else', altText:$5, signalType: yy.LINETYPE.ALT_ELSE});
// Content in other alt
$3 = $3.concat($6);
// 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]); }
;
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");} ;
%%

View File

@@ -0,0 +1,798 @@
/* parser generated by jison 0.4.17 */
/*
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, 24, 25, 27, 28, 39], $Vh = [1, 32], $Vi = [4, 5, 10, 15, 16, 18, 20, 21, 22, 23, 25, 28, 39], $Vj = [4, 5, 10, 15, 16, 18, 20, 21, 22, 23, 25, 27, 28, 39], $Vk = [37, 38, 39]
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': 24, 'par': 25, 'par_sections': 26, 'and': 27, 'note': 28, 'placement': 29, 'over': 30, 'actor_pair': 31, 'spaceList': 32, ',': 33, 'left_of': 34, 'right_of': 35, 'signaltype': 36, '+': 37, '-': 38, 'ACTOR': 39, 'SOLID_OPEN_ARROW': 40, 'DOTTED_OPEN_ARROW': 41, 'SOLID_ARROW': 42, 'DOTTED_ARROW': 43, 'SOLID_CROSS': 44, 'DOTTED_CROSS': 45, 'TXT': 46, '$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', 24: 'else', 25: 'par', 27: 'and', 28: 'note', 30: 'over', 33: ',', 34: 'left_of', 35: 'right_of', 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'},
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, 7], [9, 4], [26, 1], [26, 4], [17, 4], [17, 4], [32, 2], [32, 1], [31, 3], [31, 1], [29, 1], [29, 1], [14, 5], [14, 5], [14, 4], [11, 1], [36, 1], [36, 1], [36, 1], [36, 1], [36, 1], [36, 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 - 4].unshift({type: 'altStart', altText: $$[$0 - 5], signalType: yy.LINETYPE.ALT_START})
// Content in alt is already in $$[$0-4]
// Else
$$[$0 - 4].push({type: 'else', altText: $$[$0 - 2], signalType: yy.LINETYPE.ALT_ELSE})
// Content in other alt
$$[$0 - 4] = $$[$0 - 4].concat($$[$0 - 1])
// End
$$[$0 - 4].push({type: 'altEnd', signalType: yy.LINETYPE.ALT_END})
this.$ = $$[$0 - 4]
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 22:
this.$ = [$$[$0 - 1], {type: 'addNote', placement: $$[$0 - 2], actor: $$[$0 - 1].actor, text: $$[$0]}]
break
case 23:
// 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 26:
this.$ = [$$[$0 - 2], $$[$0]]
break
case 27:
this.$ = $$[$0]
break
case 28:
this.$ = yy.PLACEMENT.LEFTOF
break
case 29:
this.$ = yy.PLACEMENT.RIGHTOF
break
case 30:
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 31:
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 32:
this.$ = [$$[$0 - 3], $$[$0 - 1], {type: 'addMessage', from: $$[$0 - 3].actor, to: $$[$0 - 1].actor, signalType: $$[$0 - 2], msg: $$[$0]}]
break
case 33:
this.$ = {type: 'addActor', actor: $$[$0]}
break
case 34:
this.$ = yy.LINETYPE.SOLID_OPEN
break
case 35:
this.$ = yy.LINETYPE.DOTTED_OPEN
break
case 36:
this.$ = yy.LINETYPE.SOLID
break
case 37:
this.$ = yy.LINETYPE.DOTTED
break
case 38:
this.$ = yy.LINETYPE.SOLID_CROSS
break
case 39:
this.$ = yy.LINETYPE.DOTTED_CROSS
break
case 40:
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, 28, 39], $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, 28: $Ve, 39: $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, 28: $Ve, 39: $Vf}, o($Vg, [2, 7]), o($Vg, [2, 8]), {11: 26, 39: $Vf}, {5: [1, 27]}, {11: 28, 39: $Vf}, {11: 29, 39: $Vf}, {5: [1, 30]}, {19: 31, 46: $Vh}, {13: [1, 33]}, {13: [1, 34]}, {13: [1, 35]}, {13: [1, 36]}, {36: 37, 40: [1, 38], 41: [1, 39], 42: [1, 40], 43: [1, 41], 44: [1, 42], 45: [1, 43]}, {29: 44, 30: [1, 45], 34: [1, 46], 35: [1, 47]}, o([5, 12, 33, 40, 41, 42, 43, 44, 45, 46], [2, 33]), 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, 40]}, o($Vi, $V3, {7: 53}), o($Vi, $V3, {7: 54}), o([4, 5, 10, 15, 16, 18, 20, 22, 23, 24, 25, 28, 39], $V3, {7: 55}), o($Vj, $V3, {26: 56, 7: 57}), {11: 60, 37: [1, 58], 38: [1, 59], 39: $Vf}, o($Vk, [2, 34]), o($Vk, [2, 35]), o($Vk, [2, 36]), o($Vk, [2, 37]), o($Vk, [2, 38]), o($Vk, [2, 39]), {11: 61, 39: $Vf}, {11: 63, 31: 62, 39: $Vf}, {39: [2, 28]}, {39: [2, 29]}, {13: [1, 64]}, 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, 65], 22: $Vb, 23: $Vc, 25: $Vd, 28: $Ve, 39: $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, 66], 22: $Vb, 23: $Vc, 25: $Vd, 28: $Ve, 39: $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, 22: $Vb, 23: $Vc, 24: [1, 67], 25: $Vd, 28: $Ve, 39: $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, 20], 22: $Vb, 23: $Vc, 25: $Vd, 27: [1, 69], 28: $Ve, 39: $Vf}, {11: 70, 39: $Vf}, {11: 71, 39: $Vf}, {19: 72, 46: $Vh}, {19: 73, 46: $Vh}, {19: 74, 46: $Vh}, {33: [1, 75], 46: [2, 27]}, {5: [1, 76]}, o($Vg, [2, 16]), o($Vg, [2, 17]), {13: [1, 77]}, o($Vg, [2, 19]), {13: [1, 78]}, {19: 79, 46: $Vh}, {19: 80, 46: $Vh}, {5: [2, 32]}, {5: [2, 22]}, {5: [2, 23]}, {11: 81, 39: $Vf}, o($Vg, [2, 9]), o($Vi, $V3, {7: 82}), o($Vj, $V3, {7: 57, 26: 83}), {5: [2, 30]}, {5: [2, 31]}, {46: [2, 26]}, {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, 84], 22: $Vb, 23: $Vc, 25: $Vd, 28: $Ve, 39: $Vf}, {21: [2, 21]}, o($Vg, [2, 18])],
defaultActions: {5: [2, 1], 6: [2, 2], 32: [2, 40], 46: [2, 28], 47: [2, 29], 72: [2, 32], 73: [2, 22], 74: [2, 23], 79: [2, 30], 80: [2, 31], 81: [2, 26], 83: [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
}
var lex = function () {
var token
token = lexer.lex() || EOF
if (typeof token !== 'number') {
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 39
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 24
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 34
break
case 18:return 35
break
case 19:return 30
break
case 20:return 28
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 33
break
case 26:return 5
break
case 27: yy_.yytext = yy_.yytext.trim(); return 39
break
case 28:return 42
break
case 29:return 43
break
case 30:return 40
break
case 31:return 41
break
case 32:return 44
break
case 33:return 45
break
case 34:return 46
break
case 35:return 37
break
case 36:return 38
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))
}
}

View File

@@ -0,0 +1,161 @@
/**
* Created by knut on 14-11-19.
*/
var actors = {}
var messages = []
var notes = []
var title = ''
var Logger = require('../../logger')
var log = Logger.Log
exports.addActor = function (id, name, description) {
// Don't allow description nulling
var 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 }
}
exports.addMessage = function (idFrom, idTo, message, answer) {
messages.push({ from: idFrom, to: idTo, message: message, answer: answer })
}
exports.addSignal = function (idFrom, idTo, message, messageType) {
log.debug('Adding message from=' + idFrom + ' to=' + idTo + ' message=' + message + ' type=' + messageType)
messages.push({ from: idFrom, to: idTo, message: message, type: messageType })
}
exports.getMessages = function () {
return messages
}
exports.getActors = function () {
return actors
}
exports.getActor = function (id) {
return actors[id]
}
exports.getActorKeys = function () {
return Object.keys(actors)
}
exports.getTitle = function () {
return title
}
exports.clear = function () {
actors = {}
messages = []
}
exports.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
}
exports.ARROWTYPE = {
FILLED: 0,
OPEN: 1
}
exports.PLACEMENT = {
LEFTOF: 0,
RIGHTOF: 1,
OVER: 2
}
exports.addNote = function (actor, placement, message) {
var note = { actor: actor, placement: placement, message: message }
// Coerce actor into a [to, from, ...] array
var actors = [].concat(actor, actor)
notes.push(note)
messages.push({ from: actors[0], to: actors[1], message: message, type: exports.LINETYPE.NOTE, placement: placement })
}
exports.setTitle = function (titleText) {
title = titleText
}
exports.parseError = function (err, hash) {
global.mermaidAPI.parseError(err, hash)
}
exports.apply = function (param) {
if (param instanceof Array) {
param.forEach(function (item) {
exports.apply(item)
})
} else {
switch (param.type) {
case 'addActor':
exports.addActor(param.actor, param.actor, param.description)
break
case 'activeStart':
exports.addSignal(param.actor, undefined, undefined, param.signalType)
break
case 'activeEnd':
exports.addSignal(param.actor, undefined, undefined, param.signalType)
break
case 'addNote':
exports.addNote(param.actor, param.placement, param.text)
break
case 'addMessage':
exports.addSignal(param.from, param.to, param.msg, param.signalType)
break
case 'loopStart':
exports.addSignal(undefined, undefined, param.loopText, param.signalType)
break
case 'loopEnd':
exports.addSignal(undefined, undefined, undefined, param.signalType)
break
case 'optStart':
exports.addSignal(undefined, undefined, param.optText, param.signalType)
break
case 'optEnd':
exports.addSignal(undefined, undefined, undefined, param.signalType)
break
case 'altStart':
exports.addSignal(undefined, undefined, param.altText, param.signalType)
break
case 'else':
exports.addSignal(undefined, undefined, param.altText, param.signalType)
break
case 'altEnd':
exports.addSignal(undefined, undefined, undefined, param.signalType)
break
case 'setTitle':
exports.setTitle(param.text)
break
case 'parStart':
exports.addSignal(undefined, undefined, param.parText, param.signalType)
break
case 'and':
exports.addSignal(undefined, undefined, param.parText, param.signalType)
break
case 'parEnd':
exports.addSignal(undefined, undefined, undefined, param.signalType)
break
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,506 @@
/**
* Created by knut on 14-11-23.
*/
var sq = require('./parser/sequenceDiagram').parser
sq.yy = require('./sequenceDb')
var svgDraw = require('./svgDraw')
var d3 = require('../../d3')
var Logger = require('../../logger')
var log = Logger.Log
var 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'
}
exports.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) {
var _self = this
var 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
var 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(exports.bounds.data, 'startx', startx - n * conf.boxMargin, Math.min)
_self.updateVal(exports.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(exports.bounds.data, 'starty', starty - n * conf.boxMargin, Math.min)
_self.updateVal(exports.bounds.data, 'stopy', stopy + n * conf.boxMargin, Math.max)
}
}
}
this.sequenceItems.forEach(updateFn())
this.activations.forEach(updateFn('activation'))
},
insert: function (startx, starty, stopx, stopy) {
var _startx, _starty, _stopx, _stopy
_startx = Math.min(startx, stopx)
_stopx = Math.max(startx, stopx)
_starty = Math.min(starty, stopy)
_stopy = Math.max(starty, stopy)
this.updateVal(exports.bounds.data, 'startx', _startx, Math.min)
this.updateVal(exports.bounds.data, 'starty', _starty, Math.min)
this.updateVal(exports.bounds.data, 'stopx', _stopx, Math.max)
this.updateVal(exports.bounds.data, 'stopy', _stopy, Math.max)
this.updateBounds(_startx, _starty, _stopx, _stopy)
},
newActivation: function (message, diagram) {
var actorRect = sq.yy.getActors()[message.from.actor]
var stackedSize = actorActivations(message.from.actor).length
var 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
var lastActorActivationIdx = this.activations
.map(function (activation) { return activation.actor })
.lastIndexOf(message.from.actor)
var 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 () {
var loop = this.sequenceItems.pop()
return loop
},
addSectionToLoop: function (message) {
var loop = this.sequenceItems.pop()
loop.sections = loop.sections || []
loop.sectionTitles = loop.sectionTitles || []
loop.sections.push(exports.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
}
}
/**
* 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
*/
var drawNote = function (elem, startx, verticalPos, msg, forceWidth) {
var rect = svgDraw.getNoteRect()
rect.x = startx
rect.y = verticalPos
rect.width = forceWidth || conf.width
rect.class = 'note'
var g = elem.append('g')
var rectElem = svgDraw.drawRect(g, rect)
var textObj = svgDraw.getTextObj()
textObj.x = startx - 4
textObj.y = verticalPos - 13
textObj.textMargin = conf.noteMargin
textObj.dy = '1em'
textObj.text = msg.message
textObj.class = 'noteText'
var textElem = svgDraw.drawText(g, textObj, rect.width - conf.noteMargin)
var textHeight = textElem[0][0].getBBox().height
if (!forceWidth && textHeight > conf.width) {
textElem.remove()
g = elem.append('g')
textElem = svgDraw.drawText(g, textObj, 2 * rect.width - conf.noteMargin)
textHeight = textElem[0][0].getBBox().height
rectElem.attr('width', 2 * rect.width)
exports.bounds.insert(startx, verticalPos, startx + 2 * rect.width, verticalPos + 2 * conf.noteMargin + textHeight)
} else {
exports.bounds.insert(startx, verticalPos, startx + rect.width, verticalPos + 2 * conf.noteMargin + textHeight)
}
rectElem.attr('height', textHeight + 2 * conf.noteMargin)
exports.bounds.bumpVerticalPos(textHeight + 2 * conf.noteMargin)
}
/**
* Draws a message
* @param elem
* @param startx
* @param stopx
* @param verticalPos
* @param txtCenter
* @param msg
*/
var drawMessage = function (elem, startx, stopx, verticalPos, msg) {
var g = elem.append('g')
var txtCenter = startx + (stopx - startx) / 2
var 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)
var textWidth
if (typeof textElem[0][0].getBBox !== 'undefined') {
textWidth = textElem[0][0].getBBox().width
} else {
textWidth = textElem[0][0].getBoundingClientRect()
}
var 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))
exports.bounds.bumpVerticalPos(30)
var dx = Math.max(textWidth / 2, 100)
exports.bounds.insert(startx - dx, exports.bounds.getVerticalPos() - 10, stopx + dx, exports.bounds.getVerticalPos())
} else {
line = g.append('line')
line.attr('x1', startx)
line.attr('y1', verticalPos)
line.attr('x2', stopx)
line.attr('y2', verticalPos)
exports.bounds.insert(startx, exports.bounds.getVerticalPos() - 10, stopx, exports.bounds.getVerticalPos())
}
// Make an SVG Container
// Draw the line
if (msg.type === sq.yy.LINETYPE.DOTTED || msg.type === sq.yy.LINETYPE.DOTTED_CROSS || msg.type === sq.yy.LINETYPE.DOTTED_OPEN) {
line.style('stroke-dasharray', ('3, 3'))
line.attr('class', 'messageLine1')
} else {
line.attr('class', 'messageLine0')
}
var 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 === sq.yy.LINETYPE.SOLID || msg.type === sq.yy.LINETYPE.DOTTED) {
line.attr('marker-end', 'url(' + url + '#arrowhead)')
}
if (msg.type === sq.yy.LINETYPE.SOLID_CROSS || msg.type === sq.yy.LINETYPE.DOTTED_CROSS) {
line.attr('marker-end', 'url(' + url + '#crosshead)')
}
}
module.exports.drawActors = function (diagram, actors, actorKeys, verticalPos) {
var i
// Draw the actors
for (i = 0; i < actorKeys.length; i++) {
var 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)
exports.bounds.insert(actors[key].x, verticalPos, actors[key].x + conf.width, conf.height)
}
// Add a margin between the actor boxes and the first arrow
exports.bounds.bumpVerticalPos(conf.height)
}
module.exports.setConf = function (cnf) {
var keys = Object.keys(cnf)
keys.forEach(function (key) {
conf[key] = cnf[key]
})
}
var actorActivations = function (actor) {
return module.exports.bounds.activations.filter(function (activation) {
return activation.actor === actor
})
}
var actorFlowVerticaBounds = function (actor) {
// handle multiple stacked activations for same actor
var actors = sq.yy.getActors()
var activations = actorActivations(actor)
var left = activations.reduce(function (acc, activation) { return Math.min(acc, activation.startx) }, actors[actor].x + conf.width / 2)
var 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
*/
module.exports.draw = function (text, id) {
sq.yy.clear()
sq.parse(text + '\n')
exports.bounds.init()
var diagram = d3.select('#' + id)
var startx
var stopx
var forceWidth
// Fetch data from the parsing
var actors = sq.yy.getActors()
var actorKeys = sq.yy.getActorKeys()
var messages = sq.yy.getMessages()
var title = sq.yy.getTitle()
module.exports.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) {
var activationData = exports.bounds.endActivation(msg)
if (activationData.starty + 18 > verticalPos) {
activationData.starty = verticalPos - 6
verticalPos += 12
}
svgDraw.drawActivation(diagram, activationData, verticalPos, conf)
exports.bounds.insert(activationData.startx, verticalPos - 10, activationData.stopx, verticalPos)
}
// var lastMsg
// Draw the messages/signals
messages.forEach(function (msg) {
var loopData
switch (msg.type) {
case sq.yy.LINETYPE.NOTE:
exports.bounds.bumpVerticalPos(conf.boxMargin)
startx = actors[msg.from].x
stopx = actors[msg.to].x
if (msg.placement === sq.yy.PLACEMENT.RIGHTOF) {
drawNote(diagram, startx + (conf.width + conf.actorMargin) / 2, exports.bounds.getVerticalPos(), msg)
} else if (msg.placement === sq.yy.PLACEMENT.LEFTOF) {
drawNote(diagram, startx - (conf.width + conf.actorMargin) / 2, exports.bounds.getVerticalPos(), msg)
} else if (msg.to === msg.from) {
// Single-actor over
drawNote(diagram, startx, exports.bounds.getVerticalPos(), msg)
} else {
// Multi-actor over
forceWidth = Math.abs(startx - stopx) + conf.actorMargin
drawNote(diagram, (startx + stopx + conf.width - forceWidth) / 2, exports.bounds.getVerticalPos(), msg,
forceWidth)
}
break
case sq.yy.LINETYPE.ACTIVE_START:
exports.bounds.newActivation(msg, diagram)
break
case sq.yy.LINETYPE.ACTIVE_END:
activeEnd(msg, exports.bounds.getVerticalPos())
break
case sq.yy.LINETYPE.LOOP_START:
exports.bounds.bumpVerticalPos(conf.boxMargin)
exports.bounds.newLoop(msg.message)
exports.bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
break
case sq.yy.LINETYPE.LOOP_END:
loopData = exports.bounds.endLoop()
svgDraw.drawLoop(diagram, loopData, 'loop', conf)
exports.bounds.bumpVerticalPos(conf.boxMargin)
break
case sq.yy.LINETYPE.OPT_START:
exports.bounds.bumpVerticalPos(conf.boxMargin)
exports.bounds.newLoop(msg.message)
exports.bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
break
case sq.yy.LINETYPE.OPT_END:
loopData = exports.bounds.endLoop()
svgDraw.drawLoop(diagram, loopData, 'opt', conf)
exports.bounds.bumpVerticalPos(conf.boxMargin)
break
case sq.yy.LINETYPE.ALT_START:
exports.bounds.bumpVerticalPos(conf.boxMargin)
exports.bounds.newLoop(msg.message)
exports.bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
break
case sq.yy.LINETYPE.ALT_ELSE:
exports.bounds.bumpVerticalPos(conf.boxMargin)
loopData = exports.bounds.addSectionToLoop(msg.message)
exports.bounds.bumpVerticalPos(conf.boxMargin)
break
case sq.yy.LINETYPE.ALT_END:
loopData = exports.bounds.endLoop()
svgDraw.drawLoop(diagram, loopData, 'alt', conf)
exports.bounds.bumpVerticalPos(conf.boxMargin)
break
case sq.yy.LINETYPE.PAR_START:
exports.bounds.bumpVerticalPos(conf.boxMargin)
exports.bounds.newLoop(msg.message)
exports.bounds.bumpVerticalPos(conf.boxMargin + conf.boxTextMargin)
break
case sq.yy.LINETYPE.PAR_AND:
exports.bounds.bumpVerticalPos(conf.boxMargin)
loopData = exports.bounds.addSectionToLoop(msg.message)
exports.bounds.bumpVerticalPos(conf.boxMargin)
break
case sq.yy.LINETYPE.PAR_END:
loopData = exports.bounds.endLoop()
svgDraw.drawLoop(diagram, loopData, 'par', conf)
exports.bounds.bumpVerticalPos(conf.boxMargin)
break
default:
try {
// lastMsg = msg
exports.bounds.bumpVerticalPos(conf.messageMargin)
var fromBounds = actorFlowVerticaBounds(msg.from)
var toBounds = actorFlowVerticaBounds(msg.to)
var fromIdx = fromBounds[0] <= toBounds[0] ? 1 : 0
var toIdx = fromBounds[0] < toBounds[0] ? 0 : 1
startx = fromBounds[fromIdx]
stopx = toBounds[toIdx]
var verticalPos = exports.bounds.getVerticalPos()
drawMessage(diagram, startx, stopx, verticalPos, msg)
var allBounds = fromBounds.concat(toBounds)
exports.bounds.insert(Math.min.apply(null, allBounds), verticalPos, Math.max.apply(null, allBounds), verticalPos)
} catch (e) {
console.error('error while drawing message', e)
}
}
})
if (conf.mirrorActors) {
// Draw actors below diagram
exports.bounds.bumpVerticalPos(conf.boxMargin * 2)
module.exports.drawActors(diagram, actors, actorKeys, exports.bounds.getVerticalPos())
}
var box = exports.bounds.getBounds()
// Adjust line height of actor lines now that the height of the diagram is known
log.debug('For line height fix Querying: #' + id + ' .actor-line')
var actorLines = d3.selectAll('#' + id + ' .actor-line')
actorLines.attr('y2', box.stopy)
var height = box.stopy - box.starty + 2 * conf.diagramMarginY
if (conf.mirrorActors) {
height = height - conf.boxMargin + conf.bottomMarginAdj
}
var 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)
}
var extraVertForTitle = title ? 40 : 0
diagram.attr('viewBox', (box.startx - conf.diagramMarginX) + ' -' + (conf.diagramMarginY + extraVertForTitle) + ' ' + width + ' ' + (height + extraVertForTitle))
}

View File

@@ -0,0 +1,319 @@
/**
* Created by knut on 14-12-20.
*/
exports.drawRect = function (elem, rectData) {
var 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
}
exports.drawText = function (elem, textData, width) {
// Remove and ignore br:s
var nText = textData.text.replace(/<br\/?>/ig, ' ')
var 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)
}
var span = textElem.append('tspan')
span.attr('x', textData.x + textData.textMargin * 2)
span.attr('fill', textData.fill)
span.text(nText)
if (typeof textElem.textwrap !== 'undefined') {
textElem.textwrap({
x: textData.x, // bounding box is 300 pixels from the left
y: textData.y, // bounding box is 400 pixels from the top
width: width, // bounding box is 500 pixels across
height: 1800 // bounding box is 600 pixels tall
}, textData.textMargin)
}
return textElem
}
exports.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)
}
var 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
exports.drawText(elem, txtObject)
}
var 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
*/
exports.drawActor = function (elem, left, verticalPos, description, conf) {
var center = left + (conf.width / 2)
var 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')
}
var rect = exports.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
exports.drawRect(g, rect)
_drawTextCandidateFunc(conf)(description, g,
rect.x, rect.y, rect.width, rect.height, { 'class': 'actor' })
}
exports.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
*/
exports.drawActivation = function (elem, bounds, verticalPos) {
var rect = exports.getNoteRect()
var 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
exports.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
*/
exports.drawLoop = function (elem, bounds, labelText, conf) {
var g = elem.append('g')
var 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')
})
}
var txt = exports.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.
exports.drawLabel(g, txt)
txt = exports.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'
exports.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
exports.drawText(g, txt)
}
})
}
}
/**
* Setup arrow head and define the marker. The result is appended to the svg.
*/
exports.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.
*/
exports.insertArrowCrossHead = function (elem) {
var defs = elem.append('defs')
var 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
}
exports.getTextObj = function () {
var txt = {
x: 0,
y: 0,
'fill': 'black',
'text-anchor': 'start',
style: '#666',
width: 100,
height: 100,
textMargin: 0,
rx: 0,
ry: 0
}
return txt
}
exports.getNoteRect = function () {
var rect = {
x: 0,
y: 0,
fill: '#EDF2AE',
stroke: '#666',
width: 100,
anchor: 'start',
height: 100,
rx: 0,
ry: 0
}
return rect
}
var _drawTextCandidateFunc = (function () {
function byText (content, g, x, y, width, height, textAttrs) {
var 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) {
var 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)
if (typeof (text.textwrap) !== 'undefined') {
text.textwrap({ // d3textwrap
x: x + width / 2, y: y, width: width, height: height
}, 0)
// vertical aligment after d3textwrap expans tspan to multiple tspans
var tspans = text.selectAll('tspan')
if (tspans.length > 0 && tspans[0].length > 0) {
tspans = tspans[0]
// set y of <text> to the mid y of the first line
text.attr('y', y + (height / 2.0 - text[0][0].getBBox().height * (1 - 1.0 / tspans.length) / 2.0))
.attr('dominant-baseline', 'central')
.attr('alignment-baseline', 'central')
}
}
_setTextAttrs(text, textAttrs)
}
function byFo (content, g, x, y, width, height, textAttrs) {
var s = g.append('switch')
var f = s.append('foreignObject')
.attr('x', x).attr('y', y)
.attr('width', width).attr('height', height)
var 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 (var 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)
}
})()

View File

@@ -1,198 +0,0 @@
/**
* Created by knut on 14-11-03.
*/
var vertices = {};
var edges = [];
var classes = [];
var direction;
// Functions to be run after graph rendering
var funs = [];
/**
* Function called by parser when a node definition has been found
* @param id
* @param text
* @param type
* @param style
*/
exports.addVertex = function (id, text, type, style) {
//console.log('Got node ' + id + ' ' + type + ' ' + text + ' styles: ' + JSON.stringify(style));
if (typeof vertices[id] === 'undefined') {
vertices[id] = {id: id, styles: [], classes:[]};
}
if (typeof text !== 'undefined') {
vertices[id].text = text;
}
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
*/
exports.addLink = function (start, end, type, linktext) {
var edge = {start: start, end: end, type: undefined, text: ''};
var linktext = type.text;
if (typeof linktext !== 'undefined') {
edge.text = linktext;
}
if (typeof type !== 'undefined') {
edge.type = type.type;
}
edges.push(edge);
};
/**
* Updates a link with a style
* @param pos
* @param style
*/
exports.updateLink = function (pos, style) {
var position = pos.substr(1);
edges[position].style = style;
};
exports.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
*/
exports.setDirection = function (dir) {
direction = dir;
};
/**
* Called by parser when a graph definition is found, stores the direction of the chart.
* @param dir
*/
exports.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);
}
}
};
/**
* Called by parser when a graph definition is found, stores the direction of the chart.
* @param dir
*/
exports.setClickEvent = function (id,functionName) {
if(id.indexOf(',')>0){
id.split(',').forEach(function(id2) {
if (typeof vertices[id2] !== 'undefined') {
funs.push(function () {
var elem = document.getElementById(id2);
if (elem !== null) {
elem.onclick = function () {
eval(functionName + '(\'' + id2 + '\')');
};
}
});
}
});
}else{
//console.log('Checking now for ::'+id);
if(typeof vertices[id] !== 'undefined'){
funs.push(function(){
var elem = document.getElementById(id);
if(elem !== null){
//console.log('id was NOT null: '+id);
elem.onclick = function(){eval(functionName+'(\'' + id + '\')');};
}
else{
//console.log('id was null: '+id);
}
});
}
}
};
exports.bindFunctions = function(){
//setTimeout(function(){
funs.forEach(function(fun){
fun();
});
//},1000);
};
exports.getDirection = function () {
return direction;
};
/**
* Retrieval function for fetching the found nodes after parsing has completed.
* @returns {{}|*|vertices}
*/
exports.getVertices = function () {
return vertices;
};
/**
* Retrieval function for fetching the found links after parsing has completed.
* @returns {{}|*|edges}
*/
exports.getEdges = function () {
return edges;
};
/**
* Retrieval function for fetching the found class definitions after parsing has completed.
* @returns {{}|*|classes}
*/
exports.getClasses = function () {
return classes;
};
/**
* Clears the internal graph db so that a new graph can be parsed.
*/
exports.clear = function () {
vertices = {};
classes = {};
edges = [];
funs = [];
};
/**
*
* @returns {string}
*/
exports.defaultStyle = function () {
return "fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;";
};

View File

@@ -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;
}
svg .classLabel .box {
stroke: none;
stroke-width: 0;
fill: @nodeBkg;
opacity: 0.5;
}
svg .classLabel .label {
fill: @nodeBorder;
font-size: 10px;
}
.relation {
stroke: @nodeBorder;
stroke-width: 1;
fill: none;
}
.composition {
fill: @nodeBorder;
stroke: @nodeBorder;
stroke-width: 1;
}
#compositionStart {
.composition;
}
#compositionEnd {
.composition;
}
.aggregation {
fill: @nodeBkg;
stroke: @nodeBorder;
stroke-width: 1;
}
#aggregationStart {
.aggregation;
}
#aggregationEnd {
.aggregation;
}
#dependencyStart {
.composition;
}
#dependencyEnd {
.composition;
}
#extensionStart {
.composition;
}
#extensionEnd {
.composition;
}

35
src/less/dark/flow.less Normal file
View File

@@ -0,0 +1,35 @@
.mermaid .label {
color: @darkTextColor
}
.node rect,
.node circle,
.node ellipse,
.node polygon {
fill: @mainBkg;
stroke: @border1;
stroke-width: 1px;
}
.arrowheadPath {
fill: @arrowheadColor;
}
.edgePath .path {
stroke: @lineColor;
}
.edgeLabel {
background-color: @edgeLabelBackground;
}
.cluster rect {
fill: @secondBkg !important;
rx: 4 !important;
stroke: @clusterBorder !important;
stroke-width: 1px !important;
}
.cluster text {
fill: @titleColor;
}

212
src/less/dark/gantt.less Normal file
View File

@@ -0,0 +1,212 @@
/** 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: @sectionBkgColor;
opacity: 0.3;
shape-rendering: crispEdges;
}
.grid .tick text {
fill: @taskTextLightColor;
opacity: 0.5;
}
.grid path {
stroke-width: 0;
}
/* Today line */
.today {
fill: none;
stroke: @todayLineColor;
stroke-width: 2px;
}
/* Task styling */
/* Default task */
.task {
stroke-width: 1;
}
.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 {
fill: @doneTaskBkgColor;
}
.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: 1;
cursor: pointer;
shape-rendering: crispEdges;
}
.doneCritText0,
.doneCritText1,
.doneCritText2,
.doneCritText3 {
fill: @taskTextLightColor !important;
}
.activeCritText0,
.activeCritText1,
.activeCritText2,
.activeCritText3 {
fill: @taskTextDarkColor !important;
}
.titleText {
text-anchor: middle;
font-size: 18px;
fill: @taskTextLightColor;
}

View File

@@ -0,0 +1,6 @@
.commit-id,
.commit-msg,
.branch-label {
fill: lightgrey;
color: lightgrey;
}

View File

@@ -0,0 +1,24 @@
@import "variables";
@import "flow";
@import "sequenceDiagram";
@import "gantt";
@import "classDiagram";
@import "gitGraph";
.node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
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;
}

View File

@@ -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 !important;
}
#crosshead path {
fill: @signalColor !important;
stroke: @signalColor !important;
}
.messageText {
fill: @signalTextColor;
stroke: none;
}
.labelBox {
stroke: @labelBoxBorderColor;
fill: @labelBoxBkgColor;
}
.labelText {
fill: @darkTextColor;
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;
}

View File

@@ -0,0 +1,52 @@
@mainBkg: #BDD5EA;
@secondBkg: #6D6D65;
@mainContrastColor: lightgrey;
@darkTextColor: #323D47;
@lineColor: @mainContrastColor;
@border1: #81B1DB;
@border2: rgba(255, 255, 255, 0.25);
@arrowheadColor: @mainContrastColor;
/* Flowchart variables */
@nodeBkg: @mainBkg;
@nodeBorder: purple;
@clusterBkg: @secondBkg;
@clusterBorder: @border2;
@defaultLinkColor: @lineColor;
@titleColor: #F9FFFE;
@edgeLabelBackground: #e8e8e8;
/* Sequence Diagram variables */
@actorBorder: @border1;
@actorBkg: @mainBkg;
@actorTextColor: black;
@actorLineColor: @mainContrastColor;
@signalColor: @mainContrastColor;
@signalTextColor: @mainContrastColor;
@labelBoxBkgColor: @actorBkg;
@labelBoxBorderColor: @actorBorder;
@labelTextColor: @mainContrastColor;
@noteBorderColor: @border2;
@noteBkgColor: #fff5ad;
/* Gantt chart variables */
@sectionBkgColor: rgba(255, 255, 255, 0.3);
@altSectionBkgColor: white;
@sectionBkgColor2: #EAE8B9;
@taskBorderColor: rgba(255, 255, 255, 0.5);
@taskBkgColor: @mainBkg;
@taskTextColor: @darkTextColor;
@taskTextOutsideColor: @taskTextLightColor;
@activeTaskBorderColor: rgba(255, 255, 255, 0.5);
@activeTaskBkgColor: #81B1DB;
@gridColor: @mainContrastColor;
@doneTaskBkgColor: @mainContrastColor;
@doneTaskBorderColor: grey;
@critBorderColor: #E83737;
@critBkgColor: #E83737;
@taskTextLightColor: @mainContrastColor;
@taskTextDarkColor: @darkTextColor;
@todayLineColor: #DB5757;

View File

@@ -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;
}
svg .classLabel .box {
stroke: none;
stroke-width: 0;
fill: @nodeBkg;
opacity: 0.5;
}
svg .classLabel .label {
fill: @nodeBorder;
font-size: 10px;
}
.relation {
stroke: @nodeBorder;
stroke-width: 1;
fill: none;
}
.composition {
fill: @nodeBorder;
stroke: @nodeBorder;
stroke-width: 1;
}
#compositionStart {
.composition;
}
#compositionEnd {
.composition;
}
.aggregation {
fill: @nodeBkg;
stroke: @nodeBorder;
stroke-width: 1;
}
#aggregationStart {
.aggregation;
}
#aggregationEnd {
.aggregation;
}
#dependencyStart {
.composition;
}
#dependencyEnd {
.composition;
}
#extensionStart {
.composition;
}
#extensionEnd {
.composition;
}

View File

@@ -0,0 +1,35 @@
.mermaid .label {
color: #333
}
.node rect,
.node circle,
.node ellipse,
.node polygon {
fill: @mainBkg;
stroke: @border1;
stroke-width: 1px;
}
.arrowheadPath {
fill: @arrowheadColor;
}
.edgePath .path {
stroke: @lineColor;
}
.edgeLabel {
background-color: @edgeLabelBackground;
}
.cluster rect {
fill: @secondBkg !important;
rx: 4 !important;
stroke: @clusterBorder !important;
stroke-width: 1px !important;
}
.cluster text {
fill: @titleColor;
}

209
src/less/default/gantt.less Normal file
View File

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

View File

@@ -0,0 +1,23 @@
@import "variables";
@import "flow";
@import "sequenceDiagram";
@import "gantt";
@import "classDiagram";
.node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
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;
}

View File

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

View File

@@ -0,0 +1,50 @@
@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;
@taskTextColor: @taskTextLightColor;
@taskTextOutsideColor: @taskTextDarkColor;
@activeTaskBorderColor: #534fbc;
@activeTaskBkgColor: #bfc7ff;
@gridColor: lightgrey;
@doneTaskBkgColor: lightgrey;
@doneTaskBorderColor: grey;
@critBorderColor: #ff8888;
@critBkgColor: red;
@taskTextLightColor: white;
@taskTextDarkColor: black;
@todayLineColor: red;

View File

@@ -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;
}
svg .classLabel .box {
stroke: none;
stroke-width: 0;
fill: @nodeBkg;
opacity: 0.5;
}
svg .classLabel .label {
fill: @nodeBorder;
font-size: 10px;
}
.relation {
stroke: @nodeBorder;
stroke-width: 1;
fill: none;
}
.composition {
fill: @nodeBorder;
stroke: @nodeBorder;
stroke-width: 1;
}
#compositionStart {
.composition;
}
#compositionEnd {
.composition;
}
.aggregation {
fill: @nodeBkg;
stroke: @nodeBorder;
stroke-width: 1;
}
#aggregationStart {
.aggregation;
}
#aggregationEnd {
.aggregation;
}
#dependencyStart {
.composition;
}
#dependencyEnd {
.composition;
}
#extensionStart {
.composition;
}
#extensionEnd {
.composition;
}

37
src/less/forest/flow.less Normal file
View File

@@ -0,0 +1,37 @@
.mermaid .label {
font-family: 'trebuchet ms', verdana, arial;
color: #333
}
.node rect,
.node circle,
.node ellipse,
.node polygon {
fill: @mainBkg;
stroke: @nodeBorder;
stroke-width: 1px;
}
.arrowheadPath {
fill: @arrowheadColor;
}
.edgePath .path {
stroke: @lineColor;
stroke-width: 1.5px;
}
.edgeLabel {
background-color: @edgeLabelBackground;
}
.cluster rect {
fill: @secondBkg !important;
rx: 4 !important;
stroke: @clusterBorder !important;
stroke-width: 1px !important;
}
.cluster text {
fill: @titleColor;
}

209
src/less/forest/gantt.less Normal file
View File

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

View File

@@ -0,0 +1,23 @@
@import "variables";
@import "flow";
@import "sequenceDiagram";
@import "gantt";
@import "classDiagram";
.node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
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;
}

View File

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

View File

@@ -0,0 +1,52 @@
@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;
@taskTextColor: @taskTextLightColor;
@taskTextOutsideColor: @taskTextDarkColor;
@activeTaskBorderColor: @taskBorderColor;
@activeTaskBkgColor: @mainBkg;
@gridColor: lightgrey;
@doneTaskBkgColor: lightgrey;
@doneTaskBorderColor: grey;
@critBorderColor: #ff8888;
@critBkgColor: red;
@taskTextLightColor: white;
@taskTextDarkColor: black;
@todayLineColor: red;

View File

@@ -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;
}
svg .classLabel .box {
stroke: none;
stroke-width: 0;
fill: @nodeBkg;
opacity: 0.5;
}
svg .classLabel .label {
fill: @nodeBorder;
font-size: 10px;
}
.relation {
stroke: @nodeBorder;
stroke-width: 1;
fill: none;
}
.composition {
fill: @nodeBorder;
stroke: @nodeBorder;
stroke-width: 1;
}
#compositionStart {
.composition;
}
#compositionEnd {
.composition;
}
.aggregation {
fill: @nodeBkg;
stroke: @nodeBorder;
stroke-width: 1;
}
#aggregationStart {
.aggregation;
}
#aggregationEnd {
.aggregation;
}
#dependencyStart {
.composition;
}
#dependencyEnd {
.composition;
}
#extensionStart {
.composition;
}
#extensionEnd {
.composition;
}

View File

@@ -0,0 +1,32 @@
.mermaid .label {
color: @text
}
.node rect,
.node circle,
.node ellipse,
.node polygon {
fill: @mainBkg;
stroke: @nodeBorder;
stroke-width: 1px;
}
.edgePath .path {
stroke: @lineColor;
stroke-width: 1.5px;
}
.edgeLabel {
background-color: @edgeLabelBackground;
}
.cluster rect {
fill: @secondBkg !important;
rx: 4 !important;
stroke: @clusterBorder !important;
stroke-width: 1px !important;
}
.cluster text {
fill: @titleColor;
}

208
src/less/neutral/gantt.less Normal file
View File

@@ -0,0 +1,208 @@
/** 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;
}

View File

@@ -0,0 +1,23 @@
@import "variables";
@import "flow";
@import "sequenceDiagram";
@import "gantt";
@import "classDiagram";
.node text {
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
}
div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
background: @secondBkg;
border: 1px solid @border2;
border-radius: 2px;
pointer-events: none;
z-index: 100;
}

Some files were not shown because too many files have changed in this diff Show More