This commit is contained in:
Knut Sveidqvist
2016-10-30 16:13:11 +01:00
23 changed files with 538 additions and 91 deletions

View File

@@ -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 The code below would render the following image
<table> <table>
<tr><th>Code</th><th>Rendered diagram</th></tr> <tr><th>Code</th><th>Rendered diagram</th></tr>
<tr><td> <tr>
<pre> <td>
<code> <pre>
<code>
graph TD; graph TD;
A-->B; A-->B;
A-->C; A-->C;
B-->D; B-->D;
C-->D; C-->D;
<code> </code>
</pre> </pre>
</td> </td>
<td> <td>
<img src='http://www.sveido.com/mermaid/img/ex1.png' alt='Example 1'> <p align="center">
</td> <img src='http://www.sveido.com/mermaid/img/ex1.png' alt='Example 1'>
</tr> </p>
<tr> </td>
<td> </tr>
<pre> <tr>
<code> <td>
<pre>
<code>
sequenceDiagram sequenceDiagram
participant Alice participant Alice
participant Bob participant Bob
@@ -43,13 +46,38 @@ sequenceDiagram
John-->>Alice: Great! John-->>Alice: Great!
John->>Bob: How about you? John->>Bob: How about you?
Bob-->>John: Jolly good! Bob-->>John: Jolly good!
</code> </code>
</pre> </pre>
</td> </td>
<td> <td>
<img src='http://www.sveido.com/mermaid/img/seq1.png' alt='Example 2'> <img src='http://www.sveido.com/mermaid/img/seq1.png' alt='Example 2'>
</td> </td>
</tr> </tr>
<tr>
<td>
<pre>
<code>
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
</code>
</pre>
</td>
<td>
<img src='./docs/img/class-diagram.png' alt='Example 3'>
</td>
</tr>
</table> </table>
## Further reading ## Further reading

View File

