mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-10-31 02:44:17 +01:00 
			
		
		
		
	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
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,3 +3,5 @@ bower_components/ | |||||||
| *.sublime-project | *.sublime-project | ||||||
| *.sublime-workspace | *.sublime-workspace | ||||||
| .DS_Store | .DS_Store | ||||||
|  | .idea | ||||||
|  | coverage | ||||||
| @@ -2,7 +2,7 @@ | |||||||
|  |  | ||||||
| Great that you want to be involved in this project! Contributing is fun and contributions are GREAT! :) | Great that you want to be involved in this project! Contributing is fun and contributions are GREAT! :) | ||||||
|  |  | ||||||
| This page is currently a starting point is not so rigorous to start with. | This page is currently a starting point and is not so rigorous to start with. | ||||||
|  |  | ||||||
| Some important guidlines: | Some important guidlines: | ||||||
|  |  | ||||||
| @@ -16,7 +16,7 @@ The issue list and the items marked with **help wanted** is a good starting poin | |||||||
| ## Guidelines for avoiding duplicate 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 | 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 cant because someone else just pushed the same fix so you have wasted your time. The guidelines below are in | 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: | place to prevent this: | ||||||
|  |  | ||||||
| * Comment in the issue that you are working on it. You will then be added as an assignee (eventually). | * Comment in the issue that you are working on it. You will then be added as an assignee (eventually). | ||||||
|   | |||||||
							
								
								
									
										75
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,3 +1,4 @@ | |||||||
|  |  | ||||||
| mermaid [](https://travis-ci.org/knsv/mermaid) [](https://codeclimate.com/github/knsv/mermaid) | mermaid [](https://travis-ci.org/knsv/mermaid) [](https://codeclimate.com/github/knsv/mermaid) | ||||||
| ======= | ======= | ||||||
|  |  | ||||||
| @@ -8,41 +9,61 @@ 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. | 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 | The code below would render the following image | ||||||
|  | <table> | ||||||
| ``` | <tr><th>Code</th><th>Rendered diagram</th></tr> | ||||||
|  | <tr><td> | ||||||
|  | <pre> | ||||||
|  | <code> | ||||||
| graph TD; | graph TD; | ||||||
|     A-->B; |     A-->B; | ||||||
|     A-->C; |     A-->C; | ||||||
|     B-->D; |     B-->D; | ||||||
|     C-->D; |     C-->D; | ||||||
| ``` | <code> | ||||||
|  | </pre> | ||||||
| would render this lovely chart: | </td> | ||||||
|  | <td> | ||||||
|  | <img src='http://www.sveido.com/mermaid/img/ex1.png' alt='Example 1'> | ||||||
|  | </td> | ||||||
| A page with a live example can be seen [here](http://www.sveido.com/mermaid/demo/html/web.html). You can also look at mermaid in action using [jsbin](http://jsbin.com/faxunexeku/1/edit?html,output). If you want a live demo, there is an editor provided in the mermaid project or you can simply look at this [great editor](http://danielmschmidt.github.io/mermaid-demo/) | </tr> | ||||||
|  | <tr> | ||||||
|  | <td> | ||||||
| # [The main documentation is located in the wiki](https://github.com/knsv/mermaid/wiki) | <pre> | ||||||
|  | <code> | ||||||
|  | sequenceDiagram | ||||||
|  |     participant Alice | ||||||
| # Another graph example |     participant Bob | ||||||
|  |     Alice->John: Hello John, how are you? | ||||||
| ``` |     loop Healthcheck | ||||||
| graph LR; |         John->John: Fight against hypochondria | ||||||
|     A[Hard edge]-->|Link text|B(Round edge); |     end | ||||||
|     B-->C{Decision}; |     Note right of John: Rational thoughts <br/>prevail... | ||||||
|     C-->|One|D[Result one]; |     John-->Alice: Great! | ||||||
|     C-->|Two|E[Result two]; |     John->Bob: How about you? | ||||||
| ``` |     Bob-->John: Jolly good! | ||||||
|  | </code> | ||||||
|  | </pre> | ||||||
|  | </td> | ||||||
|  | <td> | ||||||
|  | <img src='http://www.sveido.com/mermaid/img/seq1.png' alt='Example 2'> | ||||||
|  | </td> | ||||||
|  | </tr> | ||||||
|  | </table> | ||||||
|  |  | ||||||
|  | ## Further reading | ||||||
|  |  | ||||||
|  | * [Usage](http://knsv.github.io/mermaid/usage.html) | ||||||
|  | * [Flowchart syntax](http://knsv.github.io/mermaid/flowchart.html) | ||||||
|  | * [Sequence diagram syntax](http://knsv.github.io/mermaid/sequenceDiagram.html) | ||||||
|  | * [Mermaid client](http://knsv.github.io/mermaid/mermaidCLI.html) | ||||||
|  | * [Demos](http://knsv.github.io/mermaid/demos.html) | ||||||
|  |  | ||||||
| # Credits | # Credits | ||||||
| Many thanks to the [d3](http://d3js.org/) and [dagre-d3](https://github.com/cpettitt/dagre-d3) projects for providing the graphical layout and drawing libraries! Thanks also to the [js-sequence-diagram](http://bramp.github.io/js-sequence-diagrams) project for usage of the grammar for the sequence diagrams. | 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. | ||||||
|  |  | ||||||
| *Mermaid was created by Knut Sveidqvist for easier documentation.* | *Mermaid was created by Knut Sveidqvist for easier documentation.* | ||||||
|  |  | ||||||
|  | Knut has not done all work by him self, here is the full list of the projects [contributors](https://github.com/knsv/mermaid/graphs/contributors). | ||||||
|   | |||||||
							
								
								
									
										27
									
								
								bin/mermaid.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										27
									
								
								bin/mermaid.js
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | #!/usr/bin/env node | ||||||
|  |  | ||||||
|  | var fs = require('fs') | ||||||
|  |   , chalk = require('chalk') | ||||||
|  |   , error = chalk.bold.red | ||||||
|  |   , cli = require('../lib/cli.js') | ||||||
|  |   , lib = require('../lib') | ||||||
|  |  | ||||||
|  | cli.parse(process.argv.slice(2), function(err, message, options) { | ||||||
|  |   if (err) { | ||||||
|  |     console.error( | ||||||
|  |       error('\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) | ||||||
|  | }) | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "mermaid", |   "name": "mermaid", | ||||||
|   "version": "0.2.15", |   "version": "0.3.2", | ||||||
|   "authors": [ |   "authors": [ | ||||||
|     "knsv <knut@sveido.com>" |     "knsv <knut@sveido.com>" | ||||||
|   ], |   ], | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ gulp.task('jison', shell.task([ | |||||||
|   'jison src/diagrams/flowchart/parser/flow.jison -o src/diagrams/flowchart/parser/flow.js', |   'jison src/diagrams/flowchart/parser/flow.jison -o src/diagrams/flowchart/parser/flow.js', | ||||||
|   'jison src/diagrams/flowchart/parser/dot.jison -o src/diagrams/flowchart/parser/dot.js', |   'jison src/diagrams/flowchart/parser/dot.jison -o src/diagrams/flowchart/parser/dot.js', | ||||||
|   'jison src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison -o src/diagrams/sequenceDiagram/parser/sequenceDiagram.js', |   'jison src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison -o src/diagrams/sequenceDiagram/parser/sequenceDiagram.js', | ||||||
|   //'jison src/diagrams/sequenceDiagram/parser/sequence.jison -o src/diagrams/sequenceDiagram/parser/sequence.js' |   //'jison src/diagrams/sequenceDiagram/parser/sequenceDiagram.jison -o src/diagrams/sequenceDiagram/parser/sequenceDiagram.js' | ||||||
| ])); | ])); | ||||||
|  |  | ||||||
| gulp.task('jison2', function() { | gulp.task('jison2', function() { | ||||||
| @@ -37,6 +37,7 @@ gulp.task('jison2', function() { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task('dist', ['slimDist', 'fullDist','jasmine']); | gulp.task('dist', ['slimDist', 'fullDist','jasmine']); | ||||||
|  | gulp.task('rdist', ['slimDist', 'fullDist']); | ||||||
|  |  | ||||||
| var jasmine = require('gulp-jasmine'); | var jasmine = require('gulp-jasmine'); | ||||||
|  |  | ||||||
| @@ -45,6 +46,8 @@ gulp.task('jasmine',['jison','lint'], function () { | |||||||
|         .pipe(jasmine({includeStackTrace:true})); |         .pipe(jasmine({includeStackTrace:true})); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | gulp.task('tape', shell.task(['./node_modules/.bin/tape ./test/cli_test-*.js'])); | ||||||
|  |  | ||||||
| gulp.task('coverage', function (cb) { | gulp.task('coverage', function (cb) { | ||||||
|     gulp.src(['src/**/*.js', '!src/**/*.spec.js']) |     gulp.src(['src/**/*.js', '!src/**/*.spec.js']) | ||||||
|         .pipe(istanbul()) // Covering files |         .pipe(istanbul()) // Covering files | ||||||
| @@ -175,3 +178,5 @@ gulp.task('lint', function() { | |||||||
|         .pipe(jshint()) |         .pipe(jshint()) | ||||||
|         .pipe(jshint.reporter(stylish)); |         .pipe(jshint.reporter(stylish)); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | gulp.task('test',['coverage','tape']); | ||||||
|   | |||||||
							
								
								
									
										157
									
								
								lib/cli.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								lib/cli.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | |||||||
|  | var fs = require('fs') | ||||||
|  |   , exec = require('child_process').exec | ||||||
|  |   , chalk = require('chalk') | ||||||
|  |   , which = require('which') | ||||||
|  |   , parseArgs = require('minimist') | ||||||
|  |   , semver = require('semver') | ||||||
|  |  | ||||||
|  | var PHANTOM_VERSION = "^1.9.0" | ||||||
|  |  | ||||||
|  | var info = chalk.blue.bold | ||||||
|  |   , note = chalk.green.bold | ||||||
|  |  | ||||||
|  | module.exports = function() { | ||||||
|  |   return new cli() | ||||||
|  | }() | ||||||
|  |  | ||||||
|  | function cli(options) { | ||||||
|  |   this.options = { | ||||||
|  |       alias: { | ||||||
|  |           help: 'h' | ||||||
|  |         , png: 'p' | ||||||
|  |         , outputDir: 'o' | ||||||
|  |         , svg: 's' | ||||||
|  |         , verbose: 'v' | ||||||
|  |         , phantomPath: 'e' | ||||||
|  |       } | ||||||
|  |     , 'boolean': ['help', 'png', 'svg'] | ||||||
|  |     , 'string': ['outputDir'] | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   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`" | ||||||
|  |     , "  -e --phantomPath  Specify the path to the phantomjs executable" | ||||||
|  |     , "  -h --help         Show this message" | ||||||
|  |     , "  -v --verbose      Show logging" | ||||||
|  |     , "  --version         Print version and quit" | ||||||
|  |   ] | ||||||
|  |  | ||||||
|  |   return this | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cli.prototype.parse = function(argv, next) { | ||||||
|  |   var options = parseArgs(argv, this.options) | ||||||
|  |     , phantom | ||||||
|  |  | ||||||
|  |   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', 'phantomPath'].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 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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 | ||||||
|  |     , phantomVersion | ||||||
|  |  | ||||||
|  |   return function checkPhantom(_next) { | ||||||
|  |     var next = _next || function() {} | ||||||
|  |       , 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) | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								lib/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								lib/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | var os = require('os') | ||||||
|  |   , fs = require('fs') | ||||||
|  |   , path = require('path') | ||||||
|  |   , 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 || {} | ||||||
|  |     , outputDir = options.outputDir || process.cwd() | ||||||
|  |     , next = _next || function() {} | ||||||
|  |     , phantomArgs = [ | ||||||
|  |           phantomscript | ||||||
|  |         , outputDir | ||||||
|  |         , options.png | ||||||
|  |         , options.svg | ||||||
|  |         , options.verbose | ||||||
|  |       ] | ||||||
|  |  | ||||||
|  |   files.forEach(function(file) { | ||||||
|  |     phantomArgs.push(file) | ||||||
|  |   }) | ||||||
|  |  | ||||||
|  |   mkdirp(outputDir, function(err) { | ||||||
|  |     if (err) { | ||||||
|  |       throw err | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |     phantom = spawn(options.phantomPath, phantomArgs) | ||||||
|  |  | ||||||
|  |     phantom.on('exit', next) | ||||||
|  |  | ||||||
|  |     phantom.stderr.pipe(process.stderr) | ||||||
|  |     phantom.stdout.pipe(process.stdout) | ||||||
|  |   }) | ||||||
|  | } | ||||||
							
								
								
									
										213
									
								
								lib/phantomscript.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								lib/phantomscript.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,213 @@ | |||||||
|  | /** | ||||||
|  |  * 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 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | 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')) | ||||||
|  |   phantom.exit(1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var system = require('system') | ||||||
|  |   , fs = require('fs') | ||||||
|  |   , webpage = require('webpage') | ||||||
|  |  | ||||||
|  | var page = webpage.create() | ||||||
|  |   , files = phantom.args.slice(4, phantom.args.length) | ||||||
|  |   , options = { | ||||||
|  |         outputDir: phantom.args[0] | ||||||
|  |       , png: phantom.args[1] === 'true' ? true : false | ||||||
|  |       , svg: phantom.args[2] === 'true' ? true : false | ||||||
|  |       , verbose: phantom.args[3] === 'true' ? true : false | ||||||
|  |     } | ||||||
|  |   , log = logger(options.verbose) | ||||||
|  |  | ||||||
|  | page.content = [ | ||||||
|  |     '<html>' | ||||||
|  |   , '<head>' | ||||||
|  |   , '<style type="text/css">' | ||||||
|  |   , '* { margin: 0; padding: 0; }' | ||||||
|  |   , '</style>' | ||||||
|  |   , '</head>' | ||||||
|  |   , '<body>' | ||||||
|  |   , '</body>' | ||||||
|  |   , '</html>' | ||||||
|  | ].join('\n') | ||||||
|  |  | ||||||
|  | page.injectJs('../dist/mermaid.full.js') | ||||||
|  |  | ||||||
|  | files.forEach(function(file) { | ||||||
|  |   var contents = fs.read(file) | ||||||
|  |     , filename = file.split(fs.separator).slice(-1) | ||||||
|  |     , oParser = new DOMParser() | ||||||
|  |     , oDOM | ||||||
|  |     , svgContent | ||||||
|  |     , allElements | ||||||
|  |  | ||||||
|  |   // 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) | ||||||
|  |   oDOM = oParser.parseFromString(svgContent, "text/xml") | ||||||
|  |  | ||||||
|  |   resolveSVGElement(oDOM.firstChild) | ||||||
|  |  | ||||||
|  |   // traverse the SVG, and replace all foreignObject elements | ||||||
|  |   // can be removed when https://github.com/knsv/mermaid/issues/58 is resolved | ||||||
|  |   allElements = traverse(oDOM) | ||||||
|  |   for (var i = 0, len = allElements.length; i < len; i++) { | ||||||
|  |     resolveForeignObjects(allElements[i]) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (options.png) { | ||||||
|  |     page.viewportSize = { | ||||||
|  |         width: ~~oDOM.documentElement.attributes.getNamedItem('width').value | ||||||
|  |       , height: ~~oDOM.documentElement.attributes.getNamedItem('height').value | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     page.render(options.outputDir + fs.separator + filename + '.png') | ||||||
|  |     log('saved png: ' + filename + '.png') | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (options.svg) { | ||||||
|  |     var serialize = new XMLSerializer() | ||||||
|  |     fs.write( | ||||||
|  |         options.outputDir + fs.separator + filename + '.svg' | ||||||
|  |       , serialize.serializeToString(oDOM) | ||||||
|  |       , 'w' | ||||||
|  |     ) | ||||||
|  |     log('saved svg: ' + filename + '.svg') | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | phantom.exit() | ||||||
|  |  | ||||||
|  | function logger(_verbose) { | ||||||
|  |   var verbose = _verbose | ||||||
|  |  | ||||||
|  |   return function(_message, _level) { | ||||||
|  |     var level = level | ||||||
|  |       , message = _message | ||||||
|  |       , log | ||||||
|  |  | ||||||
|  |     log = level === 'error' ? system.stderr : system.stdout | ||||||
|  |  | ||||||
|  |     if (verbose) { | ||||||
|  |       log.write(message + '\n') | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function traverse(obj){ | ||||||
|  |   var tree = [] | ||||||
|  |  | ||||||
|  |   tree.push(obj) | ||||||
|  |   visit(obj) | ||||||
|  |  | ||||||
|  |   function visit(node) { | ||||||
|  |     if (node && node.hasChildNodes()) { | ||||||
|  |       var child = node.firstChild | ||||||
|  |       while (child) { | ||||||
|  |         if (child.nodeType === 1 && child.nodeName != 'SCRIPT'){ | ||||||
|  |           tree.push(child) | ||||||
|  |           visit(child) | ||||||
|  |         } | ||||||
|  |         child = child.nextSibling | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return tree | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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" | ||||||
|  |       } | ||||||
|  |     , doctype = '<!DOCTYPE svg:svg PUBLIC' | ||||||
|  |         + ' "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"' | ||||||
|  |         + ' "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">' | ||||||
|  |  | ||||||
|  |   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 resolveForeignObjects(element) { | ||||||
|  |   var children | ||||||
|  |     , textElement | ||||||
|  |     , textSpan | ||||||
|  |  | ||||||
|  |   if (element.tagName === 'foreignObject') { | ||||||
|  |     textElement = document.createElement('text') | ||||||
|  |     textSpan = document.createElement('tspan') | ||||||
|  |     textSpan.setAttribute( | ||||||
|  |         'style' | ||||||
|  |       , 'font-size: 11.5pt; font-family: "sans-serif";' | ||||||
|  |     ) | ||||||
|  |     textSpan.setAttribute('x', 0) | ||||||
|  |     textSpan.setAttribute('y', 14.5) | ||||||
|  |     textSpan.textContent = element.textContent | ||||||
|  |  | ||||||
|  |     textElement.appendChild(textSpan) | ||||||
|  |     element.parentElement.appendChild(textElement) | ||||||
|  |     element.parentElement.removeChild(element) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // The sandboxed function that's executed in-page by phantom | ||||||
|  | function executeInPage(contents) { | ||||||
|  |   var xmlSerializer = new XMLSerializer() | ||||||
|  |     , toRemove | ||||||
|  |     , el | ||||||
|  |     , elContent | ||||||
|  |     , svg | ||||||
|  |     , svgValue | ||||||
|  |  | ||||||
|  |   toRemove = document.getElementsByClassName('mermaid') | ||||||
|  |   if (toRemove && toRemove.length) { | ||||||
|  |     for (var i = 0, len = toRemove.length; i < len; i++) { | ||||||
|  |       toRemove[i].parentNode.removeChild(toRemove[i]) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   el = document.createElement("div") | ||||||
|  |   el.className = 'mermaid' | ||||||
|  |   elContent = document.createTextNode(contents) | ||||||
|  |   el.appendChild(elContent) | ||||||
|  |  | ||||||
|  |   document.body.appendChild(el) | ||||||
|  |  | ||||||
|  |   mermaid.init() | ||||||
|  |  | ||||||
|  |   svg = document.querySelector('svg') | ||||||
|  |   svgValue = xmlSerializer.serializeToString(svg) | ||||||
|  |  | ||||||
|  |   return svgValue | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,10 +1,13 @@ | |||||||
| { | { | ||||||
|   "name": "mermaid", |   "name": "mermaid", | ||||||
|   "version": "0.2.15", |   "version": "0.3.2", | ||||||
|   "description": "Markdownish syntax for generating flowcharts", |   "description": "Markdownish syntax for generating flowcharts", | ||||||
|   "main": "src/main.js", |   "main": "src/main.js", | ||||||
|  |   "bin": { | ||||||
|  |     "mermaid": "./bin/mermaid.js" | ||||||
|  |   }, | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "test": "gulp coverage" |     "test": "gulp test" | ||||||
|   }, |   }, | ||||||
|   "repository": { |   "repository": { | ||||||
|     "type": "git", |     "type": "git", | ||||||
| @@ -13,19 +16,31 @@ | |||||||
|   "author": "", |   "author": "", | ||||||
|   "license": "MIT", |   "license": "MIT", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "chalk": "^0.5.1", | ||||||
|  |     "dagre-d3": "~0.3.2", | ||||||
|     "he": "^0.5.0", |     "he": "^0.5.0", | ||||||
|     "dagre-d3": "~0.3.2" |     "minimist": "^1.1.0", | ||||||
|  |     "mkdirp": "^0.5.0", | ||||||
|  |     "semver": "^4.1.1", | ||||||
|  |     "which": "^1.0.8" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|  |     "async": "^0.9.0", | ||||||
|     "browserify": "~6.2.0", |     "browserify": "~6.2.0", | ||||||
|  |     "clone": "^0.2.0", | ||||||
|     "codeclimate-test-reporter": "0.0.4", |     "codeclimate-test-reporter": "0.0.4", | ||||||
|     "d3": "~3.4.13", |     "d3": "~3.4.13", | ||||||
|     "dagre-d3": "~0.3.2", |     "dagre-d3": "~0.3.2", | ||||||
|  |     "event-stream": "^3.2.0", | ||||||
|  |     "foundation": "^4.2.1-1", | ||||||
|  |     "front-matter": "^0.2.0", | ||||||
|     "gulp": "~3.8.9", |     "gulp": "~3.8.9", | ||||||
|     "gulp-browserify": "^0.5.0", |     "gulp-browserify": "^0.5.0", | ||||||
|     "gulp-bump": "^0.1.11", |     "gulp-bump": "^0.1.11", | ||||||
|     "gulp-concat": "~2.4.1", |     "gulp-concat": "~2.4.1", | ||||||
|  |     "gulp-data": "^1.1.1", | ||||||
|     "gulp-ext-replace": "~0.1.0", |     "gulp-ext-replace": "~0.1.0", | ||||||
|  |     "gulp-hogan": "^1.1.0", | ||||||
|     "gulp-istanbul": "^0.4.0", |     "gulp-istanbul": "^0.4.0", | ||||||
|     "gulp-jasmine": "~1.0.1", |     "gulp-jasmine": "~1.0.1", | ||||||
|     "gulp-jison": "~1.0.0", |     "gulp-jison": "~1.0.0", | ||||||
| @@ -36,6 +51,7 @@ | |||||||
|     "gulp-tag-version": "^1.2.1", |     "gulp-tag-version": "^1.2.1", | ||||||
|     "gulp-uglify": "~1.0.1", |     "gulp-uglify": "~1.0.1", | ||||||
|     "he": "^0.5.0", |     "he": "^0.5.0", | ||||||
|  |     "hogan.js": "^3.0.2", | ||||||
|     "jasmine": "~2.0.1", |     "jasmine": "~2.0.1", | ||||||
|     "jison": "~0.4.15", |     "jison": "~0.4.15", | ||||||
|     "jshint-stylish": "^1.0.0", |     "jshint-stylish": "^1.0.0", | ||||||
| @@ -51,9 +67,13 @@ | |||||||
|     "lodash.defaults": "^2.4.1", |     "lodash.defaults": "^2.4.1", | ||||||
|     "lodash.templatesettings": "^2.4.1", |     "lodash.templatesettings": "^2.4.1", | ||||||
|     "lodash.values": "^2.4.1", |     "lodash.values": "^2.4.1", | ||||||
|  |     "marked": "^0.3.2", | ||||||
|     "mock-browser": "^0.90.27", |     "mock-browser": "^0.90.27", | ||||||
|     "path": "^0.4.9", |     "path": "^0.4.9", | ||||||
|     "phantomjs": "^1.9.12", |     "phantomjs": "^1.9.12", | ||||||
|     "rewire": "^2.1.3" |     "rewire": "^2.1.3", | ||||||
|  |     "rimraf": "^2.2.8", | ||||||
|  |     "semantic-ui": "^1.4.1", | ||||||
|  |     "tape": "^3.0.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -38,6 +38,8 @@ exports.addVertices = function (vert, g) { | |||||||
|          */ |          */ | ||||||
|         var classStr = ''; |         var classStr = ''; | ||||||
|  |  | ||||||
|  |         //console.log(vertice.classes); | ||||||
|  |  | ||||||
|         if(vertice.classes.length >0){ |         if(vertice.classes.length >0){ | ||||||
|             classStr = vertice.classes.join(" "); |             classStr = vertice.classes.join(" "); | ||||||
|         } |         } | ||||||
| @@ -58,6 +60,7 @@ exports.addVertices = function (vert, g) { | |||||||
|             verticeText = vertice.text; |             verticeText = vertice.text; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | <<<<<<< HEAD | ||||||
|         var labelTypeStr = ''; |         var labelTypeStr = ''; | ||||||
|         if(equals('html',mermaid_config.labelType)) { |         if(equals('html',mermaid_config.labelType)) { | ||||||
|             labelTypeStr = 'html'; |             labelTypeStr = 'html'; | ||||||
| @@ -65,6 +68,9 @@ exports.addVertices = function (vert, g) { | |||||||
|             verticeText = verticeText.replace(/<br>/g, "\n"); |             verticeText = verticeText.replace(/<br>/g, "\n"); | ||||||
|             labelTypeStr = 'text'; |             labelTypeStr = 'text'; | ||||||
|         } |         } | ||||||
|  | ======= | ||||||
|  |         console.log(verticeText); | ||||||
|  | >>>>>>> master | ||||||
|  |  | ||||||
|         var radious = 0; |         var radious = 0; | ||||||
|         var _shape = ''; |         var _shape = ''; | ||||||
| @@ -115,16 +121,34 @@ exports.addEdges = function (edges, g) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         var style = ''; |         var style = ''; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         if(typeof edge.style !== 'undefined'){ |         if(typeof edge.style !== 'undefined'){ | ||||||
|             edge.style.forEach(function(s){ |             edge.style.forEach(function(s){ | ||||||
|                 style = style + s +';'; |                 style = style + s +';'; | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  |         else{ | ||||||
|  |             switch(edge.stroke){ | ||||||
|  |                 case 'normal': | ||||||
|  |                     style = 'stroke: #333; stroke-width: 1.5px;fill:none'; | ||||||
|  |                     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; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|         // Add the edge to the graph |         // Add the edge to the graph | ||||||
|         if (typeof edge.text === 'undefined') { |         if (typeof edge.text === 'undefined') { | ||||||
|             if(typeof edge.style === 'undefined'){ |             if(typeof edge.style === 'undefined'){ | ||||||
|                 g.setEdge(edge.start, edge.end,{ style: "stroke: #333; stroke-width: 1.5px;fill:none", arrowheadStyle: "fill: #333", arrowhead: aHead},cnt); |                 g.setEdge(edge.start, edge.end,{ style: style, arrowhead: aHead},cnt); | ||||||
|             }else{ |             }else{ | ||||||
|                 g.setEdge(edge.start, edge.end, { |                 g.setEdge(edge.start, edge.end, { | ||||||
|                     style: style, arrowheadStyle: "fill: #333", arrowhead: aHead |                     style: style, arrowheadStyle: "fill: #333", arrowhead: aHead | ||||||
| @@ -135,7 +159,11 @@ exports.addEdges = function (edges, g) { | |||||||
|         else { |         else { | ||||||
|             var edgeText = edge.text.replace(/<br>/g, "\n"); |             var edgeText = edge.text.replace(/<br>/g, "\n"); | ||||||
|             if(typeof edge.style === 'undefined'){ |             if(typeof edge.style === 'undefined'){ | ||||||
|  | <<<<<<< HEAD | ||||||
|                 g.setEdge(edge.start, edge.end,{labelType: "text", style: "stroke: #333; stroke-width: 1.5px;fill:none", labelpos:'c', label: edgeText, arrowheadStyle: "fill: #333", arrowhead: aHead},cnt); |                 g.setEdge(edge.start, edge.end,{labelType: "text", style: "stroke: #333; stroke-width: 1.5px;fill:none", labelpos:'c', label: edgeText, arrowheadStyle: "fill: #333", arrowhead: aHead},cnt); | ||||||
|  | ======= | ||||||
|  |                 g.setEdge(edge.start, edge.end,{labelType: "html",style: style, labelpos:'c', label: '<span style="background:#e8e8e8">'+edge.text+'</span>', arrowheadStyle: "fill: #333", arrowhead: aHead},cnt); | ||||||
|  | >>>>>>> master | ||||||
|             }else{ |             }else{ | ||||||
|                 g.setEdge(edge.start, edge.end, { |                 g.setEdge(edge.start, edge.end, { | ||||||
|                     labelType: "text",style: style, arrowheadStyle: "fill: #333", label: edgeText, arrowhead: aHead |                     labelType: "text",style: style, arrowheadStyle: "fill: #333", label: edgeText, arrowhead: aHead | ||||||
| @@ -205,7 +233,10 @@ exports.draw = function (text, id,isDot) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Create the input mermaid.graph |     // Create the input mermaid.graph | ||||||
|     var g = new dagreD3.graphlib.Graph({multigraph:true}) |     var g = new dagreD3.graphlib.Graph({ | ||||||
|  |         multigraph:true, | ||||||
|  |         compound: true | ||||||
|  |     }) | ||||||
|         .setGraph({ |         .setGraph({ | ||||||
|             rankdir: dir, |             rankdir: dir, | ||||||
|             marginx: 20, |             marginx: 20, | ||||||
| @@ -216,9 +247,35 @@ exports.draw = function (text, id,isDot) { | |||||||
|             return {}; |             return {}; | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |     var subGraphs = graph.getSubGraphs(); | ||||||
|  |     var i = 0; | ||||||
|  |     subGraphs.forEach(function(subG){ | ||||||
|  |         i = i + 1; | ||||||
|  |         var id = 'subG'+i; | ||||||
|  |         graph.addVertex(id,undefined,undefined,undefined); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     // Fetch the verices/nodes and edges/links from the parsed graph definition |     // Fetch the verices/nodes and edges/links from the parsed graph definition | ||||||
|     var vert = graph.getVertices(); |     var vert = graph.getVertices(); | ||||||
|  |  | ||||||
|  |     //console.log(vert); | ||||||
|     var edges = graph.getEdges(); |     var edges = graph.getEdges(); | ||||||
|  |     //g.setParent("A", "p"); | ||||||
|  |     //g.setParent("B", "p"); | ||||||
|  |  | ||||||
|  |     //console.log(subGraphs); | ||||||
|  |     i = 0; | ||||||
|  |     subGraphs.forEach(function(subG){ | ||||||
|  |         i = i + 1; | ||||||
|  |         var id = 'subG'+i; | ||||||
|  |  | ||||||
|  |         d3.selectAll('cluster').append('text'); | ||||||
|  |  | ||||||
|  |         subG.nodes.forEach(function(node){ | ||||||
|  |             //console.log('Setting node',node,' to subgraph '+id); | ||||||
|  |             g.setParent(node,id); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|     exports.addVertices(vert, g); |     exports.addVertices(vert, g); | ||||||
|     exports.addEdges(edges, g); |     exports.addEdges(edges, g); | ||||||
|  |  | ||||||
| @@ -294,8 +351,57 @@ exports.draw = function (text, id,isDot) { | |||||||
|  |  | ||||||
|     // Run the renderer. This is what draws the final graph. |     // Run the renderer. This is what draws the final graph. | ||||||
|     render(d3.select("#" + id + " g"), g); |     render(d3.select("#" + id + " g"), g); | ||||||
|  |     var svgb = document.querySelector('#mermaidChart0'); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  var xPos = document.querySelectorAll('.clusters rect')[0].x.baseVal.value; | ||||||
|  |  var width = document.querySelectorAll('.clusters rect')[0].width.baseVal.value; | ||||||
|  |     var cluster = d3.selectAll('.cluster'); | ||||||
|  |     var te = cluster.append('text'); | ||||||
|  |     te.attr('x', xPos+width/2); | ||||||
|  |     te.attr('y', 12); | ||||||
|  |     //te.stroke('black'); | ||||||
|  |     te.attr('id', 'apa12'); | ||||||
|  |     te.style('text-anchor', 'middle'); | ||||||
|  |     te.text('Title for cluster'); | ||||||
|  | */ | ||||||
|     // Center the graph |     // Center the graph | ||||||
|     svg.attr("height", g.graph().height ); |     svg.attr("height", g.graph().height ); | ||||||
|     svg.attr("width", g.graph().width ); |     svg.attr("width", g.graph().width ); | ||||||
|  |     svg.attr("viewBox", svgb.getBBox().x + ' 0 '+ g.graph().width+' '+ g.graph().height); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     setTimeout(function(){ | ||||||
|  |         console.log('Fixing titles'); | ||||||
|  |         var i = 0; | ||||||
|  |         subGraphs.forEach(function(subG){ | ||||||
|  |             console.log('Setting id '+id); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             var clusterRects = document.querySelectorAll('#' + id + ' .clusters rect'); | ||||||
|  |             var clusters     = document.querySelectorAll('#' + id + ' .cluster'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             if(subG.title !== 'undefined'){ | ||||||
|  |                 console.log(clusterRects[i]); | ||||||
|  |                 var xPos = clusterRects[i].x.baseVal.value; | ||||||
|  |                 var yPos = clusterRects[i].y.baseVal.value; | ||||||
|  |                 var width = clusterRects[i].width.baseVal.value; | ||||||
|  |                 var cluster = d3.select(clusters[i]); | ||||||
|  |                 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'); | ||||||
|  |                 console.log('Title '+subG.title); | ||||||
|  |                 console.log('i',i); | ||||||
|  |                 console.log('x'+xPos+width/2); | ||||||
|  |                 console.log('y'+xPos); | ||||||
|  |                 te.text(subG.title); | ||||||
|  |             } | ||||||
|  |             i = i + 1; | ||||||
|  |         }); | ||||||
|  |     },200); | ||||||
| }; | }; | ||||||
| @@ -5,6 +5,7 @@ | |||||||
| var vertices = {}; | var vertices = {}; | ||||||
| var edges = []; | var edges = []; | ||||||
| var classes = []; | var classes = []; | ||||||
|  | var subGraphs = []; | ||||||
| var direction; | var direction; | ||||||
| // Functions to be run after graph rendering | // Functions to be run after graph rendering | ||||||
| var funs = []; | var funs = []; | ||||||
| @@ -63,6 +64,7 @@ exports.addLink = function (start, end, type, linktext) { | |||||||
|  |  | ||||||
|     if (typeof type !== 'undefined') { |     if (typeof type !== 'undefined') { | ||||||
|         edge.type = type.type; |         edge.type = type.type; | ||||||
|  |         edge.stroke = type.stroke; | ||||||
|     } |     } | ||||||
|     edges.push(edge); |     edges.push(edge); | ||||||
| }; | }; | ||||||
| @@ -197,6 +199,7 @@ exports.clear = function () { | |||||||
|     classes = {}; |     classes = {}; | ||||||
|     edges = []; |     edges = []; | ||||||
|     funs = []; |     funs = []; | ||||||
|  |     subGraphs = []; | ||||||
| }; | }; | ||||||
| /** | /** | ||||||
|  * |  * | ||||||
| @@ -205,3 +208,30 @@ exports.clear = function () { | |||||||
| exports.defaultStyle = function () { | exports.defaultStyle = function () { | ||||||
|     return "fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;"; |     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":{}}, objs = []; | ||||||
|  |  | ||||||
|  |         return a.filter(function(item) { | ||||||
|  |             var type = typeof item; | ||||||
|  |             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 subG = []; | ||||||
|  |  | ||||||
|  |     subG = uniq(subG.concat.apply(subG,list)); | ||||||
|  |     //console.log(subG); | ||||||
|  |  | ||||||
|  |     subGraphs.push({nodes:subG,title:title}); | ||||||
|  | }; | ||||||
|  | exports.getSubGraphs = function (list) { | ||||||
|  |     return subGraphs; | ||||||
|  | }; | ||||||
|   | |||||||
| @@ -10,31 +10,50 @@ | |||||||
| "class"               return 'CLASS'; | "class"               return 'CLASS'; | ||||||
| "click"               return 'CLICK'; | "click"               return 'CLICK'; | ||||||
| "graph"               return 'GRAPH'; | "graph"               return 'GRAPH'; | ||||||
|  | "subgraph"            return 'subgraph'; | ||||||
|  | "end"                 return 'end'; | ||||||
| "LR"                  return 'DIR'; | "LR"                  return 'DIR'; | ||||||
| "RL"                  return 'DIR'; | "RL"                  return 'DIR'; | ||||||
| "TB"                  return 'DIR'; | "TB"                  return 'DIR'; | ||||||
| "BT"                  return 'DIR'; | "BT"                  return 'DIR'; | ||||||
| "TD"                  return 'DIR'; | "TD"                  return 'DIR'; | ||||||
| "BR"                  return 'DIR'; | "BR"                  return 'DIR'; | ||||||
| [0-9]                 return 'NUM'; | [0-9]+                 return 'NUM'; | ||||||
| \#                    return 'BRKT'; | \#                    return 'BRKT'; | ||||||
| ":"                   return 'COLON'; | ":"                   return 'COLON'; | ||||||
| ";"                   return 'SEMI'; | ";"                   return 'SEMI'; | ||||||
| ","                   return 'COMMA'; | ","                   return 'COMMA'; | ||||||
| "="                   return 'EQUALS'; |  | ||||||
| "*"                   return 'MULT'; | "*"                   return 'MULT'; | ||||||
| "."                   return 'DOT'; |  | ||||||
| "<"                   return 'TAGSTART'; | "<"                   return 'TAGSTART'; | ||||||
| ">"                   return 'TAGEND'; | ">"                   return 'TAGEND'; | ||||||
|  | "^"                   return 'UP'; | ||||||
|  | "v"                   return 'DOWN'; | ||||||
| \-\-[x]               return 'ARROW_CROSS'; | \-\-[x]               return 'ARROW_CROSS'; | ||||||
| \-\-\>                return 'ARROW_POINT'; | \-\-\>                return 'ARROW_POINT'; | ||||||
| \-\-[o]               return 'ARROW_CIRCLE'; | \-\-[o]               return 'ARROW_CIRCLE'; | ||||||
| \-\-\-                return 'ARROW_OPEN'; | \-\-\-                return 'ARROW_OPEN'; | ||||||
|  | \-\.\-[x]             return 'DOTTED_ARROW_CROSS'; | ||||||
|  | \-\.\-\>              return 'DOTTED_ARROW_POINT'; | ||||||
|  | \-\.\-[o]             return 'DOTTED_ARROW_CIRCLE'; | ||||||
|  | \-\.\-                return 'DOTTED_ARROW_OPEN'; | ||||||
|  | .\-[x]                return 'DOTTED_ARROW_CROSS'; | ||||||
|  | \.\-\>                return 'DOTTED_ARROW_POINT'; | ||||||
|  | \.\-[o]               return 'DOTTED_ARROW_CIRCLE'; | ||||||
|  | \.\-                  return 'DOTTED_ARROW_OPEN'; | ||||||
|  | \=\=[x]               return 'THICK_ARROW_CROSS'; | ||||||
|  | \=\=\>                return 'THICK_ARROW_POINT'; | ||||||
|  | \=\=[o]               return 'THICK_ARROW_CIRCLE'; | ||||||
|  | \=\=[\=]              return 'THICK_ARROW_OPEN'; | ||||||
|  | \-\-                  return '--'; | ||||||
|  | \-\.                  return '-.'; | ||||||
|  | \=\=                  return '=='; | ||||||
| \-                    return 'MINUS'; | \-                    return 'MINUS'; | ||||||
|  | "."                   return 'DOT'; | ||||||
| \+                    return 'PLUS'; | \+                    return 'PLUS'; | ||||||
| \%                    return 'PCT'; | \%                    return 'PCT'; | ||||||
|  | "="                   return 'EQUALS'; | ||||||
| \=                    return 'EQUALS'; | \=                    return 'EQUALS'; | ||||||
| [\u0021-\u0027\u002A-\u002E\u003F\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| | [\u0021-\u0027\u002A-\u002E\u003F\u0041-\u005A\u005C\u005F-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| | ||||||
| [\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| | [\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| | ||||||
| [\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]| | [\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]| | ||||||
| [\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]| | [\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]| | ||||||
| @@ -114,25 +133,47 @@ | |||||||
|  |  | ||||||
| %left '^' | %left '^' | ||||||
|  |  | ||||||
| %start expressions | %start mermaidDoc | ||||||
|  |  | ||||||
| %% /* language grammar */ | %% /* language grammar */ | ||||||
|  |  | ||||||
| expressions | mermaidDoc: graphConfig document ; | ||||||
|     : graphConfig statements EOF |  | ||||||
|     | graphConfig spaceListNewline statements EOF | document | ||||||
|         {$$=$1;} | 	: /* empty */ | ||||||
|     ; | 	{ $$ = [];} | ||||||
|  | 	| document line | ||||||
|  | 	{ | ||||||
|  | 	    if($2 !== []){ | ||||||
|  | 	        $1.push($2); | ||||||
|  | 	    } | ||||||
|  | 	    $$=$1;} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | line | ||||||
|  | 	: spaceListNewline statement | ||||||
|  | 	{$$=$2;} | ||||||
|  | 	| statement | ||||||
|  | 	{$$=$1;} | ||||||
|  | 	| SEMI | ||||||
|  | 	| EOF | ||||||
|  | 	; | ||||||
|  |  | ||||||
| graphConfig | graphConfig | ||||||
|     : GRAPH SPACE DIR SEMI |     : GRAPH SPACE DIR FirstStmtSeperator | ||||||
|         { yy.setDirection($3);$$ = $3;} |         { 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;} | ||||||
|     ; |     ; | ||||||
|  |  | ||||||
| statements | FirstStmtSeperator  | ||||||
|     : statement spaceListNewline statements |     : SEMI | NEWLINE | spaceList NEWLINE ; | ||||||
|     | statement |  | ||||||
|     ; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| spaceListNewline | spaceListNewline | ||||||
| @@ -150,20 +191,36 @@ spaceList | |||||||
|  |  | ||||||
| statement | statement | ||||||
|     : commentStatement NEWLINE |     : commentStatement NEWLINE | ||||||
|     {$$='Comment';} |     {$$=[];} | ||||||
|     | verticeStatement SEMI |     | verticeStatement separator | ||||||
|     | styleStatement SEMI |     {$$=$1} | ||||||
|     | linkStyleStatement SEMI |     | styleStatement separator | ||||||
|     | classDefStatement SEMI |     {$$=[];} | ||||||
|     | classStatement SEMI |     | linkStyleStatement separator | ||||||
|     | clickStatement SEMI |     {$$=[];} | ||||||
|  |     | classDefStatement separator | ||||||
|  |     {$$=[];} | ||||||
|  |     | classStatement separator | ||||||
|  |     {$$=[];} | ||||||
|  |     | clickStatement separator | ||||||
|  |     {$$=[];} | ||||||
|  |     | subgraph  text separator document endStatement separator | ||||||
|  |     {yy.addSubGraph($4,$2);} | ||||||
|  |     | subgraph separator document endStatement separator | ||||||
|  |     {yy.addSubGraph($3,undefined);} | ||||||
|     ; |     ; | ||||||
|  |  | ||||||
|  | endStatement: end | ||||||
|  |     | SPACE endStatement | ||||||
|  |     ; | ||||||
|  |  | ||||||
|  | separator: NEWLINE | SEMI | EOF ; | ||||||
|  |  | ||||||
| verticeStatement: | verticeStatement: | ||||||
|      vertex link vertex |      vertex link vertex | ||||||
|         { yy.addLink($1,$3,$2);$$ = 'oy'} |         { yy.addLink($1,$3,$2);$$ = [$1,$3];} | ||||||
|      | vertex |      | vertex | ||||||
|         {$$ = 'yo';} |         {$$ = [$1];} | ||||||
|     ; |     ; | ||||||
|  |  | ||||||
| vertex:  alphaNum SQS text SQE | vertex:  alphaNum SQS text SQE | ||||||
| @@ -186,8 +243,6 @@ vertex:  alphaNum SQS text SQE | |||||||
|         {$$ = $1;yy.addVertex($1,$3,'odd');} |         {$$ = $1;yy.addVertex($1,$3,'odd');} | ||||||
|     | alphaNum TAGEND text SQE SPACE |     | alphaNum TAGEND text SQE SPACE | ||||||
|         {$$ = $1;yy.addVertex($1,$3,'odd');} |         {$$ = $1;yy.addVertex($1,$3,'odd');} | ||||||
|     | alphaNum TAGSTART text TAGEND |  | ||||||
|         {$$ = $1;yy.addVertex($1,$3,'diamond');} |  | ||||||
|     | alphaNum |     | alphaNum | ||||||
|         {$$ = $1;yy.addVertex($1);} |         {$$ = $1;yy.addVertex($1);} | ||||||
|     | alphaNum SPACE |     | alphaNum SPACE | ||||||
| @@ -197,7 +252,7 @@ vertex:  alphaNum SQS text SQE | |||||||
| alphaNum | alphaNum | ||||||
|     : alphaNumStatement |     : alphaNumStatement | ||||||
|     {$$=$1;} |     {$$=$1;} | ||||||
|     | alphaNumStatement alphaNum |     | alphaNum alphaNumStatement | ||||||
|     {$$=$1+''+$2;} |     {$$=$1+''+$2;} | ||||||
|     ; |     ; | ||||||
|  |  | ||||||
| @@ -217,17 +272,45 @@ link: linkStatement arrowText | |||||||
|     {$$ = $1;} |     {$$ = $1;} | ||||||
|     | linkStatement SPACE |     | linkStatement SPACE | ||||||
|     {$$ = $1;} |     {$$ = $1;} | ||||||
|  |     | '--' SPACE text SPACE linkStatement | ||||||
|  |     {$5.text = $3;$$ = $5;} | ||||||
|  |     | '--' SPACE text SPACE linkStatement SPACE | ||||||
|  |     {$5.text = $3;$$ = $5;} | ||||||
|  |     | '-.' SPACE text SPACE linkStatement | ||||||
|  |     {$5.text = $3;$$ = $5;} | ||||||
|  |     | '-.' SPACE text SPACE linkStatement SPACE | ||||||
|  |     {$5.text = $3;$$ = $5;} | ||||||
|  |     | '==' SPACE text SPACE linkStatement | ||||||
|  |     {$5.text = $3;$$ = $5;} | ||||||
|  |     | '==' SPACE text SPACE linkStatement SPACE | ||||||
|  |     {$5.text = $3;$$ = $5;} | ||||||
|     ; |     ; | ||||||
|  |  | ||||||
| linkStatement: ARROW_POINT | linkStatement: ARROW_POINT | ||||||
|         {$$ = {"type":"arrow"};} |         {$$ = {"type":"arrow","stroke":"normal"};} | ||||||
|     | ARROW_CIRCLE |     | ARROW_CIRCLE | ||||||
|         {$$ = {"type":"arrow_circle"};} |         {$$ = {"type":"arrow_circle","stroke":"normal"};} | ||||||
|     | ARROW_CROSS |     | ARROW_CROSS | ||||||
|         {$$ = {"type":"arrow_cross"};} |         {$$ = {"type":"arrow_cross","stroke":"normal"};} | ||||||
|     | ARROW_OPEN |     | ARROW_OPEN | ||||||
|         {$$ = {"type":"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: | arrowText: | ||||||
|     PIPE text PIPE |     PIPE text PIPE | ||||||
| @@ -250,7 +333,7 @@ commentText: commentToken | |||||||
|  |  | ||||||
|  |  | ||||||
| keywords | keywords | ||||||
|     : STYLE | LINKSTYLE | CLASSDEF | CLASS | CLICK | GRAPH | DIR; |     : STYLE | LINKSTYLE | CLASSDEF | CLASS | CLICK | GRAPH | DIR | subgraph | end ; | ||||||
|  |  | ||||||
|  |  | ||||||
| textNoTags: textNoTagsToken | textNoTags: textNoTagsToken | ||||||
| @@ -296,13 +379,13 @@ style: styleComponent | |||||||
|     {$$ = $1 + $2;} |     {$$ = $1 + $2;} | ||||||
|     ; |     ; | ||||||
|  |  | ||||||
| styleComponent: ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT; | styleComponent: ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ; | ||||||
|  |  | ||||||
| /* Token lists */ | /* Token lists */ | ||||||
|  |  | ||||||
| commentToken   : textToken | graphCodeTokens ; | commentToken   : textToken | graphCodeTokens ; | ||||||
|  |  | ||||||
| textToken      : textNoTagsToken | TAGSTART | TAGEND ; | textToken      : textNoTagsToken | TAGSTART | TAGEND | '=='  | '--' ; | ||||||
|  |  | ||||||
| textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ; | textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ; | ||||||
|  |  | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -10,12 +10,12 @@ describe('when parsing ',function(){ | |||||||
|         flow.parser.yy = require('../graphDb'); |         flow.parser.yy = require('../graphDb'); | ||||||
|         flow.parser.yy.clear(); |         flow.parser.yy.clear(); | ||||||
|         /*flow.parser.parse.parseError= function parseError(str, hash) { |         /*flow.parser.parse.parseError= function parseError(str, hash) { | ||||||
|             console.log(str); |             console.logconsole.log(str); | ||||||
|         }*/ |         }*/ | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle a nodes and edges',function(){ |     it('should handle a nodes and edges',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;A-->B;'); |         var res = flow.parser.parse('graph TD;\nA-->B;'); | ||||||
|  |  | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
| @@ -30,6 +30,84 @@ describe('when parsing ',function(){ | |||||||
|         expect(edges[0].text).toBe(''); |         expect(edges[0].text).toBe(''); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     it('should handle angle bracket '>' as direction LR',function(){ | ||||||
|  |         var res = flow.parser.parse('graph >;A-->B;'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         var vert = flow.parser.yy.getVertices(); | ||||||
|  |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |         var direction = flow.parser.yy.getDirection(); | ||||||
|  |  | ||||||
|  |         expect(direction).toBe('LR'); | ||||||
|  |  | ||||||
|  |         expect(vert['A'].id).toBe('A'); | ||||||
|  |         expect(vert['B'].id).toBe('B'); | ||||||
|  |         expect(edges.length).toBe(1); | ||||||
|  |         expect(edges[0].start).toBe('A'); | ||||||
|  |         expect(edges[0].end).toBe('B'); | ||||||
|  |         expect(edges[0].type).toBe('arrow'); | ||||||
|  |         expect(edges[0].text).toBe(''); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should handle angle bracket '<' as direction RL',function(){ | ||||||
|  |         var res = flow.parser.parse('graph <;A-->B;'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         var vert = flow.parser.yy.getVertices(); | ||||||
|  |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |         var direction = flow.parser.yy.getDirection(); | ||||||
|  |  | ||||||
|  |         expect(direction).toBe('RL'); | ||||||
|  |  | ||||||
|  |         expect(vert['A'].id).toBe('A'); | ||||||
|  |         expect(vert['B'].id).toBe('B'); | ||||||
|  |         expect(edges.length).toBe(1); | ||||||
|  |         expect(edges[0].start).toBe('A'); | ||||||
|  |         expect(edges[0].end).toBe('B'); | ||||||
|  |         expect(edges[0].type).toBe('arrow'); | ||||||
|  |         expect(edges[0].text).toBe(''); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     it('should handle caret '^' as direction BT',function(){ | ||||||
|  |         var res = flow.parser.parse('graph ^;A-->B;'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         var vert = flow.parser.yy.getVertices(); | ||||||
|  |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |         var direction = flow.parser.yy.getDirection(); | ||||||
|  |  | ||||||
|  |         expect(direction).toBe('BT'); | ||||||
|  |  | ||||||
|  |         expect(vert['A'].id).toBe('A'); | ||||||
|  |         expect(vert['B'].id).toBe('B'); | ||||||
|  |         expect(edges.length).toBe(1); | ||||||
|  |         expect(edges[0].start).toBe('A'); | ||||||
|  |         expect(edges[0].end).toBe('B'); | ||||||
|  |         expect(edges[0].type).toBe('arrow'); | ||||||
|  |         expect(edges[0].text).toBe(''); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     it('should handle lower-case \'v\' as direction TB',function(){ | ||||||
|  |         var res = flow.parser.parse('graph v;A-->B;'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         var vert = flow.parser.yy.getVertices(); | ||||||
|  |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |         var direction = flow.parser.yy.getDirection(); | ||||||
|  |  | ||||||
|  |         expect(direction).toBe('TB'); | ||||||
|  |  | ||||||
|  |         expect(vert['A'].id).toBe('A'); | ||||||
|  |         expect(vert['B'].id).toBe('B'); | ||||||
|  |         expect(edges.length).toBe(1); | ||||||
|  |         expect(edges[0].start).toBe('A'); | ||||||
|  |         expect(edges[0].end).toBe('B'); | ||||||
|  |         expect(edges[0].type).toBe('arrow'); | ||||||
|  |         expect(edges[0].text).toBe(''); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     it('should handle a nodes and edges and a space between link and node',function(){ |     it('should handle a nodes and edges and a space between link and node',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;A --> B;'); |         var res = flow.parser.parse('graph TD;A --> B;'); | ||||||
|  |  | ||||||
| @@ -46,6 +124,37 @@ describe('when parsing ',function(){ | |||||||
|         expect(edges[0].text).toBe(''); |         expect(edges[0].text).toBe(''); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     it('should handle a nodes and edges, a space between link and node and each line ending without semicolon',function(){ | ||||||
|  |         var res = flow.parser.parse('graph TD\nA --> B\n style e red'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         var vert = flow.parser.yy.getVertices(); | ||||||
|  |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |         expect(vert['A'].id).toBe('A'); | ||||||
|  |         expect(vert['B'].id).toBe('B'); | ||||||
|  |         expect(edges.length).toBe(1); | ||||||
|  |         expect(edges[0].start).toBe('A'); | ||||||
|  |         expect(edges[0].end).toBe('B'); | ||||||
|  |         expect(edges[0].type).toBe('arrow'); | ||||||
|  |         expect(edges[0].text).toBe(''); | ||||||
|  |     }); | ||||||
|  |     it('should handle statements ending without semicolon',function(){ | ||||||
|  |         var res = flow.parser.parse('graph TD\nA-->B\nB-->C'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         var vert = flow.parser.yy.getVertices(); | ||||||
|  |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |         expect(vert['A'].id).toBe('A'); | ||||||
|  |         expect(vert['B'].id).toBe('B'); | ||||||
|  |         expect(edges.length).toBe(2); | ||||||
|  |         expect(edges[1].start).toBe('B'); | ||||||
|  |         expect(edges[1].end).toBe('C'); | ||||||
|  |         expect(edges[0].type).toBe('arrow'); | ||||||
|  |         expect(edges[0].text).toBe(''); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     it('should handle a comments',function(){ |     it('should handle a comments',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;\n%% CComment\n A-->B;'); |         var res = flow.parser.parse('graph TD;\n%% CComment\n A-->B;'); | ||||||
|  |  | ||||||
| @@ -95,7 +204,7 @@ describe('when parsing ',function(){ | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('it should handle a trailing whitespaces after statememnts',function(){ |     it('it should handle a trailing whitespaces after statememnts',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;\n\n\n %% CComment\n A-->B; \nB-->C;'); |         var res = flow.parser.parse('graph TD;\n\n\n %% CComment\n A-->B; \n B-->C;'); | ||||||
|  |  | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
| @@ -139,76 +248,341 @@ describe('when parsing ',function(){ | |||||||
|  |  | ||||||
|         expect(edges[0].type).toBe('arrow_circle'); |         expect(edges[0].type).toBe('arrow_circle'); | ||||||
|     }); |     }); | ||||||
|  |     it('should handle subgraphs',function(){ | ||||||
|     it('should handle text on edges without space',function(){ |         var res = flow.parser.parse('graph TD;A-->B;subgraph myTitle;c-->d;end;'); | ||||||
|         var res = flow.parser.parse('graph TD;A--x|textNoSpace|B;'); |  | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
|         var edges = flow.parser.yy.getEdges(); |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|         expect(edges[0].type).toBe('arrow_cross'); |         expect(edges[0].type).toBe('arrow'); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle text on edges without space and space between vertices and link',function(){ |     it('should handle subgraphs',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;A --x|textNoSpace| B;'); |         var res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-->d\nend\n'); | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
|         var edges = flow.parser.yy.getEdges(); |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|         expect(edges[0].type).toBe('arrow_cross'); |         expect(edges[0].type).toBe('arrow'); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle text on edges with space',function(){ |     it('should handle subgraphs',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;A--x|text including space|B;'); |         var res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-->d\nend;'); | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
|         var edges = flow.parser.yy.getEdges(); |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|         expect(edges[0].type).toBe('arrow_cross'); |         expect(edges[0].type).toBe('arrow'); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle text on edges with space',function(){ |     it('should handle subgraphs',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;A--x|text with / should work|B;'); |         var res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-- text -->d\nd-->e\n end;'); | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
|         var edges = flow.parser.yy.getEdges(); |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|         expect(edges[0].text).toBe('text with / should work'); |         expect(edges[0].type).toBe('arrow'); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle text on edges with space CAPS',function(){ |     it('should handle classDefs with style in classes',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;A--x|text including CAPS space|B;'); |         var res = flow.parser.parse('graph TD\nA-->B\nclassDef exClass font-style:bold;'); | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
|         var edges = flow.parser.yy.getEdges(); |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|         expect(edges[0].type).toBe('arrow_cross'); |         expect(edges[0].type).toBe('arrow'); | ||||||
|     }); |     }); | ||||||
|     it('should handle text on edges with space dir',function(){ |  | ||||||
|         var res = flow.parser.parse('graph TD;A--x|text including URL space|B;'); |     it('should handle classDefs with % in classes',function(){ | ||||||
|  |         var res = flow.parser.parse('graph TD\nA-->B\nclassDef exClass fill:#f96,stroke:#333,stroke-width:4px,font-size:50%,font-style:bold;'); | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
|         var edges = flow.parser.yy.getEdges(); |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|         expect(edges[0].type).toBe('arrow_cross'); |         expect(edges[0].type).toBe('arrow'); | ||||||
|         expect(edges[0].text).toBe('text including URL space'); |  | ||||||
|  |  | ||||||
|     }); |     }); | ||||||
|     it('should handle text on edges with graph keyword',function(){ |  | ||||||
|         var res = flow.parser.parse('graph TD;A--x|text including graph space|B;'); |     it('should handle style definitons with more then 1 digit in a row',function(){ | ||||||
|  |         var res = flow.parser.parse('graph TD\n' + | ||||||
|  |         'A-->B1\n' + | ||||||
|  |         'A-->B2\n' + | ||||||
|  |         'A-->B3\n' + | ||||||
|  |         'A-->B4\n' + | ||||||
|  |         'A-->B5\n' + | ||||||
|  |         'A-->B6\n' + | ||||||
|  |         'A-->B7\n' + | ||||||
|  |         'A-->B8\n' + | ||||||
|  |         'A-->B9\n' + | ||||||
|  |         'A-->B10\n' + | ||||||
|  |         'A-->B11\n' + | ||||||
|  |         'linkStyle 10 stroke-width:1px;'); | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
|         var edges = flow.parser.yy.getEdges(); |         var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|         expect(edges[0].text).toBe('text including graph space'); |  | ||||||
|  |         expect(edges[0].type).toBe('arrow'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe("it should handle text on edges",function(){ | ||||||
|  |         it('it should handle text without space',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A--x|textNoSpace|B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should handle  with space',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A--x|text including space|B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('it should handle text with /',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A--x|text with / should work|B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].text).toBe('text with / should work'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('it should handle space and space between vertices and link',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A --x|textNoSpace| B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should handle space and CAPS',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A--x|text including CAPS space|B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should handle space and dir',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A--x|text including URL space|B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |             expect(edges[0].text).toBe('text including URL space'); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should handle space and dir (TD)',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A--x|text including R TD space|B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |             expect(edges[0].text).toBe('text including R TD space'); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |         it('should handle `',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A--x|text including `|B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |             expect(edges[0].text).toBe('text including `'); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |         it('should handle keywords',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A--x|text including graph space|B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |             expect(edges[0].text).toBe('text including graph space'); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     describe("it should handle new line type notation",function() { | ||||||
|  |         it('it should handle regular lines', function () { | ||||||
|  |             var res = flow.parser.parse('graph TD;A-->B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |             expect(edges[0].stroke).toBe('normal'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle dotted lines', function () { | ||||||
|  |             var res = flow.parser.parse('graph TD;A-.->B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].stroke).toBe('dotted'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle dotted lines', function () { | ||||||
|  |             var res = flow.parser.parse('graph TD;A==>B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].stroke).toBe('thick'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle text on lines', function () { | ||||||
|  |             var res = flow.parser.parse('graph TD;A-- test text with == -->B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].stroke).toBe('normal'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle text on lines', function () { | ||||||
|  |             var res = flow.parser.parse('graph TD;A-. test text with == .->B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].stroke).toBe('dotted'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle text on lines', function () { | ||||||
|  |             var res = flow.parser.parse('graph TD;A== test text with -- ==>B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].stroke).toBe('thick'); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe("it should handle text on edges using the new notation",function(){ | ||||||
|  |         it('it should handle text without space',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-- textNoSpace --xB;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('it should handle text with multiple leading space',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A--    textNoSpace --xB;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         it('should handle  with space',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-- text including space --xB;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('it should handle text with /',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A -- text with / should work --x B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].text).toBe('text with / should work'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('it should handle space and space between vertices and link',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A -- textNoSpace --x B;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should handle space and CAPS',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-- text including CAPS space --xB;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should handle space and dir',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-- text including URL space --xB;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |             expect(edges[0].text).toBe('text including URL space'); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should handle space and dir (TD)',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-- text including R TD space --xB;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             expect(edges[0].type).toBe('arrow_cross'); | ||||||
|  |             expect(edges[0].text).toBe('text including R TD space'); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |         it('should handle keywords',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-- text including graph space --xB;'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |             expect(edges[0].text).toBe('text including graph space'); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     it('should handle multi-line text',function(){ |     it('should handle multi-line text',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;A--o|text space|B;\n B-->|more text with space|C;'); |         var res = flow.parser.parse('graph TD;A--o|text space|B;\n B-->|more text with space|C;'); | ||||||
|  |  | ||||||
| @@ -229,6 +603,7 @@ describe('when parsing ',function(){ | |||||||
|         expect(edges[1].text).toBe('more text with space'); |         expect(edges[1].text).toBe('more text with space'); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |  | ||||||
|     it('should handle multiple edges',function(){ |     it('should handle multiple edges',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;A---|This is the 123 s text|B;\nA---|This is the second edge|B;'); |         var res = flow.parser.parse('graph TD;A---|This is the 123 s text|B;\nA---|This is the second edge|B;'); | ||||||
|         var vert = flow.parser.yy.getVertices(); |         var vert = flow.parser.yy.getVertices(); | ||||||
| @@ -314,60 +689,79 @@ describe('when parsing ',function(){ | |||||||
|         expect(edges[0].text).toBe(',.?!+-*'); |         expect(edges[0].text).toBe(',.?!+-*'); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     describe("it should handle text in vertices, ",function(){ | ||||||
|  |  | ||||||
|     it('should handle text in vertices with space',function(){ |         it('it should handle space',function(){ | ||||||
|         var res = flow.parser.parse('graph TD;A-->C(Chimpansen hoppar);'); |             var res = flow.parser.parse('graph TD;A-->C(Chimpansen hoppar);'); | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |             var vert = flow.parser.yy.getVertices(); | ||||||
|         var edges = flow.parser.yy.getEdges(); |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|         expect(vert['C'].type).toBe('round'); |             expect(vert['C'].type).toBe('round'); | ||||||
|         expect(vert['C'].text).toBe('Chimpansen hoppar'); |             expect(vert['C'].text).toBe('Chimpansen hoppar'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle åäö and minus',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |             expect(vert['C'].type).toBe('diamond'); | ||||||
|  |             expect(vert['C'].text).toBe('Chimpansen hoppar åäö-ÅÄÖ'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('it should handle with åäö, minus and space and br',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-->C(Chimpansen hoppar åäö  <br> -  ÅÄÖ);'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |             expect(vert['C'].type).toBe('round'); | ||||||
|  |             expect(vert['C'].text).toBe('Chimpansen hoppar åäö  <br> -  ÅÄÖ'); | ||||||
|  |         }); | ||||||
|  |         xit('it should handle åäö, minus and space and br',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD; A[Object(foo,bar)]-->B(Thing);'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |             expect(vert['C'].type).toBe('round'); | ||||||
|  |             expect(vert['C'].text).toBe(' A[Object(foo,bar)]-->B(Thing);'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle unicode chars',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-->C(Начало);'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |  | ||||||
|  |             expect(vert['C'].text).toBe('Начало'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle backslask',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-->C(c:\\windows);'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |  | ||||||
|  |             expect(vert['C'].text).toBe('c:\\windows'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle CAPS',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-->C(some CAPS);'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |             expect(vert['C'].type).toBe('round'); | ||||||
|  |             expect(vert['C'].text).toBe('some CAPS'); | ||||||
|  |         }); | ||||||
|  |         it('it should handle directions',function(){ | ||||||
|  |             var res = flow.parser.parse('graph TD;A-->C(some URL);'); | ||||||
|  |  | ||||||
|  |             var vert = flow.parser.yy.getVertices(); | ||||||
|  |             var edges = flow.parser.yy.getEdges(); | ||||||
|  |  | ||||||
|  |             expect(vert['C'].type).toBe('round'); | ||||||
|  |             expect(vert['C'].text).toBe('some URL'); | ||||||
|  |         }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle text in vertices with åäö and minus',function(){ |  | ||||||
|         var res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};'); |  | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |  | ||||||
|         var edges = flow.parser.yy.getEdges(); |  | ||||||
|  |  | ||||||
|         expect(vert['C'].type).toBe('diamond'); |  | ||||||
|         expect(vert['C'].text).toBe('Chimpansen hoppar åäö-ÅÄÖ'); |  | ||||||
|     }); |  | ||||||
|     it('should handle text in vertices with åäö, minus and space and br',function(){ |  | ||||||
|         var res = flow.parser.parse('graph TD;A-->C(Chimpansen hoppar åäö  <br> -  ÅÄÖ);'); |  | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |  | ||||||
|         var edges = flow.parser.yy.getEdges(); |  | ||||||
|  |  | ||||||
|         expect(vert['C'].type).toBe('round'); |  | ||||||
|         expect(vert['C'].text).toBe('Chimpansen hoppar åäö  <br> -  ÅÄÖ'); |  | ||||||
|     }); |  | ||||||
|     it('should handle text in vertices with unicode chars',function(){ |  | ||||||
|         var res = flow.parser.parse('graph TD;A-->C(Начало);'); |  | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |  | ||||||
|  |  | ||||||
|         expect(vert['C'].text).toBe('Начало'); |  | ||||||
|     }); |  | ||||||
|     it('should handle text in vertices with CAPS',function(){ |  | ||||||
|         var res = flow.parser.parse('graph TD;A-->C(some CAPS);'); |  | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |  | ||||||
|         var edges = flow.parser.yy.getEdges(); |  | ||||||
|  |  | ||||||
|         expect(vert['C'].type).toBe('round'); |  | ||||||
|         expect(vert['C'].text).toBe('some CAPS'); |  | ||||||
|     }); |  | ||||||
|     it('should handle text in vertices with directions',function(){ |  | ||||||
|         var res = flow.parser.parse('graph TD;A-->C(some URL);'); |  | ||||||
|  |  | ||||||
|         var vert = flow.parser.yy.getVertices(); |  | ||||||
|         var edges = flow.parser.yy.getEdges(); |  | ||||||
|  |  | ||||||
|         expect(vert['C'].type).toBe('round'); |  | ||||||
|         expect(vert['C'].text).toBe('some URL'); |  | ||||||
|     }); |  | ||||||
|     it('should handle a single node',function(){ |     it('should handle a single node',function(){ | ||||||
|         // Silly but syntactically correct |         // Silly but syntactically correct | ||||||
|         var res = flow.parser.parse('graph TD;A;'); |         var res = flow.parser.parse('graph TD;A;'); | ||||||
|   | |||||||
| @@ -14,9 +14,19 @@ | |||||||
| %% | %% | ||||||
|  |  | ||||||
| [\n]+             return 'NL'; | [\n]+             return 'NL'; | ||||||
|  | [\-][x]			  { return 'SOLID_CROSS';} | ||||||
|  | [\-][\-][x]		  { return 'DOTTED_CROSS';} | ||||||
|  | [\-][>][>]		  	  { return 'SOLID_ARROW';} | ||||||
|  | [\-][\-][>][>]		  { return 'DOTTED_ARROW';} | ||||||
| \s+               /* skip whitespace */ | \s+               /* skip whitespace */ | ||||||
| \#[^\n]*          /* skip comments */ | \#[^\n]*          /* skip comments */ | ||||||
|  | \%%[^\n]*          /* skip comments */ | ||||||
| "participant"     return 'participant'; | "participant"     return 'participant'; | ||||||
|  | "opt"     		  return 'opt'; | ||||||
|  | "loop"     		  return 'loop'; | ||||||
|  | "alt"     		  return 'alt'; | ||||||
|  | "else"     		  return 'else'; | ||||||
|  | "end"     		  return 'end'; | ||||||
| "left of"         return 'left_of'; | "left of"         return 'left_of'; | ||||||
| "right of"        return 'right_of'; | "right of"        return 'right_of'; | ||||||
| "over"            return 'over'; | "over"            return 'over'; | ||||||
| @@ -24,47 +34,79 @@ | |||||||
| "title"           return 'title'; | "title"           return 'title'; | ||||||
| "sequenceDiagram" return 'SD'; | "sequenceDiagram" return 'SD'; | ||||||
| ","               return ','; | ","               return ','; | ||||||
| [^\->:\n,]+       return 'ACTOR'; | ";"               return 'NL'; | ||||||
| "--"              return 'DOTLINE'; | [^\->:\n,;]+       return 'ACTOR'; | ||||||
| "-"               return 'LINE'; | "->"		      return 'SOLID_OPEN_ARROW'; | ||||||
| ">>"              return 'OPENARROW'; | "-->"		 	  return 'DOTTED_OPEN_ARROW'; | ||||||
| ">"               return 'ARROW'; | "->>"			  return 'SOLID_ARROW'; | ||||||
| :[^#\n]+          return 'MESSAGE'; | "-->>"            return 'DOTTED_ARROW'; | ||||||
|  | ":"[^#\n;]+        return 'TXT'; | ||||||
| <<EOF>>           return 'EOF'; | <<EOF>>           return 'EOF'; | ||||||
| .                 return 'INVALID'; | .                 return 'INVALID'; | ||||||
|  |  | ||||||
| /lex | /lex | ||||||
|  |  | ||||||
|  | %left '^' | ||||||
|  |  | ||||||
| %start start | %start start | ||||||
|  |  | ||||||
| %% /* language grammar */ | %% /* language grammar */ | ||||||
|  |  | ||||||
| start | start | ||||||
| 	: SD document 'EOF' { return yy; } | 	: SD document 'EOF' { yy.apply($2);return $2; } | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| document | document | ||||||
| 	: /* empty */ | 	: /* empty */ { $$ = [] } | ||||||
| 	| document line | 	| document line {$1.push($2);$$ = $1} | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| line | line | ||||||
| 	: statement { } | 	: SPACE statement { $$ = $2 } | ||||||
| 	| 'NL' | 	| statement { $$ = $1 } | ||||||
|  | 	| NL { $$=[];} | ||||||
|  | 	| EOF { $$=[];} | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| statement | statement | ||||||
| 	: 'participant' actor  { $$='actor'; } | 	: 'participant' actor 'NL' {$$=$2;} | ||||||
| 	| signal               { $$='signal'; } | 	| signal 'NL' | ||||||
| 	| note_statement       { $$='note';  } | 	| note_statement 'NL' | ||||||
| 	| 'title' message      { yy.setTitle($2);  } | 	| 'title' SPACE text 'NL' | ||||||
|  | 	| 'loop' actor document end | ||||||
|  | 	{ | ||||||
|  | 		$3.unshift({type: 'loopStart', loopText:$2.actor, signalType: yy.LINETYPE.LOOP_START}); | ||||||
|  | 		$3.push({type: 'loopEnd', loopText:$2, signalType: yy.LINETYPE.LOOP_END}); | ||||||
|  | 		$$=$3;} | ||||||
|  | 	| opt actor document end | ||||||
|  | 	{ | ||||||
|  | 		$3.unshift({type: 'optStart', optText:$2.actor, signalType: yy.LINETYPE.OPT_START}); | ||||||
|  | 		$3.push({type: 'optEnd', optText:$2.actor, signalType: yy.LINETYPE.OPT_END}); | ||||||
|  | 		$$=$3;} | ||||||
|  | 	| alt actor document else actor document end | ||||||
|  | 	{ | ||||||
|  | 		// Alt start | ||||||
|  | 		$3.unshift({type: 'altStart', altText:$2.actor, signalType: yy.LINETYPE.ALT_START}); | ||||||
|  | 		// Content in alt is already in $3 | ||||||
|  | 		// Else | ||||||
|  | 		$3.push({type: 'else', altText:$5.actor, signalType: yy.LINETYPE.ALT_ELSE}); | ||||||
|  | 		// Content in other alt | ||||||
|  | 		$3 = $3.concat($6); | ||||||
|  | 		// End | ||||||
|  | 		$3.push({type: 'altEnd', signalType: yy.LINETYPE.ALT_END}); | ||||||
|  |  | ||||||
|  | 		$$=$3;} | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| note_statement | note_statement | ||||||
| 	: 'note' placement actor message   { $$ = yy.addNote($3, $2, $4); } | 	: 'note' placement actor text2 {$$=[$3,{type:'addNote', placement:$2, actor:$3.actor, text:$4}];} | ||||||
| 	| 'note' 'over' actor_pair message { $$ = yy.addNote($3, yy.PLACEMENT.OVER, $4); } | 	| 'note' 'over' spaceList actor_pair actor | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
|  | spaceList | ||||||
|  |     : SPACE spaceList | ||||||
|  |     | SPACE | ||||||
|  |     ; | ||||||
| actor_pair | actor_pair | ||||||
| 	: actor             { $$ = $1; } | 	: actor             { $$ = $1; } | ||||||
| 	| actor ',' actor   { $$ = [$1, $3]; } | 	| actor ',' actor   { $$ = [$1, $3]; } | ||||||
| @@ -76,32 +118,26 @@ placement | |||||||
| 	; | 	; | ||||||
|  |  | ||||||
| signal | signal | ||||||
| 	: actor signaltype actor message | 	: actor signaltype actor text2 | ||||||
| 	{ yy.addSignal($1, $3, $4, $2); } | 	{$$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]} | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
|  | actors: actors actor | ||||||
|  | 	  | actor | ||||||
|  | 	  ; | ||||||
| actor | actor | ||||||
| 	/*: ACTOR { $$ = yy.getActor($1); }*/ | 	: ACTOR {$$={type: 'addActor', actor:$1}} | ||||||
| 	: ACTOR { yy.addActor($1,$1,$1); } |  | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| signaltype | signaltype | ||||||
| 	: linetype arrowtype  { $$ = $1 | ($2 << 2); } | 	: SOLID_OPEN_ARROW  { $$ = yy.LINETYPE.SOLID_OPEN; } | ||||||
| 	| linetype            { $$ = $1; } | 	| 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; } | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
| linetype | text2: TXT {$$ = $1.substring(1).trim().replace(/\\n/gm, "\n");} ; | ||||||
| 	: LINE      { $$ = yy.LINETYPE.SOLID; } |  | ||||||
| 	| DOTLINE   { $$ = yy.LINETYPE.DOTTED; } |  | ||||||
| 	; |  | ||||||
|  |  | ||||||
| arrowtype |  | ||||||
| 	: ARROW     { $$ = yy.ARROWTYPE.FILLED; } |  | ||||||
| 	| OPENARROW { $$ = yy.ARROWTYPE.OPEN; } |  | ||||||
| 	; |  | ||||||
|  |  | ||||||
| message |  | ||||||
| 	: MESSAGE { $$ = $1.substring(1).trim().replace(/\\n/gm, "\n"); } |  | ||||||
| 	; |  | ||||||
|  |  | ||||||
| %% | %% | ||||||
| @@ -72,81 +72,107 @@ | |||||||
|   } |   } | ||||||
| */ | */ | ||||||
| var parser = (function(){ | 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,14,16,24],$V1=[1,14],$V2=[1,17],$V3=[24,29,30]; | 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,15,17,19,20,22,33],$V1=[2,2],$V2=[1,6],$V3=[1,8],$V4=[1,9],$V5=[1,12],$V6=[1,13],$V7=[1,14],$V8=[1,15],$V9=[1,17],$Va=[1,18],$Vb=[2,7],$Vc=[6,8,10,11,15,17,18,19,20,21,22,33],$Vd=[6,8,10,11,15,17,18,19,20,22,33],$Ve=[1,46],$Vf=[1,49],$Vg=[1,53]; | ||||||
| var parser = {trace: function trace() { }, | var parser = {trace: function trace() { }, | ||||||
| yy: {}, | yy: {}, | ||||||
| symbols_: {"error":2,"start":3,"SD":4,"document":5,"EOF":6,"line":7,"statement":8,"NL":9,"participant":10,"actor":11,"signal":12,"note_statement":13,"title":14,"message":15,"note":16,"placement":17,"over":18,"actor_pair":19,",":20,"left_of":21,"right_of":22,"signaltype":23,"ACTOR":24,"linetype":25,"arrowtype":26,"LINE":27,"DOTLINE":28,"ARROW":29,"OPENARROW":30,"MESSAGE":31,"$accept":0,"$end":1}, | symbols_: {"error":2,"start":3,"SD":4,"document":5,"EOF":6,"line":7,"SPACE":8,"statement":9,"NL":10,"participant":11,"actor":12,"signal":13,"note_statement":14,"title":15,"text":16,"loop":17,"end":18,"opt":19,"alt":20,"else":21,"note":22,"placement":23,"text2":24,"over":25,"spaceList":26,"actor_pair":27,",":28,"left_of":29,"right_of":30,"signaltype":31,"actors":32,"ACTOR":33,"SOLID_OPEN_ARROW":34,"DOTTED_OPEN_ARROW":35,"SOLID_ARROW":36,"DOTTED_ARROW":37,"SOLID_CROSS":38,"DOTTED_CROSS":39,"TXT":40,"$accept":0,"$end":1}, | ||||||
| terminals_: {2:"error",4:"SD",6:"EOF",9:"NL",10:"participant",14:"title",16:"note",18:"over",20:",",21:"left_of",22:"right_of",24:"ACTOR",27:"LINE",28:"DOTLINE",29:"ARROW",30:"OPENARROW",31:"MESSAGE"}, | terminals_: {2:"error",4:"SD",6:"EOF",8:"SPACE",10:"NL",11:"participant",15:"title",16:"text",17:"loop",18:"end",19:"opt",20:"alt",21:"else",22:"note",25:"over",28:",",29:"left_of",30:"right_of",33:"ACTOR",34:"SOLID_OPEN_ARROW",35:"DOTTED_OPEN_ARROW",36:"SOLID_ARROW",37:"DOTTED_ARROW",38:"SOLID_CROSS",39:"DOTTED_CROSS",40:"TXT"}, | ||||||
| productions_: [0,[3,3],[5,0],[5,2],[7,1],[7,1],[8,2],[8,1],[8,1],[8,2],[13,4],[13,4],[19,1],[19,3],[17,1],[17,1],[12,4],[11,1],[23,2],[23,1],[25,1],[25,1],[26,1],[26,1],[15,1]], | productions_: [0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,3],[9,2],[9,2],[9,4],[9,4],[9,4],[9,7],[14,4],[14,5],[26,2],[26,1],[27,1],[27,3],[23,1],[23,1],[13,4],[32,2],[32,1],[12,1],[31,1],[31,1],[31,1],[31,1],[31,1],[31,1],[24,1]], | ||||||
| performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) { | performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) { | ||||||
| /* this == yyval */ | /* this == yyval */ | ||||||
|  |  | ||||||
| var $0 = $$.length - 1; | var $0 = $$.length - 1; | ||||||
| switch (yystate) { | switch (yystate) { | ||||||
| case 1: | case 1: | ||||||
|  return yy;  |  yy.apply($$[$0-1]);return $$[$0-1];  | ||||||
| break; | break; | ||||||
| case 4: | case 2: | ||||||
|   |  this.$ = []  | ||||||
| break; | break; | ||||||
| case 6: | case 3: | ||||||
|  this.$='actor';  | $$[$0-1].push($$[$0]);this.$ = $$[$0-1] | ||||||
| break; | break; | ||||||
| case 7: | case 4: case 5: | ||||||
|  this.$='signal';  |  this.$ = $$[$0]  | ||||||
|  | break; | ||||||
|  | case 6: case 7: | ||||||
|  |  this.$=[]; | ||||||
| break; | break; | ||||||
| case 8: | case 8: | ||||||
|  this.$='note';   | this.$=$$[$0-1]; | ||||||
| break; | break; | ||||||
| case 9: | case 12: | ||||||
|  yy.setTitle($$[$0]);   |  | ||||||
| break; | 		$$[$0-1].unshift({type: 'loopStart', loopText:$$[$0-2].actor, signalType: yy.LINETYPE.LOOP_START}); | ||||||
| case 10: | 		$$[$0-1].push({type: 'loopEnd', loopText:$$[$0-2], signalType: yy.LINETYPE.LOOP_END}); | ||||||
|  this.$ = yy.addNote($$[$0-1], $$[$0-2], $$[$0]);  | 		this.$=$$[$0-1]; | ||||||
| break; |  | ||||||
| case 11: |  | ||||||
|  this.$ = yy.addNote($$[$0-1], yy.PLACEMENT.OVER, $$[$0]);  |  | ||||||
| break; |  | ||||||
| case 12: case 19: |  | ||||||
|  this.$ = $$[$0];  |  | ||||||
| break; | break; | ||||||
| case 13: | case 13: | ||||||
|  this.$ = [$$[$0-2], $$[$0]];  |  | ||||||
|  | 		$$[$0-1].unshift({type: 'optStart', optText:$$[$0-2].actor, signalType: yy.LINETYPE.OPT_START}); | ||||||
|  | 		$$[$0-1].push({type: 'optEnd', optText:$$[$0-2].actor, signalType: yy.LINETYPE.OPT_END}); | ||||||
|  | 		this.$=$$[$0-1]; | ||||||
| break; | break; | ||||||
| case 14: | case 14: | ||||||
|  this.$ = yy.PLACEMENT.LEFTOF;  |  | ||||||
|  | 		// Alt start | ||||||
|  | 		$$[$0-4].unshift({type: 'altStart', altText:$$[$0-5].actor, signalType: yy.LINETYPE.ALT_START}); | ||||||
|  | 		// Content in alt is already in $$[$0-4] | ||||||
|  | 		// Else | ||||||
|  | 		$$[$0-4].push({type: 'else', altText:$$[$0-2].actor, 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; | break; | ||||||
| case 15: | case 15: | ||||||
|  this.$ = yy.PLACEMENT.RIGHTOF;  | this.$=[$$[$0-1],{type:'addNote', placement:$$[$0-2], actor:$$[$0-1].actor, text:$$[$0]}]; | ||||||
| break; | break; | ||||||
| case 16: | case 19: | ||||||
|  yy.addSignal($$[$0-3], $$[$0-1], $$[$0], $$[$0-2]);  |  this.$ = $$[$0];  | ||||||
| break; |  | ||||||
| case 17: |  | ||||||
|  yy.addActor($$[$0],$$[$0],$$[$0]);  |  | ||||||
| break; |  | ||||||
| case 18: |  | ||||||
|  this.$ = $$[$0-1] | ($$[$0] << 2);  |  | ||||||
| break; | break; | ||||||
| case 20: | case 20: | ||||||
|  this.$ = yy.LINETYPE.SOLID;  |  this.$ = [$$[$0-2], $$[$0]];  | ||||||
| break; | break; | ||||||
| case 21: | case 21: | ||||||
|  this.$ = yy.LINETYPE.DOTTED;  |  this.$ = yy.PLACEMENT.LEFTOF;  | ||||||
| break; | break; | ||||||
| case 22: | case 22: | ||||||
|  this.$ = yy.ARROWTYPE.FILLED;  |  this.$ = yy.PLACEMENT.RIGHTOF;  | ||||||
| break; | break; | ||||||
| case 23: | case 23: | ||||||
|  this.$ = yy.ARROWTYPE.OPEN;  | this.$ = [$$[$0-3],$$[$0-1],{type: 'addMessage', from:$$[$0-3].actor, to:$$[$0-1].actor, signalType:$$[$0-2], msg:$$[$0]}] | ||||||
| break; | break; | ||||||
| case 24: | case 26: | ||||||
|  this.$ = $$[$0].substring(1).trim().replace(/\\n/gm, "\n");  | this.$={type: 'addActor', actor:$$[$0]} | ||||||
|  | break; | ||||||
|  | case 27: | ||||||
|  |  this.$ = yy.LINETYPE.SOLID_OPEN;  | ||||||
|  | break; | ||||||
|  | case 28: | ||||||
|  |  this.$ = yy.LINETYPE.DOTTED_OPEN;  | ||||||
|  | break; | ||||||
|  | case 29: | ||||||
|  |  this.$ = yy.LINETYPE.SOLID;  | ||||||
|  | break; | ||||||
|  | case 30: | ||||||
|  |  this.$ = yy.LINETYPE.DOTTED;  | ||||||
|  | break; | ||||||
|  | case 31: | ||||||
|  |  this.$ = yy.LINETYPE.SOLID_CROSS;  | ||||||
|  | break; | ||||||
|  | case 32: | ||||||
|  |  this.$ = yy.LINETYPE.DOTTED_CROSS;  | ||||||
|  | break; | ||||||
|  | case 33: | ||||||
|  | this.$ = $$[$0].substring(1).trim().replace(/\\n/gm, "\n"); | ||||||
| break; | 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:12,12:9,13:10,14:[1,11],16:[1,13],24:$V1},{1:[2,1]},o($V0,[2,3]),o($V0,[2,4]),o($V0,[2,5]),{11:15,24:$V1},o($V0,[2,7]),o($V0,[2,8]),{15:16,31:$V2},{23:18,25:19,27:[1,20],28:[1,21]},{17:22,18:[1,23],21:[1,24],22:[1,25]},o([6,9,10,14,16,20,24,27,28,31],[2,17]),o($V0,[2,6]),o($V0,[2,9]),o($V0,[2,24]),{11:26,24:$V1},{24:[2,19],26:27,29:[1,28],30:[1,29]},o($V3,[2,20]),o($V3,[2,21]),{11:30,24:$V1},{11:32,19:31,24:$V1},{24:[2,14]},{24:[2,15]},{15:33,31:$V2},{24:[2,18]},{24:[2,22]},{24:[2,23]},{15:34,31:$V2},{15:35,31:$V2},{20:[1,36],31:[2,12]},o($V0,[2,16]),o($V0,[2,10]),o($V0,[2,11]),{11:37,24:$V1},{31:[2,13]}], | table: [{3:1,4:[1,2]},{1:[3]},o($V0,$V1,{5:3}),{6:[1,4],7:5,8:$V2,9:7,10:$V3,11:$V4,12:16,13:10,14:11,15:$V5,17:$V6,19:$V7,20:$V8,22:$V9,33:$Va},o($V0,$Vb,{1:[2,1]}),o($Vc,[2,3]),{9:19,11:$V4,12:16,13:10,14:11,15:$V5,17:$V6,19:$V7,20:$V8,22:$V9,33:$Va},o($Vc,[2,5]),o($Vc,[2,6]),{12:20,33:$Va},{10:[1,21]},{10:[1,22]},{8:[1,23]},{12:24,33:$Va},{12:25,33:$Va},{12:26,33:$Va},{31:27,34:[1,28],35:[1,29],36:[1,30],37:[1,31],38:[1,32],39:[1,33]},{23:34,25:[1,35],29:[1,36],30:[1,37]},o([6,8,10,11,15,17,18,19,20,21,22,28,33,34,35,36,37,38,39,40],[2,26]),o($Vc,[2,4]),{10:[1,38]},o($Vc,[2,9]),o($Vc,[2,10]),{16:[1,39]},o($Vd,$V1,{5:40}),o($Vd,$V1,{5:41}),o([6,8,10,11,15,17,19,20,21,22,33],$V1,{5:42}),{12:43,33:$Va},{33:[2,27]},{33:[2,28]},{33:[2,29]},{33:[2,30]},{33:[2,31]},{33:[2,32]},{12:44,33:$Va},{8:$Ve,26:45},{33:[2,21]},{33:[2,22]},o($Vc,[2,8]),{10:[1,47]},{6:$Vf,7:5,8:$V2,9:7,10:$V3,11:$V4,12:16,13:10,14:11,15:$V5,17:$V6,18:[1,48],19:$V7,20:$V8,22:$V9,33:$Va},{6:$Vf,7:5,8:$V2,9:7,10:$V3,11:$V4,12:16,13:10,14:11,15:$V5,17:$V6,18:[1,50],19:$V7,20:$V8,22:$V9,33:$Va},{6:$Vf,7:5,8:$V2,9:7,10:$V3,11:$V4,12:16,13:10,14:11,15:$V5,17:$V6,19:$V7,20:$V8,21:[1,51],22:$V9,33:$Va},{24:52,40:$Vg},{24:54,40:$Vg},{12:56,27:55,33:$Va},{8:$Ve,26:57,33:[2,18]},o($Vc,[2,11]),o($Vc,[2,12]),o($Vc,$Vb),o($Vc,[2,13]),{12:58,33:$Va},{10:[2,23]},{10:[2,33]},{10:[2,15]},{12:59,33:$Va},{28:[1,60],33:[2,19]},{33:[2,17]},o($Vd,$V1,{5:61}),{10:[2,16]},{12:62,33:$Va},{6:$Vf,7:5,8:$V2,9:7,10:$V3,11:$V4,12:16,13:10,14:11,15:$V5,17:$V6,18:[1,63],19:$V7,20:$V8,22:$V9,33:$Va},{33:[2,20]},o($Vc,[2,14])], | ||||||
| defaultActions: {4:[2,1],24:[2,14],25:[2,15],27:[2,18],28:[2,22],29:[2,23],37:[2,13]}, | defaultActions: {28:[2,27],29:[2,28],30:[2,29],31:[2,30],32:[2,31],33:[2,32],36:[2,21],37:[2,22],52:[2,23],53:[2,33],54:[2,15],57:[2,17],59:[2,16],62:[2,20]}, | ||||||
| parseError: function parseError(str, hash) { | parseError: function parseError(str, hash) { | ||||||
|     if (hash.recoverable) { |     if (hash.recoverable) { | ||||||
|         this.trace(str); |         this.trace(str); | ||||||
| @@ -621,48 +647,70 @@ performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { | |||||||
|  |  | ||||||
| var YYSTATE=YY_START; | var YYSTATE=YY_START; | ||||||
| switch($avoiding_name_collisions) { | switch($avoiding_name_collisions) { | ||||||
| case 0:return 9; | case 0:return 10; | ||||||
| break; | break; | ||||||
| case 1:/* skip whitespace */ | case 1: return 38; | ||||||
| break; | break; | ||||||
| case 2:/* skip comments */ | case 2: return 39; | ||||||
| break; | break; | ||||||
| case 3:return 10; | case 3: return 36; | ||||||
| break; | break; | ||||||
| case 4:return 21; | case 4: return 37; | ||||||
| break; | break; | ||||||
| case 5:return 22; | case 5:/* skip whitespace */ | ||||||
| break; | break; | ||||||
| case 6:return 18; | case 6:/* skip comments */ | ||||||
| break; | break; | ||||||
| case 7:return 16; | case 7:/* skip comments */ | ||||||
| break; | break; | ||||||
| case 8:return 14; | case 8:return 11; | ||||||
| break; | break; | ||||||
| case 9:return 4; | case 9:return 19; | ||||||
| break; | break; | ||||||
| case 10:return 20; | case 10:return 17; | ||||||
| break; | break; | ||||||
| case 11:return 24; | case 11:return 20; | ||||||
| break; | break; | ||||||
| case 12:return 28; | case 12:return 21; | ||||||
| break; | break; | ||||||
| case 13:return 27; | case 13:return 18; | ||||||
| break; | break; | ||||||
| case 14:return 30; | case 14:return 29; | ||||||
| break; | break; | ||||||
| case 15:return 29; | case 15:return 30; | ||||||
| break; | break; | ||||||
| case 16:return 31; | case 16:return 25; | ||||||
| break; | break; | ||||||
| case 17:return 6; | case 17:return 22; | ||||||
| break; | break; | ||||||
| case 18:return 'INVALID'; | case 18:return 15; | ||||||
|  | break; | ||||||
|  | case 19:return 4; | ||||||
|  | break; | ||||||
|  | case 20:return 28; | ||||||
|  | break; | ||||||
|  | case 21:return 10; | ||||||
|  | break; | ||||||
|  | case 22:return 33; | ||||||
|  | break; | ||||||
|  | case 23:return 34; | ||||||
|  | break; | ||||||
|  | case 24:return 35; | ||||||
|  | break; | ||||||
|  | case 25:return 36; | ||||||
|  | break; | ||||||
|  | case 26:return 37; | ||||||
|  | break; | ||||||
|  | case 27:return 40; | ||||||
|  | break; | ||||||
|  | case 28:return 6; | ||||||
|  | break; | ||||||
|  | case 29:return 'INVALID'; | ||||||
| break; | break; | ||||||
| } | } | ||||||
| }, | }, | ||||||
| rules: [/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:participant\b)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:over\b)/i,/^(?:note\b)/i,/^(?:title\b)/i,/^(?:sequenceDiagram\b)/i,/^(?:,)/i,/^(?:[^\->:\n,]+)/i,/^(?:--)/i,/^(?:-)/i,/^(?:>>)/i,/^(?:>)/i,/^(?:[^#\n]+)/i,/^(?:$)/i,/^(?:.)/i], | rules: [/^(?:[\n]+)/i,/^(?:[\-][x])/i,/^(?:[\-][\-][x])/i,/^(?:[\-][>][>])/i,/^(?:[\-][\-][>][>])/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:participant\b)/i,/^(?:opt\b)/i,/^(?:loop\b)/i,/^(?:alt\b)/i,/^(?:else\b)/i,/^(?:end\b)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:over\b)/i,/^(?:note\b)/i,/^(?:title\b)/i,/^(?:sequenceDiagram\b)/i,/^(?:,)/i,/^(?:;)/i,/^(?:[^\->:\n,;]+)/i,/^(?:->)/i,/^(?:-->)/i,/^(?:->>)/i,/^(?:-->>)/i,/^(?::[^#\n;]+)/i,/^(?:$)/i,/^(?:.)/i], | ||||||
| conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18],"inclusive":true}} | conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29],"inclusive":true}} | ||||||
| }); | }); | ||||||
| return lexer; | return lexer; | ||||||
| })(); | })(); | ||||||
|   | |||||||
| @@ -41,9 +41,21 @@ exports.clear = function(){ | |||||||
| }; | }; | ||||||
|  |  | ||||||
| exports.LINETYPE = { | exports.LINETYPE = { | ||||||
|     SOLID  : 0, |     SOLID       : 0, | ||||||
|     DOTTED : 1, |     DOTTED      : 1, | ||||||
|     NOTE   : 2 |     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 | ||||||
|  |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| exports.ARROWTYPE = { | exports.ARROWTYPE = { | ||||||
| @@ -61,10 +73,60 @@ exports.addNote = function (actor, placement, message){ | |||||||
|     var note = {actor:actor, placement: placement, message:message}; |     var note = {actor:actor, placement: placement, message:message}; | ||||||
|  |  | ||||||
|     notes.push(note); |     notes.push(note); | ||||||
|     messages.push({from:actor, to:actor, message:message, type:exports.LINETYPE.NOTE}); |     messages.push({from:actor, to:actor, message:message, type:exports.LINETYPE.NOTE, placement: placement}); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| exports.parseError = function(err, hash) { | exports.parseError = function(err, hash) { | ||||||
|     console.log('Syntax error:' + err); |     console.log('Syntax error:' + err); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.apply = function(param){ | ||||||
|  |     if(param instanceof Array ){ | ||||||
|  |         param.forEach(function(item){ | ||||||
|  |             exports.apply(item); | ||||||
|  |         }); | ||||||
|  |     } else { | ||||||
|  |         // console.log(param); | ||||||
|  |         switch(param.type){ | ||||||
|  |             case 'addActor': | ||||||
|  |                 exports.addActor(param.actor, param.actor, param.actor); | ||||||
|  |                 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': | ||||||
|  |                 //console.log('Loop text: ',param.loopText); | ||||||
|  |                 exports.addSignal(undefined, undefined, param.loopText, param.signalType); | ||||||
|  |                 //yy.addSignal(undefined, undefined, $2, yy.LINETYPE.LOOP_START); | ||||||
|  |                 break; | ||||||
|  |             case 'loopEnd': | ||||||
|  |                 exports.addSignal(undefined, undefined, undefined, param.signalType); | ||||||
|  |                 break; | ||||||
|  |             case 'optStart': | ||||||
|  |                 //console.log('Loop text: ',param.loopText); | ||||||
|  |                 exports.addSignal(undefined, undefined, param.optText, param.signalType); | ||||||
|  |                 //yy.addSignal(undefined, undefined, $2, yy.LINETYPE.LOOP_START); | ||||||
|  |                 break; | ||||||
|  |             case 'optEnd': | ||||||
|  |                 exports.addSignal(undefined, undefined, undefined, param.signalType); | ||||||
|  |                 break; | ||||||
|  |             case 'altStart': | ||||||
|  |                 //console.log('Loop text: ',param.loopText); | ||||||
|  |                 exports.addSignal(undefined, undefined, param.altText, param.signalType); | ||||||
|  |                 //yy.addSignal(undefined, undefined, $2, yy.LINETYPE.LOOP_START); | ||||||
|  |                 break; | ||||||
|  |             case 'else': | ||||||
|  |                 exports.addSignal(undefined, undefined, param.altText, param.signalType); | ||||||
|  |                 break; | ||||||
|  |             case 'altEnd': | ||||||
|  |                 exports.addSignal(undefined, undefined, undefined, param.signalType); | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // console.log('xxx',param); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| @@ -2,28 +2,9 @@ | |||||||
|  * Created by knut on 14-11-18. |  * Created by knut on 14-11-18. | ||||||
|  */ |  */ | ||||||
| var sq = require('./parser/sequenceDiagram').parser; | var sq = require('./parser/sequenceDiagram').parser; | ||||||
|  | var sd = require('./sequenceRenderer'); | ||||||
|  |  | ||||||
| //console.log(sq.parse('a12:d12\na24:d24')); | var str; | ||||||
|  |  | ||||||
| str = 'a12:d12\n\na24:d24'; |  | ||||||
| //console.log(str); |  | ||||||
| //console.log(sq.parse(str)); |  | ||||||
| //console.log(sq.parse('[]\n[]')); |  | ||||||
|  |  | ||||||
| str = 'bfs:queue\n\nbfs3:queue\n'; |  | ||||||
| str =  str + 'bfs:message=someNode.setLevel\n'; |  | ||||||
| str =  str + 'bfs:message2=someNode.setLevel2'; |  | ||||||
| //console.log(str); |  | ||||||
| //console.log(sq.parse(str)); |  | ||||||
|  |  | ||||||
| str = 'bfs:BFS\n'; |  | ||||||
| str = str + 'someNode:SomeNode\n'; |  | ||||||
| str = str + 'bfs:queue.new\n'; |  | ||||||
| str = str + 'bfs:someNode.setLevel'; |  | ||||||
| //console.log(str); |  | ||||||
| //console.log(sq.parse(str)); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| describe('when parsing a sequenceDiagram',function() { | describe('when parsing a sequenceDiagram',function() { | ||||||
|     var parseError; |     var parseError; | ||||||
|     beforeEach(function () { |     beforeEach(function () { | ||||||
| @@ -38,25 +19,655 @@ describe('when parsing a sequenceDiagram',function() { | |||||||
|  |  | ||||||
|     it('it should handle a sequenceDiagram defintion', function () { |     it('it should handle a sequenceDiagram defintion', function () { | ||||||
|         str = 'sequenceDiagram\n' + |         str = 'sequenceDiagram\n' + | ||||||
|         'Alice->Bob: Hello Bob, how are you?\n' + |         'Alice->Bob:Hello Bob, how are you?\n' + | ||||||
|         'Note right of Bob: Bob thinks\n' + |         'Note right of Bob: Bob thinks\n' + | ||||||
|         'Bob-->Alice: I am good thanks!\n'; |         'Bob-->Alice: I am good thanks!\n'; | ||||||
|  |  | ||||||
|         sq.parse(str); |         sq.parse(str); | ||||||
|         var actors = sq.yy.getActors(); |         var actors = sq.yy.getActors(); | ||||||
|         expect(actors.Alice).ToBdescription = 'Alice'; |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|         actors.Bob.description = 'Bob'; |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|         //console.log('actors'); |  | ||||||
|         //console.log(actors); |  | ||||||
|  |  | ||||||
|         var messages = sq.yy.getMessages(); |         var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|         expect(messages.length).toBe(3); |         expect(messages.length).toBe(3); | ||||||
|         //console.log('messages'); |  | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[2].from).toBe('Bob'); | ||||||
|  |     }); | ||||||
|  |     it('it should space in actor names', function () { | ||||||
|  |         str = 'sequenceDiagram\n' + | ||||||
|  |         'Alice->Bob:Hello Bob, how are - you?\n' + | ||||||
|  |         'Bob-->Alice: I am good thanks!\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(2); | ||||||
|  |  | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[1].from).toBe('Bob'); | ||||||
|  |     }); | ||||||
|  |     it('it should handle in async messages', function () { | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |         'Alice-xBob:Hello Bob, how are you?\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         //console.log(actors); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         expect(actors.Bob.description).toBe('Bob'); | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(1); | ||||||
|  |  | ||||||
|  |         expect(messages[0].type).toBe(sq.yy.LINETYPE.SOLID_CROSS); | ||||||
|  |     }); | ||||||
|  |     it('it should handle in async dotted messages', function () { | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |         'Alice--xBob:Hello Bob, how are you?\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         //console.log(actors); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         expect(actors.Bob.description).toBe('Bob'); | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(1); | ||||||
|  |  | ||||||
|  |         expect(messages[0].type).toBe(sq.yy.LINETYPE.DOTTED_CROSS); | ||||||
|  |     }); | ||||||
|  |     it('it should handle in arrow messages', function () { | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice->>Bob:Hello Bob, how are you?\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         expect(actors.Bob.description).toBe('Bob'); | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|         //console.log(messages); |         //console.log(messages); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(1); | ||||||
|  |  | ||||||
|  |         expect(messages[0].type).toBe(sq.yy.LINETYPE.SOLID); | ||||||
|  |     }); | ||||||
|  |     it('it should handle in arrow messages', function () { | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice-->>Bob:Hello Bob, how are you?\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         expect(actors.Bob.description).toBe('Bob'); | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |         //console.log(messages); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(1); | ||||||
|  |  | ||||||
|  |         expect(messages[0].type).toBe(sq.yy.LINETYPE.DOTTED); | ||||||
|  |     }); | ||||||
|  |     it('it should handle comments in a sequenceDiagram', function () { | ||||||
|  |         str = 'sequenceDiagram\n' + | ||||||
|  |         'Alice->Bob: Hello Bob, how are you?\n'+ | ||||||
|  |         '%% Comment\n' + | ||||||
|  |         'Note right of Bob: Bob thinks\n' + | ||||||
|  |         'Bob-->Alice: I am good thanks!\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(3); | ||||||
|  |  | ||||||
|         expect(messages[0].from).toBe('Alice'); |         expect(messages[0].from).toBe('Alice'); | ||||||
|         expect(messages[2].from).toBe('Bob'); |         expect(messages[2].from).toBe('Bob'); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     it('it should handle new lines in a sequenceDiagram', function () { | ||||||
|  |         str = 'sequenceDiagram\n' + | ||||||
|  |         'Alice->Bob: Hello Bob, how are you?\n\n' + | ||||||
|  |         '%% Comment\n' + | ||||||
|  |         'Note right of Bob: Bob thinks\n' + | ||||||
|  |         'Bob-->Alice: I am good thanks!\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(3); | ||||||
|  |  | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[2].from).toBe('Bob'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('it should handle one leading space in lines in a sequenceDiagram', function () { | ||||||
|  |         str = 'sequenceDiagram\n' + | ||||||
|  |         ' Alice->Bob: Hello Bob, how are you?\n\n' + | ||||||
|  |         '%% Comment\n' + | ||||||
|  |         'Note right of Bob: Bob thinks\n' + | ||||||
|  |         'Bob-->Alice: I am good thanks!\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(3); | ||||||
|  |  | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[2].from).toBe('Bob'); | ||||||
|  |     }); | ||||||
|  |     it('it should handle several leading spaces in lines in a sequenceDiagram', function () { | ||||||
|  |         str = 'sequenceDiagram\n' + | ||||||
|  |         '   Alice->Bob: Hello Bob, how are you?\n\n' + | ||||||
|  |         '%% Comment\n' + | ||||||
|  |         'Note right of Bob: Bob thinks\n' + | ||||||
|  |         'Bob-->Alice: I am good thanks!\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(3); | ||||||
|  |  | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[2].from).toBe('Bob'); | ||||||
|  |     }); | ||||||
|  |     it('it should handle several leading spaces in lines in a sequenceDiagram', function () { | ||||||
|  |         str = 'sequenceDiagram\n'+ | ||||||
|  |         'participant Alice\n'+ | ||||||
|  |         'participant Bob\n'+ | ||||||
|  |         'Alice->John: Hello John, how are you?\n'+ | ||||||
|  |         '    loop Healthcheck\n'+ | ||||||
|  |         'John->John: Fight against hypochondria\n'+ | ||||||
|  |         ' end\n'+ | ||||||
|  |         'Note right of John: Rational thoughts<br/>prevail...\n'+ | ||||||
|  |         '    John-->Alice: Great!\n'+ | ||||||
|  |         '    John->Bob: How about you?\n'+ | ||||||
|  |         'Bob-->John: Jolly good!\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(8); | ||||||
|  |  | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[2].from).toBe('John'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('it should handle loop statements a sequenceDiagram', function () { | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice->Bob: Hello Bob, how are you?\n\n' + | ||||||
|  |             '%% Comment\n' + | ||||||
|  |             'Note right of Bob: Bob thinks\n' + | ||||||
|  |             'loop Multiple happy responses\n\n' + | ||||||
|  |             'Bob-->Alice: I am good thanks!\n' + | ||||||
|  |             'end'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         //console.log(actors); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |         //console.log(messages); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(5); | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[1].from).toBe('Bob'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('it should handle opt statements a sequenceDiagram', function () { | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice->Bob: Hello Bob, how are you?\n\n' + | ||||||
|  |             '%% Comment\n' + | ||||||
|  |             'Note right of Bob: Bob thinks\n' + | ||||||
|  |             'opt Perhaps a happy response\n\n' + | ||||||
|  |             'Bob-->Alice: I am good thanks!\n' + | ||||||
|  |             'end'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         //console.log(actors); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |         //console.log(messages); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(5); | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[1].from).toBe('Bob'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |     it('it should handle opt statements a sequenceDiagram', function () { | ||||||
|  |         var str = 'sequenceDiagram;Alice->Bob: Hello Bob, how are you?;opt Perhaps a happy response;Bob-->Alice: I am good thanks!;end;'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |         //console.log(actors); | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |         //console.log(messages); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(4); | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[1].type).toBe(sq.yy.LINETYPE.OPT_START); | ||||||
|  |         expect(messages[2].from).toBe('Bob'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('it should handle alt statements a sequenceDiagram', function () { | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice->Bob: Hello Bob, how are you?\n\n' + | ||||||
|  |             '%% Comment\n' + | ||||||
|  |             'Note right of Bob: Bob thinks\n' + | ||||||
|  |             'alt isWell\n\n' + | ||||||
|  |             'Bob-->Alice: I am good thanks!\n' + | ||||||
|  |             'else isSick\n' + | ||||||
|  |             'Bob-->Alice: Feel sick...\n' + | ||||||
|  |             'end'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         var actors = sq.yy.getActors(); | ||||||
|  |  | ||||||
|  |         expect(actors.Alice.description).toBe('Alice'); | ||||||
|  |         actors.Bob.description = 'Bob'; | ||||||
|  |  | ||||||
|  |         var messages = sq.yy.getMessages(); | ||||||
|  |         //console.log(messages); | ||||||
|  |  | ||||||
|  |         expect(messages.length).toBe(7); | ||||||
|  |         expect(messages[0].from).toBe('Alice'); | ||||||
|  |         expect(messages[1].from).toBe('Bob'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     });}); | ||||||
|  |  | ||||||
|  | describe('when checking the bounds in a sequenceDiagram',function() { | ||||||
|  |     var parseError, _d3, conf; | ||||||
|  |     beforeEach(function () { | ||||||
|  |         sq.yy = require('./sequenceDb'); | ||||||
|  |         sq.yy.clear(); | ||||||
|  |         parseError = function(err, hash) { | ||||||
|  |             console.log('Syntax error:' + err); | ||||||
|  |             console.log(hash); | ||||||
|  |         }; | ||||||
|  |         sq.yy.parseError = parseError; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         conf = { | ||||||
|  |             diagramMarginX:50, | ||||||
|  |             diagramMarginY:10, | ||||||
|  |             actorMargin:50, | ||||||
|  |             width:150, | ||||||
|  |             // Height of actor boxes | ||||||
|  |             height:65, | ||||||
|  |             boxMargin:10, | ||||||
|  |             messageMargin:40, | ||||||
|  |             boxTextMargin:15, | ||||||
|  |             noteMargin:25 | ||||||
|  |         }; | ||||||
|  |         sd.setConf(conf); | ||||||
|  |     }); | ||||||
|  |     it('it should handle a simple bound call', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |  | ||||||
|  |         sd.bounds.insert(100,100,200,200); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(100); | ||||||
|  |         expect(bounds.starty).toBe(100); | ||||||
|  |         expect(bounds.stopx ).toBe(200); | ||||||
|  |         expect(bounds.stopy ).toBe(200); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |     it('it should handle an expanding bound', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |  | ||||||
|  |         sd.bounds.insert(100,100,200,200); | ||||||
|  |         sd.bounds.insert(25,50,300,400); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(25); | ||||||
|  |         expect(bounds.starty).toBe(50); | ||||||
|  |         expect(bounds.stopx ).toBe(300); | ||||||
|  |         expect(bounds.stopy ).toBe(400); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |     it('it should handle inserts within the bound without changing the outer bounds', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |  | ||||||
|  |         sd.bounds.insert(100,100,200,200); | ||||||
|  |         sd.bounds.insert(25,50,300,400); | ||||||
|  |         sd.bounds.insert(125,150,150,200); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(25); | ||||||
|  |         expect(bounds.starty).toBe(50); | ||||||
|  |         expect(bounds.stopx ).toBe(300); | ||||||
|  |         expect(bounds.stopy ).toBe(400); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('it should handle a loop without expanding the area', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |  | ||||||
|  |         sd.bounds.insert(25,50,300,400); | ||||||
|  |         sd.bounds.verticalPos  = 150; | ||||||
|  |         sd.bounds.newLoop(); | ||||||
|  |         sd.bounds.insert(125,150,150,200); | ||||||
|  |  | ||||||
|  |         var loop = sd.bounds.endLoop(); | ||||||
|  |  | ||||||
|  |         expect(loop.startx).toBe(125-conf.boxMargin); | ||||||
|  |         expect(loop.starty).toBe(150-conf.boxMargin); | ||||||
|  |         expect(loop.stopx ).toBe(150+conf.boxMargin); | ||||||
|  |         expect(loop.stopy ).toBe(200+conf.boxMargin); | ||||||
|  |  | ||||||
|  |         // Check bounds of first loop | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |  | ||||||
|  |         expect(bounds.startx).toBe(25); | ||||||
|  |         expect(bounds.starty).toBe(50); | ||||||
|  |         expect(bounds.stopx ).toBe(300); | ||||||
|  |         expect(bounds.stopy ).toBe(400); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     it('it should handle multiple loops withtout expanding the bounds', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |  | ||||||
|  |         sd.bounds.insert(100,100,1000,1000); | ||||||
|  |         sd.bounds.verticalPos  = 200; | ||||||
|  |         sd.bounds.newLoop(); | ||||||
|  |         sd.bounds.newLoop(); | ||||||
|  |         sd.bounds.insert(200,200,300,300); | ||||||
|  |  | ||||||
|  |         // Check bounds of first loop | ||||||
|  |         var loop = sd.bounds.endLoop(); | ||||||
|  |  | ||||||
|  |         expect(loop.startx).toBe(200-conf.boxMargin); | ||||||
|  |         expect(loop.starty).toBe(200-conf.boxMargin); | ||||||
|  |         expect(loop.stopx ).toBe(300+conf.boxMargin); | ||||||
|  |         expect(loop.stopy ).toBe(300+conf.boxMargin); | ||||||
|  |  | ||||||
|  |         // Check bounds of second loop | ||||||
|  |         loop = sd.bounds.endLoop(); | ||||||
|  |  | ||||||
|  |         expect(loop.startx).toBe(200-2*conf.boxMargin); | ||||||
|  |         expect(loop.starty).toBe(200-2*conf.boxMargin); | ||||||
|  |         expect(loop.stopx ).toBe(300+2*conf.boxMargin); | ||||||
|  |         expect(loop.stopy ).toBe(300+2*conf.boxMargin); | ||||||
|  |  | ||||||
|  |         // Check bounds of first loop | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |  | ||||||
|  |         expect(bounds.startx).toBe(100); | ||||||
|  |         expect(bounds.starty).toBe(100); | ||||||
|  |         expect(bounds.stopx ).toBe(1000); | ||||||
|  |         expect(bounds.stopy ).toBe(1000); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('it should handle a loop that expands the area', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |  | ||||||
|  |         sd.bounds.insert(100,100,200,200); | ||||||
|  |         sd.bounds.verticalPos  = 200; | ||||||
|  |         sd.bounds.newLoop(); | ||||||
|  |         sd.bounds.insert(50,50,300,300); | ||||||
|  |  | ||||||
|  |         var loop = sd.bounds.endLoop(); | ||||||
|  |  | ||||||
|  |         expect(loop.startx).toBe(50  - conf.boxMargin); | ||||||
|  |         expect(loop.starty).toBe(50  - conf.boxMargin); | ||||||
|  |         expect(loop.stopx ).toBe(300 + conf.boxMargin); | ||||||
|  |         expect(loop.stopy ).toBe(300 + conf.boxMargin); | ||||||
|  |  | ||||||
|  |         // Check bounds after the loop | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |  | ||||||
|  |         expect(bounds.startx).toBe(loop.startx); | ||||||
|  |         expect(bounds.starty).toBe(loop.starty); | ||||||
|  |         expect(bounds.stopx ).toBe(loop.stopx); | ||||||
|  |         expect(bounds.stopy ).toBe(loop.stopy); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
|  | describe('when rendering a sequenceDiagram',function() { | ||||||
|  |     var parseError, _d3, conf; | ||||||
|  |     beforeEach(function () { | ||||||
|  |         sq.yy = require('./sequenceDb'); | ||||||
|  |         sq.yy.clear(); | ||||||
|  |         parseError = function(err, hash) { | ||||||
|  |             console.log('Syntax error:' + err); | ||||||
|  |             console.log(hash); | ||||||
|  |         }; | ||||||
|  |         sq.yy.parseError = parseError; | ||||||
|  |  | ||||||
|  |         function newD3() { | ||||||
|  |             var o = { | ||||||
|  |                 append: function (type) { | ||||||
|  |                     return newD3(); | ||||||
|  |                 }, | ||||||
|  |                 attr: function (key, val) { | ||||||
|  |                     return this; | ||||||
|  |                 }, | ||||||
|  |                 style: function (key, val) { | ||||||
|  |                     return this; | ||||||
|  |                 }, | ||||||
|  |                 text: function (txt) { | ||||||
|  |                     return this; | ||||||
|  |                 }, | ||||||
|  |                 0:{ | ||||||
|  |                     0: { | ||||||
|  |                         getBBox: function () { | ||||||
|  |                             return { | ||||||
|  |                                 height: 10, | ||||||
|  |                                 width: 20 | ||||||
|  |                             }; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             return o; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var _d3 = { | ||||||
|  |             select:function(){ | ||||||
|  |                 return new newD3(); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         d3 = _d3; | ||||||
|  |  | ||||||
|  |         conf = { | ||||||
|  |             diagramMarginX:50, | ||||||
|  |             diagramMarginY:10, | ||||||
|  |             actorMargin:50, | ||||||
|  |             width:150, | ||||||
|  |             // Height of actor boxes | ||||||
|  |             height:65, | ||||||
|  |             boxMargin:10, | ||||||
|  |             messageMargin:40, | ||||||
|  |             boxTextMargin:15, | ||||||
|  |             noteMargin:25 | ||||||
|  |         }; | ||||||
|  |         sd.setConf(conf); | ||||||
|  |     }); | ||||||
|  |     it('it should handle one actor', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'participant Alice\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         sd.draw(str,'tst'); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(0); | ||||||
|  |         expect(bounds.starty).toBe(0); | ||||||
|  |         expect(bounds.stopx ).toBe( conf.width); | ||||||
|  |         expect(bounds.stopy ).toBe(conf.height); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |     it('it should handle one actor and a note', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'participant Alice\n' + | ||||||
|  |             'Note left of Alice: Alice thinks\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         sd.draw(str,'tst'); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(-(conf.width/2)-(conf.actorMargin/2)); | ||||||
|  |         expect(bounds.starty).toBe(0); | ||||||
|  |         expect(bounds.stopx ).toBe( conf.width ); | ||||||
|  |         // 10 comes from mock of text height | ||||||
|  |         expect(bounds.stopy ).toBe( conf.height + conf.boxMargin + 2*conf.noteMargin +10); | ||||||
|  |     }); | ||||||
|  |     it('it should handle one actor and a note to the right', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'participant Alice\n' + | ||||||
|  |             'Note right of Alice: Alice thinks\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         sd.draw(str,'tst'); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(0); | ||||||
|  |         expect(bounds.starty).toBe(0); | ||||||
|  |         expect(bounds.stopx ).toBe( (conf.width/2) + (conf.actorMargin/2) + conf.width); | ||||||
|  |         // 10 comes from mock of text height | ||||||
|  |         expect(bounds.stopy ).toBe( conf.height + conf.boxMargin + 2*conf.noteMargin +10); | ||||||
|  |     }); | ||||||
|  |     it('it should handle two actors', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice->Bob: Hello Bob, how are you?\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         sd.draw(str,'tst'); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(0); | ||||||
|  |         expect(bounds.starty).toBe(0); | ||||||
|  |         expect(bounds.stopx ).toBe(conf.width*2 + conf.actorMargin); | ||||||
|  |         expect(bounds.stopy ).toBe(0 + conf.messageMargin + conf.height); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('it should draw two actors and two messages', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice->Bob: Hello Bob, how are you?\n'+ | ||||||
|  |             'Bob->Alice: Fine!\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         sd.draw(str,'tst'); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(0); | ||||||
|  |         expect(bounds.starty).toBe(0); | ||||||
|  |         expect(bounds.stopx ).toBe(0 + conf.width*2 + conf.actorMargin); | ||||||
|  |         expect(bounds.stopy ).toBe(0 + 2*conf.messageMargin + conf.height); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     it('it should draw two actors notes to the right', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice->Bob: Hello Bob, how are you?\n'+ | ||||||
|  |             'Note right of Bob: Bob thinks\n' + | ||||||
|  |             'Bob->Alice: Fine!\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         sd.draw(str,'tst'); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(0); | ||||||
|  |         expect(bounds.starty).toBe(0); | ||||||
|  |  | ||||||
|  |         var expStopX = conf.actorMargin +conf.width+ (conf.width/2) + conf.noteMargin + conf.width; | ||||||
|  |  | ||||||
|  |         expect(bounds.stopx ).toBe(expStopX); | ||||||
|  |         expect(bounds.stopy ).toBe(2*conf.messageMargin + conf.height + conf.boxMargin + 10+ 2*conf.noteMargin); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |     it('it should draw two actors notes to the left', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice->Bob: Hello Bob, how are you?\n'+ | ||||||
|  |             'Note left of Alice: Bob thinks\n' + | ||||||
|  |             'Bob->Alice: Fine!\n'; | ||||||
|  |  | ||||||
|  |         sq.parse(str); | ||||||
|  |         sd.draw(str,'tst'); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe( -(conf.width/2)-(conf.actorMargin/2)); | ||||||
|  |         expect(bounds.starty).toBe(0); | ||||||
|  |  | ||||||
|  |         expect(bounds.stopx ).toBe( conf.width*2 + conf.actorMargin); | ||||||
|  |         expect(bounds.stopy ).toBe( 2*conf.messageMargin + conf.height + conf.boxMargin +10+ 2*conf.noteMargin); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('it should draw two loops', function () { | ||||||
|  |         sd.bounds.init(); | ||||||
|  |         var str = 'sequenceDiagram\n' + | ||||||
|  |             'Alice->Bob: Hello Bob, how are you?\n'+ | ||||||
|  |             'loop Cheers\n' + | ||||||
|  |             'Bob->Alice: Fine!\n' + | ||||||
|  |             'end\n'; | ||||||
|  |         sq.parse(str); | ||||||
|  |         sd.draw(str,'tst'); | ||||||
|  |  | ||||||
|  |         var bounds = sd.bounds.getBounds(); | ||||||
|  |         expect(bounds.startx).toBe(0); | ||||||
|  |         expect(bounds.starty).toBe(0); | ||||||
|  |  | ||||||
|  |         expect(bounds.stopx ).toBe(0 + conf.width*2 + conf.actorMargin); | ||||||
|  |         expect(bounds.stopy ).toBe(0 + 2*conf.messageMargin + conf.height + 3*conf.boxMargin + conf.boxTextMargin); | ||||||
|  |  | ||||||
|  |     }); | ||||||
| }); | }); | ||||||
| @@ -5,6 +5,114 @@ | |||||||
|  |  | ||||||
| var sq = require('./parser/sequenceDiagram').parser; | var sq = require('./parser/sequenceDiagram').parser; | ||||||
| sq.yy = require('./sequenceDb'); | sq.yy = require('./sequenceDb'); | ||||||
|  | var svgDraw = require('./svgDraw'); | ||||||
|  | var conf = { | ||||||
|  |  | ||||||
|  |     diagramMarginX:50, | ||||||
|  |     diagramMarginY:10, | ||||||
|  |     // Margin between actors | ||||||
|  |     actorMargin:50, | ||||||
|  |     // Width of actor moxes | ||||||
|  |     width:150, | ||||||
|  |     // Height of actor boxes | ||||||
|  |     height:65, | ||||||
|  |     // Margin around loop boxes | ||||||
|  |     boxMargin:10, | ||||||
|  |     boxTextMargin:5, | ||||||
|  |  | ||||||
|  |     noteMargin:10, | ||||||
|  |     // Space between messages | ||||||
|  |     messageMargin:35 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.bounds = { | ||||||
|  |     data:{ | ||||||
|  |         startx:undefined, | ||||||
|  |         stopx :undefined, | ||||||
|  |         starty:undefined, | ||||||
|  |         stopy :undefined, | ||||||
|  |     }, | ||||||
|  |     verticalPos:0, | ||||||
|  |  | ||||||
|  |     list: [], | ||||||
|  |     init    : function(){ | ||||||
|  |         this.list = []; | ||||||
|  |         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]); | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     updateLoops:function(startx,starty,stopx,stopy){ | ||||||
|  |         var _self = this; | ||||||
|  |         var cnt = 0; | ||||||
|  |         this.list.forEach(function(loop){ | ||||||
|  |             cnt++; | ||||||
|  |             // The loop list is a stack so the biggest margins in the beginning of the list | ||||||
|  |             var n = _self.list.length-cnt+1; | ||||||
|  |  | ||||||
|  |             _self.updateVal(loop, 'startx',startx - n*conf.boxMargin, Math.min); | ||||||
|  |             _self.updateVal(loop, 'starty',starty - n*conf.boxMargin, Math.min); | ||||||
|  |             _self.updateVal(loop, 'stopx' ,stopx  + n*conf.boxMargin, Math.max); | ||||||
|  |             _self.updateVal(loop, 'stopy' ,stopy  + n*conf.boxMargin, Math.max); | ||||||
|  |  | ||||||
|  |             _self.updateVal(exports.bounds.data,'startx',startx - n*conf.boxMargin ,Math.min); | ||||||
|  |             _self.updateVal(exports.bounds.data,'starty',starty - n*conf.boxMargin ,Math.min); | ||||||
|  |             _self.updateVal(exports.bounds.data,'stopx' ,stopx  + n*conf.boxMargin ,Math.max); | ||||||
|  |             _self.updateVal(exports.bounds.data,'stopy' ,stopy  + n*conf.boxMargin ,Math.max); | ||||||
|  |         }); | ||||||
|  |     }, | ||||||
|  |     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.updateLoops(_startx,_starty,_stopx,_stopy); | ||||||
|  |  | ||||||
|  |     }, | ||||||
|  |     newLoop:function(title){ | ||||||
|  |         this.list.push({startx:undefined,starty:this.verticalPos,stopx:undefined,stopy:undefined, title:title}); | ||||||
|  |     }, | ||||||
|  |     endLoop:function(){ | ||||||
|  |         var loop = this.list.pop(); | ||||||
|  |         //loop.stopy =  exports.bounds.getVerticalPos(); | ||||||
|  |         return loop; | ||||||
|  |     }, | ||||||
|  |     addElseToLoop:function(message){ | ||||||
|  |         var loop = this.list.pop(); | ||||||
|  |         loop.elsey =  exports.bounds.getVerticalPos(); | ||||||
|  |         loop.elseText = message; | ||||||
|  |         this.list.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 |  * Draws an actor in the diagram with the attaced line | ||||||
| @@ -12,51 +120,124 @@ sq.yy = require('./sequenceDb'); | |||||||
|  * @param pos The position if the actor in the liost of actors |  * @param pos The position if the actor in the liost of actors | ||||||
|  * @param description The text in the box |  * @param description The text in the box | ||||||
|  */ |  */ | ||||||
| var drawNote = function(elem, startX, verticalPos, msg){ | var drawNote = function(elem, startx, verticalPos, msg){ | ||||||
|     var insertLinebreaks = function (d) { |     var rect = svgDraw.getNoteRect(); | ||||||
|         var el = d3.select(this); |     rect.x = startx; | ||||||
|         var words = d.split(' '); |     rect.y = verticalPos; | ||||||
|         el.text(''); |     rect.width = conf.width; | ||||||
|  |     rect.class = 'note'; | ||||||
|         for (var i = 0; i < words.length; i++) { |  | ||||||
|             var tspan = el.append('tspan').text(words[i]); |  | ||||||
|             if (i > 0) |  | ||||||
|                 tspan.attr('x', 0).attr('dy', '15'); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     var g = elem.append("g"); |     var g = elem.append("g"); | ||||||
|     var rectElem = g.append("rect") |     var rectElem = svgDraw.drawRect(g, rect); | ||||||
|         .attr("x", startX + 25) |  | ||||||
|         .attr("y", verticalPos -25) |  | ||||||
|         .attr("fill", '#EDF2AE') |  | ||||||
|         .attr("stroke", '#666') |  | ||||||
|         .attr("width", 150) |  | ||||||
|         .attr("height", 100) |  | ||||||
|         .attr("rx", 0) |  | ||||||
|         .attr("ry", 0); |  | ||||||
|     var textElem = g.append("text") |  | ||||||
|         .attr("x", startX + 10) |  | ||||||
|         .attr("y", verticalPos - 15) |  | ||||||
|         .style("text-anchor", "start"); |  | ||||||
|     msg.message.split('<br>').forEach(function(rowText){ |  | ||||||
|         textElem.append("tspan") |  | ||||||
|             .attr("x", startX + 35) |  | ||||||
|             .attr("dy", '1em') |  | ||||||
|             .text(rowText); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     console.log('textElem.height'); |     var textObj = svgDraw.getTextObj(); | ||||||
|     console.log(textElem[0][0].getBBox()); |     textObj.x = startx; | ||||||
|     rectElem.attr('height',textElem[0][0].getBBox().height+20); |     textObj.y = verticalPos+conf.noteMargin; | ||||||
|     //console.log(textElem.getBBox().height); |     textObj.textMargin = conf.noteMargin; | ||||||
|  |     textObj.dy = '1em'; | ||||||
|  |     textObj.text = msg.message; | ||||||
|  |     textObj.class = 'noteText'; | ||||||
|  |  | ||||||
|         //.text(msg.message + '\n' + msg.message) |     var textElem = svgDraw.drawText(g,textObj); | ||||||
|  |  | ||||||
|  |     var textHeight = textElem[0][0].getBBox().height; | ||||||
|  |     exports.bounds.insert(startx, verticalPos, startx + conf.width,  verticalPos + 2*conf.noteMargin + textHeight); | ||||||
|  |  | ||||||
|     return verticalPos + textElem[0][0].getBBox().height - 10; |     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 = textElem[0][0].getBBox().width; | ||||||
|  |  | ||||||
|  |     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"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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(#arrowhead)"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (msg.type === sq.yy.LINETYPE.SOLID_CROSS || msg.type === sq.yy.LINETYPE.DOTTED_CROSS){ | ||||||
|  |         line.attr("marker-end", "url(#crosshead)"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | module.exports.drawActors = function(diagram, actors, actorKeys){ | ||||||
|  |     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 = 0; | ||||||
|  |         actors[key].width = conf.diagramMarginY; | ||||||
|  |         actors[key].height = conf.diagramMarginY; | ||||||
|  |  | ||||||
|  |         // Draw the box with the attached line | ||||||
|  |         svgDraw.drawActor(diagram, actors[key].x, actors[key].description, conf); | ||||||
|  |         exports.bounds.insert(actors[key].x, 0, actors[key].x + conf.width, conf.height); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Add a margin between the actor boxes and the first arrow | ||||||
|  |     //exports.bounds.bumpVerticalPos(conf.height+conf.messageMargin); | ||||||
|  |     exports.bounds.bumpVerticalPos(conf.height); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | module.exports.setConf = function(cnf){ | ||||||
|  |     conf = cnf; | ||||||
|  | }; | ||||||
| /** | /** | ||||||
|  * Draws a flowchart in the tag with id: id based on the graph definition in text. |  * Draws a flowchart in the tag with id: id based on the graph definition in text. | ||||||
|  * @param text |  * @param text | ||||||
| @@ -64,160 +245,101 @@ var drawNote = function(elem, startX, verticalPos, msg){ | |||||||
|  */ |  */ | ||||||
| module.exports.draw = function (text, id) { | module.exports.draw = function (text, id) { | ||||||
|     sq.yy.clear(); |     sq.yy.clear(); | ||||||
|     sq.parse(text); |     //console.log(text); | ||||||
|  |     sq.parse(text+'\n'); | ||||||
|     // Intial config for margins etc |     exports.bounds.init(); | ||||||
|     var startMargin = 50; |  | ||||||
|     var margin = 50; |  | ||||||
|     var width = 150; |  | ||||||
|     var height = 65; |  | ||||||
|     var yStartMargin = 10; |  | ||||||
|     var diagram = d3.select('#'+id); |     var diagram = d3.select('#'+id); | ||||||
|     /** |  | ||||||
|      * 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 drawActor = function(elem, center, pos, description){ |  | ||||||
|         var g = elem.append("g"); |  | ||||||
|         g.append("line") |  | ||||||
|             .attr("x1", center) |  | ||||||
|             .attr("y1", yStartMargin) |  | ||||||
|             .attr("x2", center) |  | ||||||
|             .attr("y2", 2000) |  | ||||||
|             .attr("stroke-width", '0.5px') |  | ||||||
|             .attr("stroke", '#999'); |  | ||||||
|  |  | ||||||
|         g.append("rect") |     var startx; | ||||||
|             .attr("x", startMargin  + pos*margin +i*150) |     var stopx; | ||||||
|             .attr("y", yStartMargin) |  | ||||||
|             .attr("fill", '#eaeaea') |  | ||||||
|             .attr("stroke", '#666') |  | ||||||
|             .attr("width", width) |  | ||||||
|             .attr("height", height) |  | ||||||
|             .attr("rx", 3) |  | ||||||
|             .attr("ry", 3); |  | ||||||
|         g.append("text")      // text label for the x axis |  | ||||||
|             .attr("x", startMargin  + pos*margin +i*width + 75) |  | ||||||
|             .attr("y", yStartMargin+37.5) |  | ||||||
|             .style("text-anchor", "middle") |  | ||||||
|             .text(description) |  | ||||||
|         ; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Setup arrow head and define the marker. The result is appended to the svg. |  | ||||||
|      */ |  | ||||||
|     var insertArrowHead = function(elem){ |  | ||||||
|         elem.append("defs").append("marker") |  | ||||||
|             .attr("id", "arrowhead") |  | ||||||
|             .attr("refX", 5) /*must be smarter way to calculate shift*/ |  | ||||||
|             .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 |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     var drawMessage = function(elem, startx, stopx, verticalPos, txtCenter, msg){ |  | ||||||
|         var g = elem.append("g"); |  | ||||||
|         //Make an SVG Container |  | ||||||
|         //Draw the line |  | ||||||
|         if(msg.type !== 2) { |  | ||||||
|             if (msg.type === 1) { |  | ||||||
|                 g.append("line") |  | ||||||
|                     .attr("x1", startx) |  | ||||||
|                     .attr("y1", verticalPos) |  | ||||||
|                     .attr("x2", stopx) |  | ||||||
|                     .attr("y2", verticalPos) |  | ||||||
|                     .attr("stroke-width", 2) |  | ||||||
|                     .attr("stroke", "black") |  | ||||||
|                     .style("stroke-dasharray", ("3, 3")) |  | ||||||
|                     .attr("class", "link") |  | ||||||
|                     .attr("marker-end", "url(#arrowhead)"); |  | ||||||
|                 //.attr("d", diagonal); |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 g.append("line") |  | ||||||
|                     .attr("x1", startx) |  | ||||||
|                     .attr("y1", verticalPos) |  | ||||||
|                     .attr("x2", stopx) |  | ||||||
|                     .attr("y2", verticalPos) |  | ||||||
|                     .attr("stroke-width", 2) |  | ||||||
|                     .attr("stroke", "black") |  | ||||||
|                     .attr("class", "link") |  | ||||||
|                     .attr("marker-end", "url(#arrowhead)"); |  | ||||||
|                 //.attr("d", diagonal); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             g.append("text")      // text label for the x axis |  | ||||||
|                 .attr("x", txtCenter) |  | ||||||
|                 .attr("y", verticalPos - 10) |  | ||||||
|                 .style("text-anchor", "middle") |  | ||||||
|                 .text(msg.message); |  | ||||||
|         } |  | ||||||
|         else{ |  | ||||||
|             g.append("text")      // text label for the x axis |  | ||||||
|                 .attr("x", txtCenter) |  | ||||||
|                 .attr("y", verticalPos - 10) |  | ||||||
|                 .style("text-anchor", "middle") |  | ||||||
|                 .text(msg.message); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     // Fetch data from the parsing |     // Fetch data from the parsing | ||||||
|     var actors = sq.yy.getActors(); |     var actors = sq.yy.getActors(); | ||||||
|     var actorKeys = sq.yy.getActorKeys(); |     var actorKeys = sq.yy.getActorKeys(); | ||||||
|     var messages = sq.yy.getMessages(); |     var messages = sq.yy.getMessages(); | ||||||
|  |  | ||||||
|     var i, maxX = 0; |     module.exports.drawActors(diagram, actors, actorKeys); | ||||||
|  |  | ||||||
|     // Draw the actors |  | ||||||
|     for(i=0;i<actorKeys.length;i++){ |  | ||||||
|         var key = actorKeys[i]; |  | ||||||
|  |  | ||||||
|         // Add some rendering data to the object |  | ||||||
|         actors[key].x = startMargin  + i*margin +i*150; |  | ||||||
|         actors[key].y = yStartMargin; |  | ||||||
|         actors[key].width = yStartMargin; |  | ||||||
|         actors[key].height = yStartMargin; |  | ||||||
|  |  | ||||||
|         var center = actors[key].x + (width/2); |  | ||||||
|  |  | ||||||
|         // Keep track of width for with setting on the svg |  | ||||||
|         maxX = Math.max(maxX,actors[key].x); |  | ||||||
|  |  | ||||||
|         // Draw the box with the attached line |  | ||||||
|         drawActor(diagram, center,i, actors[key].description); |  | ||||||
|     } |  | ||||||
|     maxX = maxX + width; |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     // The arrow head definition is attached to the svg once |     // The arrow head definition is attached to the svg once | ||||||
|     insertArrowHead(diagram); |     svgDraw.insertArrowHead(diagram); | ||||||
|  |     svgDraw.insertArrowCrossHead(diagram); | ||||||
|  |  | ||||||
|     // Draw the messages/signals |     // Draw the messages/signals | ||||||
|     var verticalPos = startMargin + 30; |  | ||||||
|     messages.forEach(function(msg){ |     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 !== 0){ | ||||||
|  |                     // Right of | ||||||
|  |                     drawNote(diagram, startx + (conf.width + conf.actorMargin)/2, exports.bounds.getVerticalPos(), msg); | ||||||
|  |  | ||||||
|  |                 }else{ | ||||||
|  |                     // Left of | ||||||
|  |                     drawNote(diagram, startx - (conf.width + conf.actorMargin)/2, exports.bounds.getVerticalPos(), msg); | ||||||
|  |                 } | ||||||
|  |                 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.drawLoop(diagram, loopData); | ||||||
|  |                 exports.bounds.bumpVerticalPos(conf.boxMargin); | ||||||
|  |                 loopData = exports.bounds.addElseToLoop(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; | ||||||
|  |             default: | ||||||
|  |                 exports.bounds.bumpVerticalPos(conf.messageMargin); | ||||||
|  |                 startx = actors[msg.from].x + conf.width/2; | ||||||
|  |                 stopx = actors[msg.to].x + conf.width/2; | ||||||
|  |  | ||||||
|  |                 drawMessage(diagram, startx, stopx, exports.bounds.getVerticalPos(), msg); | ||||||
|  |  | ||||||
|         verticalPos = verticalPos + 40; |  | ||||||
|         var startx = actors[msg.from].x + width/2; |  | ||||||
|         var stopx = actors[msg.to].x + width/2; |  | ||||||
|         var txtCenter = startx + (stopx-startx)/2; |  | ||||||
|         if(msg.type === 2){ |  | ||||||
|             console.log('VP before:',verticalPos); |  | ||||||
|             verticalPos =  drawNote(diagram, startx, verticalPos, msg); |  | ||||||
|             console.log('VP after:',verticalPos); |  | ||||||
|         } else { |  | ||||||
|             drawMessage(diagram, startx, stopx, verticalPos, txtCenter, msg); |  | ||||||
|             // Keep track of width for with setting on the svg |  | ||||||
|             maxX = Math.max(maxX,startx + 176); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     diagram.attr("height", verticalPos + 40); |     var box = exports.bounds.getBounds(); | ||||||
|     diagram.attr("width", maxX ); |  | ||||||
|  |     var height = box.stopy-box.starty+2*conf.diagramMarginY; | ||||||
|  |     var width  = box.stopx-box.startx+2*conf.diagramMarginX; | ||||||
|  |  | ||||||
|  |     diagram.attr("height",height); | ||||||
|  |     diagram.attr("width", width ); | ||||||
|  |     diagram.attr("viewBox", (box.startx-conf.diagramMarginX) + ' -' +conf.diagramMarginY + ' ' + width + ' ' + height); | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										228
									
								
								src/diagrams/sequenceDiagram/svgDraw.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								src/diagrams/sequenceDiagram/svgDraw.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,228 @@ | |||||||
|  | /** | ||||||
|  |  * 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){ | ||||||
|  |     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); | ||||||
|  |  | ||||||
|  |     textData.text.split(/<br\/?>/ig).forEach(function(rowText){ | ||||||
|  |         var span = textElem.append('tspan'); | ||||||
|  |         span.attr('x', textData.x +textData.textMargin); | ||||||
|  |         span.attr('dy', textData.dy); | ||||||
|  |         span.text(rowText); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     if(typeof textData.class !== 'undefined'){ | ||||||
|  |         textElem.attr("class", textData.class); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return textElem; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.drawLabel = function(elem , txtObject){ | ||||||
|  |     var rectData = exports.getNoteRect(); | ||||||
|  |     rectData.x = txtObject.x; | ||||||
|  |     rectData.y = txtObject.y; | ||||||
|  |     rectData.width = 50; | ||||||
|  |     rectData.height = 20; | ||||||
|  |     rectData.fill = '#526e52'; | ||||||
|  |     rectData.stroke = 'none'; | ||||||
|  |     rectData.class = 'labelBox'; | ||||||
|  |     //rectData.color = 'white'; | ||||||
|  |  | ||||||
|  |     exports.drawRect(elem, rectData); | ||||||
|  |  | ||||||
|  |     txtObject.y = txtObject.y + txtObject.labelMargin; | ||||||
|  |     txtObject.x = txtObject.x + 0.5*txtObject.labelMargin; | ||||||
|  |     txtObject.fill = 'white'; | ||||||
|  |     exports.drawText(elem, txtObject); | ||||||
|  |  | ||||||
|  |     //return textElem; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 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,description,conf){ | ||||||
|  |     var center = left + (conf.width/2); | ||||||
|  |     var g = elem.append("g"); | ||||||
|  |     g.append("line") | ||||||
|  |         .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.fill = '#eaeaea'; | ||||||
|  |     rect.width = conf.width; | ||||||
|  |     rect.height = conf.height; | ||||||
|  |     rect.class = 'actor'; | ||||||
|  |     rect.rx = 3; | ||||||
|  |     rect.ry = 3; | ||||||
|  |     exports.drawRect(g, rect); | ||||||
|  |  | ||||||
|  |     g.append("text")      // text label for the x axis | ||||||
|  |         .attr("x", center) | ||||||
|  |         .attr("y", (conf.height/2)+5) | ||||||
|  |         .attr('class','actor') | ||||||
|  |         .style("text-anchor", "middle") | ||||||
|  |         .text(description) | ||||||
|  |     ; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 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){ | ||||||
|  |         g.append("line") | ||||||
|  |             .attr("x1", startx) | ||||||
|  |             .attr("y1", starty) | ||||||
|  |             .attr("x2", stopx ) | ||||||
|  |             .attr("y2", stopy ) | ||||||
|  |             .attr("stroke-width", 2) | ||||||
|  |             .attr("stroke", "#526e52") | ||||||
|  |             .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.elsey !== 'undefined'){ | ||||||
|  |         drawLoopLine(bounds.startx, bounds.elsey, bounds.stopx, bounds.elsey ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var txt = exports.getTextObj(); | ||||||
|  |     txt.text = labelText; | ||||||
|  |     txt.x = bounds.startx; | ||||||
|  |     txt.y = bounds.starty; | ||||||
|  |     txt.labelMargin =  1.5 * conf.boxMargin; | ||||||
|  |     txt.class =  'labelText'; | ||||||
|  |     txt.fill =  'white'; | ||||||
|  |  | ||||||
|  |     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.elseText !== 'undefined') { | ||||||
|  |         txt.text = '[ ' + bounds.elseText + ' ]'; | ||||||
|  |         txt.y = bounds.elsey + 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; | ||||||
|  | }; | ||||||
							
								
								
									
										62
									
								
								src/main.js
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								src/main.js
									
									
									
									
									
								
							| @@ -29,7 +29,9 @@ var init = function () { | |||||||
|         // Check if previously processed |         // Check if previously processed | ||||||
|         if(!element.getAttribute("data-processed")) { |         if(!element.getAttribute("data-processed")) { | ||||||
|             element.setAttribute("data-processed", true); |             element.setAttribute("data-processed", true); | ||||||
|         } else continue; |         } else { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         var id; |         var id; | ||||||
|  |  | ||||||
| @@ -50,7 +52,6 @@ var init = function () { | |||||||
|  |  | ||||||
|         switch(graphType){ |         switch(graphType){ | ||||||
|             case 'graph':  |             case 'graph':  | ||||||
|                 console.log('FC'); |  | ||||||
|                 classes = flowRenderer.getClasses(txt, false); |                 classes = flowRenderer.getClasses(txt, false); | ||||||
|                 flowRenderer.draw(txt, id, false); |                 flowRenderer.draw(txt, id, false); | ||||||
|                 utils.cloneCssStyles(element.firstChild, classes); |                 utils.cloneCssStyles(element.firstChild, classes); | ||||||
| @@ -64,7 +65,7 @@ var init = function () { | |||||||
|             case 'sequenceDiagram':  |             case 'sequenceDiagram':  | ||||||
|                 seq.draw(txt,id); |                 seq.draw(txt,id); | ||||||
|                 // TODO - Get styles for sequence diagram |                 // TODO - Get styles for sequence diagram | ||||||
|                 utils.cloneCssStyles(element.firstChild, classes); |                 utils.cloneCssStyles(element.firstChild, []); | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -90,28 +91,9 @@ var equals = function (val, variable){ | |||||||
|         return (val === variable); |         return (val === variable); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| if(typeof document !== 'undefined'){ |  | ||||||
|     /** |  | ||||||
|      * Wait for coument loaded before starting the execution |  | ||||||
|      */ |  | ||||||
|     document.addEventListener('DOMContentLoaded', function(){ |  | ||||||
|         // Check presence of config object |  | ||||||
|         if(typeof mermaid_config !== 'undefined'){ |  | ||||||
|             // Check if property startOnLoad is set |  | ||||||
|             if(equals(true,mermaid_config.startOnLoad)){ |  | ||||||
|                 init(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         else{ |  | ||||||
|             // No config found, do autostart in this simple case |  | ||||||
|             init(); |  | ||||||
|         } |  | ||||||
|     }, false); |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| global.mermaid = { | global.mermaid = { | ||||||
|  |     startOnLoad:true, | ||||||
|     init:function(){ |     init:function(){ | ||||||
|         init(); |         init(); | ||||||
|     }, |     }, | ||||||
| @@ -121,4 +103,36 @@ global.mermaid = { | |||||||
|     getParser:function(){ |     getParser:function(){ | ||||||
|         return flow.parser; |         return flow.parser; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | exports.contentLoaded = function(){ | ||||||
|  |     // Check state of start config mermaid namespece | ||||||
|  |     //console.log('global.mermaid.startOnLoad',global.mermaid.startOnLoad); | ||||||
|  |     //console.log('mermaid_config',mermaid_config); | ||||||
|  |     if(global.mermaid.startOnLoad) { | ||||||
|  |  | ||||||
|  |         // For backwards compatability reasons also check mermaid_config variable | ||||||
|  |         if (typeof mermaid_config !== 'undefined') { | ||||||
|  |             // Check if property startOnLoad is set | ||||||
|  |             if (equals(true, mermaid_config.startOnLoad)) { | ||||||
|  |                 global.mermaid.init(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             // No config found, do autostart in this simple case | ||||||
|  |             global.mermaid.init(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | if(typeof document !== 'undefined'){ | ||||||
|  |     /** | ||||||
|  |      * Wait for coument loaded before starting the execution | ||||||
|  |      */ | ||||||
|  |     document.addEventListener('DOMContentLoaded', function(){ | ||||||
|  |         exports.contentLoaded(); | ||||||
|  |     }, false); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,35 +6,71 @@ | |||||||
|  */ |  */ | ||||||
| var rewire = require("rewire"); | var rewire = require("rewire"); | ||||||
| var utils = require("./utils"); | var utils = require("./utils"); | ||||||
|  | var main = require("./main"); | ||||||
|  |  | ||||||
| describe('when using main and ',function() { | describe('when using main and ',function() { | ||||||
|     describe('when detecting chart type ',function() { |     describe('when detecting chart type ',function() { | ||||||
|         var main; |         //var main; | ||||||
|  |         //var document; | ||||||
|  |         //var window; | ||||||
|         beforeEach(function () { |         beforeEach(function () { | ||||||
|             var MockBrowser = require('mock-browser').mocks.MockBrowser; |             var MockBrowser = require('mock-browser').mocks.MockBrowser; | ||||||
|             var mock = new MockBrowser(); |             var mock = new MockBrowser(); | ||||||
|  |  | ||||||
|  |             delete global.mermaid_config; | ||||||
|  |  | ||||||
|             // and in the run-code inside some object |             // and in the run-code inside some object | ||||||
|             document = mock.getDocument(); |             document = mock.getDocument(); | ||||||
|  |             window = mock.getWindow(); | ||||||
|  |  | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         it('should not call start anything with an empty document', function () { |         it('should not start rendering with mermaid_config.startOnLoad set to false', function () { | ||||||
|  |  | ||||||
|             mermaid_config ={startOnLoad : false}; |  | ||||||
|             main = rewire('./main'); |             main = rewire('./main'); | ||||||
|  |  | ||||||
|             spyOn(utils,'detectType'); |  | ||||||
|             expect(utils.detectType).not.toHaveBeenCalled(); |  | ||||||
|         }); |  | ||||||
|         it('should start something with a mermaid document', function () { |  | ||||||
|             mermaid_config ={startOnLoad : false}; |             mermaid_config ={startOnLoad : false}; | ||||||
|  |  | ||||||
|  |             document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'; | ||||||
|  |             spyOn(global.mermaid,'init'); | ||||||
|  |             //console.log(main); | ||||||
|  |             main.contentLoaded(); | ||||||
|  |             expect(global.mermaid.init).not.toHaveBeenCalled(); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should not start rendering with mermaid.startOnLoad set to false', function () { | ||||||
|  |             main = rewire('./main'); | ||||||
|  |             mermaid.startOnLoad =  false; | ||||||
|  |             mermaid_config ={startOnLoad : true}; | ||||||
|  |  | ||||||
|  |             document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'; | ||||||
|  |             spyOn(global.mermaid,'init'); | ||||||
|  |             main.contentLoaded(); | ||||||
|  |             expect(global.mermaid.init).not.toHaveBeenCalled(); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should start rendering with both startOnLoad set', function () { | ||||||
|  |             main = rewire('./main'); | ||||||
|  |             mermaid.startOnLoad =  true; | ||||||
|  |             mermaid_config ={startOnLoad : true}; | ||||||
|  |             document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'; | ||||||
|  |             spyOn(global.mermaid,'init'); | ||||||
|  |             main.contentLoaded(); | ||||||
|  |             expect(global.mermaid.init).toHaveBeenCalled(); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should start rendering with mermaid.startOnLoad set and no mermaid_config defined', function () { | ||||||
|  |             main = rewire('./main'); | ||||||
|  |             mermaid.startOnLoad =  true; | ||||||
|  |             document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'; | ||||||
|  |             spyOn(global.mermaid,'init'); | ||||||
|  |             main.contentLoaded(); | ||||||
|  |             expect(global.mermaid.init).toHaveBeenCalled(); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should start rendering as a default with no changes performed', function () { | ||||||
|             main = rewire('./main'); |             main = rewire('./main'); | ||||||
|             document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'; |             document.body.innerHTML = '<div class="mermaid">graph TD;\na;</div>'; | ||||||
|             spyOn(utils,'detectType'); |             spyOn(global.mermaid,'init'); | ||||||
|             mermaid.init(); |             main.contentLoaded(); | ||||||
|             expect(utils.detectType).toHaveBeenCalled(); |             expect(global.mermaid.init).toHaveBeenCalled(); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     }); |     }); | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								src/utils.js
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/utils.js
									
									
									
									
									
								
							| @@ -9,7 +9,6 @@ | |||||||
|  */ |  */ | ||||||
| module.exports.detectType = function(text,a){ | module.exports.detectType = function(text,a){ | ||||||
|     if(text.match(/^\s*sequenceDiagram/)){ |     if(text.match(/^\s*sequenceDiagram/)){ | ||||||
|         console.log('Detected sequenceDiagram syntax'); |  | ||||||
|         return "sequenceDiagram"; |         return "sequenceDiagram"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -36,15 +35,17 @@ module.exports.cloneCssStyles = function(svg, classes){ | |||||||
|     var usedStyles = ""; |     var usedStyles = ""; | ||||||
|     var sheets = document.styleSheets; |     var sheets = document.styleSheets; | ||||||
|     for (var i = 0; i < sheets.length; i++) { |     for (var i = 0; i < sheets.length; i++) { | ||||||
|         // Only clone css from stylesheets intended for mermaid |         // Avoid multiple inclusion on pages with multiple graphs | ||||||
|         if (sheets[i].title == 'mermaid') { |         if (sheets[i].title !== 'mermaid-svg-internal-css') { | ||||||
|             var rules = sheets[i].cssRules; |             var rules = sheets[i].cssRules; | ||||||
|             for (var j = 0; j < rules.length; j++) { |             if(rules !== null) { | ||||||
|                 var rule = rules[j]; |                 for (var j = 0; j < rules.length; j++) { | ||||||
|                 if (typeof(rule.style) != "undefined") { |                     var rule = rules[j]; | ||||||
|                     var elems = svg.querySelectorAll(rule.selectorText); |                     if (typeof(rule.style) !== 'undefined') { | ||||||
|                     if (elems.length > 0) { |                         var elems = svg.querySelectorAll(rule.selectorText); | ||||||
|                         usedStyles += rule.selectorText + " { " + rule.style.cssText + " }\n"; |                         if (elems.length > 0) { | ||||||
|  |                             usedStyles += rule.selectorText + " { " + rule.style.cssText + " }\n"; | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -79,6 +80,8 @@ module.exports.cloneCssStyles = function(svg, classes){ | |||||||
|         s.setAttribute('type', 'text/css'); |         s.setAttribute('type', 'text/css'); | ||||||
|         s.setAttribute('title', 'mermaid-svg-internal-css'); |         s.setAttribute('title', 'mermaid-svg-internal-css'); | ||||||
|         s.innerHTML = "/* <![CDATA[ */\n"; |         s.innerHTML = "/* <![CDATA[ */\n"; | ||||||
|  |         // Make this CSS local to this SVG | ||||||
|  |         s.innerHTML += "#" + svg.id.trim() + " {\n";  | ||||||
|         if (defaultStyles !== "") { |         if (defaultStyles !== "") { | ||||||
|             s.innerHTML += defaultStyles; |             s.innerHTML += defaultStyles; | ||||||
|         } |         } | ||||||
| @@ -88,6 +91,7 @@ module.exports.cloneCssStyles = function(svg, classes){ | |||||||
|         if (embeddedStyles !== "") { |         if (embeddedStyles !== "") { | ||||||
|             s.innerHTML += embeddedStyles; |             s.innerHTML += embeddedStyles; | ||||||
|         } |         } | ||||||
|  |         s.innerHTML += "}\n"; | ||||||
|         s.innerHTML += "/* ]]> */\n"; |         s.innerHTML += "/* ]]> */\n"; | ||||||
|         svg.insertBefore(s, svg.firstChild); |         svg.insertBefore(s, svg.firstChild); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -208,7 +208,7 @@ describe('when cloning CSS ',function() { | |||||||
|         expect(stylesToArray(svg)).toEqual([ '.node { stroke:#fff; stroke-width:1.5px; }', '.node { stroke: #eee; }', '.node-square { stroke: #bbb; }']); |         expect(stylesToArray(svg)).toEqual([ '.node { stroke:#fff; stroke-width:1.5px; }', '.node { stroke: #eee; }', '.node-square { stroke: #bbb; }']); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should handle a default class together with stylesheet in document and classDefs', function () { |     xit('should handle a default class together with stylesheet in document and classDefs', function () { | ||||||
|         var svg = generateSVG(); |         var svg = generateSVG(); | ||||||
|         addStyleToDocument('mermaid'); |         addStyleToDocument('mermaid'); | ||||||
|         utils.cloneCssStyles(svg, { "default": { "styles": ["stroke:#fff","stroke-width:1.5px"] },  |         utils.cloneCssStyles(svg, { "default": { "styles": ["stroke:#fff","stroke-width:1.5px"] },  | ||||||
|   | |||||||
							
								
								
									
										101
									
								
								test/cli_test-output.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								test/cli_test-output.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | var fs = require('fs') | ||||||
|  |   , path = require('path') | ||||||
|  |  | ||||||
|  | var test = require('tape') | ||||||
|  |   , async = require('async') | ||||||
|  |   , clone = require('clone') | ||||||
|  |   , rimraf = require('rimraf') | ||||||
|  |  | ||||||
|  | var mermaid = require('../lib') | ||||||
|  |  | ||||||
|  | var singleFile = { | ||||||
|  |         files: ['test/fixtures/test.mermaid'] | ||||||
|  |       , outputDir: 'test/tmp/' | ||||||
|  |       , phantomPath: './node_modules/.bin/phantomjs' | ||||||
|  |     } | ||||||
|  |   , multiFile = { | ||||||
|  |         files: ['test/fixtures/test.mermaid', 'test/fixtures/test2.mermaid'] | ||||||
|  |       , outputDir: 'test/tmp/' | ||||||
|  |       , phantomPath: './node_modules/.bin/phantomjs' | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | test('output of single png', function(t) { | ||||||
|  |   t.plan(3) | ||||||
|  |  | ||||||
|  |   var expected = ['test.mermaid.png'] | ||||||
|  |  | ||||||
|  |   opt = clone(singleFile) | ||||||
|  |   opt.png = true | ||||||
|  |  | ||||||
|  |   mermaid.process(opt.files, opt, function(code) { | ||||||
|  |     t.equal(code, 0, 'has clean exit code') | ||||||
|  |  | ||||||
|  |     verifyFiles(expected, opt.outputDir, t) | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | test('output of multiple png', function(t) { | ||||||
|  |   t.plan(3) | ||||||
|  |  | ||||||
|  |   var expected = ['test.mermaid.png', 'test2.mermaid.png'] | ||||||
|  |  | ||||||
|  |   opt = clone(multiFile) | ||||||
|  |   opt.png = true | ||||||
|  |  | ||||||
|  |   mermaid.process(opt.files, opt, function(code) { | ||||||
|  |     t.equal(code, 0, 'has clean exit code') | ||||||
|  |  | ||||||
|  |     verifyFiles(expected, opt.outputDir, t) | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | test('output of single svg', function(t) { | ||||||
|  |   t.plan(3) | ||||||
|  |  | ||||||
|  |   var expected = ['test.mermaid.svg'] | ||||||
|  |  | ||||||
|  |   opt = clone(singleFile) | ||||||
|  |   opt.svg = true | ||||||
|  |  | ||||||
|  |   mermaid.process(opt.files, opt, function(code) { | ||||||
|  |     t.equal(code, 0, 'has clean exit code') | ||||||
|  |  | ||||||
|  |     verifyFiles(expected, opt.outputDir, t) | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | test('output of multiple svg', function(t) { | ||||||
|  |   t.plan(3) | ||||||
|  |  | ||||||
|  |   var expected = ['test.mermaid.svg', 'test2.mermaid.svg'] | ||||||
|  |  | ||||||
|  |   opt = clone(multiFile) | ||||||
|  |   opt.svg = true | ||||||
|  |  | ||||||
|  |   mermaid.process(opt.files, opt, function(code) { | ||||||
|  |     t.equal(code, 0, 'has clean exit code') | ||||||
|  |  | ||||||
|  |     verifyFiles(expected, opt.outputDir, t) | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | function verifyFiles(expected, dir, t) { | ||||||
|  |   async.each( | ||||||
|  |       expected | ||||||
|  |     , function(file, cb) { | ||||||
|  |         filename = path.join(dir, path.basename(file)) | ||||||
|  |         fs.stat(filename, function(err, stat) { | ||||||
|  |           cb(err) | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  |     , function(err) { | ||||||
|  |         t.notOk(err, 'all files passed') | ||||||
|  |  | ||||||
|  |         rimraf(dir, function(rmerr) { | ||||||
|  |           t.notOk(rmerr, 'cleaned up') | ||||||
|  |           t.end() | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  |   ) | ||||||
|  | } | ||||||
							
								
								
									
										100
									
								
								test/cli_test-parser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								test/cli_test-parser.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | var test = require('tape') | ||||||
|  |   , cliPath = '../lib/cli' | ||||||
|  |  | ||||||
|  | test('parses multiple files', function(t) { | ||||||
|  |   t.plan(2) | ||||||
|  |  | ||||||
|  |   var cli = require(cliPath) | ||||||
|  |     , argv = ['example/file1.mermaid', 'file2.mermaid', 'file3.mermaid'] | ||||||
|  |     , expect = ['example/file1.mermaid', 'file2.mermaid', 'file3.mermaid'] | ||||||
|  |  | ||||||
|  |   cli.parse(argv, function(err, msg, opt) { | ||||||
|  |     t.equal(opt.files.length, 3, 'should have 3 parameters') | ||||||
|  |     t.deepEqual(opt.files, expect, 'should match expected values') | ||||||
|  |  | ||||||
|  |     t.end() | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | test('defaults to png', function(t) { | ||||||
|  |   t.plan(2) | ||||||
|  |  | ||||||
|  |   var cli = require(cliPath) | ||||||
|  |     , argv = ['example/file1.mermaid'] | ||||||
|  |  | ||||||
|  |   cli.parse(argv, function(err, msg, opt) { | ||||||
|  |     t.ok(opt.png, 'png is set by default') | ||||||
|  |     t.notOk(opt.svg, 'svg is not set by default') | ||||||
|  |  | ||||||
|  |     t.end() | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | test('setting svg unsets png', function(t) { | ||||||
|  |   t.plan(2) | ||||||
|  |  | ||||||
|  |   var cli = require(cliPath) | ||||||
|  |     , argv = ['example/file1.mermaid', '-s'] | ||||||
|  |  | ||||||
|  |   cli.parse(argv, function(err, msg, opt) { | ||||||
|  |  | ||||||
|  |     t.ok(opt.svg, 'svg is set when requested') | ||||||
|  |     t.notOk(opt.png, 'png is unset when svg is set') | ||||||
|  |  | ||||||
|  |     t.end() | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | test('setting png and svg is allowed', function(t) { | ||||||
|  |   t.plan(2) | ||||||
|  |  | ||||||
|  |   var cli = require(cliPath) | ||||||
|  |     , argv = ['example/file1.mermaid', '-s', '-p'] | ||||||
|  |  | ||||||
|  |   cli.parse(argv, function(err, msg, opt) { | ||||||
|  |     t.ok(opt.png, 'png is set when requested') | ||||||
|  |     t.ok(opt.svg, 'svg is set when requested') | ||||||
|  |  | ||||||
|  |     t.end() | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | test('setting an output directory succeeds', function(t) { | ||||||
|  |   t.plan(1) | ||||||
|  |  | ||||||
|  |   var cli = require(cliPath) | ||||||
|  |     , argv = ['-o', 'example/'] | ||||||
|  |  | ||||||
|  |   cli.parse(argv, function(err, msg, opt) { | ||||||
|  |     t.equal(opt.outputDir, 'example/', 'output directory is set') | ||||||
|  |     t.end() | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | test('setting an output directory incorrectly causes an error', function(t) { | ||||||
|  |   t.plan(1) | ||||||
|  |  | ||||||
|  |   var cli = require(cliPath) | ||||||
|  |     , argv = ['-o'] | ||||||
|  |  | ||||||
|  |   cli.parse(argv, function(err) { | ||||||
|  |     t.ok(err, 'an error is raised') | ||||||
|  |  | ||||||
|  |     t.end() | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | test('a callback function is called after parsing', function(t) { | ||||||
|  |   t.plan(2) | ||||||
|  |  | ||||||
|  |   var cli = require(cliPath) | ||||||
|  |     , argv = ['example/test.mermaid'] | ||||||
|  |     , expects = ['example/test.mermaid'] | ||||||
|  |  | ||||||
|  |   cli.parse(argv, function(err, msg, opts) { | ||||||
|  |     t.ok(true, 'callback was called') | ||||||
|  |     t.deepEqual(argv, opts.files, 'options are as expected') | ||||||
|  |  | ||||||
|  |     t.end() | ||||||
|  |   }) | ||||||
|  | }) | ||||||
							
								
								
									
										8
									
								
								test/fixtures/sequence.mermaid
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								test/fixtures/sequence.mermaid
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | sequenceDiagram | ||||||
|  |   Alice->Bob: Hello Bob, how are you? | ||||||
|  |   Note right of Bob: Bob thinks | ||||||
|  |   Bob-->Alice: I am good thanks! | ||||||
|  |   Bob-->John the Long: How about you John? | ||||||
|  |   Bob-->Alice: Checking with John... | ||||||
|  |   Alice->John the Long: Yes... John, how are you? | ||||||
|  |   John the Long-->Alice: Better than you! | ||||||
							
								
								
									
										5
									
								
								test/fixtures/test.mermaid
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								test/fixtures/test.mermaid
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | graph TD; | ||||||
|  |     A-->B; | ||||||
|  |     A-->C; | ||||||
|  |     B-->D; | ||||||
|  |     C-->D; | ||||||
							
								
								
									
										7
									
								
								test/fixtures/test2.mermaid
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								test/fixtures/test2.mermaid
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | graph LR; | ||||||
|  |     A[Hard edge]-->|Link text|B(Round edge); | ||||||
|  |     B-->C{Decision}; | ||||||
|  |     C-->|One|D[Result one]; | ||||||
|  |     C-->|Two|E[Result two]; | ||||||
|  |     classDef pink fill:#f9f,stroke:#333,stroke-width:4px; | ||||||
|  |     class C pink; | ||||||
							
								
								
									
										81
									
								
								test/seq.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								test/seq.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  |  | ||||||
|  | body { | ||||||
|  |     background: #fcfcfe; | ||||||
|  |     font-family: Helvetica; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .actor { | ||||||
|  |     stroke: #CCCCFF; | ||||||
|  |     fill: #ECECFF; | ||||||
|  | } | ||||||
|  | text.actor { | ||||||
|  |     fill:black; | ||||||
|  |     stroke:none; | ||||||
|  |     font-family: Helvetica; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .actor-line { | ||||||
|  |     stroke:grey; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .messageLine0 { | ||||||
|  |     stroke-width:1.5; | ||||||
|  |     stroke-dasharray: "2 2"; | ||||||
|  |     marker-end:"url(#arrowhead)"; | ||||||
|  |     stroke:black; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .messageLine1 { | ||||||
|  |     stroke-width:1.5; | ||||||
|  |     stroke-dasharray: "2 2"; | ||||||
|  |     stroke:black; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #arrowhead { | ||||||
|  |     fill:black; | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .messageText { | ||||||
|  |     fill:black; | ||||||
|  |     stroke:none; | ||||||
|  |     font-family: 'trebuchet ms', verdana, arial; | ||||||
|  |     font-size:14px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .labelBox { | ||||||
|  |     stroke: #CCCCFF; | ||||||
|  |     fill: #ECECFF; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .labelText { | ||||||
|  |     fill:black; | ||||||
|  |     stroke:none; | ||||||
|  |     font-family: 'trebuchet ms', verdana, arial; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .loopText { | ||||||
|  |     fill:black; | ||||||
|  |     stroke:none; | ||||||
|  |     font-family: 'trebuchet ms', verdana, arial; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .loopLine { | ||||||
|  |     stroke-width:2; | ||||||
|  |     stroke-dasharray: "2 2"; | ||||||
|  |     marker-end:"url(#arrowhead)"; | ||||||
|  |     stroke: #CCCCFF; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .note { | ||||||
|  |     stroke: #decc93; | ||||||
|  |     stroke: #CCCCFF; | ||||||
|  |     fill: #fff5ad; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noteText { | ||||||
|  |     fill:black; | ||||||
|  |     stroke:none; | ||||||
|  |     font-family: 'trebuchet ms', verdana, arial; | ||||||
|  |     font-size:14px; | ||||||
|  | } | ||||||
							
								
								
									
										97
									
								
								test/seq.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								test/seq.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  | <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |  | ||||||
|  |     <script src="../dist/mermaid.full.js"></script> | ||||||
|  |     <link rel="stylesheet" href="seq.css"/> | ||||||
|  |     <script> | ||||||
|  |         var mermaid_config = { | ||||||
|  |             startOnLoad:true | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
|  |     <script> | ||||||
|  |         function apa(){ | ||||||
|  |             console.log('CLICKED'); | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
|  |  | ||||||
|  | </head> | ||||||
|  |     <body> | ||||||
|  |     <h1>No line breaks</h1> | ||||||
|  |     <div class="mermaid"> | ||||||
|  |         sequenceDiagram;Alice->>Bob: Hello Bob, how are you?;Bob-->Bob: Hmmm?;Bob-->Alice: Ok; | ||||||
|  |     </div> | ||||||
|  |     <div class="mermaid"> | ||||||
|  |         sequenceDiagram;loop Daily query;Alice->>Bob: Hello Bob, how are you?;alt is sick;Bob->>Alice: Not so good :(;else is well;Bob->>Alice: Feeling fresh like a daisy;end;opt Extra response;Bob->>Alice: Thanks for asking;end;end; | ||||||
|  |     </div> | ||||||
|  |     <h1>Message types</h1> | ||||||
|  |  | ||||||
|  |     <div class="mermaid"> | ||||||
|  |         sequenceDiagram | ||||||
|  |         Alice->>Bob: Hello Bob, how are you? | ||||||
|  |         Bob-->>John: How about you John? | ||||||
|  |         Bob--xAlice: I am good thanks! | ||||||
|  |         Bob-xJohn: I am good thanks! | ||||||
|  |         Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row. | ||||||
|  |  | ||||||
|  |         Bob-->Alice: Checking with John... | ||||||
|  |         Alice->John: Yes... John, how are you? | ||||||
|  |     </div> | ||||||
|  |     <h1>Loops, alt and opt</h1> | ||||||
|  |     <div class="mermaid"> | ||||||
|  |         sequenceDiagram | ||||||
|  |         loop Daily query | ||||||
|  |             Alice->>Bob: Hello Bob, how are you? | ||||||
|  |             alt is sick | ||||||
|  |                 Bob->>Alice: Not so good :( | ||||||
|  |             else is well | ||||||
|  |                 Bob->>Alice: Feeling fresh like a daisy | ||||||
|  |             end | ||||||
|  |         opt Extra response | ||||||
|  |         Bob->>Alice: Thanks for asking | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         end | ||||||
|  |     </div> | ||||||
|  |     <h1>Message to self in loop</h1> | ||||||
|  |     <div class="mermaid"> | ||||||
|  |         sequenceDiagram | ||||||
|  |         participant Alice | ||||||
|  |         participant Bob | ||||||
|  |         Alice->>John: Hello John, how are you? | ||||||
|  |         loop Healthcheck | ||||||
|  |             John->>John: Fight against hypochondria | ||||||
|  |         end | ||||||
|  |         Note right of John: Rational thoughts<br/>prevail... | ||||||
|  |         John-->>Alice: Great! | ||||||
|  |         John->>Bob: How about you? | ||||||
|  |         Bob-->>John: Jolly good! | ||||||
|  |         </div> | ||||||
|  |     <h1>Bounding test & async message to self</h1> | ||||||
|  |     <div class="mermaid"> | ||||||
|  |         sequenceDiagram | ||||||
|  |         participant Alice | ||||||
|  |         participant Bob | ||||||
|  |         participant John the Long | ||||||
|  |         Alice->Bob: Hello Bob, how are you? | ||||||
|  |         loop Outer loop | ||||||
|  |         Note left of Alice: Bob thinks about <br/> things <br/> to think about | ||||||
|  |         Bob-xBob: I am good thanks! | ||||||
|  |         loop Inner loop | ||||||
|  |         Bob->>John the Long: How about you John? | ||||||
|  |         Note right of John the Long: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit. | ||||||
|  |         end | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         Bob-->>Alice: Checking with John... | ||||||
|  |         Alice->>John the Long: Yes... John, how are you? | ||||||
|  |         John the Long-->>Alice: Super! | ||||||
|  |     </div> | ||||||
|  |     <br/> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										173
									
								
								test/web.html
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								test/web.html
									
									
									
									
									
								
							| @@ -4,73 +4,176 @@ | |||||||
|     <meta charset="UTF-8"> |     <meta charset="UTF-8"> | ||||||
|  |  | ||||||
|     <script src="../dist/mermaid.full.js"></script> |     <script src="../dist/mermaid.full.js"></script> | ||||||
|  |     <scrpt src="https://cdn.rawgit.com/knsv/mermaid/0.3.0/dist/mermaid.full.min.js"></scrpt> | ||||||
|     <script> |     <script> | ||||||
|         var mermaid_config = { |         var mermaid_config = { | ||||||
|             startOnLoad:true |             startOnLoad:true | ||||||
|         } |         } | ||||||
|  |         mermaid.startOnLoad=true; | ||||||
|     </script> |     </script> | ||||||
|     <script> |     <script> | ||||||
|         function apa(){ |         function apa(){ | ||||||
|             console.log('CLICKED'); |             console.log('CLICKED'); | ||||||
|         } |         } | ||||||
|     </script> |     </script> | ||||||
|  |     <style> | ||||||
|  |         .cluster { | ||||||
|  |             fill: #fcac93; | ||||||
|  |             rx:4px; | ||||||
|  |             stroke: grey; | ||||||
|  |         } | ||||||
|  |     </style> | ||||||
|  |     <link rel="stylesheet" href="seq.css"/> | ||||||
| </head> | </head> | ||||||
|     <body> |     <body> | ||||||
|     <h1>Shapes</h1> |     <h1>Shapes</h1> | ||||||
|     Shape examples: |  | ||||||
|     <pre> |  | ||||||
|         graph TD; |  | ||||||
|             sq[Square shape]-->ci((Circle shape)); |  | ||||||
|             od>Odd shape]---|Two line <br>edge comment|ro; |  | ||||||
|             od2>Really long text in an Odd shape]-->od3>Really long text with linebreak <br>in an Odd shape]; |  | ||||||
|             di{Diamond is  <br> broken}-->ro(Rounded <br>square <br>shape); |  | ||||||
|  |  | ||||||
|             %% Comments after double percent signs |  | ||||||
|             di-->ro2(Rounded square shape); |  | ||||||
|             e((Inner circle))-->f(,.?!+-*ز); |  | ||||||
|             style e red; |  | ||||||
|     </pre> |  | ||||||
|  |  | ||||||
|     <div class="mermaid"> |     <div class="mermaid"> | ||||||
|         graph TD; |     graph TD; | ||||||
|         sq[Square shape]-->ci((Circle shape)); |         A-->B; | ||||||
|         od>Odd shape]---|Two line<br>edge comment|ro; |         A-->C; | ||||||
|         od2>Really long text in an Odd shape]-->od3>Really long text with linebreak<br>in an Odd shape]; |         A-->D; | ||||||
|         di{Diamond is <br/> broken}-->ro(Rounded<br>square<br>shape); |         B-->D; | ||||||
|         di-->ro2(Rounded square shape); |         A-->|Link text|B | ||||||
|  |         classDef default fill:#9f6,stroke:#333,stroke-width:2px; | ||||||
|  |         classDef green fill:#9f6,stroke:#333,stroke-width:2px; | ||||||
|  |         class green B; | ||||||
|  |     </div> | ||||||
|  |     <h1>Sub graphs</h1> | ||||||
|  |     <div class="mermaid">graph LR | ||||||
|  |         subgraph old sys 1 | ||||||
|  |             a1(new client)-->b1(sys1 server) | ||||||
|  |             oc1(Old client)-->b2 | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         subgraph old sys 2 | ||||||
|  |             a2(new client)-->b2(sys2 server) | ||||||
|  |             oc2(Old client)-->b2 | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         subgraph old sys 3 | ||||||
|  |             a3(new client)-->b3(sys3 server) | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         subgraph New sys | ||||||
|  |             a1 | ||||||
|  |             a2 | ||||||
|  |             a3 | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |     </div> | ||||||
|  |     <div class="mermaid2">graph TB | ||||||
|  |         subgraph one | ||||||
|  |         a1-->a2 | ||||||
|  |         end | ||||||
|  |         subgraph two | ||||||
|  |         b1-->b2 | ||||||
|  |         end | ||||||
|  |         subgraph three | ||||||
|  |         c1-->c2 | ||||||
|  |         end | ||||||
|  |         c1-->a2 | ||||||
|  |  | ||||||
|  |     </div> | ||||||
|  |     <div class="mermaid2">graph TB | ||||||
|  |         subgraph | ||||||
|  |             sq[Square shape] -.-> ci((Circle shape)) | ||||||
|  |             od>Odd shape]-. Two line<br>edge comment .-> ro | ||||||
|  |             di{Diamond with <br/> line break} ==> ro(Rounded<br>square<br>shape) | ||||||
|  |             di-->ro2(Rounded square shape) | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         %% Notice that no text in shape are added here instead that is appended further down | ||||||
|  |         subgraph Go go | ||||||
|  |             e --> od3>Really long text with linebreak<br>in an Odd shape] | ||||||
|  |  | ||||||
|  |             e((Inner / circle<br>and some odd <br>special characters)) --> f(,.?!+-*ز) | ||||||
|  |  | ||||||
|  |             cyr[Cyrillic]-->cyr2((Circle shape Начало)) | ||||||
|  |         end | ||||||
|  |         classDef green fill:#9f6,stroke:#333,stroke-width:2px; | ||||||
|  |         classDef orange fill:#f96,stroke:#333,stroke-width:4px,font-size:50%,font-style:bold; | ||||||
|  |         class sq,e green | ||||||
|  |         class di orange | ||||||
|  |     </div> | ||||||
|  |     <div class="mermaid2"> | ||||||
|  |         graph TB | ||||||
|  |         subgraph | ||||||
|  |             sq[Square shape]-->ci((Circle shape)) | ||||||
|  |             od>Odd shape]---|Two line<br>edge comment|ro | ||||||
|  |         end | ||||||
|  |         subgraph | ||||||
|  |             od2>Really long text in an Odd shape]-->od3>Really long text with linebreak<br>in an Odd shape]; | ||||||
|  |             di{Diamond is <br/> broken}-->ro(Rounded<br>square<br>shape); | ||||||
|  |             di-->ro2(Rounded square shape) | ||||||
|  |         end | ||||||
|         %% Comments after double percent signs |         %% Comments after double percent signs | ||||||
|         e((Inner / circle))-->f(,.?!+-*ز); |         subgraph | ||||||
|         cyr[Cyrillic]-->cyr2((Circle shape Начало)); |             e((Inner / circle))-->f(,.?!+-*ز); | ||||||
|  |             cyr[Cyrillic]-->cyr2((Circle shape Начало)); | ||||||
|  |             A[Object foo,bar]-->B(Thing) | ||||||
|  |         end | ||||||
|         style e red; |         style e red; | ||||||
|  |         classDef green fill:#9f6,stroke:#333,stroke-width:2px; | ||||||
|  |         class green sq | ||||||
|  |     </div> | ||||||
|  |     <div class="mermaid2"> | ||||||
|  |         graph LR; | ||||||
|  |         A(Central Message Router); | ||||||
|  |         B(R TD); | ||||||
|  |         C(XYZ); | ||||||
|  |         D(S Writer); | ||||||
|  |         A-->|R TD Router|B; | ||||||
|  |         B-->C; | ||||||
|  |         C-->|XYZ Router|D; | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <h1>Sequence diagrams (experimental)</h1> |     <h1>Sequence diagrams (experimental)</h1> | ||||||
|     <pre> |     <pre> | ||||||
|         sequenceDiagram |         sequenceDiagram | ||||||
|  |         participant John the Long | ||||||
|         Alice->Bob: Hello Bob, how are you? |         Alice->Bob: Hello Bob, how are you? | ||||||
|         Note right of Bob: Bob thinks |         Note left of Bob: Bob thinks | ||||||
|         Bob-->Alice: I am good thanks! |         Bob-->Alice: I am good thanks! | ||||||
|         Bob-->John the Long: How about you John? |         Bob-->John the Long: How about you John? | ||||||
|  |         Note left of John the Long: Bob thinks | ||||||
|         Bob-->Alice: Checking with John... |         Bob-->Alice: Checking with John... | ||||||
|  |  | ||||||
|  |         loop Multiple status checks | ||||||
|  |         loog Naging | ||||||
|         Alice->John the Long: Yes... John, how are you? |         Alice->John the Long: Yes... John, how are you? | ||||||
|  |         end | ||||||
|         John the Long-->Alice: Better then you! |         John the Long-->Alice: Better then you! | ||||||
|  |         end | ||||||
|     </pre> |     </pre> | ||||||
|     <div class="mermaid"> |     <div class="mermaid2"> | ||||||
|         sequenceDiagram |         sequenceDiagram | ||||||
|         Alice->Bob: Hello Bob, how are you? |         participant Alice | ||||||
|         Note right of Bob: Bob thinks about <br/> things <br/> to think about |         Note left of Alice: Bob thinks about <br/> things <br/> to think about | ||||||
|         Bob-->Alice: I am good thanks! |     </div> | ||||||
|         Bob-->John the Long: How about you John? |     <div class="mermaid2"> | ||||||
|         Bob-->Alice: Checking with John... |         sequenceDiagram | ||||||
|  |         participant Alice | ||||||
|  |         participant Bob | ||||||
|  |         participant John | ||||||
|  |         Alice->>Bob: Hello Bob, how are you? | ||||||
|  |         Note left of Alice: Bob thinks about <br/> things <br/> to think about | ||||||
|  |         Bob-->>Alice: I am good thanks! | ||||||
|  |         loop Multiple status checks | ||||||
|  |         Bob--xJohn: How about you John? | ||||||
|  |         Note right of John: Bob thinks | ||||||
|  |         end | ||||||
|  |  | ||||||
|  |         Bob--xAlice: Checking with John... | ||||||
|         Alice->John the Long: Yes... John, how are you? |         Alice->John the Long: Yes... John, how are you? | ||||||
|         John the Long-->Alice: Better then you! |         John the Long-->Alice: Better then you!! | ||||||
|  |  | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <div  class="mermaid"> |     <div  class="mermaid2"> | ||||||
| graph LR; | graph LR; | ||||||
|                 A[Start]-->B{a = '1,2'}; |                 A[Start]-->B{a = '1,2'} | ||||||
|                 B-->|True|C[test = 1]; |                 B-->|True|C[test = 1] | ||||||
|                 B-->|False|Z[Store]; |                 B-->|False|Z[Store] | ||||||
|                 C-->D{condition}; |                 C-->D{condition}; | ||||||
|                 D-->|True|E[test = 2]; |                 D-->|True|E[test = 2]; | ||||||
|                 D-->|False|F[test = 3]; |                 D-->|False|F[test = 3]; | ||||||
| @@ -96,7 +199,7 @@ graph LR; | |||||||
|         a -- e; |         a -- e; | ||||||
|         } |         } | ||||||
|     </pre> |     </pre> | ||||||
|     <div class="mermaid"> |     <div class="mermaid2"> | ||||||
|         digraph |         digraph | ||||||
|         { |         { | ||||||
|         a -> b -> c -- d -> e; |         a -> b -> c -- d -> e; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Björn Weström
					Björn Weström