Merge pull request #14 from mermaid-js/develop

merge from
This commit is contained in:
Justin Greywolf 2020-01-24 15:58:42 -08:00 committed by GitHub
commit 425b071a50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 356 additions and 72 deletions

View File

@ -374,6 +374,7 @@ describe('Flowchart', () => {
click B testClick "click test" click B testClick "click test"
classDef someclass fill:#f96; classDef someclass fill:#f96;
class A someclass; class A someclass;
class C someclass;
`, `,
{ {
listUrl: false, listUrl: false,
@ -396,6 +397,7 @@ describe('Flowchart', () => {
{ flowchart: { htmlLabels: false } } { flowchart: { htmlLabels: false } }
); );
}); });
it('16: Render Stadium shape', () => { it('16: Render Stadium shape', () => {
imgSnapshotTest( imgSnapshotTest(
` graph TD ` graph TD
@ -408,10 +410,13 @@ describe('Flowchart', () => {
click A "index.html#link-clicked" "link test" click A "index.html#link-clicked" "link test"
click B testClick "click test" click B testClick "click test"
classDef someclass fill:#f96; classDef someclass fill:#f96;
class A someclass;`, class A someclass;
class C someclass;
`,
{ flowchart: { htmlLabels: false } } { flowchart: { htmlLabels: false } }
); );
}); });
it('17: Render multiline texts', () => { it('17: Render multiline texts', () => {
imgSnapshotTest( imgSnapshotTest(
`graph LR `graph LR
@ -428,6 +433,7 @@ describe('Flowchart', () => {
{ flowchart: { htmlLabels: false } } { flowchart: { htmlLabels: false } }
); );
}); });
it('18: Chaining of nodes', () => { it('18: Chaining of nodes', () => {
imgSnapshotTest( imgSnapshotTest(
`graph LR `graph LR
@ -436,6 +442,7 @@ describe('Flowchart', () => {
{ flowchart: { htmlLabels: false } } { flowchart: { htmlLabels: false } }
); );
}); });
it('19: Multiple nodes and chaining in one statement', () => { it('19: Multiple nodes and chaining in one statement', () => {
imgSnapshotTest( imgSnapshotTest(
`graph LR `graph LR
@ -444,6 +451,7 @@ describe('Flowchart', () => {
{ flowchart: { htmlLabels: false } } { flowchart: { htmlLabels: false } }
); );
}); });
it('20: Multiple nodes and chaining in one statement', () => { it('20: Multiple nodes and chaining in one statement', () => {
imgSnapshotTest( imgSnapshotTest(
`graph TD `graph TD
@ -453,6 +461,7 @@ describe('Flowchart', () => {
{ flowchart: { htmlLabels: false } } { flowchart: { htmlLabels: false } }
); );
}); });
it('21: Render cylindrical shape', () => { it('21: Render cylindrical shape', () => {
imgSnapshotTest( imgSnapshotTest(
`graph LR `graph LR
@ -474,6 +483,7 @@ describe('Flowchart', () => {
{ flowchart: { htmlLabels: false } } { flowchart: { htmlLabels: false } }
); );
}); });
it('22: Render a simple flowchart with nodeSpacing set to 100', () => { it('22: Render a simple flowchart with nodeSpacing set to 100', () => {
imgSnapshotTest( imgSnapshotTest(
`graph TD `graph TD
@ -487,6 +497,7 @@ describe('Flowchart', () => {
{ flowchart: { nodeSpacing: 50 } } { flowchart: { nodeSpacing: 50 } }
); );
}); });
it('23: Render a simple flowchart with rankSpacing set to 100', () => { it('23: Render a simple flowchart with rankSpacing set to 100', () => {
imgSnapshotTest( imgSnapshotTest(
`graph TD `graph TD
@ -500,4 +511,17 @@ describe('Flowchart', () => {
{ flowchart: { rankSpacing: '100' } } { flowchart: { rankSpacing: '100' } }
); );
}); });
it('24: Keep node label text (if already defined) when a style is applied', () => {
imgSnapshotTest(
`graph LR
A(( )) -->|step 1| B(( ))
B(( )) -->|step 2| C(( ))
C(( )) -->|step 3| D(( ))
linkStyle 1 stroke:greenyellow,stroke-width:2px
style C fill:greenyellow,stroke:green,stroke-width:4px
`,
{ flowchart: { htmlLabels: false } }
);
});
}); });

View File

@ -121,6 +121,18 @@ describe('State diagram', () => {
{} {}
); );
}); });
it('should handle multiline notes with different line breaks', () => {
imgSnapshotTest(
`
stateDiagram
State1
note right of State1
Line1<br>Line2<br/>Line3<br />Line4<br />Line5
end note
`,
{}
);
});
it('should render a states with descriptions including multi-line descriptions', () => { it('should render a states with descriptions including multi-line descriptions', () => {
imgSnapshotTest( imgSnapshotTest(

107
dist/index.html vendored
View File

@ -289,16 +289,17 @@ graph TB
style 456ac9b0d15a8b7f1e71073221059886 fill:#f9f,stroke:#333,stroke-width:4px style 456ac9b0d15a8b7f1e71073221059886 fill:#f9f,stroke:#333,stroke-width:4px
</div> </div>
<div class="mermaid"> <div class="mermaid">
graph TD graph TD
A[Christmas] -->|Get money| B(Go shopping) A[Christmas] -->|Get money| B(Go shopping)
B --> C{{Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?}} B --> C{{Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?}}
C -->|One| D[Laptop] C -->|One| D[Laptop]
C -->|Two| E[iPhone] C -->|Two| E[iPhone]
C -->|Three| F[Car] C -->|Three| F[Car]
click A "index.html#link-clicked" "link test" click A "index.html#link-clicked" "link test"
click B testClick "click test" click B testClick "click test"
classDef someclass fill:#f96; classDef someclass fill:#f96;
class A someclass; class A someclass;
class C someclass;
</div> </div>
<div class="mermaid"> <div class="mermaid">
graph TD graph TD
@ -312,6 +313,7 @@ class A someclass;
click B testClick "click test" click B testClick "click test"
classDef someclass fill:#f96; classDef someclass fill:#f96;
class A someclass; class A someclass;
class C someclass;
</div> </div>
<div class="mermaid"> <div class="mermaid">
graph LR graph LR
@ -343,6 +345,14 @@ class A someclass;
linkStyle 1 stroke:DarkGray,stroke-width:2px linkStyle 1 stroke:DarkGray,stroke-width:2px
linkStyle 2 stroke:DarkGray,stroke-width:2px linkStyle 2 stroke:DarkGray,stroke-width:2px
</div> </div>
<div class="mermaid">
graph LR
A(( )) -->|step 1| B(( ))
B(( )) -->|step 2| C(( ))
C(( )) -->|step 3| D(( ))
linkStyle 1 stroke:greenyellow,stroke-width:2px
style C fill:greenyellow,stroke:green,stroke-width:4px
</div>
<hr/> <hr/>
@ -519,45 +529,46 @@ class Class10 {
int id int id
size() size()
} }
</div> </div>
<div class="mermaid"> <div class="mermaid">
classDiagram classDiagram
Class01~T~ <|-- AveryLongClass : Cool Class01~T~ <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01 &lt;&lt;interface&gt;&gt; Class01
Class03~T~ "0" *-- "0..n" Class04 Class03~T~ "0" *-- "0..n" Class04
Class05 "1" o-- "many" Class06 Class05 "1" o-- "many" Class06
Class07~T~ .. Class08 Class07~T~ .. Class08
Class09 "many" --> "1" C2 : Where am i? Class09 "many" --> "1" C2 : Where am i?
Class09 "0" --* "1..n" C3 Class09 "0" --* "1..n" C3
Class09 --|> Class07 Class09 --|> Class07
Class07 : equals() Class07 : equals()
Class07 : Object[] elementData Class07 : Object[] elementData
Class01 : #size() Class01 : #size()
Class01 : -int chimp Class01 : -int chimp
Class01 : +int gorilla Class01 : +int gorilla
Class08 <--> C2: Cool label Class08 <--> C2: Cool label
class Class10 { class Class10 {
&lt;&lt;service&gt;&gt; &lt;&lt;service&gt;&gt;
int id int id
size() size()
} }
</div> </div>
<div class="mermaid"> <div class="mermaid">
stateDiagram stateDiagram
State1 State1
</div> </div>
<hr> <hr>
<div class="mermaid">
stateDiagram <div class="mermaid">
[*] --> First stateDiagram
state First { [*] --> First
[*] --> second state First {
second --> [*] [*] --> second
} second --> [*]
</div> }
</div>
<div class="mermaid"> <div class="mermaid">
stateDiagram stateDiagram
State1: The state with a note State1: The state with a note
@ -567,8 +578,14 @@ class Class10 {
end note end note
State1 --> State2 State1 --> State2
note left of State2 : This is the note to the left. note left of State2 : This is the note to the left.
</div> </div>
<div class="mermaid">
stateDiagram
State1
note right of State1
Line1<br>Line2<br/>Line3<br />Line4<br />Line5
end note
</div>
<script src="./mermaid.js"></script> <script src="./mermaid.js"></script>
<script> <script>

View File

@ -314,6 +314,37 @@ graph LR
A -- text --> B -- text2 --> C A -- text --> B -- text2 --> C
``` ```
It is also possible to declare multiple nodes links in the same line as per below:
```
graph LR
a --> b & c--> d
```
```mermaid
graph LR
a --> b & c--> d
```
You can then describe dependencies in a very expressive way. Like the onliner below:
```
graph TB
A & B--> C & D
```
```mermaid
graph TB
A & B--> C & D
```
If you describe the same diagram using the the basic syntax, it will take four lines. A
word of warning, one could go overboard with this making the graph harder to read in
markdown form. The Swedish word `lagom` comes to mind. It means, not to much and not to little.
This goes for expressive syntaxes as well.
```
graph TB
A --> C
A --> D
B --> C
B --> D
```
## Special characters that break syntax ## Special characters that break syntax
@ -512,6 +543,19 @@ It is also possible to attach a class to a list of nodes in one statement:
class nodeId1,nodeId2 className; class nodeId1,nodeId2 className;
``` ```
A shorter form of adding a class is to attach the classname to the node using the `:::`operator as per below:
```
graph LR
A:::someclass --> B
classDef someclass fill:#f96;
```
```mermaid
graph LR
A:::someclass --> B
classDef someclass fill:#f96;
```
### Css classes ### Css classes

View File

@ -19,7 +19,7 @@ These are the default options which can be overridden with the initialization ca
<pre> <pre>
mermaid.initialize({ mermaid.initialize({
flowchart:{ flowchart:{
htmlLabels: false htmlLabels: false
} }
}); });
</pre> </pre>
@ -27,7 +27,7 @@ mermaid.initialize({
**Example 2:** **Example 2:**
<pre> <pre>
<script> &lt;script>
var config = { var config = {
startOnLoad:true, startOnLoad:true,
flowchart:{ flowchart:{
@ -39,10 +39,10 @@ mermaid.initialize({
securityLevel:'loose', securityLevel:'loose',
}; };
mermaid.initialize(config); mermaid.initialize(config);
</script> &lt;/script>
</pre> </pre>
A summary of all options and their defaults is found [here](https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults). A description of each option follows below.
A summary of all options and their defaults is found [here][2]. A description of each option follows below.
## theme ## theme
@ -333,3 +333,5 @@ mermaidAPI.initialize({
</pre> </pre>
[1]: https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#render [1]: https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#render
[2]: https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults

View File

@ -1,6 +1,6 @@
{ {
"name": "mermaid", "name": "mermaid",
"version": "8.4.5", "version": "8.4.6",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "dist/mermaid.core.js", "main": "dist/mermaid.core.js",
"keywords": [ "keywords": [

View File

@ -140,7 +140,7 @@ export const addMembers = function(className, members) {
export const cleanupLabel = function(label) { export const cleanupLabel = function(label) {
if (label.substring(0, 1) === ':') { if (label.substring(0, 1) === ':') {
return label.substr(2).trim(); return label.substr(1).trim();
} else { } else {
return label.trim(); return label.trim();
} }

View File

@ -67,6 +67,48 @@ describe('class diagram, ', function () {
parser.parse(str); parser.parse(str);
}); });
it('should break when another `{`is encountered before closing the first one while defining generic class with brackets', function() {
const str =
'classDiagram\n' +
'class Dummy_Class~T~ {\n' +
'String data\n' +
' void methods()\n' +
'}\n' +
'\n' +
'class Dummy_Class {\n' +
'class Flight {\n' +
' flightNumber : Integer\n' +
' departureTime : Date\n' +
'}';
let testPased =false;
try{
parser.parse(str);
}catch (error){
console.log(error.name);
testPased = true;
}
expect(testPased).toBe(true);
});
it('should break when EOF is encountered before closing the first `{` while defining generic class with brackets', function() {
const str =
'classDiagram\n' +
'class Dummy_Class~T~ {\n' +
'String data\n' +
' void methods()\n' +
'}\n' +
'\n' +
'class Dummy_Class {\n';
let testPased =false;
try{
parser.parse(str);
}catch (error){
console.log(error.name);
testPased = true;
}
expect(testPased).toBe(true);
});
it('should handle generic class with brackets', function() { it('should handle generic class with brackets', function() {
const str = const str =
'classDiagram\n' + 'classDiagram\n' +
@ -79,8 +121,6 @@ describe('class diagram, ', function () {
' flightNumber : Integer\n' + ' flightNumber : Integer\n' +
' departureTime : Date\n' + ' departureTime : Date\n' +
'}'; '}';
parser.parse(str);
}); });
it('should handle class definitions', function() { it('should handle class definitions', function() {

View File

@ -540,9 +540,14 @@ export const draw = function(text, id) {
logger.info( logger.info(
'tjoho' + getGraphId(relation.id1) + getGraphId(relation.id2) + JSON.stringify(relation) 'tjoho' + getGraphId(relation.id1) + getGraphId(relation.id2) + JSON.stringify(relation)
); );
g.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), { g.setEdge(
relation: relation getGraphId(relation.id1),
}); getGraphId(relation.id2),
{
relation: relation
},
relation.title || 'DEFAULT'
);
}); });
dagre.layout(g); dagre.layout(g);
g.nodes().forEach(function(v) { g.nodes().forEach(function(v) {

View File

@ -14,6 +14,8 @@
\s+ /* skip whitespace */ \s+ /* skip whitespace */
"classDiagram" return 'CLASS_DIAGRAM'; "classDiagram" return 'CLASS_DIAGRAM';
[\{] { this.begin("struct"); /*console.log('Starting struct');*/return 'STRUCT_START';} [\{] { this.begin("struct"); /*console.log('Starting struct');*/return 'STRUCT_START';}
<struct><<EOF>> return "EOF_IN_STRUCT";
<struct>[\{] return "OPEN_IN_STRUCT";
<struct>\} { /*console.log('Ending struct');*/this.popState(); return 'STRUCT_STOP';}} <struct>\} { /*console.log('Ending struct');*/this.popState(); return 'STRUCT_STOP';}}
<struct>[\n] /* nothing */ <struct>[\n] /* nothing */
<struct>[^\{\}\n]* { /*console.log('lex-member: ' + yytext);*/ return "MEMBER";} <struct>[^\{\}\n]* { /*console.log('lex-member: ' + yytext);*/ return "MEMBER";}

View File

@ -52,7 +52,7 @@ export const addVertex = function(_id, text, type, style, classes) {
vertices[id].text = txt; vertices[id].text = txt;
} else { } else {
if (!vertices[id].text) { if (typeof vertices[id].text === 'undefined') {
vertices[id].text = _id; vertices[id].text = _id;
} }
} }

View File

@ -88,7 +88,7 @@ export const addVertices = function(vert, g, svgId) {
} else { } else {
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
const rows = vertexText.split(/<br[/]{0,1}>/); const rows = vertexText.split(/<br\s*\/?>/gi);
for (let j = 0; j < rows.length; j++) { for (let j = 0; j < rows.length; j++) {
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
@ -237,7 +237,7 @@ export const addEdges = function(edges, g) {
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>'; edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
} else { } else {
edgeData.labelType = 'text'; edgeData.labelType = 'text';
edgeData.label = edge.text.replace(/<br\s*\/?>/g, '\n'); edgeData.label = edge.text.replace(/<br\s*\/?>/gi, '\n');
if (typeof edge.style === 'undefined') { if (typeof edge.style === 'undefined') {
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none'; edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
@ -461,6 +461,7 @@ export const draw = function(text, id) {
const node = d3.select('#' + id + ' [id="' + key + '"]'); const node = d3.select('#' + id + ' [id="' + key + '"]');
if (node) { if (node) {
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a'); const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link); link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener'); link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');

View File

@ -55,6 +55,43 @@ describe('the flowchart renderer', function() {
expect(addedNodes[0][1]).toHaveProperty('ry', expectedRadios); expect(addedNodes[0][1]).toHaveProperty('ry', expectedRadios);
}); });
}); });
[
'Multi<br>Line',
'Multi<br/>Line',
'Multi<br />Line',
'Multi<br\t/>Line'
].forEach(function(labelText) {
it('should handle multiline texts with different line breaks', function() {
const addedNodes = [];
const mockG = {
setNode: function(id, object) {
addedNodes.push([id, object]);
}
};
addVertices(
{
v1: {
type: 'rect',
id: 'my-node-id',
classes: [],
styles: [],
text: 'Multi<br>Line'
}
},
mockG,
'svg-id'
);
expect(addedNodes).toHaveLength(1);
expect(addedNodes[0][0]).toEqual('my-node-id');
expect(addedNodes[0][1]).toHaveProperty('id', 'my-node-id');
expect(addedNodes[0][1]).toHaveProperty('labelType', 'svg');
expect(addedNodes[0][1].label).toBeDefined();
expect(addedNodes[0][1].label).toBeDefined(); // <text> node
expect(addedNodes[0][1].label.firstChild.innerHTML).toEqual('Multi'); // <tspan> node, line 1
expect(addedNodes[0][1].label.lastChild.innerHTML).toEqual('Line'); // <tspan> node, line 2
});
});
}); });
[ [
@ -109,9 +146,11 @@ describe('the flowchart renderer', function() {
{ text: 'Multi<br>Line' }, { text: 'Multi<br>Line' },
{ text: 'Multi<br/>Line' }, { text: 'Multi<br/>Line' },
{ text: 'Multi<br />Line' }, { text: 'Multi<br />Line' },
{ text: 'Multi<br\t/>Line' },
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br>Line' }, { style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br>Line' },
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br/>Line' }, { style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br/>Line' },
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br />Line' } { style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br />Line' },
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br\t/>Line' }
], ],
mockG, mockG,
'svg-id' 'svg-id'

View File

@ -86,6 +86,17 @@ describe('[Style] when parsing', () => {
expect(vert['T'].styles[1]).toBe('border:1px solid red'); expect(vert['T'].styles[1]).toBe('border:1px solid red');
}); });
it('should keep node label text (if already defined) when a style is applied', function() {
const res = flow.parser.parse('graph TD;A(( ));B((Test));C;style A background:#fff;style D border:1px solid red;');
const vert = flow.parser.yy.getVertices();
expect(vert['A'].text).toBe('');
expect(vert['B'].text).toBe('Test');
expect(vert['C'].text).toBe('C');
expect(vert['D'].text).toBe('D');
});
it('should be possible to declare a class', function() { it('should be possible to declare a class', function() {
const res = flow.parser.parse( const res = flow.parser.parse(
'graph TD;classDef exClass background:#bbb,border:1px solid red;' 'graph TD;classDef exClass background:#bbb,border:1px solid red;'

View File

@ -16,6 +16,25 @@ export const addActor = function(id, name, description) {
actors[id] = { name: name, description: description }; actors[id] = { name: name, description: description };
}; };
const activationCount = part => {
let i = 0;
let count = 0;
for (i = 0; i < messages.length; i++) {
// console.warn(i, messages[i]);
if (messages[i].type === LINETYPE.ACTIVE_START) {
if (messages[i].from.actor === part) {
count++;
}
}
if (messages[i].type === LINETYPE.ACTIVE_END) {
if (messages[i].from.actor === part) {
count--;
}
}
}
return count;
};
export const addMessage = function(idFrom, idTo, message, answer) { export const addMessage = function(idFrom, idTo, message, answer) {
messages.push({ from: idFrom, to: idTo, message: message, answer: answer }); messages.push({ from: idFrom, to: idTo, message: message, answer: answer });
}; };
@ -24,7 +43,25 @@ export const addSignal = function(idFrom, idTo, message, messageType) {
logger.debug( logger.debug(
'Adding message from=' + idFrom + ' to=' + idTo + ' message=' + message + ' type=' + messageType 'Adding message from=' + idFrom + ' to=' + idTo + ' message=' + message + ' type=' + messageType
); );
if (messageType === LINETYPE.ACTIVE_END) {
const cnt = activationCount(idFrom.actor);
logger.debug('Adding message from=', messages, cnt);
if (cnt < 1) {
// Bail out as there is an activation signal from an inactive participant
var error = new Error('Trying to inactivate an inactive participant (' + idFrom.actor + ')');
error.hash = {
text: '->>-',
token: '->>-',
line: '1',
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
expected: ["'ACTIVE_PARTICIPANT'"]
};
throw error;
}
}
messages.push({ from: idFrom, to: idTo, message: message, type: messageType }); messages.push({ from: idFrom, to: idTo, message: message, type: messageType });
return true;
}; };
export const getMessages = function() { export const getMessages = function() {

View File

@ -214,6 +214,33 @@ describe('when parsing a sequenceDiagram', function() {
expect(messages[7].type).toBe(parser.yy.LINETYPE.ACTIVE_END); expect(messages[7].type).toBe(parser.yy.LINETYPE.ACTIVE_END);
expect(messages[7].from.actor).toBe('Carol'); expect(messages[7].from.actor).toBe('Carol');
}); });
it('it should handle fail parsing when activating an inactive participant', function() {
const str =
`sequenceDiagram
participant user as End User
participant Server as Server
participant System as System
participant System2 as System2
user->>+Server: Test
user->>+Server: Test2
user->>System: Test
Server->>-user: Test
Server->>-user: Test2
%% The following deactivation of Server will fail
Server->>-user: Test3`;
let error = false;
try {
parser.parse(str);
} catch(e) {
console.log(e.hash);
error = true;
}
expect(error).toBe(true);
});
it('it should handle comments in a sequenceDiagram', function() { it('it should handle comments in a sequenceDiagram', function() {
const str = const str =
'sequenceDiagram\n' + 'sequenceDiagram\n' +

View File

@ -280,7 +280,7 @@ const drawForkJoinState = (g, stateDef) => {
export const drawText = function(elem, textData) { export const drawText = function(elem, textData) {
// Remove and ignore br:s // Remove and ignore br:s
const nText = textData.text.replace(/<br\/?>/gi, ' '); const nText = textData.text.replace(/<br\s*\/?>/gi, ' ');
const textElem = elem.append('text'); const textElem = elem.append('text');
textElem.attr('x', textData.x); textElem.attr('x', textData.x);
@ -308,7 +308,7 @@ const _drawLongText = (_text, x, y, g) => {
let text = _text.replace(/\r\n/g, '<br/>'); let text = _text.replace(/\r\n/g, '<br/>');
text = text.replace(/\n/g, '<br/>'); text = text.replace(/\n/g, '<br/>');
const lines = text.split(/<br\/?>/gi); const lines = text.split(/<br\s*\/?>/gi);
let tHeight = 1.25 * getConfig().state.noteMargin; let tHeight = 1.25 * getConfig().state.noteMargin;
for (const line of lines) { for (const line of lines) {
@ -392,7 +392,7 @@ export const drawState = function(elem, stateDef) {
}; };
const getRows = s => { const getRows = s => {
let str = s.replace(/<br\/?>/gi, '#br#'); let str = s.replace(/<br\s*\/?>/gi, '#br#');
str = str.replace(/\\n/g, '#br#'); str = str.replace(/\\n/g, '#br#');
return str.split('#br#'); return str.split('#br#');
}; };

View File

@ -261,6 +261,16 @@ describe('state diagram, ', function() {
parser.parse(str); parser.parse(str);
}); });
it('should handle multiline notes with different line breaks', function() {
const str = `stateDiagram
State1
note right of State1
Line1<br>Line2<br/>Line3<br />Line4<br />Line5
end note
`;
parser.parse(str);
});
it('should handle floating notes', function() { it('should handle floating notes', function() {
const str = `stateDiagram const str = `stateDiagram
foo: bar foo: bar

View File

@ -102,7 +102,7 @@ const getLabelWidth = text => {
/* TODO: REMOVE DUPLICATION, SEE SHAPES */ /* TODO: REMOVE DUPLICATION, SEE SHAPES */
const getRows = s => { const getRows = s => {
if (!s) return 1; if (!s) return 1;
let str = s.replace(/<br\/?>/gi, '#br#'); let str = s.replace(/<br\s*\/?>/gi, '#br#');
str = str.replace(/\\n/g, '#br#'); str = str.replace(/\\n/g, '#br#');
return str.split('#br#'); return str.split('#br#');
}; };

View File

@ -98,7 +98,7 @@ const init = function() {
txt = he txt = he
.decode(txt) .decode(txt)
.trim() .trim()
.replace(/<br>/gi, '<br/>'); .replace(/<br\s*\/?>/gi, '<br/>');
mermaidAPI.render( mermaidAPI.render(
id, id,

View File

@ -52,14 +52,14 @@ for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
* <pre> * <pre>
* mermaid.initialize({ * mermaid.initialize({
* flowchart:{ * flowchart:{
* htmlLabels: false * htmlLabels: false
* } * }
* }); * });
* </pre> * </pre>
* *
* **Example 2:** * **Example 2:**
* <pre> * <pre>
* <script> * &lt;script>
* var config = { * var config = {
* startOnLoad:true, * startOnLoad:true,
* flowchart:{ * flowchart:{
@ -71,7 +71,7 @@ for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
* securityLevel:'loose', * securityLevel:'loose',
* }; * };
* mermaid.initialize(config); * mermaid.initialize(config);
* </script> * &lt;/script>
* </pre> * </pre>
* A summary of all options and their defaults is found [here](https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults). A description of each option follows below. * A summary of all options and their defaults is found [here](https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults). A description of each option follows below.
* *
@ -142,6 +142,20 @@ const config = {
*/ */
htmlLabels: true, htmlLabels: true,
/**
* Defines the spacing between nodes on the same level (meaning horizontal spacing for
* TB or BT graphs, and the vertical spacing for LR as well as RL graphs).
* **Default value 50**.
*/
nodeSpacing: 50,
/**
* Defines the spacing between nodes on different levels (meaning vertical spacing for
* TB or BT graphs, and the horizontal spacing for LR as well as RL graphs).
* **Default value 50**.
*/
rankSpacing: 50,
/** /**
* How mermaid renders curves for flowcharts. Possible values are * How mermaid renders curves for flowcharts. Possible values are
* * basis * * basis

View File

@ -84,8 +84,7 @@ export const sanitize = (text, config) => {
htmlLabels = false; htmlLabels = false;
if (config.securityLevel !== 'loose' && htmlLabels) { // eslint-disable-line if (config.securityLevel !== 'loose' && htmlLabels) { // eslint-disable-line
txt = txt.replace(/<br>/g, '#br#'); txt = txt.replace(/<br\s*\/?>/gi, '#br#');
txt = txt.replace(/<br\S*?\/>/g, '#br#');
txt = txt.replace(/</g, '&lt;').replace(/>/g, '&gt;'); txt = txt.replace(/</g, '&lt;').replace(/>/g, '&gt;');
txt = txt.replace(/=/g, '&equals;'); txt = txt.replace(/=/g, '&equals;');
txt = txt.replace(/#br#/g, '<br/>'); txt = txt.replace(/#br#/g, '<br/>');