diff --git a/README.md b/README.md index 317fee0fc..de9887bcc 100644 --- a/README.md +++ b/README.md @@ -12,26 +12,29 @@ This is why mermaid was born, a simple markdown-like script language for generat The code below would render the following image - - + + - - - - + + + + - - + + + + + + + + +
CodeRendered diagram
-
-
+    
CodeRendered diagram
+
+                
 graph TD;
     A-->B;
     A-->C;
     B-->D;
     C-->D;
-
-
-
-Example 1 -
-
-
+                
+            
+
+

+ Example 1 +

+
+
+                
 sequenceDiagram
     participant Alice
     participant Bob
@@ -43,13 +46,38 @@ sequenceDiagram
     John-->>Alice: Great!
     John->>Bob: How about you?
     Bob-->>John: Jolly good!
-
-
-
-Example 2 -
+ Example 2 +
+
+                
+classDiagram
+    Class01 <|-- AveryLongClass : Cool
+    Class03 *-- Class04
+    Class05 o-- Class06
+    Class07 .. Class08
+    Class09 --> C2 : Where am i?
+    Class09 --* C3
+    Class09 --|> Class07
+    Class07 : equals()
+    Class07 : Object[] elementData
+    Class01 : size()
+    Class01 : int chimp
+    Class01 : int gorilla
+    Class08 <--> C2: Cool label
+                
+            
+
+ Example 3 +
## Further reading diff --git a/docs/content/index.md b/docs/content/index.md index c870418db..8de3fcf91 100644 --- a/docs/content/index.md +++ b/docs/content/index.md @@ -86,6 +86,7 @@ Mermaid is supported in a number of publishing systems and editors. Please repor * [Plugin for atom](https://atom.io/packages/atom-mermaid) * [Markdown Plus](http://mdp.tylingsoft.com/) * [Vim Plugin](https://github.com/kannokanno/previm) +* [Sphinx extension](https://github.com/mgaitan/sphinxcontrib-mermaid) # Online live editor diff --git a/docs/content/sequenceDiagram.md b/docs/content/sequenceDiagram.md index 8261456b3..a5c3141c6 100755 --- a/docs/content/sequenceDiagram.md +++ b/docs/content/sequenceDiagram.md @@ -190,7 +190,7 @@ else end ``` -or if there is sequence that is optionat (if without else). +or if there is sequence that is optional (if without else). ``` opt Describing text diff --git a/docs/content/usage.md b/docs/content/usage.md index 9627b9e00..9781e7926 100644 --- a/docs/content/usage.md +++ b/docs/content/usage.md @@ -77,7 +77,7 @@ Would end up like this: ``` An id is also added to mermaid tags without id. -###Labels out of bounds +### Labels out of bounds If you use dynamically loaded fonts that are loaded through CSS, such as Google fonts, mermaid should wait for the whole page to have been loaded (dom + assets, particularly the fonts file). diff --git a/docs/img/class-diagram.png b/docs/img/class-diagram.png new file mode 100644 index 000000000..bf6e379e1 Binary files /dev/null and b/docs/img/class-diagram.png differ diff --git a/lib/cli.js b/lib/cli.js index ed037a589..db28e62e4 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -29,7 +29,7 @@ function cli(options) { , css: 't' , width: 'w' } - , 'boolean': ['help', 'png', 'svg'] + , 'boolean': ['help', 'png', 'svg', 'verbose'] , 'string': ['outputDir'] } @@ -94,25 +94,38 @@ cli.prototype.parse = function(argv, next) { else { options.png = true } - + if (options.sequenceConfig) { - options.sequenceConfig = checkConfig(options.sequenceConfig) + try { + fs.accessSync(options.sequenceConfig, fs.R_OK) + + } catch (err) { + this.errors.push(err) + } + } else { + options.sequenceConfig = null } - + if (options.ganttConfig) { - options.ganttConfig = checkConfig(options.ganttConfig) + try { + fs.accessSync(options.ganttConfig, fs.R_OK) + + } catch (err) { + this.errors.push(err) + } + } else { + options.ganttConfig = null } if (options.css) { try { - options.css = fs.readFileSync(options.css, 'utf8') - + fs.accessSync(options.css, fs.R_OK) } catch (err) { this.errors.push(err) } } else { - options.css = fs.readFileSync(path.join(__dirname, '..', 'dist', 'mermaid.css')) + options.css = path.join(__dirname, '..', 'dist', 'mermaid.css') } // set svg/png flags appropriately @@ -136,17 +149,6 @@ cli.prototype.parse = function(argv, next) { } } -function checkConfig(configPath) { - try { - var text = fs.readFileSync(configPath, 'utf8'); - JSON.parse(text) - return text - } catch (e) { - console.log(e); - return null; - } -} - function createCheckPhantom(_phantomPath) { var phantomPath = _phantomPath , phantomVersion diff --git a/lib/index.js b/lib/index.js index a7dd65e70..5da3683b2 100644 --- a/lib/index.js +++ b/lib/index.js @@ -18,7 +18,7 @@ function processMermaid(files, _options, _next) { , outputDir , options.png , options.svg - , options.css || '' + , options.css , options.sequenceConfig , options.ganttConfig , options.verbose diff --git a/lib/phantomscript.js b/lib/phantomscript.js index cd4ce259c..555591583 100644 --- a/lib/phantomscript.js +++ b/lib/phantomscript.js @@ -28,7 +28,6 @@ var system = require('system') , fs = require('fs') , webpage = require('webpage') - var page = webpage.create() , files = system.args.slice(9, system.args.length) , width = system.args[8] @@ -40,21 +39,15 @@ var options = { outputDir: system.args[1] , png: system.args[2] === 'true' ? true : false , svg: system.args[3] === 'true' ? true : false - , css: system.args[4] !== '' ? system.args[4] : '* { margin: 0; padding: 0; }' - , sequenceConfig: system.args[5] - , ganttConfig: system.args[6] + , css: fs.read(system.args[4]) + , sequenceConfig: system.args[5] !== 'null' ? JSON.parse(fs.read(system.args[5])) : '{}' + , ganttConfig: system.args[6] !== 'null' ? fs.read(system.args[6]) : '{}' , verbose: system.args[7] === 'true' ? true : false , width: width } , log = logger(options.verbose) - // If no css is suuplied make sure a fixed witdth is given to the gant renderer - if(system.args[3] !== ''){ - if(typeof options.ganttConfig === 'undefined'){ - options.ganttConfig = {}; - } - options.ganttConfig.useWidth = 1200; - } +options.sequenceConfig.useMaxWidth = false; page.content = [ '' @@ -218,7 +211,7 @@ function resolveForeignObjects(element) { function executeInPage(data) { var xmlSerializer = new XMLSerializer() , contents = data.contents - , sequenceConfig = data.sequenceConfig + , sequenceConfig = JSON.stringify(data.sequenceConfig) , ganttConfig = data.ganttConfig , toRemove , el @@ -229,7 +222,7 @@ function executeInPage(data) { , width , height , confWidth = data.confWidth - + toRemove = document.getElementsByClassName('mermaid') if (toRemove && toRemove.length) { for (var i = 0, len = toRemove.length; i < len; i++) { @@ -242,41 +235,24 @@ function executeInPage(data) { elContent = document.createTextNode(contents) el.appendChild(elContent) //el.innerText = 'hello\uD800' //contents; - + document.body.appendChild(el) - - mermaid.initialize({ - sequenceDiagram:{useMaxWidth:false}, - flowchart:{useMaxWidth:false}, - logLevel:1 - }); + + var config = { + sequenceDiagram: sequenceConfig, + flowchart: {useMaxWidth: false}, + logLevel: 1 + }; + + mermaid.initialize(config); + //console.log('after initialize',sequenceConfig); - if(typeof sequenceConfig !== undefined && sequenceConfig !== 'undefined'){ - //sc = document.createElement("script") - //scContent = document.createTextNode('mermaid.sequenceConfig = JSON.parse(' + JSON.stringify(sequenceConfig) + ');') - //sc.appendChild(scContent) + sc = document.createElement("script") + scContent = document.createTextNode('mermaid.ganttConfig = ' + ganttConfig + ';') + sc.appendChild(scContent) - //document.body.appendChild(sc) - mermaid.initialize({ - sequenceDiagram:JSON.parse(sequenceConfig) - }); - } - - //console.log('after initialize 2'); - if(typeof ganttConfig !== undefined && ganttConfig !== 'undefined'){ - sc = document.createElement("script") - scContent = document.createTextNode('mermaid.ganttConfig = JSON.parse(' + JSON.stringify(ganttConfig) + ');') - sc.appendChild(scContent) - - document.body.appendChild(sc) - }else{ - sc = document.createElement("script") - scContent = document.createTextNode('mermaid.ganttConfig = {useWidth:1200};') - sc.appendChild(scContent) - - document.body.appendChild(sc) - } + document.body.appendChild(sc) mermaid.init(); diff --git a/src/diagrams/flowchart/flowRenderer.js b/src/diagrams/flowchart/flowRenderer.js index 997ccdbd7..96cfa67de 100644 --- a/src/diagrams/flowchart/flowRenderer.js +++ b/src/diagrams/flowchart/flowRenderer.js @@ -425,6 +425,25 @@ exports.draw = function (text, id,isDot) { dagreD3.util.applyStyle(path, edge[type + 'Style']); }; + // Override normal arrowhead defined in d3. Remove style & add class to allow css styling. + render.arrows().normal = function normal(parent, id, edge, type) { + var marker = parent.append("marker") + .attr("id", id) + .attr("viewBox", "0 0 10 10") + .attr("refX", 9) + .attr("refY", 5) + .attr("markerUnits", "strokeWidth") + .attr("markerWidth", 8) + .attr("markerHeight", 6) + .attr("orient", "auto") + + var path = marker.append("path") + .attr("d", "M 0 0 L 10 5 L 0 10 z") + .attr("class", "arrowheadPath") + .style("stroke-width", 1) + .style("stroke-dasharray", "1,0"); + }; + // Set up an SVG group so that we can translate the final graph. var svg = d3.select('#' + id); //svgGroup = d3.select('#' + id + ' g'); diff --git a/src/less/dark/flow.less b/src/less/dark/flow.less index 2457e77a8..a108bbccd 100644 --- a/src/less/dark/flow.less +++ b/src/less/dark/flow.less @@ -9,6 +9,11 @@ stroke-width: 1px; } + +.arrowheadPath { + fill: @arrowheadColor; +} + .edgePath .path { stroke: @lineColor; } diff --git a/src/less/dark/variables.less b/src/less/dark/variables.less index 0206f8949..c77932990 100644 --- a/src/less/dark/variables.less +++ b/src/less/dark/variables.less @@ -5,6 +5,7 @@ @lineColor: @mainContrastColor; @border1: #81B1DB; @border2: rgba(255, 255, 255, 0.25); +@arrowheadColor: @mainContrastColor; /* Flowchart variables */ diff --git a/src/less/default/flow.less b/src/less/default/flow.less index 3d999bd8a..dcd66c1ba 100644 --- a/src/less/default/flow.less +++ b/src/less/default/flow.less @@ -9,6 +9,10 @@ stroke-width: 1px; } +.arrowheadPath { + fill: @arrowheadColor; +} + .edgePath .path { stroke: @lineColor; } diff --git a/src/less/default/variables.less b/src/less/default/variables.less index 0634374ca..3a69d86d4 100644 --- a/src/less/default/variables.less +++ b/src/less/default/variables.less @@ -3,6 +3,7 @@ @lineColor: #333333; @border1:#CCCCFF; @border2:#aaaa33; +@arrowheadColor: #333333; /* Flowchart variables */ @nodeBkg:@mainBkg; diff --git a/src/less/forest/flow.less b/src/less/forest/flow.less index 47e8de05f..26ca2dae7 100644 --- a/src/less/forest/flow.less +++ b/src/less/forest/flow.less @@ -12,6 +12,10 @@ color:#333 stroke-width: 1px; } +.arrowheadPath { + fill: @arrowheadColor; +} + .edgePath .path { stroke: @lineColor; stroke-width: 1.5px; diff --git a/src/less/forest/variables.less b/src/less/forest/variables.less index 8bfcdceb5..d797c96f4 100644 --- a/src/less/forest/variables.less +++ b/src/less/forest/variables.less @@ -4,6 +4,7 @@ @lineColor: green; @border1: #13540c; @border2: #6eaa49; +@arrowheadColor: green; /* Flowchart variables */ @nodeBkg:@mainBkg; diff --git a/src/less/neutral/classDiagram.less b/src/less/neutral/classDiagram.less new file mode 100644 index 000000000..cb7bf0fe3 --- /dev/null +++ b/src/less/neutral/classDiagram.less @@ -0,0 +1,69 @@ +g.classGroup text { + fill: @nodeBorder; + stroke:none; + font-family: Arial, Helvetica, sans-serif; + font-size: 14px; +} + +g.classGroup rect { + fill:@nodeBkg; + stroke: @nodeBorder; +} + +g.classGroup line { + stroke: @nodeBorder; + stroke-width:1; +} + +svg .classLabel .box { + stroke: none; + stroke-width:0; + fill: @nodeBkg; + opacity: 0.5; +} + +svg .classLabel .label { + fill: @nodeBorder; +} + +.relation { + stroke: @nodeBorder; + stroke-width: 1; + fill:none; +} + +.composition{ + fill : @nodeBorder; + stroke: @nodeBorder; + stroke-width:1; +} +#compositionStart { + .composition; +} +#compositionEnd { + .composition; +} +.aggregation{ + fill : @nodeBkg; + stroke: @nodeBorder; + stroke-width:1; +} +#aggregationStart { + .aggregation; +} +#aggregationEnd { + .aggregation; +} + +#dependencyStart { + .composition; +} +#dependencyEnd { + .composition; +} +#extensionStart { + .composition; +} +#extensionEnd { + .composition; +} \ No newline at end of file diff --git a/src/less/neutral/flow.less b/src/less/neutral/flow.less new file mode 100644 index 000000000..270d5f3f0 --- /dev/null +++ b/src/less/neutral/flow.less @@ -0,0 +1,30 @@ +.mermaid .label { color: @text } + +.node rect, +.node circle, +.node ellipse, +.node polygon { + fill: @mainBkg; + stroke: @nodeBorder; + stroke-width: 1px; +} + +.edgePath .path { + stroke: @lineColor; + stroke-width: 1.5px; +} + +.edgeLabel { + background-color: @edgeLabelBackground; +} + +.cluster rect { + fill: @secondBkg !important; + rx: 4 !important; + stroke: @clusterBorder !important; + stroke-width: 1px !important; +} + +.cluster text { + fill: @titleColor; +} diff --git a/src/less/neutral/gantt.less b/src/less/neutral/gantt.less new file mode 100644 index 000000000..76368387e --- /dev/null +++ b/src/less/neutral/gantt.less @@ -0,0 +1,135 @@ +/** Section styling */ +.section { + stroke: none; + opacity:0.2; +} +.section0{ + fill: @sectionBkgColor; +} +.section2 { + fill: @sectionBkgColor2; +} +.section1,.section3 { + fill: @altSectionBkgColor; + opacity: 0.2; +} + +.sectionTitle0 { fill: @titleColor;} +.sectionTitle1 { fill: @titleColor;} +.sectionTitle2 { fill: @titleColor;} +.sectionTitle3 { fill: @titleColor;} + + +.sectionTitle { + text-anchor: start; + font-size: 11px; + text-height: 14px; +} + +/* Grid and axis */ +.grid .tick { + stroke: @gridColor; + opacity: 0.3; + shape-rendering: crispEdges; +} +.grid path { + stroke-width: 0; +} + +/* Today line */ +.today { + fill: none; + stroke: @todayLineColor; + stroke-width: 2px; +} + +/* Task styling */ +/* Default task */ +.task { + stroke-width: 2; +} + +.taskText { + text-anchor: middle; + font-size: 11px; +} +.taskTextOutsideRight { + fill: @taskTextDarkColor; + text-anchor: start; + font-size: 11px; +} +.taskTextOutsideLeft { + fill: @taskTextDarkColor; + text-anchor: end; + font-size: 11px; +} + +/* Specific task settings for the sections*/ + +.taskText0, .taskText1, .taskText2, .taskText3 { + fill: @taskTextColor; +} + +.task0, .task1, .task2, .task3 { + fill: @taskBkgColor; + stroke: @taskBorderColor; +} + +.taskTextOutside0,.taskTextOutside2, { + fill: @taskTextOutsideColor; +} +.taskTextOutside1, .taskTextOutside3 { + fill: @taskTextOutsideColor; +} + +/* Active task */ +.active0, .active1, .active2, .active3 { + fill: @activeTaskBkgColor; + stroke: @activeTaskBorderColor; +} + +.activeText0, .activeText1, .activeText2, .activeText3 { + fill: @taskTextDarkColor !important; +} +/* Completed task */ +.done0, .done1, .done2, .done3 { + stroke: @doneTaskBorderColor; + fill: @doneTaskBkgColor; + stroke-width: 2; +} + +.doneText0, .doneText1, .doneText2, .doneText3 { + fill: @taskTextDarkColor !important; +} + +/* Tasks on the critical line */ +.crit0, .crit1, .crit2, .crit3 { + stroke:@critBorderColor; + fill: @critBkgColor; + stroke-width: 2; +} +.activeCrit0, .activeCrit1, .activeCrit2, .activeCrit3 { + stroke: @critBorderColor; + fill: @activeTaskBkgColor; + stroke-width: 2; +} +.doneCrit0, .doneCrit1, .doneCrit2, .doneCrit3 { + stroke: @critBorderColor; + fill: @doneTaskBkgColor; + stroke-width: 2; + cursor: pointer; + //shape-rendering: crispEdges; +} +.doneCritText0, .doneCritText1, .doneCritText2, .doneCritText3 { + fill: @taskTextDarkColor !important; +} +.activeCritText0, .activeCritText1, .activeCritText2, .activeCritText3 { + fill: @taskTextDarkColor !important; +} + +.titleText { + text-anchor: middle; + font-size: 18px; + fill: @taskTextDarkColor; +} + diff --git a/src/less/neutral/mermaid.less b/src/less/neutral/mermaid.less new file mode 100644 index 000000000..06568095d --- /dev/null +++ b/src/less/neutral/mermaid.less @@ -0,0 +1,24 @@ +@import "variables"; +@import "flow"; +@import "sequenceDiagram"; +@import "gantt"; +@import "classDiagram"; + +.node text { + font-family: Arial, Helvetica, sans-serif; + font-size:14px; +} + +div.mermaidTooltip { + position: absolute; + text-align: center; + max-width: 200px; + padding: 2px; + font-family: Arial, Helvetica, sans-serif; + font-size: 12px; + background: @secondBkg; + border: 1px solid @border2; + border-radius: 2px; + pointer-events: none; + z-index:100; +} \ No newline at end of file diff --git a/src/less/neutral/sequenceDiagram.less b/src/less/neutral/sequenceDiagram.less new file mode 100644 index 000000000..e42c061de --- /dev/null +++ b/src/less/neutral/sequenceDiagram.less @@ -0,0 +1,75 @@ + + +.actor { + stroke: @actorBorder; + fill: @actorBkg; +} +text.actor { + fill: @actorTextColor; + stroke: none; +} + +.actor-line { + stroke: @actorLineColor; +} + +.messageLine0 { + stroke-width: 1.5; + stroke-dasharray: "2 2"; + marker-end: "url(#arrowhead)"; + stroke: @signalColor; +} + +.messageLine1 { + stroke-width: 1.5; + stroke-dasharray: "2 2"; + stroke: @signalColor; +} + +#arrowhead { + fill: @signalColor; +} +#crosshead path { + fill: @signalColor !important; + stroke: @signalColor !important; + +} +.messageText { + fill: @signalTextColor; + stroke: none; +} + +.labelBox { + stroke: @labelBoxBorderColor; + fill: @labelBoxBkgColor; +} + +.labelText { + fill: @labelTextColor; + stroke: none; +} + +.loopText { + fill: @labelTextColor; + stroke: none; +} + +.loopLine { + stroke-width: 2; + stroke-dasharray: "2 2"; + marker-end: "url(#arrowhead)"; + stroke: @labelBoxBorderColor; +} + +.note { + //stroke: #decc93; + stroke: @noteBorderColor; + fill: @noteBkgColor; +} + +.noteText { + fill: black; + stroke: none; + font-family: Arial, Helvetica, sans-serif; + font-size: 14px; +} \ No newline at end of file diff --git a/src/less/neutral/variables.less b/src/less/neutral/variables.less new file mode 100644 index 000000000..ccf93fb01 --- /dev/null +++ b/src/less/neutral/variables.less @@ -0,0 +1,66 @@ +@mainBkg: #eee; +@secondBkg: lighten(@contrast, 55%); +@lineColor: #666; +@border1: #999; +@border2: @contrast; + +@note: #ffa; +@text: #333; +@contrast: #26a; +@critical: #d42; +@done: #bbb; + +/* Flowchart variables */ +@nodeBkg: @mainBkg; +@nodeBorder: @border1; + +@clusterBkg: @secondBkg; +@clusterBorder: @border2; + +@defaultLinkColor: @lineColor; + +@titleColor: @text; + +@edgeLabelBackground: white; + +/* Sequence Diagram variables */ +@actorBorder: @border1; +@actorBkg: @mainBkg; +@actorTextColor: @text; +@actorLineColor: @lineColor; + +@signalColor: @text; +@signalTextColor: @text; + +@labelBoxBkgColor: @contrast; +@labelBoxBorderColor: @contrast; +@labelTextColor: white; + +@noteBorderColor: darken(@note, 60%); +@noteBkgColor: @note; + +/* Gantt chart variables */ +@sectionBkgColor: lighten(@contrast, 30%); +@altSectionBkgColor: white; +@sectionBkgColor2: lighten(@contrast, 30%); + +@taskBorderColor: darken(@contrast, 10%); +@taskBkgColor: @contrast; +@taskTextColor: @taskTextLightColor; +@taskTextOutsideColor: @taskTextDarkColor; + +@activeTaskBorderColor: @taskBorderColor; +@activeTaskBkgColor: @mainBkg; + +@gridColor: lighten(@border1, 30%); + +@doneTaskBkgColor: @done; +@doneTaskBorderColor: @lineColor; + +@critBorderColor: darken(@critBkgColor, 10%); +@critBkgColor: @critical; + +@taskTextLightColor: white; +@taskTextDarkColor: @text; + +@todayLineColor: @critBkgColor; \ No newline at end of file diff --git a/src/mermaidAPI.js b/src/mermaidAPI.js index b4e284fe1..e1949b341 100644 --- a/src/mermaidAPI.js +++ b/src/mermaidAPI.js @@ -71,7 +71,7 @@ var config = { startOnLoad: true, /** - * **arrowMarkerAbsolute** - This options controls whether or arrow markers in html code will be absolute pats or + * **arrowMarkerAbsolute** - This options controls whether or arrow markers in html code will be absolute paths or * an anchor, #. This matters if you are using base tag settings. */ arrowMarkerAbsolute: false, diff --git a/test/cli_test-output.js b/test/cli_test-output.js index befaa0f08..775193e27 100644 --- a/test/cli_test-output.js +++ b/test/cli_test-output.js @@ -24,12 +24,18 @@ var singleFile = { , outputDir: path.join(process.cwd(),'test/tmp2/') , phantomPath: path.join(process.cwd(),phantomCmd) , width : 1200 + , css: path.join(__dirname, '..', 'dist', 'mermaid.css') + , sequenceConfig: null + , ganttConfig: null } , multiFile = { files: [path.join('test','fixtures','test.mermaid'), path.join('test','fixtures','test2.mermaid')] , outputDir: 'test/tmp2/' , phantomPath: path.join(process.cwd(),phantomCmd) , width : 1200 + , css: path.join(__dirname, '..', 'dist', 'mermaid.css') + , sequenceConfig: null + , ganttConfig: null } @@ -40,7 +46,7 @@ test('output of single png', function(t) { opt = clone(singleFile) opt.png = true - + mermaid.process(opt.files, opt, function(code) { t.equal(code, 0, 'has clean exit code') @@ -113,7 +119,7 @@ test('output including CSS', function(t) { one = fs.statSync(filename) //console.log('one: '+opt.files[0]); - opt2.css = fs.readFileSync(path.join('test','fixtures','test.css'), 'utf8') + opt2.css = path.join('test','fixtures','test.css') //console.log(opt2.css); console.log('Generating #2');