@@ -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) * [Plugin for atom](https://atom.io/packages/atom-mermaid)
* [Markdown Plus](http://mdp.tylingsoft.com/) * [Markdown Plus](http://mdp.tylingsoft.com/)
* [Vim Plugin](https://github.com/kannokanno/previm) * [Vim Plugin](https://github.com/kannokanno/previm)
* [Sphinx extension](https://github.com/mgaitan/sphinxcontrib-mermaid)
# Online live editor # Online live editor

View File

@@ -190,7 +190,7 @@ else
end 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 opt Describing text

View File

@@ -77,7 +77,7 @@ Would end up like this:
``` ```
An id is also added to mermaid tags without id. 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 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). whole page to have been loaded (dom + assets, particularly the fonts file).

BIN
docs/img/class-diagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -29,7 +29,7 @@ function cli(options) {
, css: 't' , css: 't'
, width: 'w' , width: 'w'
} }
, 'boolean': ['help', 'png', 'svg'] , 'boolean': ['help', 'png', 'svg', 'verbose']
, 'string': ['outputDir'] , 'string': ['outputDir']
} }
@@ -94,25 +94,38 @@ cli.prototype.parse = function(argv, next) {
else { else {
options.png = true options.png = true
} }
if (options.sequenceConfig) { 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) { 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) { if (options.css) {
try { try {
options.css = fs.readFileSync(options.css, 'utf8') fs.accessSync(options.css, fs.R_OK)
} catch (err) { } catch (err) {
this.errors.push(err) this.errors.push(err)
} }
} else { } else {
options.css = fs.readFileSync(path.join(__dirname, '..', 'dist', 'mermaid.css')) options.css = path.join(__dirname, '..', 'dist', 'mermaid.css')
} }
// set svg/png flags appropriately // 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) { function createCheckPhantom(_phantomPath) {
var phantomPath = _phantomPath var phantomPath = _phantomPath
, phantomVersion , phantomVersion

View File

@@ -18,7 +18,7 @@ function processMermaid(files, _options, _next) {
, outputDir , outputDir
, options.png , options.png
, options.svg , options.svg
, options.css || '' , options.css
, options.sequenceConfig , options.sequenceConfig
, options.ganttConfig , options.ganttConfig
, options.verbose , options.verbose

View File

@@ -28,7 +28,6 @@ var system = require('system')
, fs = require('fs') , fs = require('fs')
, webpage = require('webpage') , webpage = require('webpage')
var page = webpage.create() var page = webpage.create()
, files = system.args.slice(9, system.args.length) , files = system.args.slice(9, system.args.length)
, width = system.args[8] , width = system.args[8]
@@ -40,21 +39,15 @@ var options = {
outputDir: system.args[1] outputDir: system.args[1]
, png: system.args[2] === 'true' ? true : false , png: system.args[2] === 'true' ? true : false
, svg: system.args[3] === 'true' ? true : false , svg: system.args[3] === 'true' ? true : false
, css: system.args[4] !== '' ? system.args[4] : '* { margin: 0; padding: 0; }' , css: fs.read(system.args[4])
, sequenceConfig: system.args[5] , sequenceConfig: system.args[5] !== 'null' ? JSON.parse(fs.read(system.args[5])) : '{}'
, ganttConfig: system.args[6] , ganttConfig: system.args[6] !== 'null' ? fs.read(system.args[6]) : '{}'
, verbose: system.args[7] === 'true' ? true : false , verbose: system.args[7] === 'true' ? true : false
, width: width , width: width
} }
, log = logger(options.verbose) , log = logger(options.verbose)
// If no css is suuplied make sure a fixed witdth is given to the gant renderer options.sequenceConfig.useMaxWidth = false;
if(system.args[3] !== ''){
if(typeof options.ganttConfig === 'undefined'){
options.ganttConfig = {};
}
options.ganttConfig.useWidth = 1200;
}
page.content = [ page.content = [
'<html>' '<html>'
@@ -218,7 +211,7 @@ function resolveForeignObjects(element) {
function executeInPage(data) { function executeInPage(data) {
var xmlSerializer = new XMLSerializer() var xmlSerializer = new XMLSerializer()
, contents = data.contents , contents = data.contents
, sequenceConfig = data.sequenceConfig , sequenceConfig = JSON.stringify(data.sequenceConfig)
, ganttConfig = data.ganttConfig , ganttConfig = data.ganttConfig
, toRemove , toRemove
, el , el
@@ -229,7 +222,7 @@ function executeInPage(data) {
, width , width
, height , height
, confWidth = data.confWidth , confWidth = data.confWidth
toRemove = document.getElementsByClassName('mermaid') toRemove = document.getElementsByClassName('mermaid')
if (toRemove && toRemove.length) { if (toRemove && toRemove.length) {
for (var i = 0, len = toRemove.length; i < len; i++) { for (var i = 0, len = toRemove.length; i < len; i++) {
@@ -242,41 +235,24 @@ function executeInPage(data) {
elContent = document.createTextNode(contents) elContent = document.createTextNode(contents)
el.appendChild(elContent) el.appendChild(elContent)
//el.innerText = '<b>hello</b>\uD800' //contents; //el.innerText = '<b>hello</b>\uD800' //contents;
document.body.appendChild(el) document.body.appendChild(el)
mermaid.initialize({ var config = {
sequenceDiagram:{useMaxWidth:false}, sequenceDiagram: sequenceConfig,
flowchart:{useMaxWidth:false}, flowchart: {useMaxWidth: false},
logLevel:1 logLevel: 1
}); };
mermaid.initialize(config);
//console.log('after initialize',sequenceConfig); //console.log('after initialize',sequenceConfig);
if(typeof sequenceConfig !== undefined && sequenceConfig !== 'undefined'){ sc = document.createElement("script")
//sc = document.createElement("script") scContent = document.createTextNode('mermaid.ganttConfig = ' + ganttConfig + ';')
//scContent = document.createTextNode('mermaid.sequenceConfig = JSON.parse(' + JSON.stringify(sequenceConfig) + ');') sc.appendChild(scContent)
//sc.appendChild(scContent)
//document.body.appendChild(sc) 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)
}
mermaid.init(); mermaid.init();

View File

@@ -425,6 +425,25 @@ exports.draw = function (text, id,isDot) {
dagreD3.util.applyStyle(path, edge[type + 'Style']); 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. // Set up an SVG group so that we can translate the final graph.
var svg = d3.select('#' + id); var svg = d3.select('#' + id);
//svgGroup = d3.select('#' + id + ' g'); //svgGroup = d3.select('#' + id + ' g');

View File

@@ -9,6 +9,11 @@
stroke-width: 1px; stroke-width: 1px;
} }
.arrowheadPath {
fill: @arrowheadColor;
}
.edgePath .path { .edgePath .path {
stroke: @lineColor; stroke: @lineColor;
} }

View File

@@ -5,6 +5,7 @@
@lineColor: @mainContrastColor; @lineColor: @mainContrastColor;
@border1: #81B1DB; @border1: #81B1DB;
@border2: rgba(255, 255, 255, 0.25); @border2: rgba(255, 255, 255, 0.25);
@arrowheadColor: @mainContrastColor;
/* Flowchart variables */ /* Flowchart variables */

View File

@@ -9,6 +9,10 @@
stroke-width: 1px; stroke-width: 1px;
} }
.arrowheadPath {
fill: @arrowheadColor;
}
.edgePath .path { .edgePath .path {
stroke: @lineColor; stroke: @lineColor;
} }

View File

@@ -3,6 +3,7 @@
@lineColor: #333333; @lineColor: #333333;
@border1:#CCCCFF; @border1:#CCCCFF;
@border2:#aaaa33; @border2:#aaaa33;
@arrowheadColor: #333333;
/* Flowchart variables */ /* Flowchart variables */
@nodeBkg:@mainBkg; @nodeBkg:@mainBkg;

View File

@@ -12,6 +12,10 @@ color:#333
stroke-width: 1px; stroke-width: 1px;
} }
.arrowheadPath {
fill: @arrowheadColor;
}
.edgePath .path { .edgePath .path {
stroke: @lineColor; stroke: @lineColor;
stroke-width: 1.5px; stroke-width: 1.5px;

View File

@@ -4,6 +4,7 @@
@lineColor: green; @lineColor: green;
@border1: #13540c; @border1: #13540c;
@border2: #6eaa49; @border2: #6eaa49;
@arrowheadColor: green;
/* Flowchart variables */ /* Flowchart variables */
@nodeBkg:@mainBkg; @nodeBkg:@mainBkg;

View File

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

View File

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

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

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

View File

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

View File

@@ -0,0 +1,75 @@
.actor {
stroke: @actorBorder;
fill: @actorBkg;
}
text.actor {
fill: @actorTextColor;
stroke: none;
}
.actor-line {
stroke: @actorLineColor;
}
.messageLine0 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: @signalColor;
}
.messageLine1 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
stroke: @signalColor;
}
#arrowhead {
fill: @signalColor;
}
#crosshead path {
fill: @signalColor !important;
stroke: @signalColor !important;
}
.messageText {
fill: @signalTextColor;
stroke: none;
}
.labelBox {
stroke: @labelBoxBorderColor;
fill: @labelBoxBkgColor;
}
.labelText {
fill: @labelTextColor;
stroke: none;
}
.loopText {
fill: @labelTextColor;
stroke: none;
}
.loopLine {
stroke-width: 2;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: @labelBoxBorderColor;
}
.note {
//stroke: #decc93;
stroke: @noteBorderColor;
fill: @noteBkgColor;
}
.noteText {
fill: black;
stroke: none;
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
}

View File

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

View File

@@ -71,7 +71,7 @@ var config = {
startOnLoad: true, 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. * an anchor, #. This matters if you are using base tag settings.
*/ */
arrowMarkerAbsolute: false, arrowMarkerAbsolute: false,

View File

@@ -24,12 +24,18 @@ var singleFile = {
, outputDir: path.join(process.cwd(),'test/tmp2/') , outputDir: path.join(process.cwd(),'test/tmp2/')
, phantomPath: path.join(process.cwd(),phantomCmd) , phantomPath: path.join(process.cwd(),phantomCmd)
, width : 1200 , width : 1200
, css: path.join(__dirname, '..', 'dist', 'mermaid.css')
, sequenceConfig: null
, ganttConfig: null
} }
, multiFile = { , multiFile = {
files: [path.join('test','fixtures','test.mermaid'), path.join('test','fixtures','test2.mermaid')] files: [path.join('test','fixtures','test.mermaid'), path.join('test','fixtures','test2.mermaid')]
, outputDir: 'test/tmp2/' , outputDir: 'test/tmp2/'
, phantomPath: path.join(process.cwd(),phantomCmd) , phantomPath: path.join(process.cwd(),phantomCmd)
, width : 1200 , 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 = clone(singleFile)
opt.png = true opt.png = true
mermaid.process(opt.files, opt, function(code) { mermaid.process(opt.files, opt, function(code) {
t.equal(code, 0, 'has clean exit code') t.equal(code, 0, 'has clean exit code')
@@ -113,7 +119,7 @@ test('output including CSS', function(t) {
one = fs.statSync(filename) one = fs.statSync(filename)
//console.log('one: '+opt.files[0]); //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(opt2.css);
console.log('Generating #2'); console.log('Generating #2');