Merge branch 'release/8.8.2'

This commit is contained in:
Knut Sveidqvist 2020-10-08 19:45:50 +02:00
commit acc4f31c3a
99 changed files with 4640 additions and 1429 deletions

View File

@ -3,7 +3,7 @@
![banner](./img/header.png)
**Edit this Page** [![N|Solid](./docs/assets/img/GitHub-Mark-32px.png)](https://github.com/mermaid-js/mermaid/blob/develop/docs/README.md)
:trophy: **Mermaid was nominated and won the [JS Open Source Awards (2019)](https://osawards.com/javascript/#nominees) in the category "The most exciting use of technology"!!!**
:trophy: **Mermaid was nominated and won the [JS Open Source Awards (2019)](https://osawards.com/javascript/2019) in the category "The most exciting use of technology"!!!**
**Thanks to all involved, people committing pull requests, people answering questions and special thanks to Tyler Long who is helping me maintain the project 🙏**
@ -23,7 +23,7 @@ Even non-programmers can create diagrams through the [Mermaid Live Editor](https
[Tutorials](./docs/getting-started/Tutorials.md) has video tutorials.
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/overview/integrations.md).
For a more detailed introduction to Mermaid and some of it's more basic uses, look to the [Beginner's Guide](./docs/overview/n00b-overview.md) and [Usage](./docs/getting-started/usage.md).
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/overview/n00b-overview.md) and [Usage](./docs/getting-started/usage.md).
🌐 [CDN](https://unpkg.com/mermaid/) | 📖 [Documentation](https://mermaidjs.github.io) | 🙌 [Contribution](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md) | 📜 [Changelog](./docs/tutorials-and-community/CHANGELOG.md)

View File

@ -1,4 +1,5 @@
/* eslint-env jest */
/* global cy */
import { Base64 } from 'js-base64';
export const mermaidUrl = (graphStr, options, api) => {

View File

@ -1,44 +1,89 @@
/* eslint-env jest */
describe('Interaction', () => {
describe('Interaction - security level loose', () => {
it('should handle a click on a node with a bound function', () => {
it('Graph: should handle a click on a node with a bound function', () => {
const url = 'http://localhost:9000/click_security_loose.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g#Function')
.find('g#flowchart-Function-2')
.click();
cy.get('.created-by-click').should('have.text', 'Clicked By Flow');
});
it('should handle a click on a node with a bound function where the node starts with a number', () => {
it('Graph: should handle a click on a node with a bound function where the node starts with a number', () => {
const url = 'http://localhost:9000/click_security_loose.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g[id="1Function"]')
.find('g[id="flowchart-1Function-6"]')
.click();
cy.get('.created-by-click').should('have.text', 'Clicked By Flow');
});
it('should handle a click on a node with a bound url', () => {
it('Graph: should handle a click on a node with a bound url', () => {
const url = 'http://localhost:9000/click_security_loose.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g#URL')
.find('#flowchart-URL-3')
.click();
cy.location().should(location => {
expect(location.href).to.eq('http://localhost:9000/webpackUsage.html');
});
});
it('should handle a click on a node with a bound url where the node starts with a number', () => {
it('Graph: should handle a click on a node with a bound url where the node starts with a number', () => {
const url = 'http://localhost:9000/click_security_loose.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g[id="2URL"]')
.find('g[id="flowchart-2URL-7"]')
.click();
cy.location().should(location => {
expect(location.href).to.eq('http://localhost:9000/webpackUsage.html');
});
});
it('Flowchart-v2: should handle a click on a node with a bound function', () => {
const url = 'http://localhost:9000/click_security_loose.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g#flowchart-Function-10')
.click();
cy.get('.created-by-click').should('have.text', 'Clicked By Flow');
});
it('Flowchart-v2: should handle a click on a node with a bound function where the node starts with a number', () => {
const url = 'http://localhost:9000/click_security_loose.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g[id="flowchart-1Function-14"]')
.click();
cy.get('.created-by-click').should('have.text', 'Clicked By Flow');
});
it('Flowchart-v2: should handle a click on a node with a bound url', () => {
const url = 'http://localhost:9000/click_security_loose.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('#flowchart-URL-11')
.click();
cy.location().should(location => {
expect(location.href).to.eq('http://localhost:9000/webpackUsage.html');
});
});
it('Flowchart-v2: should handle a click on a node with a bound url where the node starts with a number', () => {
const url = 'http://localhost:9000/click_security_loose.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g[id="flowchart-2URL-15"]')
.click();
cy.location().should(location => {
@ -120,7 +165,7 @@ describe('Interaction', () => {
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g#Function')
.find('g#flowchart-Function-2')
.click();
cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow');
@ -130,7 +175,7 @@ describe('Interaction', () => {
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g[id="1Function"]')
.find('g[id="flowchart-1Function-6"]')
.click();
cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow');
@ -140,7 +185,7 @@ describe('Interaction', () => {
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g#URL')
.find('g#flowchart-URL-3')
.click();
cy.location().should(location => {
@ -152,7 +197,7 @@ describe('Interaction', () => {
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g[id="2URL"]')
.find('g[id="flowchart-2URL-7"]')
.click();
cy.location().should(location => {
@ -208,31 +253,31 @@ describe('Interaction', () => {
describe('Interaction - security level other, missspelling', () => {
it('should handle a click on a node with a bound function', () => {
const url = 'http://localhost:9000/click_security_strict.html';
const url = 'http://localhost:9000/click_security_other.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g#Function')
.find('g#flowchart-Function-2')
.click();
cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow');
});
it('should handle a click on a node with a bound function where the node starts with a number', () => {
const url = 'http://localhost:9000/click_security_strict.html';
const url = 'http://localhost:9000/click_security_other.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g[id="1Function"]')
.find('g[id="flowchart-1Function-6"]')
.click();
cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow');
});
it('should handle a click on a node with a bound url', () => {
const url = 'http://localhost:9000/click_security_strict.html';
const url = 'http://localhost:9000/click_security_other.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
.find('g#URL')
.find('g#flowchart-URL-3')
.click();
cy.location().should(location => {
@ -241,7 +286,7 @@ describe('Interaction', () => {
});
it('should handle a click on a task with a bound function', () => {
const url = 'http://localhost:9000/click_security_strict.html';
const url = 'http://localhost:9000/click_security_other.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')
@ -251,7 +296,7 @@ describe('Interaction', () => {
cy.get('.created-by-gant-click').should('not.have.text', 'Clicked By Gant cl2');
});
it('should handle a click on a task with a bound function', () => {
const url = 'http://localhost:9000/click_security_strict.html';
const url = 'http://localhost:9000/click_security_other.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('body')

View File

@ -11,12 +11,12 @@ describe('Rerendering', () => {
const url = 'http://localhost:9000/rerender.html';
cy.viewport(1440, 1024);
cy.visit(url);
cy.get('#graph #A').should('have.text', 'XMas');
cy.get('#graph [id^=flowchart-A]').should('have.text', 'XMas');
cy.get('body')
.find('#rerender')
.click({ force: true });
cy.get('#graph #A').should('have.text', 'Saturday');
cy.get('#graph [id^=flowchart-A]').should('have.text', 'Saturday');
});
});

View File

@ -0,0 +1,373 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util';
describe('Class diagram V2', () => {
it('0: should render a simple class diagram', () => {
imgSnapshotTest(
`
classDiagram-v2
classA -- classB : Inheritance
classA -- classC : link
classC -- classD : link
classB -- classD
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('1: should render a simple class diagram', () => {
imgSnapshotTest(
`
classDiagram-v2
Class01 <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 *-- Class04
Class05 o-- Class06
Class07 .. Class08
Class09 --> C2 : Where am i?
Class09 --* C3
Class09 --|> Class07
Class12 <|.. Class08
Class11 ..>Class12
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class01 : -int privateChimp
Class01 : +int publicGorilla
Class01 : #int protectedMarmoset
Class08 <--> C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('2: should render a simple class diagrams with cardinality', () => {
imgSnapshotTest(
`
classDiagram-v2
Class01 "1" <|--|> "*" AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 "1" <--> "*" C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('should render a simple class diagram with different visibilities', () => {
imgSnapshotTest(
`
classDiagram-v2
Class01 <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class01 : -privateMethod()
Class01 : +publicMethod()
Class01 : #protectedMethod()
Class01 : -int privateChimp
Class01 : +int publicGorilla
Class01 : #int protectedMarmoset
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('should render multiple class diagrams', () => {
imgSnapshotTest(
[
`
classDiagram-v2
Class01 "1" <|--|> "*" AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 "1" <--> "*" C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
`
classDiagram-v2
Class01 "1" <|--|> "*" AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 "1" <--> "*" C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
],
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('4: should render a simple class diagram with comments', () => {
imgSnapshotTest(
`
classDiagram-v2
%% this is a comment
Class01 <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
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
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('5: should render a simple class diagram with abstract method', () => {
imgSnapshotTest(
`
classDiagram-v2
Class01 <|-- AveryLongClass : Cool
Class01 : someMethod()*
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('6: should render a simple class diagram with static method', () => {
imgSnapshotTest(
`
classDiagram-v2
Class01 <|-- AveryLongClass : Cool
Class01 : someMethod()$
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('7: should render a simple class diagram with Generic class', () => {
imgSnapshotTest(
`
classDiagram-v2
class Class01~T~
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 <--> C2: Cool label
class Class10~T~ {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('8: should render a simple class diagram with Generic class and relations', () => {
imgSnapshotTest(
`
classDiagram-v2
Class01~T~ <|-- AveryLongClass : Cool
Class03~T~ *-- Class04~T~
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 <--> C2: Cool label
class Class10~T~ {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('9: should render a simple class diagram with clickable link', () => {
imgSnapshotTest(
`
classDiagram-v2
Class01~T~ <|-- AveryLongClass : Cool
Class03~T~ *-- Class04~T~
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 <--> C2: Cool label
class Class10~T~ {
&lt;&lt;service&gt;&gt;
int id
test()
}
link Class01 "google.com" "A Tooltip"
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('10: should render a simple class diagram with clickable callback', () => {
imgSnapshotTest(
`
classDiagram-v2
Class01~T~ <|-- AveryLongClass : Cool
Class03~T~ *-- Class04~T~
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 <--> C2: Cool label
class Class10~T~ {
&lt;&lt;service&gt;&gt;
int id
test()
}
callback Class01 "functionCall" "A Tooltip"
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('11: should render a simple class diagram with return type on method', () => {
imgSnapshotTest(
`
classDiagram-v2
class Class10~T~ {
int[] id
test(int[] ids) bool
testArray() bool[]
}
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('12: should render a simple class diagram with generic types', () => {
imgSnapshotTest(
`
classDiagram-v2
class Class10~T~ {
int[] id
List~int~ ids
test(List~int~ ids) List~bool~
testArray() bool[]
}
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('13: should render a simple class diagram with css classes applied', () => {
imgSnapshotTest(
`
classDiagram-v2
class Class10 {
int[] id
List~int~ ids
test(List~int~ ids) List~bool~
testArray() bool[]
}
cssClass "Class10" exClass2
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('14: should render a simple class diagram with css classes applied directly', () => {
imgSnapshotTest(
`
classDiagram-v2
class Class10:::exClass2 {
int[] id
List~int~ ids
test(List~int~ ids) List~bool~
testArray() bool[]
}
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
it('15: should render a simple class diagram with css classes applied two multiple classes', () => {
imgSnapshotTest(
`
classDiagram-v2
class Class10
class Class20
cssClass "Class10, class20" exClass2
`,
{logLevel : 1, flowchart: { "htmlLabels": false },}
);
cy.get('svg');
});
});

View File

@ -1,5 +1,5 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util';
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
describe('Class diagram', () => {
it('1: should render a simple class diagram', () => {
@ -65,7 +65,7 @@ describe('Class diagram', () => {
cy.get('svg');
});
it('should render a simple class diagram with different visibilities', () => {
it('3: should render a simple class diagram with different visibilities', () => {
imgSnapshotTest(
`
classDiagram
@ -83,59 +83,6 @@ describe('Class diagram', () => {
cy.get('svg');
});
it('should render multiple class diagrams', () => {
imgSnapshotTest(
[
`
classDiagram
Class01 "1" <|--|> "*" AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 "1" <--> "*" C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
`
classDiagram
Class01 "1" <|--|> "*" AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 "1" <--> "*" C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
],
{}
);
cy.get('svg');
});
it('4: should render a simple class diagram with comments', () => {
imgSnapshotTest(
`
@ -246,7 +193,7 @@ describe('Class diagram', () => {
int id
test()
}
link class01 "google.com" "A Tooltip"
link Class01 "google.com" "A Tooltip"
`,
{}
);
@ -268,7 +215,7 @@ describe('Class diagram', () => {
int id
test()
}
callback class01 "functionCall" "A Tooltip"
callback Class01 "functionCall" "A Tooltip"
`,
{}
);
@ -353,4 +300,107 @@ describe('Class diagram', () => {
);
cy.get('svg');
});
it('16: should render multiple class diagrams', () => {
imgSnapshotTest(
[
`
classDiagram
Class01 "1" <|--|> "*" AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 "1" <--> "*" C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
`
classDiagram
Class01 "1" <|--|> "*" AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 "1" <--> "*" C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
`,
],
{}
);
cy.get('svg');
});
it('17: should render a class diagram when useMaxWidth is true (default)', () => {
renderGraph(
`
classDiagram
Class01 <|-- AveryLongClass : Cool
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class01 : -int privateChimp
Class01 : +int publicGorilla
Class01 : #int protectedMarmoset
`,
{ class: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height', '218');
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseInt(style.match(/[\d.]+/g).join(''));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(maxWidthValue).to.be.within(160 * .95, 160 * 1.05);
});
});
it('18: should render a class diagram when useMaxWidth is false', () => {
renderGraph(
`
classDiagram
Class01 <|-- AveryLongClass : Cool
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class01 : -int privateChimp
Class01 : +int publicGorilla
Class01 : #int protectedMarmoset
`,
{ class: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(width).to.be.within(160 * .95, 160 * 1.05);
expect(svg).to.have.attr('height', '218');
expect(svg).to.not.have.attr('style');
});
});
});

View File

@ -1,5 +1,5 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util';
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
describe('Entity Relationship Diagram', () => {
it('should render a simple ER diagram', () => {
@ -101,4 +101,44 @@ describe('Entity Relationship Diagram', () => {
);
cy.get('svg');
});
it('should render an ER diagrams when useMaxWidth is true (default)', () => {
renderGraph(
`
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE-ITEM : contains
`,
{ er: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height', '465');
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(maxWidthValue).to.be.within(140 * .95, 140 * 1.05);
});
});
it('should render an ER when useMaxWidth is false', () => {
renderGraph(
`
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE-ITEM : contains
`,
{ er: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(width).to.be.within(140 * .95, 140 * 1.05);
expect(svg).to.have.attr('height', '465');
expect(svg).to.not.have.attr('style');
});
});
});

View File

@ -1,5 +1,5 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util';
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
describe('Flowchart v2', () => {
it('1: should render a simple flowchart', () => {
@ -47,7 +47,7 @@ describe('Flowchart v2', () => {
);
});
it('Length of edges', () => {
it('4: Length of edges', () => {
imgSnapshotTest(
`flowchart TD
L1 --- L2
@ -64,7 +64,7 @@ describe('Flowchart v2', () => {
{ flowchart: { diagramPadding: 0 } }
);
});
it('36: should render escaped without html labels', () => {
it('5: should render escaped without html labels', () => {
imgSnapshotTest(
`flowchart TD
a["<strong>Haiya</strong>"]---->b
@ -72,13 +72,257 @@ describe('Flowchart v2', () => {
{htmlLabels: false, flowchart: {htmlLabels: false}}
);
});
it('37: should render non-escaped with html labels', () => {
it('6: should render non-escaped with html labels', () => {
imgSnapshotTest(
`flowchart TD
a["<strong>Haiya</strong>"]===>b
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('7: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ flowchart: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height');
// use within because the absolute value can be slightly different depending on the environment ±5%
const height = parseFloat(svg.attr('height'));
expect(height).to.be.within(446 * .95, 446 * 1.05);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.be.within(300 * .95-1, 300 * 1.05);
});
});
it('8: should render a flowchart when useMaxWidth is false', () => {
renderGraph(
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ flowchart: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(height).to.be.within(446 * .95, 446 * 1.05);
expect(width).to.be.within(300 * .95-1, 300 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
it('V2 - 16: Render Stadium shape', () => {
imgSnapshotTest(
` flowchart TD
A([stadium shape test])
A -->|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?])
C -->|One| D([Laptop])
C -->|Two| E([iPhone])
C -->|Three| F([Car<br/>wroom wroom])
click A "index.html#link-clicked" "link test"
click B testClick "click test"
classDef someclass fill:#f96;
class A someclass;
class C someclass;
`,
{ flowchart: { htmlLabels: false } , fontFamily: 'courier'}
);
});
it('50: handle nested subgraphs in reverse order', () => {
imgSnapshotTest(
`flowchart LR
a -->b
subgraph A
B
end
subgraph B
b
end
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('51: handle nested subgraphs in reverse order', () => {
imgSnapshotTest(
`flowchart LR
a -->b
subgraph A
B
end
subgraph B
b
end
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('52: handle nested subgraphs in several levels', () => {
imgSnapshotTest(
`flowchart TB
b-->B
a-->c
subgraph O
A
end
subgraph B
c
end
subgraph A
a
b
B
end
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('53: handle nested subgraphs with edges in and out', () => {
imgSnapshotTest(
`flowchart TB
internet
nat
routeur
lb1
lb2
compute1
compute2
subgraph project
routeur
nat
subgraph subnet1
compute1
lb1
end
subgraph subnet2
compute2
lb2
end
end
internet --> routeur
routeur --> subnet1 & subnet2
subnet1 & subnet2 --> nat --> internet
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('54: handle nested subgraphs with outgoing links', () => {
imgSnapshotTest(
`flowchart TD
subgraph main
subgraph subcontainer
subcontainer-child
end
subcontainer-child--> subcontainer-sibling
end
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('55: handle nested subgraphs with outgoing links 2', () => {
imgSnapshotTest(
`flowchart TD
subgraph one[One]
subgraph sub_one[Sub One]
_sub_one
end
subgraph sub_two[Sub Two]
_sub_two
end
_one
end
%% here, either the first or the second one
sub_one --> sub_two
_one --> b
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('56: handle nested subgraphs with outgoing links 3', () => {
imgSnapshotTest(
`flowchart TB
subgraph container_Beta
process_C-->Process_D
end
subgraph container_Alpha
process_A-->process_B
process_A-->|messages|process_C
end
process_B-->|via_AWSBatch|container_Beta
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('57: handle nested subgraphs with outgoing links 4', () => {
imgSnapshotTest(
`flowchart LR
subgraph A
a -->b
end
subgraph B
b
end
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('57: handle nested subgraphs with outgoing links 2', () => {
imgSnapshotTest(
`flowchart TB
c1-->a2
subgraph one
a1-->a2
end
subgraph two
b1-->b2
end
subgraph three
c1-->c2
end
one --> two
three --> two
two --> c2
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('58: handle styling with style expressions', () => {
imgSnapshotTest(
`
flowchart LR
id1(Start)-->id2(Stop)
style id1 fill:#f9f,stroke:#333,stroke-width:4px
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
});

View File

@ -1,5 +1,5 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util';
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
describe('Flowchart', () => {
it('1: should render a simple flowchart no htmlLabels', () => {
@ -731,4 +731,61 @@ describe('Flowchart', () => {
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
it('38: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ flowchart: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height');
// use within because the absolute value can be slightly different depending on the environment ±5%
const height = parseFloat(svg.attr('height'));
expect(height).to.be.within(446 * .95, 446 * 1.05);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.be.within(300 * .95, 300 * 1.05);
});
});
it('39: should render a flowchart when useMaxWidth is false', () => {
renderGraph(
`graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ flowchart: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(height).to.be.within(446 * .95, 446 * 1.05);
expect(width).to.be.within(300 * .95, 300 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
it('58: handle styling with style expressions', () => {
imgSnapshotTest(
`
graph LR
id1(Start)-->id2(Stop)
style id1 fill:#f9f,stroke:#333,stroke-width:4px
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
`,
{htmlLabels: true, flowchart: {htmlLabels: true}, securityLevel: 'loose'}
);
});
});

View File

@ -1,5 +1,5 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util.js';
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
describe('Gantt diagram', () => {
beforeEach(()=>{
@ -163,4 +163,99 @@ describe('Gantt diagram', () => {
{}
);
});
it('should render a gantt diagram when useMaxWidth is true (default)', () => {
renderGraph(
`
gantt
dateFormat YYYY-MM-DD
axisFormat %d/%m
title Adding GANTT diagram to mermaid
excludes weekdays 2014-01-10
section A section
Completed task :done, des1, 2014-01-06,2014-01-08
Active task :active, des2, 2014-01-09, 3d
Future task : des3, after des2, 5d
Future task2 : des4, after des3, 5d
section Critical tasks
Completed task in the critical line :crit, done, 2014-01-06,24h
Implement parser and jison :crit, done, after des1, 2d
Create tests for parser :crit, active, 3d
Future task in critical line :crit, 5d
Create tests for renderer :2d
Add to mermaid :1d
section Documentation
Describe gantt syntax :active, a1, after des1, 3d
Add gantt diagram to demo page :after a1 , 20h
Add another diagram to demo page :doc1, after a1 , 48h
section Last section
Describe gantt syntax :after doc1, 3d
Add gantt diagram to demo page : 20h
Add another diagram to demo page : 48h
`,
{ gantt: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height');
// use within because the absolute value can be slightly different depending on the environment ±5%
const height = parseFloat(svg.attr('height'));
expect(height).to.be.within(484 * .95, 484 * 1.05);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.be.within(984 * .95, 984 * 1.05);
});
});
it('should render a gantt diagram when useMaxWidth is false', () => {
renderGraph(
`
gantt
dateFormat YYYY-MM-DD
axisFormat %d/%m
title Adding GANTT diagram to mermaid
excludes weekdays 2014-01-10
section A section
Completed task :done, des1, 2014-01-06,2014-01-08
Active task :active, des2, 2014-01-09, 3d
Future task : des3, after des2, 5d
Future task2 : des4, after des3, 5d
section Critical tasks
Completed task in the critical line :crit, done, 2014-01-06,24h
Implement parser and jison :crit, done, after des1, 2d
Create tests for parser :crit, active, 3d
Future task in critical line :crit, 5d
Create tests for renderer :2d
Add to mermaid :1d
section Documentation
Describe gantt syntax :active, a1, after des1, 3d
Add gantt diagram to demo page :after a1 , 20h
Add another diagram to demo page :doc1, after a1 , 48h
section Last section
Describe gantt syntax :after doc1, 3d
Add gantt diagram to demo page : 20h
Add another diagram to demo page : 48h
`,
{ gantt: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(height).to.be.within(484 * .95, 484 * 1.05);
expect(width).to.be.within(984 * .95, 984 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
});

View File

@ -1,5 +1,5 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util.js';
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
describe('User journey diagram', () => {
it('Simple test', () => {
@ -28,4 +28,43 @@ section Order from website
{}
);
});
it('should render a user journey diagram when useMaxWidth is true (default)', () => {
renderGraph(
`journey
title Adding journey diagram functionality to mermaid
section Order from website
`,
{ journey: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height');
const height = parseFloat(svg.attr('height'));
expect(height).to.eq(20);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.eq(400);
});
});
it('should render a user journey diagram when useMaxWidth is false', () => {
renderGraph(
`journey
title Adding journey diagram functionality to mermaid
section Order from website
`,
{ journey: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
expect(height).to.eq(20);
expect(width).to.eq(400);
expect(svg).to.not.have.attr('style');
});
});
});

View File

@ -1,5 +1,5 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util.js';
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
describe('Pie Chart', () => {
it('should render a simple pie diagram', () => {
@ -37,4 +37,45 @@ describe('Pie Chart', () => {
);
cy.get('svg');
});
it('should render a pie diagram when useMaxWidth is true (default)', () => {
renderGraph(
`
pie title Sports in Sweden
"Bandy" : 40
"Ice-Hockey" : 80
"Football" : 90
`,
{ pie: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height');
const height = parseFloat(svg.attr('height'));
expect(height).to.eq(450);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.eq(984);
});
});
it('should render a pie diagram when useMaxWidth is false', () => {
renderGraph(
`
pie title Sports in Sweden
"Bandy" : 40
"Ice-Hockey" : 80
"Football" : 90
`,
{ pie: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
expect(height).to.eq(450);
expect(width).to.eq(984);
expect(svg).to.not.have.attr('style');
});
});
});

View File

@ -1,6 +1,6 @@
/// <reference types="Cypress" />
import { imgSnapshotTest } from '../../helpers/util';
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
context('Sequence diagram', () => {
it('should render a simple sequence diagram', () => {
@ -505,7 +505,7 @@ context('Sequence diagram', () => {
});
});
context('directives', () => {
it('should overide config with directive settings', () => {
it('should override config with directive settings', () => {
imgSnapshotTest(
`
%%{init: { "config": { "mirrorActors": true }}}%%
@ -517,7 +517,7 @@ context('Sequence diagram', () => {
{ logLevel:0, sequence: { mirrorActors: false, noteFontSize: 18, noteFontFamily: 'Arial' } }
);
});
it('should overide config with directive settings', () => {
it('should override config with directive settings', () => {
imgSnapshotTest(
`
%%{init: { "config": { "mirrorActors": false, "wrap": true }}}%%
@ -530,4 +530,85 @@ context('Sequence diagram', () => {
);
});
});
context('svg size', () => {
it('should render a sequence diagram when useMaxWidth is true (default)', () => {
renderGraph(
`
sequenceDiagram
participant Alice
participant Bob
participant John as John<br/>Second Line
Alice ->> Bob: Hello Bob, how are you?
Bob-->>John: How about you John?
Bob--x Alice: I am good thanks!
Bob-x John: 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...
alt either this
Alice->>John: Yes
else or this
Alice->>John: No
else or this will happen
Alice->John: Maybe
end
par this happens in parallel
Alice -->> Bob: Parallel message 1
and
Alice -->> John: Parallel message 2
end
`,
{ sequence: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height');
const height = parseFloat(svg.attr('height'));
expect(height).to.eq(920);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(maxWidthValue).to.be.within(820 * .95, 820 * 1.05);
});
});
it('should render a sequence diagram when useMaxWidth is false', () => {
renderGraph(
`
sequenceDiagram
participant Alice
participant Bob
participant John as John<br/>Second Line
Alice ->> Bob: Hello Bob, how are you?
Bob-->>John: How about you John?
Bob--x Alice: I am good thanks!
Bob-x John: 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...
alt either this
Alice->>John: Yes
else or this
Alice->>John: No
else or this will happen
Alice->John: Maybe
end
par this happens in parallel
Alice -->> Bob: Parallel message 1
and
Alice -->> John: Parallel message 2
end
`,
{ sequence: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
expect(height).to.eq(920);
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(width).to.be.within(820 * .95, 820 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
});
});

View File

@ -1,5 +1,5 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util';
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
describe('State diagram', () => {
it('v2 should render a simple info', () => {
@ -47,7 +47,7 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('v2 should render a single state with short descr', () => {
it('v2 should render a single state with short descriptions', () => {
imgSnapshotTest(
`
stateDiagram-v2
@ -58,7 +58,7 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('v2 should render a transition descrions with new lines', () => {
it('v2 should render a transition descriptions with new lines', () => {
imgSnapshotTest(
`
stateDiagram-v2
@ -201,7 +201,7 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('v2 should render composit states', () => {
it('v2 should render composite states', () => {
imgSnapshotTest(
`
stateDiagram-v2
@ -220,7 +220,7 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('v2 should render multiple composit states', () => {
it('v2 should render multiple composite states', () => {
imgSnapshotTest(
`
stateDiagram-v2
@ -249,7 +249,7 @@ describe('State diagram', () => {
{ logLevel: 0, fontFamily: 'courier' }
);
});
it('v2 should render forks in composit states', () => {
it('v2 should render forks in composite states', () => {
imgSnapshotTest(
`
stateDiagram-v2
@ -329,7 +329,7 @@ describe('State diagram', () => {
}
);
});
it('v2 Simplest composit state', () => {
it('v2 Simplest composite state', () => {
imgSnapshotTest(
`
stateDiagram-v2
@ -354,5 +354,47 @@ describe('State diagram', () => {
}
);
});
it('v2 should render a state diagram when useMaxWidth is true (default)', () => {
renderGraph(
`
stateDiagram-v2
[*] --> State1
State1 --> [*]
`,
{ state: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height');
const height = parseFloat(svg.attr('height'));
expect(height).to.eq(177);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(maxWidthValue).to.be.within(135 * .95, 135 * 1.05);
});
});
it('v2 should render a state diagram when useMaxWidth is false', () => {
renderGraph(
`
stateDiagram-v2
[*] --> State1
State1 --> [*]
`,
{ state: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
expect(height).to.eq(177);
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(width).to.be.within(135 * .95, 135 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
});

View File

@ -1,5 +1,5 @@
/* eslint-env jest */
import { imgSnapshotTest } from '../../helpers/util';
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
describe('State diagram', () => {
it('should render a simple state diagrams', () => {
@ -37,7 +37,7 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('should render a single state with short descr', () => {
it('should render a single state with short descriptions', () => {
imgSnapshotTest(
`
stateDiagram
@ -48,7 +48,7 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('should render a transition descrions with new lines', () => {
it('should render a transition descriptions with new lines', () => {
imgSnapshotTest(
`
stateDiagram
@ -191,7 +191,7 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('should render composit states', () => {
it('should render composite states', () => {
imgSnapshotTest(
`
stateDiagram
@ -280,7 +280,7 @@ describe('State diagram', () => {
);
cy.get('svg');
});
it('should render conurrency states', () => {
it('should render concurrency states', () => {
imgSnapshotTest(
`
stateDiagram
@ -319,7 +319,7 @@ describe('State diagram', () => {
}
);
});
it('Simplest composit state', () => {
it('Simplest composite state', () => {
imgSnapshotTest(
`
stateDiagram
@ -344,5 +344,45 @@ describe('State diagram', () => {
}
);
});
it('should render a state diagram when useMaxWidth is true (default)', () => {
renderGraph(
`
stateDiagram
[*] --> State1
State1 --> [*]
`,
{ state: { useMaxWidth: true } }
);
cy.get('svg')
.should((svg) => {
expect(svg).to.have.attr('width', '100%');
expect(svg).to.have.attr('height');
const height = parseFloat(svg.attr('height'));
expect(height).to.eq(139);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(maxWidthValue).to.be.within(112 * .95, 112 * 1.05);
});
});
it('should render a state diagram when useMaxWidth is false', () => {
renderGraph(
`
stateDiagram
[*] --> State1
State1 --> [*]
`,
{ state: { useMaxWidth: false } }
);
cy.get('svg')
.should((svg) => {
const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
expect(height).to.eq(139);
// use within because the absolute value can be slightly different depending on the environment ±5%
expect(width).to.be.within(112 * .95, 112 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
});

View File

@ -16,19 +16,79 @@
.mermaid2 {
display: none;
}
.customCss > rect, .customCss{
fill:#FF0000 !important;
stroke:#FFFF00 !important;
stroke-width:4px !important;
}
</style>
</head>
<body>
<h1>info below</h1>
<div class="mermaid" style="width: 100%; height: 20%;">
%%{init: {'theme': 'base', 'fontFamily': 'arial', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
classDiagram-v2
<div class="mermaid" style="width: 100%; height: 20%;">
%%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%%
classDiagram
class BankAccount{
+String owner
+BigDecimal balance
+deposit(amount) bool
+withdrawl(amount) int
}
cssClass "BankAccount" customCss
</div>
<div class="mermaid" style="width: 100%; height: 20%;">
%%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%%
classDiagram-v2
class BankAccount{
+String owner
+BigDecimal balance
+deposit(amount) bool
+withdrawl(amount) int
}
cssClass "BankAccount" customCss
</div>
<div class="mermaid2" style="width: 100%; height: 20%;">
%%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%%
classDiagram
class BankAccount{
+String owner
+BigDecimal balance
+deposit(amount) bool
+withdrawl(amount) int
}
}
Class01~T~ <|-- AveryLongClass : Cool
Class03~T~ *-- Class04~T~
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 <--> C2: Cool label
class Class10~T~ {
&lt;&lt;service&gt;&gt;
int id
test()
}
callback Class01 "callback" "A Tooltip"
</div>
<div class="mermaid2" style="width: 100%; height: 20%;">
flowchart TB
a_a(Aftonbladet) --> b_b[gorilla]:::apa --> c_c{chimp}:::apa -->a_a
a_a --> c --> d_d --> c_c
classDef apa fill:#f9f,stroke:#333,stroke-width:4px;
class a_a apa;
click a_a "http://www.aftonbladet.se" "apa"
</div>
<div class="mermaid2" style="width: 100%; height: 20%;">
classDiagram-v2
classA -- classB : Inheritance
classA -- classC : link
classC -- classD : link
classB -- classD
classA --|> classB : Inheritance
classC --* classD : Composition
classE --o classF : Aggregation
@ -42,15 +102,15 @@
classA : method1()
&lt;&lt;interface&gt;&gt; classB
classB : method2() int
</div>
<div class="mermaid" style="width: 100%; height: 20%;">
classDiagram-v2
Customer "1" --> "*" Ticket
Student "1" --> "1..*" Course
Galaxy --> "many" Star : Contains
&lt;&lt;interface&gt;&gt; Customer
classA -- classB : Inheritance
classA -- classC : link
classC -- classD : link
classB -- classD
class Shape
callback Shape "callbackFunction" "This is a tooltip for a callback"
</div>
<script src="./mermaid.js"></script>
<script>
@ -62,7 +122,7 @@
// arrowMarkerAbsolute: true,
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
logLevel: 0,
flowchart: { curve: 'linear', "htmlLabels": false },
flowchart: { curve: 'linear', "htmlLabels": true },
// gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorMargin: 50, showSequenceNumbers: true },
// sequenceDiagram: { actorMargin: 300 } // deprecated

View File

@ -5,8 +5,14 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
<style>
.mermaid2 {
display: none;
}
</style>
</head>
<body>
<div style="display: flex">
<div id="FirstLine" class="mermaid">
graph TB
Function-->URL
@ -15,9 +21,38 @@
</div>
<div id="FirstLine" class="mermaid">
graph TB
1Function-->2URL
1Function--->2URL
click 1Function clickByFlow "Add a div"
click 2URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>"
</div>
<div id="FirstLine" class="mermaid">
flowchart TB
Function-->URL
click Function clickByFlow "Add a div"
click URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>" _self
</div>
<div id="FirstLine" class="mermaid">
flowchart TB
1Function--->2URL
click 1Function clickByFlow "Add a div"
click 2URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>" _self
</div>
<div id="FirstLine" class="mermaid">
classDiagram
class ShapeLink
link ShapeLink "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link"
class ShapeCallback
callback ShapeCallback "clickByClass" "This is a tooltip for a callback"
</div>
<div id="FirstLine" class="mermaid">
classDiagram-v2
class ShapeLink2
link ShapeLink2 "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link"
class ShapeCallback2
callback ShapeCallback2 "clickByClass" "This is a tooltip for a callback"
</div>
</div>
<div class="mermaid">
@ -82,6 +117,14 @@
document.getElementsByTagName('body')[0].appendChild(div)
}
function clickByClass() {
const div = document.createElement('div')
div.className = 'created-by-class-click'
div.style = 'padding: 20px; background: purple; color: white;'
div.innerText = 'Clicked By Class'
document.getElementsByTagName('body')[0].appendChild(div)
}
mermaid.initialize({ startOnLoad: true, securityLevel: 'loose', logLevel: 1 });
</script>
</body>

View File

@ -74,26 +74,93 @@ stateDiagram-v2
A --> D: asd123
</div>
</div>
<div class="mermaid" style="width: 50%; height: 20%;">
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
classDiagram
Animal <|-- Duck
<div class="mermaid" style="width: 50%; height: 40%;">
%% this does not produce the desired result
flowchart TB
subgraph container_Beta
process_C-->Process_D
end
subgraph container_Alpha
process_A-->process_B
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|process_C
end
</div>
<div class="mermaid2" style="width: 50%; height: 20%;">
graph TD
A(Start) --> B[/Another/]
A[/Another/] --> C[End]
subgraph section
B
C
end
<div class="mermaid" style="width: 50%; height: 40%;">
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
flowchart TB
b-->B
a-->c
subgraph O
A
end
subgraph B
c
end
subgraph A
a
b
B
end
</div>
<div class="mermaid2" style="width: 50%; height: 20%;">
sequenceDiagram
Alice->Bob: Hello Bob, how are you?
Note over Alice,Bob: Looks
Note over Bob,Alice: Looks back
<div class="mermaid" style="width: 50%; height: 50%;">
flowchart TB
internet
nat
routeur
lb1
lb2
compute1
compute2
subgraph project
routeur
nat
subgraph subnet1
compute1
lb1
end
subgraph subnet2
compute2
lb2
end
end
internet --> routeur
routeur --> subnet1 & subnet2
subnet1 & subnet2 --> nat --> internet
</div>
<div class="mermaid" style="width: 50%; height: 50%;">
flowchart TD
subgraph one[One]
subgraph sub_one[Sub One]
_sub_one
end
end
subgraph two[Two]
_two
end
sub_one --> two
</div>
<div class="mermaid" style="width: 50%; height: 50%;">
flowchart TD
subgraph one[One]
subgraph sub_one[Sub One]
_sub_one
end
subgraph sub_two[Sub Two]
_sub_two
end
_one
end
%% here, either the first or the second one
sub_one --> sub_two
_one --> b
</div>
<script src="./mermaid.js"></script>

View File

@ -0,0 +1,129 @@
<html>
<head>
<link
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
rel="stylesheet"
/>
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet">
<style>
body {
/* background: rgb(221, 208, 208); */
/* background:#333; */
font-family: 'Arial';
}
h1 { color: grey;}
.mermaid2 {
display: none;
}
</style>
</head>
<body>
<h1>info below</h1>
<div class="flex">
<div class="mermaid2" style="width: 50%; height: 20%;">
flowchart BT
subgraph two
b1
end
subgraph three
c1-->c2
end
c1 --apa apa apa--> b1
two --> c2
</div>
<div class="mermaid2" style="width: 50%; height: 200px;">
sequenceDiagram
Alice->>Bob:Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
</div>
<div class="mermaid2" style="width: 50%; height: 200px;">
%%{init: {'securityLevel': 'loose'}}%%
graph TD
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?}}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
click A "index.html#link-clicked" "link test"
click B callback "click test"
classDef someclass fill:#f96;
class A someclass;
class C someclass;
</div>
<div class="mermaid2" style="width: 50%; height: 200px;">
flowchart BT
subgraph a
b1 -- ok --> b2
end
a -- sert --> c
c --> d
b1 --> d
a --asd123 --> d
</div>
<div class="mermaid2" style="width: 50%; height: 20%;">
stateDiagram-v2
state A {
B1 --> B2: ok
}
A --> C: sert
C --> D
B1 --> D
A --> D: asd123
</div>
</div>
<div class="mermaid2" style="width: 50%; height: 20%;">
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#ff0000'}}}%%
flowchart LR
a -->b
subgraph A
B
end
subgraph B
b
end
</div>
<div class="mermaid" style="width: 50%; height: 20%;">
flowchart TB
subgraph A
b-->B
a-->c
end
subgraph B
c
end
</div>
<div class="mermaid2" style="width: 50%; height: 20%;">
sequenceDiagram
Alice->Bob: Hello Bob, how are you?
Note over Alice,Bob: Looks
Note over Bob,Alice: Looks back
</div>
<script src="./mermaid.js"></script>
<script>
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};
mermaid.initialize({
// theme: 'forest',
// themeVariables:{primaryColor: '#ff0000'},
// arrowMarkerAbsolute: true,
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
logLevel: 0,
flowchart: { curve: 'cardinal', "htmlLabels": false },
// gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorMargin: 50, showSequenceNumbers: true },
// sequenceDiagram: { actorMargin: 300 } // deprecated
fontFamily: '"arial", sans-serif',
curve: 'cardinal',
securityLevel: 'strict'
});
function callback(){alert('It worked');}
</script>
</body>
</html>

View File

@ -27,6 +27,9 @@
svg {
border: 2px solid darkred;
}
.exClass2 > rect, .exClass {
fill: greenyellow !important;
}
</style>
</head>
<body>

View File

@ -0,0 +1,122 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
<style>
.mermaid2 {
display: none;
}
</style>
</head>
<body>
<div style="display: flex">
<div id="FirstLine" class="mermaid">
graph TB
Function-->URL
click Function clickByFlow "Add a div"
click URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>"
</div>
<div id="FirstLine" class="mermaid2">
graph TB
1Function-->2URL
click 1Function clickByFlow "Add a div"
click 2URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>"
</div>
<div id="FirstLine" class="mermaid2">
classDiagram
class Test
class ShapeLink
link ShapeLink "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link"
class ShapeCallback
callback ShapeCallback "clickByClass" "This is a tooltip for a callback"
</div>
<div id="FirstLine" class="mermaid">
classDiagram-v2
class ShapeCallback
callback ShapeCallback "clickByClass" "This is a tooltip for a callback"
</div>
<div id="FirstLine" class="mermaid">
classDiagram-v2
class ShapeLink
link ShapeLink "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link"
</div>
</div>
<div class="mermaid2">
gantt
dateFormat YYYY-MM-DD
axisFormat %d/%m
title Adding GANTT diagram to mermaid
excludes weekdays 2014-01-10
section A section
Completed task :done, des1, 2014-01-06,2014-01-08
Active task :active, des2, 2014-01-09, 3d
Future task : des3, after des2, 5d
Future task2 : des4, after des3, 5d
section Critical tasks
Completed task in the critical line :crit, done, 2014-01-06,24h
Implement parser and jison :crit, done, after des1, 2d
Create tests for parser :crit, active, 3d
Future task in critical line :crit, 5d
Create tests for renderer :2d
Add to mermaid :1d
section Documentation
Describe gantt syntax :active, a1, after des1, 3d
Add gantt diagram to demo page :after a1 , 20h
Add another diagram to demo page :doc1, after a1 , 48h
section Clickable
Visit mermaidjs :active, cl1, 2014-01-07,2014-01-10
Calling a Callback (look at the console log) :cl2, after cl1, 3d
Calling a Callback with args :cl3, after cl1, 3d
click cl1 href "http://localhost:9000/webpackUsage.html"
click cl2 call clickByGantt()
click cl3 call clickByGantt("test1", test2, test3)
section Last section
Describe gantt syntax :after doc1, 3d
Add gantt diagram to demo page : 20h
Add another diagram to demo page : 48h
</div>
<script src="./mermaid.js"></script>
<script>
function clickByFlow(elemName) {
const div = document.createElement('div')
div.className = 'created-by-click'
div.style = 'padding: 20px; background: green; color: white;'
div.innerText = 'Clicked By Flow'
document.getElementsByTagName('body')[0].appendChild(div)
}
function clickByGantt(arg1, arg2, arg3) {
const div = document.createElement('div')
div.className = 'created-by-gant-click'
div.style = 'padding: 20px; background: green; color: white;'
div.innerText = 'Clicked By Gant'
if (arg1) div.innerText += ' ' + arg1;
if (arg2) div.innerText += ' ' + arg2;
if (arg3) div.innerText += ' ' + arg3;
document.getElementsByTagName('body')[0].appendChild(div)
}
function clickByClass() {
const div = document.createElement('div')
div.className = 'created-by-class-click'
div.style = 'padding: 20px; background: purple; color: white;'
div.innerText = 'Clicked By Class'
document.getElementsByTagName('body')[0].appendChild(div)
}
mermaid.initialize({ startOnLoad: true, securityLevel: 'loose', logLevel: 1 });
</script>
</body>
</html>

View File

@ -22,19 +22,36 @@
<body>
<h1>info below</h1>
<div class="flex">
<div class="mermaid" style="width: 50%; height: 400px;">
graph TD
a["<strong>Haiya</strong>"]-->b
<div class="mermaid2" style="width: 50%; height: 400px;">
flowchart TD
subgraph main
subgraph subcontainer
subcontainer-child
end
subcontainer-child--> subcontainer-sibling
end
</div>
<div class="mermaid2" style="width: 50%; height: 20%;">
%%{init: { 'theme': 'base', 'themeVariables':{ 'primaryColor': '#ff0000'}}}%%
graph TD
A(Start) --> B[/Another/]
A[/Another/] --> C[End]
subgraph section
B
C
end
<div class="mermaid" style="width: 50%; height: 400px;">
flowchart TB
b-->B
a-->c
subgraph B
c
end
subgraph A
a
b
B
end
</div>
<div class="mermaid" style="width: 50%; height: 20%;">
flowchart TB
subgraph A
a -->b
end
subgraph B
b
end
</div>
<div class="mermaid2" style="width: 50%; height: 20%;">
%%{init: {"fontFamily": "arial2"}}%%

1091
dist/mermaid.core.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

1091
dist/mermaid.js vendored

File diff suppressed because it is too large Load Diff

2
dist/mermaid.js.map vendored

File diff suppressed because one or more lines are too long

10
dist/mermaid.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

5
docs/.debug.yml Normal file
View File

@ -0,0 +1,5 @@
remote_theme: false
baseurl: /mermaid
theme: jekyll-rtd-theme

View File

@ -1,5 +1,5 @@
default:
@bundle install
@gem install jekyll bundler && bundle install
update:
@bundle update
@ -7,8 +7,8 @@ update:
clean:
@bundle exec jekyll clean
server: clean
@bundle exec jekyll server
build: clean
@bundle exec jekyll build --profile
@bundle exec jekyll build --profile --config _config.yml,.debug.yml
server: clean
@bundle exec jekyll server --livereload --config _config.yml,.debug.yml

View File

@ -9,29 +9,31 @@
## About
<!-- <Main description> -->
Mermaid is a Javascript based diagramming and charting tool that uses Markdown-inspired text definitions and a renderer to create and modify complex diagrams. The main purpose of Mermaid is to help documentation catch up with development.
Mermaid simplifies complex diagrams. It is a Javascript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically. The main purpose of Mermaid is to help Documentation catch up with Development.
> Doc-Rot is a Catch-22 that Mermaid helps to solve.
Diagramming and documentation costs precious developer time and gets outdated quickly.
But not having diagrams or docs ruins productivity and hurts organizational learning. <br/>
Mermaid addresses this problem by cutting the time, effort and tooling that is required to create modifiable diagrams and charts, for smarter and more reusable content.
The text definitions for Mermaid diagrams allows for it to be updated easily, it can also be made part of production scripts (and other pieces of code).
So less time needs to be spent on documenting, as a separate and laborious task. <br/>
Mermaid addresses this Catch-22 by cutting the time, effort and tooling that is required to create modifiable diagrams and charts, for smarter and more reusable content.
Mermaid, as a text-based diagramming tool allows for quick and easy updates, it can also be made part of production scripts (and other pieces of code), to make documentation much easier.
With Mermaid less time needs to be spent on making diagrams, as a separate documentation task. <br/>
Even non-programmers can create diagrams through the [Mermaid Live Editor](https://github.com/mermaidjs/mermaid-live-editor), visit [Mermaid Overview](./n00b-overview.md) for the video tutorials.
> Mermaid is a Diagramming tool for everyone.
Want to see what can be built with mermaid, or what applications already support it? Read the [Integrations and Usages for Mermaid](./integrations.md).
Even non-programmers can create diagrams through the [Mermaid Live Editor](https://github.com/mermaidjs/mermaid-live-editor), Visit the [Tutorials Page](href='https://github.com/mermaid-js/mermaid/blob/develop/docs/Tutorials.md) for the Live Editor video tutorials.
For a more detailed introduction to Mermaid and some of it's more basic uses, look to the [Beginner's Guide](./n00b-overview.md) and [Usage](./usage.md).
Want to see what can be built with mermaid, or what applications already support it? Read the [Integrations and Usages for Mermaid](overview/integrations.md).
🌐 [CDN](https://unpkg.com/mermaid/) | 📖 [Documentation](https://mermaidjs.github.io) | 🙌 [Contribution](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md) | 📜 [Version Log](./CHANGELOG.md)
For a more detailed introduction to Mermaid and some of it's more basic uses, look to the [Beginner's Guide](overview/n00b-overview.md) and [Usage](getting-started/usage.md).
🌐 [CDN](https://unpkg.com/mermaid/) | 📖 [Documentation](https://mermaidjs.github.io) | 🙌 [Contribution](https://github.com/mermaid-js/mermaid/blob/develop/docs/tutorials-and-community/development.md) | 📜 [Version Log](tutorials-and-community/CHANGELOG.md)
> 🖖 Keep a steady pulse: mermaid needs more Collaborators, [Read More](https://github.com/knsv/mermaid/issues/866).
# Diagrams that mermaid can render:
### [Flowchart](https://mermaid-js.github.io/mermaid/#/flowchart)
### [Flowchart](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/flowchart.html)
```
graph TD;
@ -43,7 +45,7 @@ graph TD;
![Flowchart](assets/img/flow.png)
### [Sequence diagram](https://mermaid-js.github.io/mermaid/#/sequenceDiagram)
### [Sequence diagram](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/sequenceDiagram.html)
```
sequenceDiagram
@ -61,7 +63,7 @@ sequenceDiagram
![Sequence diagram](assets/img/sequence.png)
### [Gantt diagram](https://mermaid-js.github.io/mermaid/#/gantt)
### [Gantt diagram](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/gantt.html)
```
gantt
@ -78,7 +80,7 @@ Future task2 : des4, after des3, 5d
![Gantt diagram](assets/img/gantt.png)
### [Class diagram - :exclamation: experimental](https://mermaid-js.github.io/mermaid/#/classDiagram)
### [Class diagram - :exclamation: experimental](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/classDiagram.html)
```
classDiagram
@ -122,7 +124,7 @@ merge newbranch
```
![Git graph](assets/img/git.png)
### [Entity Relationship Diagram - :exclamation: experimental](https://mermaid-js.github.io/mermaid/#/entityRelationshipDiagram)
### [Entity Relationship Diagram - :exclamation: experimental](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/entityRelationshipDiagram.html)
```
erDiagram
@ -134,7 +136,7 @@ erDiagram
![ER diagram](assets/img/simple-er.png)
### [User Journey Diagram](https://mermaid-js.github.io/mermaid/#/user-journey)
### [User Journey Diagram](https://mermaid-js.github.io/mermaid/diagrams-and-syntax-and-examples/user-journey.html)
```markdown
journey
@ -150,9 +152,9 @@ journey
![Journey diagram](assets/img/user-journey.png)
# Installation
## In depth guides and examples can be found in [Getting Started](./n00b-gettingStarted.md) and [Usage](./usage.md).
## In depth guides and examples can be found in [Getting Started](getting-started/n00b-gettingStarted.md) and [Usage](getting-started/usage.md).
## It would also be helpful to learn more about mermaid's [Syntax](./n00b-syntaxReference.md).
## It would also be helpful to learn more about mermaid's [Syntax](diagrams-and-syntax-and-examples/n00b-syntaxReference.md).
### CDN
@ -166,7 +168,7 @@ Replace `<version>` with the desired version number.
Alternatively, you can also adjust the version number in the page itself.
Latest Version: [https://unpkg.com/browse/mermaid@8.6.0/](https://unpkg.com/browse/mermaid@8.6.0/)
Latest Version: [https://unpkg.com/browse/mermaid@8.8.0/](https://unpkg.com/browse/mermaid@8.8.0/)
## Incorporating mermaid to a website
To support mermaid on your website, all you have to do is add Mermaids JavaScript package
@ -191,7 +193,7 @@ To support mermaid on your website, all you have to do is add Mermaids JavaSc
```
## Doing so will command the mermaid parser to look for the `<div>` tags with `class="mermaid"` in your HTML Document. From these tags mermaid will try to read the diagram/chart definitons and render them as svg charts.
## Examples can be found in [Getting Started](./n00b-gettingStarted.md)
## Examples can be found in [Getting Started](getting-started/n00b-gettingStarted.md)
# Sibling projects
- [Mermaid Live Editor](https://github.com/mermaidjs/mermaid-live-editor)

View File

@ -2,13 +2,13 @@ title: mermaid
lang: en
description: Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.
# theme: jekyll-rtd-theme
remote_theme: rundocs/jekyll-rtd-theme
mermaid:
custom: https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js
copyright:
since: 2014
revision: true
edit: true

View File

@ -18,10 +18,10 @@
- Contributions and Community
<!-- - [Development and Contribution](development.md)
- [Mermaid Versions](versionUpdates.md)
- [Changelog](CHANGELOG.md) -->
<!-- - [Development and Contribution](development.md)-->
<!-- - [Adding Diagrams](newDiagram.md)-->
<!-- - [Changelog](CHANGELOG.md) -->
- Diagrams and Syntax and Examples

View File

@ -612,7 +612,7 @@ The main styling of the class diagram is done with a preset number of css classe
#### Sample stylesheet
```css
```scss
body {
background: white;
}

View File

@ -4,7 +4,7 @@ sort: 10
## Directives
## Directives were added in [Version 8.6.0](/8.6.0_docs.md). Please Read it for more information.
## Directives were added in [Version 8.6.0](../getting-started/8.6.0_docs.md). Please Read it for more information.
## Directives
With this version, directives are supported. Directives are divided in two sets, by priority. the first set, containing 'init' or 'initialize' directives take priority. While the other set, containing all other kinds of directives are considered only after 'init' and the graph-type declared.

View File

@ -6,9 +6,9 @@ sort: 2
This page contains a collection of examples of diagrams and charts that can be created through mermaid and its myriad applications.
## If you wish to learn how to support mermaid on your webpage, read the [Beginner's Guide](https://mermaid-js.github.io/mermaid/#/n00b-gettingStarted).
## If you wish to learn how to support mermaid on your webpage, read the [Beginner's Guide](../getting-started/n00b-gettingStarted.md).
## If you wish to learn about mermaid's syntax, Read the [Diagram Syntax](https://mermaid-js.github.io/mermaid/#/n00b-syntaxReference) section.
## If you wish to learn about mermaid's syntax, Read the [Diagram Syntax](n00b-syntaxReference.md) section.
## Basic Pie Chart

View File

@ -609,7 +609,7 @@ click nodeId callback
Examples of tooltip usage below:
```
```html
<script>
var callback = function(){
alert('A callback was triggered');
@ -654,7 +654,7 @@ graph LR;
```
Beginners tip, a full example using interactive links in a html context:
```
```html
<body>
<div class="mermaid">
graph LR;

View File

@ -354,7 +354,7 @@ click taskId href URL
* callback is the name of a javascript function defined on the page displaying the graph, the function will be called with the taskId as the parameter if no other arguments are specified..
Beginners tip, a full example using interactive links in an html context:
```
```html
<body>
<div class="mermaid">
gantt

View File

@ -5,12 +5,12 @@ title: Diagram syntax intro
## Diagram syntax
If you are new to mermaid, read the [Getting Started](n00b-gettingStarted.md) and [Overview](n00b-overview.md) sections, to learn the basics of mermaid.
If you are new to mermaid, read the [Getting Started](../getting-started/n00b-gettingStarted.md) and [Overview](../overview/n00b-overview.md) sections, to learn the basics of mermaid.
Video Tutorials can be found at the bottom of the Overview Section.
This section is a list of diagram types supported by mermaid. Below is a list of links to aricles that explain the syntax of the diagrams or charts that 0can be called.
This section is a list of diagram types supported by mermaid. Below is a list of links to aricles that explain the syntax of the diagrams or charts that can be called.
They also detail how diagrams can be defined, or described in the manner with which the diagram is to be rendered by the renderer.
They also detail how diagrams can be defined, or described in the manner with which the diagram is to be rendered by the renderer.
### The benefits of text based diagramming are its speed and modifiability. mermaid allows for easy maintenance and modification of diagrams. This means your diagrams will always be up to date and closely follow your code and improve your documentation.
@ -30,12 +30,4 @@ These definitions can also be entered into the [mermaid live editor](https://mer
This would then offer
- [Flowchart](flowchart.md)
- [Sequence diagram](sequenceDiagram.md)
- [Class Diagram](classDiagram.md)
- [State Diagram](stateDiagram.md)
- [Gantt](gantt.md)
- [Pie Chart](pie.md)
- [Entity Relationship Diagram](entityRelationshipDiagram.md)
- [User Journey Diagram](user-journey.md)
- [Directives](directives.md)
{% include list.liquid %}

View File

@ -20,9 +20,11 @@ sequenceDiagram
John-->>Alice: Great!
```
## A note on nodes, the word "end" could potentially break the diagram, due to the way that the mermaid language is scripted.
## If unavoidable, one must use parentheses(), quotation marks "", or brackets {},[], to enclose the word "end". i.e : (end), [end], {end}.
```note
A note on nodes, the word "end" could potentially break the diagram, due to the way that the mermaid language is scripted.
If unavoidable, one must use parentheses(), quotation marks "", or brackets {},[], to enclose the word "end". i.e : (end), [end], {end}.
```
## Syntax

View File

@ -8,7 +8,7 @@ sort: 6
Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make it easier for users to share diagrams between mermaid and plantUml.
```markdown
```
stateDiagram-v2
[*] --> Still
Still --> [*]
@ -86,7 +86,7 @@ Transitions are path/edges when one state passes into another. This is represent
When you define a transition between two states and the states are not already defined the undefined states are defined with the id from the transition. You can later add descriptions to states defined this way.
```markdown
```
stateDiagram-v2
s1 --> s2
```
@ -98,7 +98,7 @@ stateDiagram-v2
It is possible to add text to a transition. To describe what it represents.
```markdown
```
stateDiagram-v2
s1 --> s2: A transition
```
@ -112,7 +112,7 @@ stateDiagram-v2
There are two special states indicating the start and stop of the diagram. These are written with the [\*] syntax and the direction of the transition to it defines it either as a start or a stop state.
```markdown
```
stateDiagram-v2
[*] --> s1
s1 --> [*]
@ -131,7 +131,7 @@ have several internal states. These are called composite states in this terminol
In order to define a composite state you need to use the state keyword followed by and id and the body of the composite state between \{\}. See the example below:
```markdown
```
stateDiagram-v2
[*] --> First
state First {
@ -151,7 +151,7 @@ stateDiagram-v2
You can do this in several layers:
```markdown
```
stateDiagram-v2
[*] --> First
@ -190,7 +190,7 @@ stateDiagram-v2
You can also define transitions also between composite states:
```markdown
```
stateDiagram-v2
[*] --> First
First --> Second
@ -236,7 +236,7 @@ stateDiagram-v2
It is possible to specify a fork in the diagram using &lt;&lt;fork&gt;&gt; &lt;&lt;join&gt;&gt;.
```markdown
```
stateDiagram-v2
state fork_state <<fork>>
[*] --> fork_state
@ -271,7 +271,7 @@ Sometimes nothing says it better then a Post-it note. That is also the case in s
Here you can choose to put the note to the *right of* or to the *left of* a node.
```markdown
```
stateDiagram-v2
State1: The state with a note
note right of State1
@ -298,42 +298,42 @@ Here you can choose to put the note to the *right of* or to the *left of* a node
As in plantUml you can specify concurrency using the -- symbol.
```markdown
stateDiagram-v2
[*] --> Active
```
stateDiagram-v2
[*] --> Active
state Active {
[*] --> NumLockOff
NumLockOff --> NumLockOn : EvNumLockPressed
NumLockOn --> NumLockOff : EvNumLockPressed
--
[*] --> CapsLockOff
CapsLockOff --> CapsLockOn : EvCapsLockPressed
CapsLockOn --> CapsLockOff : EvCapsLockPressed
--
[*] --> ScrollLockOff
ScrollLockOff --> ScrollLockOn : EvCapsLockPressed
ScrollLockOn --> ScrollLockOff : EvCapsLockPressed
}
state Active {
[*] --> NumLockOff
NumLockOff --> NumLockOn : EvNumLockPressed
NumLockOn --> NumLockOff : EvNumLockPressed
--
[*] --> CapsLockOff
CapsLockOff --> CapsLockOn : EvCapsLockPressed
CapsLockOn --> CapsLockOff : EvCapsLockPressed
--
[*] --> ScrollLockOff
ScrollLockOff --> ScrollLockOn : EvCapsLockPressed
ScrollLockOn --> ScrollLockOff : EvCapsLockPressed
}
```
```mermaid
stateDiagram-v2
[*] --> Active
stateDiagram-v2
[*] --> Active
state Active {
[*] --> NumLockOff
NumLockOff --> NumLockOn : EvNumLockPressed
NumLockOn --> NumLockOff : EvNumLockPressed
--
[*] --> CapsLockOff
CapsLockOff --> CapsLockOn : EvCapsLockPressed
CapsLockOn --> CapsLockOff : EvCapsLockPressed
--
[*] --> ScrollLockOff
ScrollLockOff --> ScrollLockOn : EvCapsLockPressed
ScrollLockOn --> ScrollLockOff : EvCapsLockPressed
}
state Active {
[*] --> NumLockOff
NumLockOff --> NumLockOn : EvNumLockPressed
NumLockOn --> NumLockOff : EvNumLockPressed
--
[*] --> CapsLockOff
CapsLockOff --> CapsLockOn : EvCapsLockPressed
CapsLockOn --> CapsLockOff : EvCapsLockPressed
--
[*] --> ScrollLockOff
ScrollLockOff --> ScrollLockOn : EvCapsLockPressed
ScrollLockOn --> ScrollLockOff : EvCapsLockPressed
}
```
## Comments

View File

@ -79,15 +79,15 @@ When deployed within code, init is called before the graph/diagram description.
**for example**:
```
%%{init: {"theme": "default", "logLevel": 1 }}%%
graph LR
a-->b
b-->c
c-->d
d-->e
e-->f
f-->g
g-->
%%{init: {"theme": "default", "logLevel": 1 }}%%
graph LR
a-->b
b-->c
c-->d
d-->e
e-->f
f-->g
g-->
```
# Wrap
@ -214,4 +214,4 @@ Ensures options parameter does not attempt to override siteConfig secure keys.
default: current siteConfig (optional, default `getSiteConfig()`)
```
## For more information, read [Setup](https://mermaid-js.github.io/mermaid/#/Setup).
## For more information, read [Setup](Setup.md).

View File

@ -146,9 +146,8 @@ mermaidAPI.initialize({
## mermaidAPI configuration defaults
<pre>
&lt;script>
```html
<script>
var config = {
theme:'default',
logLevel:'fatal',
@ -203,7 +202,7 @@ mermaidAPI.initialize({
}
};
mermaid.initialize(config);
&lt;/script>
</pre>
</script>
```
[1]: Setup.md?id=render

View File

@ -1,12 +1,12 @@
# Tutorials
# Tutorials
This is list of publicly available Tutorials in Mermaid. This is intended as a basic introduction for the use of the Live Editor and HTML with mermaid.
For the purposes, the Live-Editor is capable of taking care of all diagramming needs, and these are the most common use cases for it.
For the purposes, the Live-Editor is capable of taking care of all diagramming needs, and these are the most common use cases for it.
# Live-Editor Tutorials
The definitions that can be generated the Live-Editor are also backwards-compatible as of version 8.7.0.
## Live-Editor Tutorials
The definitions that can be generated the Live-Editor are also backwards-compatible as of version 8.7.0.
https://www.youtube.com/watch?v=SQ9QmuTHuSI&t=438s
@ -20,10 +20,10 @@ https://www.youtube.com/watch?v=7_2IroEs6Is&t=207s
https://www.youtube.com/watch?v=9HZzKkAqrX8
# Mermaid with HTML:
Examples are provided in [Gettting Started](./docs/n00b-gettingStarted.md)
## Mermaid with HTML:
Examples are provided in [Gettting Started](n00b-gettingStarted.md)
**CodePen Examples:**
**CodePen Examples:**
https://codepen.io/CarlBoneri/pen/BQwZzq
@ -32,7 +32,6 @@ https://codepen.io/tdkn/pen/vZxQzd
https://codepen.io/janzeteachesit/pen/OWWZKN
## Mermaid with Text Area:
https://codepen.io/Ryuno-Ki/pen/LNxwgR
## Mermaid with Text Area:
https://codepen.io/Ryuno-Ki/pen/LNxwgR

View File

@ -14,13 +14,13 @@ This then renders a diagram based on that code in SVG, alternatively it
Most web browsers, such as Firefox, Chrome and Safari, can render mermaid, Internet Explorer however cannot.
## For beginners, there are four relatively easy ways you can use mermaid:
1. Using the mermaid [live editor](https://mermaid-js.github.io/mermaid-live-editor/). For some popular video tutorials on the live editor go to [Overview](./n00b-overview.md).
2. Using one of the many [mermaid plugins](https://mermaid-js.github.io/mermaid/#/integrations).
1. Using the mermaid [live editor](https://mermaid-js.github.io/mermaid-live-editor/). For some popular video tutorials on the live editor go to [Overview](../overview/n00b-overview.md).
2. Using one of the many [mermaid plugins](../overview/integrations.md).
3. Hosting mermaid on a webpage, with an absolute link.
4. Downloading mermaid and hosting it on your Web Page.
**Notes**: More in depth information can be found on [Usage](./usage.md).
.
# Following any of these examples, you can get started with creating your own diagrams using mermaid code.
## 1. The mermaid live editor:
@ -31,7 +31,7 @@ In the `Code` section one can write or edit raw mermaid code, and instantly `Pre
**This is a great way to learn how to define a mermaid diagram.**
For some popular video tutorials on the live editor go to [Overview](./n00b-overview.md).
For some popular video tutorials on the live editor go to [Overview](../overview/n00b-overview.md).
![Flowchart](../assets/img/n00b-liveEditor.png)
@ -41,12 +41,12 @@ You can also click "Copy Markdown" to copy the markdown code for the diagram, th
![Flowchart](../assets/img/liveEditorOptions.png)
The `Mermaid configuration` is for controlling mermaid behaviour. An easy introduction to mermaid configuration is found in the [Advanced usage](n00b-advanced.md) section. A complete configuration reference cataloguing default values is found on the [mermaidAPI](https://mermaid-js.github.io/mermaid/#/Setup) page.
The `Mermaid configuration` is for controlling mermaid behaviour. An easy introduction to mermaid configuration is found in the [Advanced usage](n00b-advanced.md) section. A complete configuration reference cataloguing default values is found on the [mermaidAPI](Setup.md) page.
## 2. Using mermaid plugins:
Thanks to the growing popularity of mermaid, many plugins already exist which incorporate a mermaid renderer. An extensive list can be found [here](./integrations.md).
Thanks to the growing popularity of mermaid, many plugins already exist which incorporate a mermaid renderer. An extensive list can be found [here](../overview/integrations.md).
One example in the list is the [Atlassian Confluence mermaid plugin](https://marketplace.atlassian.com/apps/1214124/mermaid-plugin-for-confluence?hosting=server&tab=overview)
@ -75,11 +75,11 @@ When the mermaid plugin is installed on a Confluence server, one can insert a me
- Save the page and the diagram appears.
![Flowchart](../../assets/img/n00b-Confluence4.png)
![Flowchart](../assets/img/n00b-Confluence4.png)
---
## The following are two ways of hosting mermaid on a webpage.
**This is covered in greater detail in the [Usage section](https://mermaid-js.github.io/mermaid/#/usage)**
**This is covered in greater detail in the [Usage section](usage.md)**
## 3. Using the Mermaid API: The quick and dirty way of deploying mermaid
@ -106,7 +106,7 @@ c. The `mermaid.initialize()` API call to start the rendering process.
### a. A reference to the address of the `mermaid.js` or the `mermaid.min.js` file has to be contained in a `<script src>` tag like so:
```
```html
<body>
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
</body>
@ -114,7 +114,7 @@ c. The `mermaid.initialize()` API call to start the rendering process.
### b. The embedded mermaid diagram definition needs to be contained inside a `<div>` tag that signifies that it is a mermaid diagram:
```
```html
<body>
Here is a mermaid diagram:
<div class="mermaid">
@ -131,7 +131,7 @@ c. The `mermaid.initialize()` API call to start the rendering process.
`mermaid.initialize()` calls take all the definitions contained in `<div class="mermaid">` tags it can find in the html body and starts to render them one by one. It is called this way:
```
```html
<body>
<script>mermaid.initialize({startOnLoad:true});</script>
</body>
@ -142,7 +142,7 @@ c. The `mermaid.initialize()` API call to start the rendering process.
### If the three steps mentioned are followed you will end up with something like this:
```
```html
<html>
<body>
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
@ -190,14 +190,14 @@ This method is similar to 3, if only a little more involved. The difference may
c. select copy as path from the options.
6. Paste it within the `script` tag as the `src`.
```
```html
<script src="Paste the mermaid.min.js file address here"></script>
<script>mermaid.initialize({startOnLoad:true});</script>
<script>mermaid.initialize({startOnLoad:true});</script>
```
7. It should look something like this
```
<script src="C:\Users\myPC\mermaid\dist\mermaid.js"></script>
<script>mermaid.initialize({startOnLoad:true});</script>
```html
<script src="C:\Users\myPC\mermaid\dist\mermaid.js"></script>
<script>mermaid.initialize({startOnLoad:true});</script>
```
8. Add the graph and diagram definitions as you would in number 3.
a. be mindful of the `div` tags.
@ -208,7 +208,7 @@ This method is similar to 3, if only a little more involved. The difference may
**Note** placing the HTML file on the same folder the `mermaid` file you've downloaded is a good practice and allows you to shorten the address on the `src` section.
**As seen here, in this full example:**
```
```html
<html lang="en">
<head>
<meta charset="utf-8">

View File

@ -47,23 +47,24 @@ This topic explored in greater depth in the [User Guide for Beginners](./n00b-ge
The easiest way to integrate mermaid on a web page requires three elements:
1. Inclusion of the mermaid address in the html page using a `script` tag, in the `src` section.Example:
`<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>`
```html
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
```
2. The `mermaidAPI` call, in a separate `script` tag. Example:
`<script>mermaid.initialize({startOnLoad:true});</script>`
```html
<script>mermaid.initialize({startOnLoad:true});</script>
```
3. A graph definition, inside `<div>` tags labeled `class=mermaid`. Example:
```html
<div class="mermaid">
graph LR
A --- B
B-->C[fa:fa-ban forbidden]
B-->D(fa:fa-spinner);
</div>
```
```html
<div class="mermaid">
graph LR
A --- B
B-->C[fa:fa-ban forbidden]
B-->D(fa:fa-spinner);
</div>
```
**If these things are in place mermaid starts at the page load event and when fired (when the page has loaded) it will
locate the graph definitions inside the `div` tags with `class="mermaid"` on the page and transform them to svg charts or diagrams.**
@ -125,9 +126,9 @@ This changes the default behaviour of mermaid so that after upgrade to 8.2, if t
## To chage `securityLevel` with `mermaidAPI.initialize`:
```javascript
mermaidAPI.initialize({
securityLevel: 'loose'
});
mermaidAPI.initialize({
securityLevel: 'loose'
});
```
@ -153,11 +154,13 @@ $(document).ready(function() {
Not doing so will most likely result in mermaid rendering graphs that have labels out of bounds. The default integration in mermaid uses the window.load event to start rendering.
If your page has other fonts in its body those might be used instead of the mermaid font. Specifying the font in your styling is a workaround for this.
```css
div.mermaid {
font-family: 'trebuchet ms', verdana, arial;
}
```
div.mermaid {
font-family: 'trebuchet ms', verdana, arial;
}
```
# This likely requires a `script.js` file, separate from the `HTML`.
### Calling `mermaid.init`
@ -335,7 +338,7 @@ setting the options in mermaid.
4. Instantiation of the configuration using the **mermaid.init** call
The list above has two ways too many of doing this. Three are deprecated and will eventually be removed. The list of
configuration objects are described [in the mermaidAPI documentation](mermaidAPI.html).
configuration objects are described [in the mermaidAPI documentation](Setup.md).
## Using the `mermaidAPI.initialize`/`mermaid.initialize` call

View File

@ -66,7 +66,9 @@ They also serve as proof of concept, for the variety of things that cen be built
## Wikis
- [Media Wiki](https://www.mediawiki.org/wiki/Extension:Mermaid)
- [MediaWiki](https://www.mediawiki.org)
- [Mermaid Extension](https://www.mediawiki.org/wiki/Extension:Mermaid)
- [Flex Diagrams Extension](https://www.mediawiki.org/wiki/Extension:Flex_Diagrams)
- [Semantic Media Wiki](https://semantic-mediawiki.org)
- [Mermaid Plugin](https://github.com/SemanticMediaWiki/Mermaid)
- [FosWiki](https://foswiki.org)

View File

@ -28,14 +28,15 @@ mermaid seeks to change that. mermaid is a javascript based tool that utilizes a
**Mermaid definitions**
>These are the instrunctions for how the diagram is to rendered, written in mermaid, which is based on Markdown. These can be found inside `<div>` tags, with the `class=mermaid`.
```
```html
<div class="mermaid">
graph TD
A[Client] --> B[Load Balancer]
B --> C[Server01]
B --> D[Server02]
</div>
```
```
**render**
@ -55,7 +56,7 @@ mermaid seeks to change that. mermaid is a javascript based tool that utilizes a
## The catch-22 of Diagrams and Charts:
**Diagramming and charting is a gigantic waste of developer time, but not having diagrams ruins productivity. **
**Diagramming and charting is a gigantic waste of developer time, but not having diagrams ruins productivity.**
mermaid solves this by cutting the time, effort and tooling that is required to create diagrams and charts.

View File

@ -13,13 +13,13 @@ Here is the list of the newest versions in Descending Order, beginning from the
## 8.7.0 (2020-08-09)
🔖 [Release Notes](https://github.com/mermaid-js/mermaid/releases/tag/8.7.0) |
🔖 [Release Notes](https://github.com/mermaid-js/mermaid/releases/tag/8.7.0) |
📜 [Full Changelog](https://github.com/mermaid-js/mermaid/compare/8.6.0...8.7.0)
This version brings with it a system for [dynamic and integrated configuration of the diagram themes](./theming.md).
The objective of this is to increase the customizability of mermaid and the ease of Styling, with the customization of themes through the `%%init%%` directive and `initialize` calls.
The objective of this is to increase the customizability of mermaid and the ease of Styling, with the customization of themes through the `%%init%%` directive and `initialize` calls.
Themes follow and build upon the Levels of Configuration and employ `directives` to modify and create custom configurations, as they were introduced in Version [8.6.0](./8.6.0_docs.md).
Themes follow and build upon the Levels of Configuration and employ `directives` to modify and create custom configurations, as they were introduced in Version [8.6.0](../getting-started/8.6.0_docs.md).
**These Theming Configurations, similar to directives, will also be made applicable in the Live-Editor, for easier styling.**
@ -30,12 +30,12 @@ Site-wide themes are still declared via `initialize` by site owners.
Example of `Initalize` call setting `theme` to `base`:
```js
mermaidAPI.initialize({
'securityLevel': 'loose', 'theme': 'base'
});
mermaidAPI.initialize({
'securityLevel': 'loose', 'theme': 'base'
});
```
**Notes**: Only site owners can use the `mermaidAPI.initialize` call, to set values. Site-Users will have to use `%%init%%` to modify or create the theme for their diagrams.
**Notes**: Only site owners can use the `mermaidAPI.initialize` call, to set values. Site-Users will have to use `%%init%%` to modify or create the theme for their diagrams.
### Themes at the Local or Current Level
@ -86,7 +86,7 @@ Leaving it empty will set all variable values to default.
🔖 [Release Notes](https://github.com/mermaid-js/mermaid/releases/tag/8.6.0)
[Version 8.6.0](./8.6.0_docs.md) introduces New Configuration Protocols and Directives and a Beta for the [New Mermaid Live-Editor](https://mermaid-js.github.io/docs/mermaid-live-editor-beta/#/edit/eyJjb2RlIjoiJSV7aW5pdDoge1widGhlbWVcIjogXCJmb3Jlc3RcIiwgXCJsb2dMZXZlbFwiOiAxIH19JSVcbmdyYXBoIFREXG4gIEFbQ2hyaXN0bWFzXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgQyAtLT58T25lfCBEW0xhcHRvcF1cbiAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl1cblx0XHQiLCJtZXJtYWlkIjp7InRoZW1lIjoiZGFyayJ9fQ)
[Version 8.6.0](../getting-started/8.6.0_docs.md) introduces New Configuration Protocols and Directives and a Beta for the [New Mermaid Live-Editor](https://mermaid-js.github.io/docs/mermaid-live-editor-beta/#/edit/eyJjb2RlIjoiJSV7aW5pdDoge1widGhlbWVcIjogXCJmb3Jlc3RcIiwgXCJsb2dMZXZlbFwiOiAxIH19JSVcbmdyYXBoIFREXG4gIEFbQ2hyaXN0bWFzXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgQyAtLT58T25lfCBEW0xhcHRvcF1cbiAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl1cblx0XHQiLCJtZXJtYWlkIjp7InRoZW1lIjoiZGFyayJ9fQ)
**With version 8.6.0 comes the release of directives for mermaid, a new system for modifying configurations, with the aim of establishing centralized, sane defaults and simple implementation.**
@ -113,7 +113,7 @@ Version 8.2.0, introduces a security improvement.
A `securityLevel` configuration has to first be cleared, `securityLevel` sets the level of trust for the parsed diagrams and limits click functionality.
This was introduced in version 8.2 as a security improvement, aimed at preventing malicious use.
### securityLevel
| Parameter | Description | Type | Required | Values |
@ -126,17 +126,17 @@ This was introduced in version 8.2 as a security improvement, aimed at preventin
- **loose**: tags in text are allowed, click functionality is enabled
- **antiscript**: html tags in text are allowed, (only script element is removed), click functionality is enabled
⚠️ **Note**: This changes the default behaviour of mermaid so that after upgrade to 8.2, if the `securityLevel` is not configured, tags in flowcharts are encoded as tags and clicking is prohibited.
⚠️ **Note**: This changes the default behaviour of mermaid so that after upgrade to 8.2, if the `securityLevel` is not configured, tags in flowcharts are encoded as tags and clicking is prohibited.
If you are taking resposibility for the diagram source security you can set the `securityLevel` to a value of your choosing.
By doing this clicks and tags are again allowed.
### To change `securityLevel` with `mermaidAPI.initialize`:
### To change `securityLevel` with `mermaidAPI.initialize`:
```javascript
mermaidAPI.initialize({
securityLevel: 'loose'
});
```javascript
mermaidAPI.initialize({
securityLevel: 'loose'
});
```
**Closed issues:**
@ -990,4 +990,3 @@ By doing this clicks and tags are again allowed.
[Full Changelog](https://github.com/knsv/mermaid/compare/0.1.0...0.1.1)
## [0.1.0](https://github.com/knsv/mermaid/tree/0.1.0) (2014-11-16)

View File

@ -3,14 +3,125 @@ sort: 1
title: Development and Contribution
---
# Development
# Development 🙌
## Updating the documentation
Please continue writing documentation at [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs).
So you want to help? That's great!
We publish documentation using GitHub Pages.
![Image of happy people jumping with excitement](https://media.giphy.com/media/BlVnrxJgTGsUw/giphy.gif)
Here are a few things to know to get you started on the right path.
**All the documents displayed in the github.io page are listed in [sidebar.md](https://github.com/mermaid-js/mermaid/edit/develop/docs/assets/_sidebar.md)**
**Note:You will have to edit the document to see its contents. Commits and PR's should be directed to the develop branch.**
## Branching
Going forward we will use a **Git Flow** inspired approach to branching. So development is done in the `develop` branch.
Once development is done we branch a release branch from develop for testing.
Once the release happens we merge the release branch to master and kill the release branch.
This means... **you should branch off your pull request from develop** and direct all Pull Requests to it.
## Contributing Code
We make all changes via pull requests. As we have many pull requests from developers new to mermaid, we have put in place a process, wherein *knsv, Knut Sveidqvist* is the primary reviewer of changes and merging pull requests. It is as follows:
* Large changes reviewed by knsv or other developer asked to review by knsv
* Smaller low-risk changes like dependecies, documentation etc can be merged by active collaborators
* documentation (updates to the docs folder are enocouraged and also allowed via direct commits)
When you commit code, create a branch, let it start with the type like feature or bug followed by the issue number for reference and text that describes the issue.
**One example:**
`feature/945_state_diagrams`
**Another:**
`bug/123_nasty_bug_branch`
## Contributing to documentation
If it is not in the documentation, it's like it never happened. Wouldn't that be sad? With all the effort that was put into the feature?
The docs are located in the `docs` folder and are written in MarkDown. Just pick the right section and start typing. If you want to propose changes to the structure of the documentation:
**All the documents displayed in the github.io page are listed in [sidebar.md](https://github.com/mermaid-js/mermaid/edit/develop/docs/assets/_sidebar.md). Click edit it to see them.
The contents of http://mermaid-js.github.io/mermaid/ are based on the docs from **Master** Branch.
## How to Contribute to Docs
We are a little less strict here, it is OK to commit directly in the `develop` branch if you are a collaborator.
The documentation is located in the `docs` directory and organized according to relevant subfolder.
We encourage contributions to the documentation at [mermaid-js/mermaid/docs](https://github.com/mermaid-js/mermaid/tree/develop/docs). We publish documentation using GitHub Pages with [jekyll-rtd-theme](https://github.com/rundocs/jekyll-rtd-theme).
## Preview
If you want to preview the documentation site on your machine, you will need to install [Ruby development environment](https://jekyllrb.com/docs/installation/):
1. Install a full [Ruby development environment](https://jekyllrb.com/docs/installation/)
2. Change into docs directory
```sh
make
```
3. Build the site and make it available on a local server
```sh
make server
```
4. Browse to [http://localhost:4000/mermaid/](http://localhost:4000/mermaid/)
### **Add unit tests for the parsing part**
This is important so that, if someone else does a change to the grammar that does not know about this great feature, gets notified early on when that change breaks the parser. Another important aspect is that without proper parsing tests refactoring is pretty much impossible.
### **Add e2e tests**
This tests the rendering and visual apearance of the diagram. This ensures that the rendering of that feature in the e2e will be reviewed in the release process going forward. Less chance that it breaks!
To start working with the e2e tests, run `yarn dev` to start the dev server, after that start cypress by running `cypress open` in the mermaid folder. (Make sure you have path to cypress in order, the binary is located in node_modules/.bin).
The rendering tests are very straightforward to create. There is a function imgSnapshotTest. This function takes a diagram in text form, the mermaid options and renders that diagram in cypress.
When running in ci it will take a snapshot of the rendered diagram and compare it with the snapshot from last build and flag for review it if it differs.
This is what a rendering test looks like:
```
it('should render forks and joins', () => {
imgSnapshotTest(
`
stateDiagram
state fork_state &lt;&lt;fork&gt;&gt;
[*] --> fork_state
fork_state --> State2
fork_state --> State3
state join_state &lt;&lt;join&gt;&gt;
State2 --> join_state
State3 --> join_state
join_state --> State4
State4 --> [*]
`,
{ logLevel: 0 }
);
cy.get('svg');
});
```
### **Docs or it Didn't Happen**
Finally, if it is not in the documentation, no one will know about it and then **no one will use it**. Wouldn't that be sad? With all the effort that was put into the feature?
The docs are located in the docs folder and are ofc written in markdown. Just pick the right section and start typing. If you want to add to the structure as in adding a new section and new file you do that via the _navbar.md.
The changes in master is reflected in http://mermaid-js.github.io/mermaid/ once released the updates are commited to https://mermaid-js.github.io/#/
### Questions and/or suggestions ?
After logging in at [GitHub.com](https://www.github.com), open or append to an issue [using the GitHub issue tracker of the mermaid-js repository](https://github.com/mermaid-js/mermaid/issues?q=is%3Aissue+is%3Aopen+label%3A%22Area%3A+Documentation%22).
@ -33,115 +144,11 @@ If you don't have such editor on your computer, you may follow these steps:
* Submit your changes by clicking the button "Propose file change" at the bottom (by automatic creation of a fork and a new branch).
* Create a pull request of your newly forked branch, by clicking the green "Create pull request" button.
## Last words
## How to add a new diagram type
Don't get daunted if it is hard in the beginning. We have a great community with only encouraging words. So if you get stuck, ask for help and hints in the slack forum. If you want to show off something good, show it off there.
[Join our slack community if you want closer contact!](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
### Step 1: Grammar & Parsing
#### Grammar
This would be to define a jison grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
For instance:
* the flowchart starts with the keyword graph.
* the sequence diagram starts with the keyword sequenceDiagram
#### Store data found during parsing
There are some jison specific sub steps here where the parser stores the data encountered when parsing the diagram, this data is later used by the renderer. You can during the parsing call a object provided to the parser by the user of the parser. This object can be called during parsing for storing data.
```
statement
: 'participant' actor { $$='actor'; }
| signal { $$='signal'; }
| note_statement { $$='note'; }
| 'title' message { yy.setTitle($2); }
;
```
In the extract of the grammar above, it is defined that a call to the setTitle method in the data object will be done when parsing and the title keyword is encountered.
```tip
Make sure that the `parseError` function for the parser is defined and calling `mermaidPAI.parseError`. This way a common way of detecting parse errors is provided for the end-user.
```
For more info look in the example diagram type:
The `yy` object has the following function:
```javascript
exports.parseError = function(err, hash){
mermaidAPI.parseError(err, hash)
};
```
when parsing the `yy` object is initialized as per below:
```javascript
var parser
parser = exampleParser.parser
parser.yy = db
```
### Step 2: Rendering
Write a renderer that given the data found during parsing renders the diagram. To look at an example look at sequendeRenderer.js rather then the flowchart renderer as this is a more generic example.
Place the renderer in the diagram folder.
### Step 3: Detection of the new diagram type
The second thing to do is to add the capability to detect the new new diagram to type to the detectType in utils.js. The detection should return a key for the new diagram type.
### Step 4: The final piece - triggering the rendering
At this point when mermaid is trying to render the diagram, it will detect it as being of the new type but there will be no match when trying to render the diagram. To fix this add a new case in the switch statement in main.js:init this should match the diagram type returned from step #2. The code in this new case statement should call the renderer for the diagram type with the data found by the parser as an argument.
## Usage of the parser as a separate module
### Setup
```javascript
var graph = require('./graphDb')
var flow = require('./parser/flow')
flow.parser.yy = graph
```
### Parsing
```javascript
flow.parser.parse(text)
```
### Data extraction
```javascript
graph.getDirection()
graph.getVertices()
graph.getEdges()
```
The parser is also exposed in the mermaid api by calling:
```javascript
var parser = mermaid.getParser()
```
Note that the parse needs a graph object to store the data as per:
```javascript
flow.parser.yy = graph
```
Look at `graphDb.js` for more details on that object.
![Image of superhero wishing you good luck](https://media.giphy.com/media/l49JHz7kJvl6MCj3G/giphy.gif)

View File

@ -0,0 +1,118 @@
---
sort: 2
title: Adding a New Diagram/Chart
---
# Adding a New Diagram/Chart 📊
### Step 1: Grammar & Parsing
#### Grammar
This would be to define a jison grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
For instance:
* the flowchart starts with the keyword graph.
* the sequence diagram starts with the keyword sequenceDiagram
#### Store data found during parsing
There are some jison specific sub steps here where the parser stores the data encountered when parsing the diagram, this data is later used by the renderer. You can during the parsing call a object provided to the parser by the user of the parser. This object can be called during parsing for storing data.
```
statement
: 'participant' actor { $$='actor'; }
| signal { $$='signal'; }
| note_statement { $$='note'; }
| 'title' message { yy.setTitle($2); }
;
```
In the extract of the grammar above, it is defined that a call to the setTitle method in the data object will be done when parsing and the title keyword is encountered.
```tip
Make sure that the `parseError` function for the parser is defined and calling `mermaidPAI.parseError`. This way a common way of detecting parse errors is provided for the end-user.
```
For more info look in the example diagram type:
The `yy` object has the following function:
```javascript
exports.parseError = function(err, hash){
mermaidAPI.parseError(err, hash)
};
```
when parsing the `yy` object is initialized as per below:
```javascript
var parser
parser = exampleParser.parser
parser.yy = db
```
### Step 2: Rendering
Write a renderer that given the data found during parsing renders the diagram. To look at an example look at sequendeRenderer.js rather then the flowchart renderer as this is a more generic example.
Place the renderer in the diagram folder.
### Step 3: Detection of the new diagram type
The second thing to do is to add the capability to detect the new new diagram to type to the detectType in utils.js. The detection should return a key for the new diagram type.
### Step 4: The final piece - triggering the rendering
At this point when mermaid is trying to render the diagram, it will detect it as being of the new type but there will be no match when trying to render the diagram. To fix this add a new case in the switch statement in main.js:init this should match the diagram type returned from step #2. The code in this new case statement should call the renderer for the diagram type with the data found by the parser as an argument.
## Usage of the parser as a separate module
### Setup
```javascript
var graph = require('./graphDb')
var flow = require('./parser/flow')
flow.parser.yy = graph
```
### Parsing
```javascript
flow.parser.parse(text)
```
### Data extraction
```javascript
graph.getDirection()
graph.getVertices()
graph.getEdges()
```
The parser is also exposed in the mermaid api by calling:
```javascript
var parser = mermaid.getParser()
```
Note that the parse needs a graph object to store the data as per:
```javascript
flow.parser.yy = graph
```
Look at `graphDb.js` for more details on that object.

View File

@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "8.8.0",
"version": "8.8.2",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "dist/mermaid.core.js",
"keywords": [
@ -28,7 +28,7 @@
"dev": "webpack-dev-server --config webpack.config.e2e.js",
"test": "yarn lint && jest src/.*",
"test:watch": "jest --watch src",
"prepublishOnly": "yarn build && yarn test && yarn e2e",
"prepublishOnly": "yarn build && yarn test",
"prepare": "yarn build"
},
"repository": {

View File

@ -6,9 +6,11 @@ import utils from '../utils';
// import { calcLabelPosition } from '../utils';
let edgeLabels = {};
let terminalLabels = {};
export const clear = () => {
edgeLabels = {};
terminalLabels = {};
};
export const insertEdgeLabel = (elem, edge) => {
@ -39,17 +41,129 @@ export const insertEdgeLabel = (elem, edge) => {
// Update the abstract data of the edge with the new information about its width and height
edge.width = bbox.width;
edge.height = bbox.height;
if (edge.startLabelLeft) {
// Create the actual text element
const startLabelElement = createLabel(edge.startLabelLeft, edge.labelStyle);
const startEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
const inner = startEdgeLabelLeft.insert('g').attr('class', 'inner');
inner.node().appendChild(startLabelElement);
const slBox = startLabelElement.getBBox();
inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')');
if (!terminalLabels[edge.id]) {
terminalLabels[edge.id] = {};
}
terminalLabels[edge.id].startLeft = startEdgeLabelLeft;
}
if (edge.startLabelRight) {
// Create the actual text element
const startLabelElement = createLabel(edge.startLabelRight, edge.labelStyle);
const startEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
const inner = startEdgeLabelRight.insert('g').attr('class', 'inner');
startEdgeLabelRight.node().appendChild(startLabelElement);
inner.node().appendChild(startLabelElement);
const slBox = startLabelElement.getBBox();
inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')');
if (!terminalLabels[edge.id]) {
terminalLabels[edge.id] = {};
}
terminalLabels[edge.id].startRight = startEdgeLabelRight;
}
if (edge.endLabelLeft) {
// Create the actual text element
const endLabelElement = createLabel(edge.endLabelLeft, edge.labelStyle);
const endEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
const inner = endEdgeLabelLeft.insert('g').attr('class', 'inner');
inner.node().appendChild(endLabelElement);
const slBox = endLabelElement.getBBox();
inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')');
endEdgeLabelLeft.node().appendChild(endLabelElement);
if (!terminalLabels[edge.id]) {
terminalLabels[edge.id] = {};
}
terminalLabels[edge.id].endLeft = endEdgeLabelLeft;
}
if (edge.endLabelRight) {
// Create the actual text element
const endLabelElement = createLabel(edge.endLabelRight, edge.labelStyle);
const endEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
const inner = endEdgeLabelRight.insert('g').attr('class', 'inner');
inner.node().appendChild(endLabelElement);
const slBox = endLabelElement.getBBox();
inner.attr('transform', 'translate(' + -slBox.width / 2 + ', ' + -slBox.height / 2 + ')');
endEdgeLabelRight.node().appendChild(endLabelElement);
if (!terminalLabels[edge.id]) {
terminalLabels[edge.id] = {};
}
terminalLabels[edge.id].endRight = endEdgeLabelRight;
}
};
export const positionEdgeLabel = (edge, points) => {
export const positionEdgeLabel = (edge, paths) => {
logger.info('Moving label', edge.id, edge.label, edgeLabels[edge.id]);
let path = paths.updatedPath ? paths.updatedPath : paths.originalPath;
if (edge.label) {
const el = edgeLabels[edge.id];
let x = edge.x;
let y = edge.y;
if (points) {
if (path) {
// // debugger;
const pos = utils.calcLabelPosition(path);
logger.info('Moving label from (', x, ',', y, ') to (', pos.x, ',', pos.y, ')');
// x = pos.x;
// y = pos.y;
}
el.attr('transform', 'translate(' + x + ', ' + y + ')');
}
//let path = paths.updatedPath ? paths.updatedPath : paths.originalPath;
if (edge.startLabelLeft) {
const el = terminalLabels[edge.id].startLeft;
let x = edge.x;
let y = edge.y;
if (path) {
// debugger;
const pos = utils.calcLabelPosition(points);
const pos = utils.calcTerminalLabelPosition(0, 'start_left', path);
x = pos.x;
y = pos.y;
}
el.attr('transform', 'translate(' + x + ', ' + y + ')');
}
if (edge.startLabelRight) {
const el = terminalLabels[edge.id].startRight;
let x = edge.x;
let y = edge.y;
if (path) {
// debugger;
const pos = utils.calcTerminalLabelPosition(0, 'start_right', path);
x = pos.x;
y = pos.y;
}
el.attr('transform', 'translate(' + x + ', ' + y + ')');
}
if (edge.endLabelLeft) {
const el = terminalLabels[edge.id].endLeft;
let x = edge.x;
let y = edge.y;
if (path) {
// debugger;
const pos = utils.calcTerminalLabelPosition(0, 'end_left', path);
x = pos.x;
y = pos.y;
}
el.attr('transform', 'translate(' + x + ', ' + y + ')');
}
if (edge.endLabelRight) {
const el = terminalLabels[edge.id].endRight;
let x = edge.x;
let y = edge.y;
if (path) {
// debugger;
const pos = utils.calcTerminalLabelPosition(0, 'end_right', path);
x = pos.x;
y = pos.y;
}
@ -354,8 +468,10 @@ export const insertEdge = function(elem, e, edge, clusterDb, diagramType, graph)
break;
default:
}
let paths = {};
if (pointsHasChanged) {
return points;
paths.updatedPath = points;
}
paths.originalPath = edge.points;
return paths;
};

View File

@ -6,7 +6,8 @@ import {
clear as clearGraphlib,
clusterDb,
adjustClustersAndEdges,
findNonClusterChild
findNonClusterChild,
sortNodesByHierarchy
} from './mermaid-graphlib';
import { insertNode, positionNode, clear as clearNodes, setNodeElem } from './nodes';
import { insertCluster, clear as clearClusters } from './clusters';
@ -14,7 +15,7 @@ import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } f
import { logger as log } from '../logger';
const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
log.info('Graph in recursive render:', graphlib.json.write(graph), parentCluster);
log.info('Graph in recursive render: XXX', graphlib.json.write(graph), parentCluster);
const dir = graph.graph().rankdir;
log.warn('Dir in recursive render - dir:', dir);
@ -22,7 +23,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
if (!graph.nodes()) {
log.info('No nodes found for', graph);
} else {
log.info('Recursive render', graph.nodes());
log.info('Recursive render XXX', graph.nodes());
}
if (graph.edges().length > 0) {
log.info('Recursive edges', graph.edge(graph.edges()[0]));
@ -39,11 +40,14 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
if (typeof parentCluster !== 'undefined') {
const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
// data.clusterPositioning = true;
log.info('Setting data for cluster', data);
log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
graph.setNode(parentCluster.id, data);
graph.setParent(v, parentCluster.id, data);
if (!graph.parent(v)) {
log.warn('Setting parent', v, parentCluster.id);
graph.setParent(v, parentCluster.id, data);
}
}
log.info('(Insert) Node ' + v + ': ' + JSON.stringify(graph.node(v)));
log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v)));
if (node && node.clusterNode) {
// const children = graph.children(v);
log.info('Cluster identified', v, node, graph.node(v));
@ -56,7 +60,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
if (graph.children(v).length > 0) {
// This is a cluster but not to be rendered recusively
// Render as before
log.info('Cluster - the non recursive path', v, node.id, node, graph);
log.info('Cluster - the non recursive path XXX', v, node.id, node, graph);
log.info(findNonClusterChild(node.id, graph));
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
// insertCluster(clusters, graph.node(v));
@ -91,7 +95,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
dagre.layout(graph);
log.info('Graph after layout:', graphlib.json.write(graph));
// Move the nodes to the correct place
graph.nodes().forEach(function(v) {
sortNodesByHierarchy(graph).forEach(function(v) {
const node = graph.node(v);
log.info('Position ' + v + ': ' + JSON.stringify(graph.node(v)));
log.info(
@ -124,8 +128,8 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
const edge = graph.edge(e);
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
const updatedPath = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph);
positionEdgeLabel(edge, updatedPath);
const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph);
positionEdgeLabel(edge, paths);
});
return elem;
@ -138,10 +142,10 @@ export const render = (elem, graph, markers, diagramtype, id) => {
clearClusters();
clearGraphlib();
log.warn('Graph before:', graphlib.json.write(graph));
log.warn('Graph at first:', graphlib.json.write(graph));
adjustClustersAndEdges(graph);
log.warn('Graph after:', graphlib.json.write(graph));
log.warn('Graph ever after:', graph.graph());
// log.warn('Graph ever after:', graphlib.json.write(graph.node('A').graph));
recursiveRender(elem, graph, diagramtype);
};

View File

@ -52,7 +52,7 @@ const edgeInCluster = (edge, clusterId) => {
};
const copy = (clusterId, graph, newGraph, rootId) => {
log.info(
log.warn(
'Copying children of ',
clusterId,
'root',
@ -68,7 +68,7 @@ const copy = (clusterId, graph, newGraph, rootId) => {
nodes.push(clusterId);
}
log.debug('Copying (nodes) clusterId', clusterId, 'nodes', nodes);
log.warn('Copying (nodes) clusterId', clusterId, 'nodes', nodes);
nodes.forEach(node => {
if (graph.children(node).length > 0) {
@ -77,8 +77,8 @@ const copy = (clusterId, graph, newGraph, rootId) => {
const data = graph.node(node);
log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId); //,node, data, ' parent is ', clusterId);
newGraph.setNode(node, data);
log.debug('Setting parent', node, graph.parent(node));
if (rootId !== graph.parent(node)) {
log.warn('Setting parent', node, graph.parent(node));
newGraph.setParent(node, graph.parent(node));
}
@ -245,40 +245,51 @@ export const adjustClustersAndEdges = (graph, depth) => {
// d1 xor d2 - if either d1 is true and d2 is false or the other way around
if (d1 ^ d2) {
log.debug('Edge: ', edge, ' leaves cluster ', id);
log.debug('Decendants of ', id, ': ', decendants[id]);
log.warn('Edge: ', edge, ' leaves cluster ', id);
log.warn('Decendants of XXX ', id, ': ', decendants[id]);
clusterDb[id].externalConnections = true;
}
}
});
} else {
log.debug('Not a cluster ', id, decendants);
}
});
extractor(graph, 0);
// For clusters with incoming and/or outgoing edges translate those edges to a real node
// in the cluster inorder to fake the edge
graph.edges().forEach(function(e) {
const edge = graph.edge(e);
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e)));
log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e)));
let v = e.v;
let w = e.w;
// Check if link is either from or to a cluster
log.trace('Fix', clusterDb, 'ids:', e.v, e.w, 'Translateing: ', clusterDb[e.v], clusterDb[e.w]);
log.warn(
'Fix XXX',
clusterDb,
'ids:',
e.v,
e.w,
'Translateing: ',
clusterDb[e.v],
' --- ',
clusterDb[e.w]
);
if (clusterDb[e.v] || clusterDb[e.w]) {
log.warn('Fixing and trixing - removing', e.v, e.w, e.name);
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
v = getAnchorId(e.v);
w = getAnchorId(e.w);
graph.removeEdge(e.v, e.w, e.name);
if (v !== e.v) edge.fromCluster = e.v;
if (w !== e.w) edge.toCluster = e.w;
log.warn('Replacing with', v, w, e.name);
log.warn('Fix Replacing with XXX', v, w, e.name);
graph.setEdge(v, w, edge, e.name);
}
});
log.warn('Adjusted Graph', graphlib.json.write(graph));
extractor(graph, 0);
log.trace(clusterDb);
@ -291,7 +302,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
};
export const extractor = (graph, depth) => {
log.debug('extractor - ', depth, graphlib.json.write(graph), graph.children('D'));
log.warn('extractor - ', depth, graphlib.json.write(graph), graph.children('D'));
if (depth > 10) {
log.error('Bailing out');
return;
@ -336,11 +347,11 @@ export const extractor = (graph, depth) => {
// break;
} else if (
!clusterDb[node].externalConnections &&
!graph.parent(node) &&
// !graph.parent(node) &&
graph.children(node) &&
graph.children(node).length > 0
) {
log.debug(
log.warn(
'Cluster without external connections, without a parent and with children',
node,
depth
@ -364,7 +375,7 @@ export const extractor = (graph, depth) => {
return {};
});
log.debug('Old graph before copy', graphlib.json.write(graph));
log.warn('Old graph before copy', graphlib.json.write(graph));
copy(node, graph, clusterGraph, node);
graph.setNode(node, {
clusterNode: true,
@ -373,10 +384,10 @@ export const extractor = (graph, depth) => {
labelText: clusterDb[node].labelText,
graph: clusterGraph
});
log.debug('New graph after copy', graphlib.json.write(clusterGraph));
log.warn('New graph after copy node: (', node, ')', graphlib.json.write(clusterGraph));
log.debug('Old graph after copy', graphlib.json.write(graph));
} else {
log.debug(
log.warn(
'Cluster ** ',
node,
' **not meeting the criteria !externalConnections:',
@ -393,13 +404,27 @@ export const extractor = (graph, depth) => {
}
nodes = graph.nodes();
log.debug('New list of nodes', nodes);
log.warn('New list of nodes', nodes);
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
const data = graph.node(node);
log.debug(' Now next leveö', node, data);
log.warn(' Now next level', node, data);
if (data.clusterNode) {
extractor(data.graph, depth + 1);
}
}
};
const sorter = (graph, nodes) => {
if (nodes.length === 0) return [];
let result = Object.assign(nodes);
nodes.forEach(node => {
const children = graph.children(node);
const sorted = sorter(graph, children);
result = result.concat(sorted);
});
return result;
};
export const sortNodesByHierarchy = graph => sorter(graph, graph.children());

View File

@ -1,6 +1,6 @@
import graphlib from 'graphlib';
import dagre from 'dagre';
import { validate, adjustClustersAndEdges, extractDecendants } from './mermaid-graphlib';
import { validate, adjustClustersAndEdges, extractDecendants, sortNodesByHierarchy } from './mermaid-graphlib';
import { setLogLevel, logger } from '../logger';
describe('Graphlib decorations', () => {
@ -43,8 +43,6 @@ describe('Graphlib decorations', () => {
g.setEdge('a', 'b');
g.setEdge('C1', 'C2');
console.log(g.nodes())
expect(validate(g)).toBe(false);
});
it('Validate should not detect edges between clusters after adjustment', function () {
@ -66,7 +64,6 @@ describe('Graphlib decorations', () => {
g.setEdge('a', 'b');
g.setEdge('C1', 'C2');
console.log(g.nodes())
adjustClustersAndEdges(g);
logger.info(g.edges())
expect(validate(g)).toBe(true);
@ -373,6 +370,31 @@ describe('Graphlib decorations', () => {
});
});
it('adjustClustersAndEdges should handle nesting GLB77', function () {
/*
flowchart TB
subgraph A
b-->B
a-->c
end
subgraph B
c
end
*/
const exportedGraph = JSON.parse('{"options":{"directed":true,"multigraph":true,"compound":true},"nodes":[{"v":"A","value":{"labelStyle":"","shape":"rect","labelText":"A","rx":0,"ry":0,"class":"default","style":"","id":"A","width":500,"type":"group","padding":15}},{"v":"B","value":{"labelStyle":"","shape":"rect","labelText":"B","rx":0,"ry":0,"class":"default","style":"","id":"B","width":500,"type":"group","padding":15},"parent":"A"},{"v":"b","value":{"labelStyle":"","shape":"rect","labelText":"b","rx":0,"ry":0,"class":"default","style":"","id":"b","padding":15},"parent":"A"},{"v":"c","value":{"labelStyle":"","shape":"rect","labelText":"c","rx":0,"ry":0,"class":"default","style":"","id":"c","padding":15},"parent":"B"},{"v":"a","value":{"labelStyle":"","shape":"rect","labelText":"a","rx":0,"ry":0,"class":"default","style":"","id":"a","padding":15},"parent":"A"}],"edges":[{"v":"b","w":"B","name":"1","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-b-B","classes":"flowchart-link LS-b LE-B"}},{"v":"a","w":"c","name":"2","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-a-c","classes":"flowchart-link LS-a LE-c"}}],"value":{"rankdir":"TB","nodesep":50,"ranksep":50,"marginx":8,"marginy":8}}');
const gr = graphlib.json.read(exportedGraph)
logger.info('Graph before', graphlib.json.write(gr))
adjustClustersAndEdges(gr);
const aGraph = gr.node('A').graph;
const bGraph = aGraph.node('B').graph;
logger.info('Graph after', graphlib.json.write(aGraph));
// logger.trace('Graph after', graphlib.json.write(g))
expect(aGraph.parent('c')).toBe('B');
expect(aGraph.parent('B')).toBe(undefined);
});
});
describe('extractDecendants', function () {
let g;
@ -426,3 +448,57 @@ describe('extractDecendants', function () {
expect(d3).toEqual(['c']);
});
});
describe('sortNodesByHierarchy', function () {
let g;
beforeEach(function () {
setLogLevel(1);
g = new graphlib.Graph({
multigraph: true,
compound: true
});
g.setGraph({
rankdir: 'TB',
nodesep: 10,
ranksep: 10,
marginx: 8,
marginy: 8
});
g.setDefaultEdgeLabel(function () {
return {};
});
});
it('it should sort proper en nodes are in reverse order', function () {
/*
a -->b
subgraph B
b
end
subgraph A
B
end
*/
g.setNode('a', { data: 1 });
g.setNode('b', { data: 2 });
g.setParent('b', 'B');
g.setParent('B', 'A');
g.setEdge('a', 'b', '1');
expect(sortNodesByHierarchy(g)).toEqual(['a', 'A', 'B', 'b']);
});
it('it should sort proper en nodes are in correct order', function () {
/*
a -->b
subgraph B
b
end
subgraph A
B
end
*/
g.setNode('a', { data: 1 });
g.setParent('B', 'A');
g.setParent('b', 'B');
g.setNode('b', { data: 2 });
g.setEdge('a', 'b', '1');
expect(sortNodesByHierarchy(g)).toEqual(['a', 'A', 'B', 'b']);
});
});

View File

@ -5,6 +5,7 @@ import { getConfig } from '../config';
import intersect from './intersect/index.js';
import createLabel from './createLabel';
import note from './shapes/note';
import { parseMember } from '../diagrams/class/svgDraw';
const question = (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
@ -260,6 +261,7 @@ const rect = (parent, node) => {
rect
.attr('class', 'basic label-container')
.attr('style', node.style)
.attr('rx', node.rx)
.attr('ry', node.ry)
.attr('x', -bbox.width / 2 - halfPadding)
@ -288,7 +290,7 @@ const rectWithTitle = (parent, node) => {
const shapeSvg = parent
.insert('g')
.attr('class', classes)
.attr('id', node.id);
.attr('id', node.domId || node.id);
// Create the title label and insert it after the rect
const rect = shapeSvg.insert('rect', ':first-child');
@ -457,7 +459,7 @@ const start = (parent, node) => {
const shapeSvg = parent
.insert('g')
.attr('class', 'node default')
.attr('id', node.id);
.attr('id', node.domId || node.id);
const circle = shapeSvg.insert('circle', ':first-child');
// center the circle around its coordinate
@ -480,7 +482,7 @@ const forkJoin = (parent, node, dir) => {
const shapeSvg = parent
.insert('g')
.attr('class', 'node default')
.attr('id', node.id);
.attr('id', node.domId || node.id);
let width = 70;
let height = 10;
@ -514,7 +516,7 @@ const end = (parent, node) => {
const shapeSvg = parent
.insert('g')
.attr('class', 'node default')
.attr('id', node.id);
.attr('id', node.domId || node.id);
const innerCircle = shapeSvg.insert('circle', ':first-child');
const circle = shapeSvg.insert('circle', ':first-child');
@ -554,7 +556,7 @@ const class_box = (parent, node) => {
const shapeSvg = parent
.insert('g')
.attr('class', classes)
.attr('id', node.id);
.attr('id', node.domId || node.id);
// Create the title label and insert it after the rect
const rect = shapeSvg.insert('rect', ':first-child');
@ -568,27 +570,60 @@ const class_box = (parent, node) => {
const hasInterface = node.classData.annotations && node.classData.annotations[0];
// 1. Create the labels
const interfaceLabelText = node.classData.annotations[0]
? '«' + node.classData.annotations[0] + '»'
: '';
const interfaceLabel = labelContainer
.node()
.appendChild(createLabel(node.classData.annotations[0], node.labelStyle, true, true));
const interfaceBBox = interfaceLabel.getBBox();
.appendChild(createLabel(interfaceLabelText, node.labelStyle, true, true));
let interfaceBBox = interfaceLabel.getBBox();
if (getConfig().flowchart.htmlLabels) {
const div = interfaceLabel.children[0];
const dv = select(interfaceLabel);
interfaceBBox = div.getBoundingClientRect();
dv.attr('width', interfaceBBox.width);
dv.attr('height', interfaceBBox.height);
}
if (node.classData.annotations[0]) {
maxHeight += interfaceBBox.height + rowPadding;
maxWidth += interfaceBBox.width;
}
let classTitleString = node.classData.id;
if (node.classData.type !== undefined && node.classData.type !== '') {
classTitleString += '<' + node.classData.type + '>';
}
const classTitleLabel = labelContainer
.node()
.appendChild(createLabel(node.labelText, node.labelStyle, true, true));
const classTitleBBox = classTitleLabel.getBBox();
.appendChild(createLabel(classTitleString, node.labelStyle, true, true));
select(classTitleLabel).attr('class', 'classTitle');
let classTitleBBox = classTitleLabel.getBBox();
if (getConfig().flowchart.htmlLabels) {
const div = classTitleLabel.children[0];
const dv = select(classTitleLabel);
classTitleBBox = div.getBoundingClientRect();
dv.attr('width', classTitleBBox.width);
dv.attr('height', classTitleBBox.height);
}
maxHeight += classTitleBBox.height + rowPadding;
if (classTitleBBox.width > maxWidth) {
maxWidth = classTitleBBox.width;
}
const classAttributes = [];
node.classData.members.forEach(str => {
const lbl = labelContainer.node().appendChild(createLabel(str, node.labelStyle, true, true));
const bbox = lbl.getBBox();
const parsedText = parseMember(str).displayText;
const lbl = labelContainer
.node()
.appendChild(createLabel(parsedText, node.labelStyle, true, true));
let bbox = lbl.getBBox();
if (getConfig().flowchart.htmlLabels) {
const div = lbl.children[0];
const dv = select(lbl);
bbox = div.getBoundingClientRect();
dv.attr('width', bbox.width);
dv.attr('height', bbox.height);
}
if (bbox.width > maxWidth) {
maxWidth = bbox.width;
}
@ -596,10 +631,22 @@ const class_box = (parent, node) => {
classAttributes.push(lbl);
});
maxHeight += lineHeight;
const classMethods = [];
node.classData.methods.forEach(str => {
const lbl = labelContainer.node().appendChild(createLabel(str, node.labelStyle, true, true));
const bbox = lbl.getBBox();
const parsedText = parseMember(str).displayText;
const lbl = labelContainer
.node()
.appendChild(createLabel(parsedText, node.labelStyle, true, true));
let bbox = lbl.getBBox();
if (getConfig().flowchart.htmlLabels) {
const div = lbl.children[0];
const dv = select(lbl);
bbox = div.getBoundingClientRect();
dv.attr('width', bbox.width);
dv.attr('height', bbox.height);
}
if (bbox.width > maxWidth) {
maxWidth = bbox.width;
}
@ -614,13 +661,10 @@ const class_box = (parent, node) => {
// position the interface label
if (hasInterface) {
let diffX = (maxWidth - interfaceBBox.width) / 2;
select(interfaceLabel).attr(
'transform',
'translate( ' +
-(maxWidth + node.padding - interfaceBBox.width / 2) / 2 +
', ' +
(-1 * maxHeight) / 2 +
')'
'translate( ' + ((-1 * maxWidth) / 2 + diffX) + ', ' + (-1 * maxHeight) / 2 + ')'
);
verticalPos = interfaceBBox.height + rowPadding;
}
@ -657,6 +701,7 @@ const class_box = (parent, node) => {
verticalPos += classTitleBBox.height + rowPadding;
});
verticalPos += lineHeight;
bottomLine
.attr('class', 'divider')
.attr('x1', -maxWidth / 2 - halfPadding)
@ -674,14 +719,14 @@ const class_box = (parent, node) => {
verticalPos += classTitleBBox.height + rowPadding;
});
//
let bbox;
if (getConfig().flowchart.htmlLabels) {
const div = interfaceLabel.children[0];
const dv = select(interfaceLabel);
bbox = div.getBoundingClientRect();
dv.attr('width', bbox.width);
dv.attr('height', bbox.height);
}
// let bbox;
// if (getConfig().flowchart.htmlLabels) {
// const div = interfaceLabel.children[0];
// const dv = select(interfaceLabel);
// bbox = div.getBoundingClientRect();
// dv.attr('width', bbox.width);
// dv.attr('height', bbox.height);
// }
// bbox = labelContainer.getBBox();
// logger.info('Text 2', text2);
@ -778,7 +823,32 @@ const shapes = {
let nodeElems = {};
export const insertNode = (elem, node, dir) => {
nodeElems[node.id] = shapes[node.shape](elem, node, dir);
let newEl;
let el;
// Add link when appropriate
if (node.link) {
newEl = elem
.insert('svg:a')
.attr('xlink:href', node.link)
.attr('target', node.linkTarget || '_blank');
el = shapes[node.shape](newEl, node, dir);
} else {
el = shapes[node.shape](elem, node, dir);
newEl = el;
}
if (node.tooltip) {
el.attr('title', node.tooltip);
}
if (node.class) {
el.attr('class', 'node default ' + node.class);
}
nodeElems[node.id] = newEl;
if (node.haveCallback) {
nodeElems[node.id].attr('class', nodeElems[node.id].attr('class') + ' clickable');
}
};
export const setNodeElem = (elem, node) => {
nodeElems[node.id] = elem;

View File

@ -12,10 +12,13 @@ export const labelHelper = (parent, node, _classes, isNode) => {
const shapeSvg = parent
.insert('g')
.attr('class', classes)
.attr('id', node.id);
.attr('id', node.domId || node.id);
// Create the label and insert it after the rect
const label = shapeSvg.insert('g').attr('class', 'label');
const label = shapeSvg
.insert('g')
.attr('class', 'label')
.attr('style', node.labelStyle);
const text = label
.node()

View File

@ -173,8 +173,20 @@ const config = {
*/
curve: 'linear',
// Only used in new experimental rendering
// repreesents the padding between the labels and the shape
padding: 15
// represents the padding between the labels and the shape
padding: 15,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| useMaxWidth | See notes | Boolean | 4 | True, False |
*
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
*available space if not the absolute space required is used.
*
***Default value true**.
*/
useMaxWidth: true
},
/**
@ -572,8 +584,23 @@ const config = {
* This might need adjustment to match your locale and preferences
***Default value '%Y-%m-%d'**.
*/
axisFormat: '%Y-%m-%d'
axisFormat: '%Y-%m-%d',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| useMaxWidth | See notes | Boolean | 4 | True, False |
*
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
*available space if not the absolute space required is used.
*
***Default value true**.
*/
useMaxWidth: true,
useWidth: undefined
},
/**
* The object containing configurations specific for journey diagrams
*/
@ -711,10 +738,35 @@ const config = {
rightAngles: false
},
class: {
arrowMarkerAbsolute: false
arrowMarkerAbsolute: false,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| useMaxWidth | See notes | Boolean | 4 | True, False |
*
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
*available space if not the absolute space required is used.
*
***Default value true**.
*/
useMaxWidth: true
},
git: {
arrowMarkerAbsolute: false
arrowMarkerAbsolute: false,
useWidth: undefined,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| useMaxWidth | See notes | Boolean | 4 | True, False |
*
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
*available space if not the absolute space required is used.
*
***Default value true**.
*/
useMaxWidth: true
},
state: {
dividerMargin: 10,
@ -734,7 +786,18 @@ const config = {
labelHeight: 16,
edgeLengthFactor: '20',
compositTitleSize: 35,
radius: 5
radius: 5,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| useMaxWidth | See notes | Boolean | 4 | True, False |
*
***Notes:**when this flag is set the height and width is set to 100% and is then scaling with the
*available space if not the absolute space required is used.
*
***Default value true**.
*/
useMaxWidth: true
},
/**
@ -821,6 +884,26 @@ const config = {
*/
fontSize: 12,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| useMaxWidth | See Notes | Boolean | Required | true, false |
*
***Notes:**
*When this flag is set to true, the diagram width is locked to 100% and
*scaled based on available space. If set to false, the diagram reserves its
*absolute width.
***Default value: true**.
*/
useMaxWidth: true
},
/**
* The object containing configurations specific for pie diagrams
*/
pie: {
useWidth: undefined,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |

View File

@ -7,8 +7,6 @@ import mermaidAPI from '../../mermaidAPI';
const MERMAID_DOM_ID_PREFIX = 'classid-';
const config = configApi.getConfig();
let relations = [];
let classes = {};
let classCounter = 0;
@ -176,6 +174,7 @@ export const setCssClass = function(ids, className) {
* @param tooltip Tooltip for the clickable element
*/
export const setLink = function(ids, linkStr, tooltip) {
const config = configApi.getConfig();
ids.split(',').forEach(function(_id) {
let id = _id;
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
@ -199,11 +198,13 @@ export const setLink = function(ids, linkStr, tooltip) {
export const setClickEvent = function(ids, functionName, tooltip) {
ids.split(',').forEach(function(id) {
setClickFunc(id, functionName, tooltip);
classes[id].haveCallback = true;
});
setCssClass(ids, 'clickable');
};
const setClickFunc = function(domId, functionName, tooltip) {
const config = configApi.getConfig();
let id = domId;
let elemId = lookUpDomId(id);

View File

@ -9,7 +9,7 @@ import { getConfig } from '../../config';
import { render } from '../../dagre-wrapper/index.js';
// import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
import { curveLinear } from 'd3';
import { interpolateToCurve, getStylesFromArray } from '../../utils';
import { interpolateToCurve, getStylesFromArray, configureSvgSize } from '../../utils';
import common from '../common/common';
parser.yy = classDb;
@ -42,7 +42,10 @@ export const addClasses = function(classes, g) {
* Variable for storing the classes for the vertex
* @type {string}
*/
let classStr = 'default';
let cssClassStr = '';
if (vertex.cssClasses.length > 0) {
cssClassStr = cssClassStr + ' ' + vertex.cssClasses.join(' ');
}
// if (vertex.classes.length > 0) {
// classStr = vertex.classes.join(' ');
// }
@ -98,9 +101,12 @@ export const addClasses = function(classes, g) {
classData: vertex,
rx: radious,
ry: radious,
class: classStr,
class: cssClassStr,
style: styles.style,
id: vertex.id,
domId: vertex.domId,
haveCallback: vertex.haveCallback,
link: vertex.link,
width: vertex.type === 'group' ? 500 : undefined,
type: vertex.type,
padding: getConfig().flowchart.padding
@ -112,7 +118,7 @@ export const addClasses = function(classes, g) {
labelText: vertexText,
rx: radious,
ry: radious,
class: classStr,
class: cssClassStr,
style: styles.style,
id: vertex.id,
width: vertex.type === 'group' ? 500 : undefined,
@ -155,6 +161,12 @@ export const addRelations = function(relations, g) {
}
logger.info(edgeData, edge);
//Set edge extra labels
//edgeData.startLabelLeft = edge.relationTitle1;
edgeData.startLabelRight = edge.relationTitle1 === 'none' ? '' : edge.relationTitle1;
edgeData.endLabelLeft = edge.relationTitle2 === 'none' ? '' : edge.relationTitle2;
//edgeData.endLabelRight = edge.relationTitle2;
//Set relation arrow types
edgeData.arrowTypeStart = getArrowMarker(edge.relation.type1);
edgeData.arrowTypeEnd = getArrowMarker(edge.relation.type2);
@ -325,13 +337,7 @@ export const drawOld = function(text, id) {
const width = svgBounds.width + padding * 2;
const height = svgBounds.height + padding * 2;
if (conf.useMaxWidth) {
diagram.attr('width', '100%');
diagram.attr('style', `max-width: ${width}px;`);
} else {
diagram.attr('height', height);
diagram.attr('width', width);
}
configureSvgSize(diagram, height, width, conf.useMaxWidth);
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;
@ -427,13 +433,7 @@ export const draw = function(text, id) {
`translate(${padding - g._label.marginx}, ${padding - g._label.marginy})`
);
if (conf.useMaxWidth) {
svg.attr('width', '100%');
svg.attr('style', `max-width: ${width}px;`);
} else {
svg.attr('height', height);
svg.attr('width', width);
}
configureSvgSize(svg, height, width, conf.useMaxWidth);
svg.attr('viewBox', `0 0 ${width} ${height}`);
svg

View File

@ -5,6 +5,7 @@ import { logger } from '../../logger';
import classDb, { lookUpDomId } from './classDb';
import { parser } from './parser/classDiagram';
import svgDraw from './svgDraw';
import { configureSvgSize } from '../../utils';
parser.yy = classDb;
@ -232,13 +233,7 @@ export const draw = function(text, id) {
const width = svgBounds.width + padding * 2;
const height = svgBounds.height + padding * 2;
if (conf.useMaxWidth) {
diagram.attr('width', '100%');
diagram.attr('style', `max-width: ${width}px;`);
} else {
diagram.attr('height', height);
diagram.attr('width', width);
}
configureSvgSize(diagram, height, width, conf.useMaxWidth);
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;

View File

@ -9,6 +9,11 @@ const getStyles = options =>
.title {
font-weight: bolder;
}
}
.classTitle {
font-weight: bolder;
}
.node rect,
.node circle,
@ -109,6 +114,11 @@ g.classGroup line {
stroke: ${options.lineColor} !important;
stroke-width: 1;
}
.edgeTerminals {
font-size: 11px;
}
`;
export default getStyles;

View File

@ -6,6 +6,7 @@ import dagre from 'dagre';
import { getConfig } from '../../config';
import { logger } from '../../logger';
import erMarkers from './erMarkers';
import { configureSvgSize } from '../../utils';
const conf = {};
@ -344,13 +345,7 @@ export const draw = function(text, id) {
const width = svgBounds.width + padding * 2;
const height = svgBounds.height + padding * 2;
if (conf.useMaxWidth) {
svg.attr('width', '100%');
svg.attr('style', `max-width: ${width}px;`);
} else {
svg.attr('height', height);
svg.attr('width', width);
}
configureSvgSize(svg, height, width, conf.useMaxWidth);
svg.attr('viewBox', `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`);
}; // draw

View File

@ -3,10 +3,10 @@ import utils from '../../utils';
import * as configApi from '../../config';
import common from '../common/common';
import mermaidAPI from '../../mermaidAPI';
import { logger } from '../../logger';
// const MERMAID_DOM_ID_PREFIX = 'mermaid-dom-id-';
const MERMAID_DOM_ID_PREFIX = '';
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
let vertexCounter = 0;
let config = configApi.getConfig();
let vertices = {};
let edges = [];
@ -17,6 +17,9 @@ let tooltips = {};
let subCount = 0;
let firstGraphFlag = true;
let direction;
let version; // As in graph
// Functions to be run after graph rendering
let funs = [];
@ -24,6 +27,21 @@ export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
/**
* Function to lookup domId from id in the graph definition.
* @param id
* @public
*/
export const lookUpDomId = function(id) {
const veritceKeys = Object.keys(vertices);
for (let i = 0; i < veritceKeys.length; i++) {
if (vertices[veritceKeys[i]].id === id) {
return vertices[veritceKeys[i]].domId;
}
}
return id;
};
/**
* Function called by parser when a node definition has been found
* @param id
@ -42,11 +60,17 @@ export const addVertex = function(_id, text, type, style, classes) {
return;
}
if (id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
// if (id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
if (typeof vertices[id] === 'undefined') {
vertices[id] = { id: id, styles: [], classes: [] };
vertices[id] = {
id: id,
domId: MERMAID_DOM_ID_PREFIX + id + '-' + vertexCounter,
styles: [],
classes: []
};
}
vertexCounter++;
if (typeof text !== 'undefined') {
config = configApi.getConfig();
txt = common.sanitizeText(text.trim(), config);
@ -91,8 +115,8 @@ export const addVertex = function(_id, text, type, style, classes) {
export const addSingleLink = function(_start, _end, type, linktext) {
let start = _start;
let end = _end;
if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
if (end[0].match(/\d/)) end = MERMAID_DOM_ID_PREFIX + end;
// if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
// if (end[0].match(/\d/)) end = MERMAID_DOM_ID_PREFIX + end;
// logger.info('Got edge...', start, end);
const edge = { start: start, end: end, type: undefined, text: '' };
@ -202,8 +226,9 @@ export const setDirection = function(dir) {
*/
export const setClass = function(ids, className) {
ids.split(',').forEach(function(_id) {
// let id = version === 'gen-2' ? lookUpDomId(_id) : _id;
let id = _id;
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
// if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
if (typeof vertices[id] !== 'undefined') {
vertices[id].classes.push(className);
}
@ -217,14 +242,14 @@ export const setClass = function(ids, className) {
const setTooltip = function(ids, tooltip) {
ids.split(',').forEach(function(id) {
if (typeof tooltip !== 'undefined') {
tooltips[id] = common.sanitizeText(tooltip, config);
tooltips[version === 'gen-1' ? lookUpDomId(id) : id] = common.sanitizeText(tooltip, config);
}
});
};
const setClickFun = function(_id, functionName) {
let id = _id;
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
const setClickFun = function(id, functionName) {
let domId = lookUpDomId(id);
// if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
if (configApi.getConfig().securityLevel !== 'loose') {
return;
}
@ -232,8 +257,9 @@ const setClickFun = function(_id, functionName) {
return;
}
if (typeof vertices[id] !== 'undefined') {
vertices[id].haveCallback = true;
funs.push(function() {
const elem = document.querySelector(`[id="${id}"]`);
const elem = document.querySelector(`[id="${domId}"]`);
if (elem !== null) {
elem.addEventListener(
'click',
@ -254,9 +280,7 @@ const setClickFun = function(_id, functionName) {
* @param tooltip Tooltip for the clickable element
*/
export const setLink = function(ids, linkStr, tooltip, target) {
ids.split(',').forEach(function(_id) {
let id = _id;
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
ids.split(',').forEach(function(id) {
if (typeof vertices[id] !== 'undefined') {
vertices[id].link = utils.formatUrl(linkStr, config);
vertices[id].linkTarget = target;
@ -362,7 +386,7 @@ funs.push(setupToolTips);
/**
* Clears the internal graph db so that a new graph can be parsed.
*/
export const clear = function() {
export const clear = function(ver) {
vertices = {};
classes = {};
edges = [];
@ -373,6 +397,10 @@ export const clear = function() {
subCount = 0;
tooltips = [];
firstGraphFlag = true;
version = ver || 'gen-1';
};
export const setGen = ver => {
version = ver || 'gen-1';
};
/**
*
@ -411,16 +439,39 @@ export const addSubGraph = function(_id, list, _title) {
let nodeList = [];
nodeList = uniq(nodeList.concat.apply(nodeList, list));
for (let i = 0; i < nodeList.length; i++) {
if (nodeList[i][0].match(/\d/)) nodeList[i] = MERMAID_DOM_ID_PREFIX + nodeList[i];
if (version === 'gen-1') {
logger.warn('LOOKING UP');
for (let i = 0; i < nodeList.length; i++) {
nodeList[i] = lookUpDomId(nodeList[i]);
}
}
id = id || 'subGraph' + subCount;
if (id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
// if (id[0].match(/\d/)) id = lookUpDomId(id);
title = title || '';
title = common.sanitizeText(title, config);
subCount = subCount + 1;
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] };
console.log('Adding', subGraph.id, subGraph.nodes);
/**
* Deletes an id from all subgraphs
*/
// const del = _id => {
// subGraphs.forEach(sg => {
// const pos = sg.nodes.indexOf(_id);
// if (pos >= 0) {
// sg.nodes.splice(pos, 1);
// }
// });
// };
// // Removes the members of this subgraph from any other subgraphs, a node only belong to one subgraph
// subGraph.nodes.forEach(_id => del(_id));
// Remove the members in the new subgraph if they already belong to another subgraph
subGraph.nodes = makeUniq(subGraph, subGraphs).nodes;
subGraphs.push(subGraph);
subGraphLookup[id] = subGraph;
return id;
@ -618,10 +669,35 @@ const destructLink = (_str, _startStr) => {
return info;
};
// Todo optimizer this by caching existing nodes
const exists = (allSgs, _id) => {
let res = false;
allSgs.forEach(sg => {
const pos = sg.nodes.indexOf(_id);
if (pos >= 0) {
res = true;
}
});
return res;
};
/**
* Deletes an id from all subgraphs
*/
const makeUniq = (sg, allSubgraphs) => {
const res = [];
sg.nodes.forEach((_id, pos) => {
if (!exists(allSubgraphs, _id)) {
res.push(sg.nodes[pos]);
}
});
return { nodes: res };
};
export default {
parseDirective,
defaultConfig: () => configApi.defaultConfig.flowchart,
addVertex,
lookUpDomId,
addLink,
updateLinkInterpolate,
updateLink,
@ -637,6 +713,7 @@ export default {
getEdges,
getClasses,
clear,
setGen,
defaultStyle,
addSubGraph,
getDepthFirstPos,
@ -645,5 +722,7 @@ export default {
destructLink,
lex: {
firstGraph
}
},
exists,
makeUniq
};

View File

@ -0,0 +1,44 @@
import flowDb from './flowDb';
describe('flow db subgraphs', () => {
let subgraphs;
beforeEach( ()=>{
subgraphs = [
{nodes:['a', 'b', 'c', 'e']},
{nodes:['f', 'g', 'h']},
{nodes:['i', 'j']},
{nodes:['k']},
];
});
describe('exist', () => {
it('should return true when the is exists in a subgraph', () => {
expect(flowDb.exists(subgraphs, 'a')).toBe(true);
expect(flowDb.exists(subgraphs, 'h')).toBe(true);
expect(flowDb.exists(subgraphs, 'j')).toBe(true);
expect(flowDb.exists(subgraphs, 'k')).toBe(true);
});
it('should return false when the is exists in a subgraph', () => {
expect(flowDb.exists(subgraphs, 'a2')).toBe(false);
expect(flowDb.exists(subgraphs, 'l')).toBe(false);
});
});
describe('makeUniq', () => {
it('should remove ids from sungraph that already exists in another subgraph even if it gets empty', () => {
const subgraph = flowDb.makeUniq({nodes:['i', 'j']}, subgraphs);
expect(subgraph.nodes).toEqual([]);
});
it('should remove ids from sungraph that already exists in another subgraph', () => {
const subgraph = flowDb.makeUniq({nodes:['i', 'j', 'o']}, subgraphs);
expect(subgraph.nodes).toEqual(['o']);
});
it('should not remove ids from subgraph if they are unique', () => {
const subgraph = flowDb.makeUniq({nodes:['q', 'r', 's']}, subgraphs);
expect(subgraph.nodes).toEqual(['q', 'r', 's']);
});
});
});

View File

@ -9,7 +9,7 @@ import { render } from '../../dagre-wrapper/index.js';
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
import { logger } from '../../logger';
import common from '../common/common';
import { interpolateToCurve, getStylesFromArray } from '../../utils';
import { interpolateToCurve, getStylesFromArray, configureSvgSize } from '../../utils';
const conf = {};
export const setConf = function(cnf) {
@ -141,6 +141,11 @@ export const addVertices = function(vert, g, svgId) {
class: classStr,
style: styles.style,
id: vertex.id,
link: vertex.link,
linkTarget: vertex.linkTarget,
tooltip: flowDb.getTooltip(vertex.id) || '',
domId: flowDb.lookUpDomId(vertex.id),
haveCallback: vertex.haveCallback,
width: vertex.type === 'group' ? 500 : undefined,
type: vertex.type,
padding: getConfig().flowchart.padding
@ -155,6 +160,7 @@ export const addVertices = function(vert, g, svgId) {
class: classStr,
style: styles.style,
id: vertex.id,
domId: flowDb.lookUpDomId(vertex.id),
width: vertex.type === 'group' ? 500 : undefined,
type: vertex.type,
padding: getConfig().flowchart.padding
@ -329,6 +335,7 @@ export const getClasses = function(text) {
export const draw = function(text, id) {
logger.info('Drawing flowchart');
flowDb.clear();
flowDb.setGen('gen-2');
const parser = flow.parser;
parser.yy = flowDb;
@ -382,11 +389,13 @@ export const draw = function(text, id) {
logger.info(edges);
let i = 0;
for (i = subGraphs.length - 1; i >= 0; i--) {
// for (let i = 0; i < subGraphs.length; i++) {
subG = subGraphs[i];
selectAll('cluster').append('text');
for (let j = 0; j < subG.nodes.length; j++) {
logger.info('Setting up subgraphs', subG.nodes[j], subG.id);
g.setParent(subG.nodes[j], subG.id);
}
}
@ -403,11 +412,6 @@ export const draw = function(text, id) {
// Run the renderer. This is what draws the final graph.
const element = select('#' + id + ' g');
render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
// dagre.layout(g);
element.selectAll('g.node').attr('title', function() {
return flowDb.getTooltip(this.id);
});
const padding = conf.diagramPadding;
const svgBounds = svg.node().getBBox();
@ -418,13 +422,7 @@ export const draw = function(text, id) {
`translate(${padding - g._label.marginx}, ${padding - g._label.marginy})`
);
if (conf.useMaxWidth) {
svg.attr('width', '100%');
svg.attr('style', `max-width: ${width}px;`);
} else {
svg.attr('height', height);
svg.attr('width', width);
}
configureSvgSize(svg, height, width, conf.useMaxWidth);
svg.attr('viewBox', `0 0 ${width} ${height}`);
svg
@ -434,28 +432,6 @@ export const draw = function(text, id) {
// Index nodes
flowDb.indexNodes('subGraph' + i);
// // reposition labels
// for (i = 0; i < subGraphs.length; i++) {
// subG = subGraphs[i];
// if (subG.title !== 'undefined') {
// const clusterRects = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"] rect');
// const clusterEl = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"]');
// const xPos = clusterRects[0].x.baseVal.value;
// const yPos = clusterRects[0].y.baseVal.value;
// const width = clusterRects[0].width.baseVal.value;
// const cluster = d3.select(clusterEl[0]);
// const te = cluster.select('.label');
// te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`);
// te.attr('id', id + 'Text');
// for (let j = 0; j < subG.classes.length; j++) {
// clusterEl[0].classList.add(subG.classes[j]);
// }
// }
// }
// Add label rects for non html labels
if (!conf.htmlLabels) {
const labels = document.querySelectorAll('[id="' + id + '"] .edgeLabel .label');

View File

@ -9,7 +9,7 @@ import dagreD3 from 'dagre-d3';
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
import { logger } from '../../logger';
import common from '../common/common';
import { interpolateToCurve, getStylesFromArray } from '../../utils';
import { interpolateToCurve, getStylesFromArray, configureSvgSize } from '../../utils';
import flowChartShapes from './flowChartShapes';
const conf = {};
@ -133,7 +133,8 @@ export const addVertices = function(vert, g, svgId) {
_shape = 'rect';
}
// Add the node
g.setNode(vertex.id, {
logger.warn('Adding node', vertex.id, vertex.domId);
g.setNode(flowDb.lookUpDomId(vertex.id), {
labelType: 'svg',
labelStyle: styles.labelStyle,
shape: _shape,
@ -142,7 +143,7 @@ export const addVertices = function(vert, g, svgId) {
ry: radious,
class: classStr,
style: styles.style,
id: vertex.id
id: flowDb.lookUpDomId(vertex.id)
});
});
};
@ -247,7 +248,7 @@ export const addEdges = function(edges, g) {
edgeData.minlen = edge.length || 1;
// Add the edge to the graph
g.setEdge(edge.start, edge.end, edgeData, cnt);
g.setEdge(flowDb.lookUpDomId(edge.start), flowDb.lookUpDomId(edge.end), edgeData, cnt);
});
};
@ -278,6 +279,7 @@ export const getClasses = function(text) {
export const draw = function(text, id) {
logger.info('Drawing flowchart');
flowDb.clear();
flowDb.setGen('gen-1');
const parser = flow.parser;
parser.yy = flowDb;
@ -323,6 +325,7 @@ export const draw = function(text, id) {
// Fetch the verices/nodes and edges/links from the parsed graph definition
const vert = flowDb.getVertices();
logger.warn('Get vertices', vert);
const edges = flowDb.getEdges();
@ -333,7 +336,13 @@ export const draw = function(text, id) {
selectAll('cluster').append('text');
for (let j = 0; j < subG.nodes.length; j++) {
g.setParent(subG.nodes[j], subG.id);
logger.warn(
'Setting subgraph',
subG.nodes[j],
flowDb.lookUpDomId(subG.nodes[j]),
flowDb.lookUpDomId(subG.id)
);
g.setParent(flowDb.lookUpDomId(subG.nodes[j]), flowDb.lookUpDomId(subG.id));
}
}
addVertices(vert, g, id);
@ -388,6 +397,8 @@ export const draw = function(text, id) {
const svg = select(`[id="${id}"]`);
svg.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
logger.warn(g);
// Run the renderer. This is what draws the final graph.
const element = select('#' + id + ' g');
render(element, g);
@ -401,13 +412,7 @@ export const draw = function(text, id) {
const width = svgBounds.width + padding * 2;
const height = svgBounds.height + padding * 2;
if (conf.useMaxWidth) {
svg.attr('width', '100%');
svg.attr('style', `max-width: ${width}px;`);
} else {
svg.attr('height', height);
svg.attr('width', width);
}
configureSvgSize(svg, height, width, conf.useMaxWidth);
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;
@ -420,10 +425,13 @@ export const draw = function(text, id) {
// reposition labels
for (i = 0; i < subGraphs.length; i++) {
subG = subGraphs[i];
if (subG.title !== 'undefined') {
const clusterRects = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"] rect');
const clusterEl = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"]');
const clusterRects = document.querySelectorAll(
'#' + id + ' [id="' + flowDb.lookUpDomId(subG.id) + '"] rect'
);
const clusterEl = document.querySelectorAll(
'#' + id + ' [id="' + flowDb.lookUpDomId(subG.id) + '"]'
);
const xPos = clusterRects[0].x.baseVal.value;
const yPos = clusterRects[0].y.baseVal.value;
@ -465,7 +473,7 @@ export const draw = function(text, id) {
const vertex = vert[key];
if (vertex.link) {
const node = select('#' + id + ' [id="' + key + '"]');
const node = select('#' + id + ' [id="' + flowDb.lookUpDomId(key) + '"]');
if (node) {
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));

View File

@ -21,7 +21,6 @@ describe('[Text] when parsing', () => {
expect(edges[0].type).toBe('arrow_point');
expect(edges.length).toBe(47917);
console.log(vert);
expect(Object.keys(vert).length).toBe(2);
});

View File

@ -10,6 +10,7 @@ describe('[Style] when parsing', () => {
beforeEach(function() {
flow.parser.yy = flowDb;
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});
// log.debug(flow.parser.parse('graph TD;style Q background:#fff;'));

View File

@ -10,8 +10,9 @@ describe('when parsing flowcharts', function() {
beforeEach(function() {
flow.parser.yy = flowDb;
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});
it('should handle chaining of vertices', function() {
const res = flow.parser.parse(`
graph TD

View File

@ -121,7 +121,6 @@ describe('when parsing ', function() {
const res = flow.parser.parse(statement);
const vertices = flow.parser.yy.getVertices();
console.log(vertices);
const classes = flow.parser.yy.getClasses();
expect(vertices['node1TB'].id).toBe('node1TB');
});
@ -132,8 +131,16 @@ describe('when parsing ', function() {
statement = statement + 'graph TD;A--x|text including URL space|B;';
const res = flow.parser.parse(statement);
const vertices = flow.parser.yy.getVertices();
console.log(vertices);
const classes = flow.parser.yy.getClasses();
expect(vertices['A'].id).toBe('A');
});
it('should be possible to use numbers as labels', function() {
let statement = '';
statement = statement + 'graph TB;subgraph "number as labels";1;end;';
const res = flow.parser.parse(statement);
const vertices = flow.parser.yy.getVertices();
const classes = flow.parser.yy.getClasses();
expect(vertices['1'].id).toBe('1');
});
});

View File

@ -1,5 +1,6 @@
import flowDb from '../flowDb';
import flow from './flow';
import filter from 'lodash/filter';
import { setConfig } from '../../../config';
setConfig({
@ -10,6 +11,7 @@ describe('when parsing subgraphs', function() {
beforeEach(function() {
flow.parser.yy = flowDb;
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});
it('should handle subgraph with tab indentation', function() {
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2\nend');
@ -238,4 +240,74 @@ describe('when parsing subgraphs', function() {
expect(edges[0].type).toBe('arrow_point');
});
it('should handle nested subgraphs 1', function() {
const res = flow.parser.parse(`flowchart TB
subgraph A
b-->B
a
end
a-->c
subgraph B
c
end`);
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = filter(subgraphs,o => o.id === 'A')[0];
const subgraphB = filter(subgraphs,o => o.id === 'B')[0];
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphA.nodes).toContain('B');
expect(subgraphA.nodes).toContain('b');
expect(subgraphA.nodes).toContain('a');
expect(subgraphA.nodes).not.toContain('c');
});
it('should handle nested subgraphs 2', function() {
const res = flow.parser.parse(`flowchart TB
b-->B
a-->c
subgraph B
c
end
subgraph A
a
b
B
end`);
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = filter(subgraphs,o => o.id === 'A')[0];
const subgraphB = filter(subgraphs,o => o.id === 'B')[0];
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphA.nodes).toContain('B');
expect(subgraphA.nodes).toContain('b');
expect(subgraphA.nodes).toContain('a');
expect(subgraphA.nodes).not.toContain('c');
});
it('should handle nested subgraphs 3', function() {
const res = flow.parser.parse(`flowchart TB
subgraph B
c
end
a-->c
subgraph A
b-->B
a
end`);
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = filter(subgraphs,o => o.id === 'A')[0];
const subgraphB = filter(subgraphs,o => o.id === 'B')[0];
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphA.nodes).toContain('B');
expect(subgraphA.nodes).toContain('b');
expect(subgraphA.nodes).toContain('a');
expect(subgraphA.nodes).not.toContain('c');
});
});

View File

@ -11,6 +11,7 @@ import {
import { parser } from './parser/gantt';
import common from '../common/common';
import ganttDb from './ganttDb';
import { configureSvgSize } from '../../utils';
parser.yy = ganttDb;
@ -53,7 +54,6 @@ export const draw = function(text, id) {
// Set height based on number of tasks
const h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding;
elem.setAttribute('height', '100%');
// Set viewBox
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
const svg = select(`[id="${id}"]`);
@ -97,9 +97,8 @@ export const draw = function(text, id) {
taskArray.sort(taskCompare);
makeGant(taskArray, w, h);
if (typeof conf.useWidth !== 'undefined') {
elem.setAttribute('width', w);
}
configureSvgSize(svg, h, w, conf.useMaxWidth);
svg
.append('text')
@ -302,7 +301,6 @@ export const draw = function(text, id) {
}
let secNum = 0;
console.log(conf);
for (let i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
secNum = i % conf.numberSectionStyles;

View File

@ -20,7 +20,7 @@
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */{ console.log('Crap after close'); }
[^\}]\%\%[^\n]* /* skip comments */{ /*console.log('');*/ }
[\n\r]+ return 'NEWLINE';
\%\%[^\n]* /* do nothing */
[\s]+ /* ignore */

View File

@ -17,7 +17,6 @@ describe('when parsing pie', function() {
"ash" : 100
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(100);
});
@ -27,7 +26,6 @@ describe('when parsing pie', function() {
"bat" : 40
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(60);
});
@ -38,7 +36,6 @@ describe('when parsing pie', function() {
"bat" : 40
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(60);
});
@ -50,7 +47,6 @@ pie
"bat" : 40
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(60);
});
@ -62,7 +58,6 @@ pie
`);
const sections = pieDb.getSections();
const title = pieDb.getTitle();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a 60/40 pie');
@ -74,7 +69,6 @@ pie
"bat" : 40
`);
const sections = pieDb.getSections();
console.log('sections: ', sections);
const section1 = sections['ash'];
expect(section1).toBe(60.67);
});

View File

@ -5,6 +5,7 @@ import { select, scaleOrdinal, schemeSet2, pie as d3pie, entries, arc } from 'd3
import pieData from './pieDb';
import pieParser from './parser/pie';
import { logger } from '../../logger';
import { configureSvgSize } from '../../utils';
const conf = {};
export const setConf = function(cnf) {
@ -20,7 +21,8 @@ export const setConf = function(cnf) {
* @param text
* @param id
*/
let w;
let width;
const height = 450;
export const draw = (txt, id) => {
try {
const parser = pieParser.parser;
@ -31,34 +33,30 @@ export const draw = (txt, id) => {
parser.parse(txt);
logger.debug('Parsed info diagram');
const elem = document.getElementById(id);
w = elem.parentElement.offsetWidth;
width = elem.parentElement.offsetWidth;
if (typeof w === 'undefined') {
w = 1200;
if (typeof width === 'undefined') {
width = 1200;
}
if (typeof conf.useWidth !== 'undefined') {
w = conf.useWidth;
width = conf.useWidth;
}
const h = 450;
elem.setAttribute('height', '100%');
const diagram = select('#' + id);
configureSvgSize(diagram, height, width, conf.useMaxWidth);
// Set viewBox
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
elem.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
// Fetch the default direction, use TD if none was found
var width = w; // 450
var height = 450;
var margin = 40;
var legendRectSize = 18;
var legendSpacing = 4;
var radius = Math.min(width, height) / 2 - margin;
var svg = select('#' + id)
.append('svg')
.attr('width', width)
.attr('height', height)
var svg = diagram
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
@ -67,9 +65,8 @@ export const draw = (txt, id) => {
Object.keys(data).forEach(function(key) {
sum += data[key];
});
logger.info(data);
// set the color scale
// Set the color scale
var color = scaleOrdinal()
.domain(data)
.range(schemeSet2);
@ -80,12 +77,12 @@ export const draw = (txt, id) => {
});
var dataReady = pie(entries(data));
// shape helper to build arcs:
// Shape helper to build arcs:
var arcGenerator = arc()
.innerRadius(0)
.outerRadius(radius);
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
// Build the pie chart: each part of the pie is a path that we build using the arc function.
svg
.selectAll('mySlices')
.data(dataReady)
@ -99,7 +96,8 @@ export const draw = (txt, id) => {
.style('stroke-width', '2px')
.style('opacity', 0.7);
// Now add the Percentage. Use the centroid method to get the best coordinates
// Now add the percentage.
// Use the centroid method to get the best coordinates.
svg
.selectAll('mySlices')
.data(dataReady)
@ -119,10 +117,10 @@ export const draw = (txt, id) => {
.append('text')
.text(parser.yy.getTitle())
.attr('x', 0)
.attr('y', -(h - 50) / 2)
.attr('y', -(height - 50) / 2)
.attr('class', 'pieTitleText');
//Add the slegend/annotations for each section
// Add the legends/annotations for each section
var legend = svg
.selectAll('.legend')
.data(color.domain())

View File

@ -43,7 +43,6 @@ const activationCount = part => {
let i;
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++;

View File

@ -942,7 +942,6 @@ describe('when rendering a sequenceDiagram APA', function() {
wrap: false,
mirrorActors: false
};
console.warn('Set site config');
configApi.setSiteConfig({ logLevel: 5, sequence: conf });
});
let conf;
@ -1249,7 +1248,6 @@ Bob->>Alice: Fine!`;
const { bounds, models } = renderer.bounds.getBounds();
const msgs = parser.yy.getMessages();
const mermaid = mermaidAPI.getConfig();
console.log(mermaid)
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
expect(bounds.starty).toBe(0);
expect(mermaid.theme).toBe('dark');

View File

@ -5,7 +5,7 @@ import { parser } from './parser/sequenceDiagram';
import common from '../common/common';
import sequenceDb from './sequenceDb';
import * as configApi from '../../config';
import utils, { assignWithDepth } from '../../utils';
import utils, { assignWithDepth, configureSvgSize } from '../../utils';
parser.yy = sequenceDb;
@ -192,7 +192,6 @@ export const bounds = {
return this.verticalPos;
},
getBounds: function() {
console.log('here', this.data);
return { bounds: this.data, models: this.models };
}
};
@ -503,7 +502,6 @@ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoop
*/
export const draw = function(text, id) {
conf = configApi.getConfig().sequence;
console.log('there ', conf);
parser.yy.clear();
parser.yy.setWrap(conf.wrap);
parser.parse(text + '\n');
@ -706,15 +704,8 @@ export const draw = function(text, id) {
.attr('y', -25);
}
if (conf.useMaxWidth) {
diagram.attr('height', '100%');
diagram.attr('width', '100%');
diagram.attr('style', 'max-width:' + width + 'px;');
// diagram.attr('style', 'max-width:100%;');
} else {
diagram.attr('height', height);
diagram.attr('width', width);
}
configureSvgSize(diagram, height, width, conf.useMaxWidth);
const extraVertForTitle = title ? 40 : 0;
diagram.attr(
'viewBox',

View File

@ -169,7 +169,6 @@ export const addTitleAndBox = (g, stateDef, altBkg) => {
// .attr('class', 'descr-divider');
const graphBox = g.node().getBBox();
// console.warn(width / 2, titleWidth / 2, getConfig().state.padding, orgBox);
// descrLine.attr('x2', graphBox.width + getConfig().state.padding);
if (stateDef.doc) {
@ -328,7 +327,6 @@ const _drawLongText = (_text, x, y, g) => {
const textBounds = span.node().getBBox();
tHeight += textBounds.height;
}
// console.warn('textBounds', textBounds);
textHeight += tHeight;
span.attr('x', x + getConfig().state.noteMargin);
span.attr('y', y + textHeight + 1.25 * getConfig().state.noteMargin);
@ -456,8 +454,6 @@ export const drawEdge = function(elem, path, relation) {
const rows = common.getRows(relation.title);
// console.warn(rows);
let titleHeight = 0;
const titleRows = [];
let maxWidth = 0;

View File

@ -6,6 +6,7 @@ import { getConfig } from '../../config';
import { render } from '../../dagre-wrapper/index.js';
import { logger } from '../../logger';
import { configureSvgSize } from '../../utils';
const conf = {};
export const setConf = function(cnf) {
@ -99,6 +100,7 @@ const setupNode = (g, parent, node, altFlag) => {
classes: nodeDb[node.id].classes, //classStr,
style: '', //styles.style,
id: node.id,
domId: 'state-' + node.id + '-' + cnt,
type: nodeDb[node.id].type,
padding: 15 //getConfig().flowchart.padding
};
@ -112,6 +114,7 @@ const setupNode = (g, parent, node, altFlag) => {
classes: 'statediagram-note', //classStr,
style: '', //styles.style,
id: node.id + '----note',
domId: 'state-' + node.id + '----note-' + cnt,
type: nodeDb[node.id].type,
padding: 15 //getConfig().flowchart.padding
};
@ -122,9 +125,12 @@ const setupNode = (g, parent, node, altFlag) => {
classes: nodeDb[node.id].classes, //classStr,
style: '', //styles.style,
id: node.id + '----parent',
domId: 'state-' + node.id + '----parent-' + cnt,
type: 'group',
padding: 0 //getConfig().flowchart.padding
};
cnt++;
g.setNode(node.id + '----parent', groupData);
g.setNode(noteData.id, noteData);
@ -169,6 +175,7 @@ const setupNode = (g, parent, node, altFlag) => {
};
let cnt = 0;
const setupDoc = (g, parent, doc, altFlag) => {
cnt = 0;
logger.trace('items', doc);
doc.forEach(item => {
if (item.stmt === 'state' || item.stmt === 'default') {
@ -211,11 +218,7 @@ export const draw = function(text, id) {
parser.yy = stateDb;
// Parse the graph definition
// try {
parser.parse(text);
// } catch (err) {
// logger.error('Parsing failed', err);
// }
// Fetch the default direction, use TD if none was found
let dir = stateDb.getDirection();
@ -256,54 +259,18 @@ export const draw = function(text, id) {
render(element, g, ['barb'], 'statediagram', id);
const padding = 8;
// const svgBounds = svg.node().getBBox();
// const width = svgBounds.width + padding * 2;
// const height = svgBounds.height + padding * 2;
// logger.debug(
// `new ViewBox 0 0 ${width} ${height}`,
// `translate(${padding + g._label.marginx}, ${padding + g._label.marginy})`
// );
// if (conf.useMaxWidth) {
// svg.attr('width', '100%');
// svg.attr('style', `max-width: ${width}px;`);
// } else {
// svg.attr('height', height);
// svg.attr('width', width);
// }
// svg.attr('viewBox', `0 0 ${width} ${height}`);
// svg
// .select('g')
// .attr('transform', `translate(${padding - g._label.marginx}, ${padding - svgBounds.y})`);
const bounds = svg.node().getBBox();
const width = bounds.width + padding * 2;
const height = bounds.height + padding * 2;
// diagram.attr('height', '100%');
// diagram.attr('style', `width: ${bounds.width * 3 + conf.padding * 2};`);
// diagram.attr('height', height);
// Zoom in a bit
svg.attr('width', width * 1.75);
svg.attr('class', 'statediagram');
// diagram.attr('height', bounds.height * 3 + conf.padding * 2);
// svg.attr(
// 'viewBox',
// `${bounds.x - conf.padding} ${bounds.y - conf.padding} ` + width + ' ' + height
// );
const svgBounds = svg.node().getBBox();
if (conf.useMaxWidth) {
svg.attr('width', '100%');
svg.attr('style', `max-width: ${width}px;`);
} else {
svg.attr('height', height);
svg.attr('width', width);
}
configureSvgSize(svg, height, width * 1.75, conf.useMaxWidth);
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
const vBox = `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;

View File

@ -8,6 +8,7 @@ import { parser } from './parser/stateDiagram';
// import idCache from './id-cache';
import { drawState, addTitleAndBox, drawEdge } from './shapes';
import { getConfig } from '../../config';
import { configureSvgSize } from '../../utils';
parser.yy = stateDb;
@ -75,14 +76,10 @@ export const draw = function(text, id) {
const width = bounds.width + padding * 2;
const height = bounds.height + padding * 2;
if (conf.useMaxWidth) {
diagram.attr('width', '100%');
diagram.attr('style', `max-width: ${width * 1.75}px;`);
} else {
// Zoom in a bit
diagram.attr('width', width * 1.75);
}
// diagram.attr('height', bounds.height * 3 + conf.padding * 2);
// zoom in a bit
const svgWidth = width * 1.75;
configureSvgSize(diagram, height, svgWidth, conf.useMaxWidth);
diagram.attr(
'viewBox',
`${bounds.x - conf.padding} ${bounds.y - conf.padding} ` + width + ' ' + height

View File

@ -2,6 +2,7 @@ import { select } from 'd3';
import { parser } from './parser/journey';
import journeyDb from './journeyDb';
import svgDraw from './svgDraw';
import { configureSvgSize } from '../../utils';
parser.yy = journeyDb;
@ -118,14 +119,8 @@ export const draw = function(text, id) {
}
const height = box.stopy - box.starty + 2 * conf.diagramMarginY;
const width = LEFT_MARGIN + box.stopx + 2 * conf.diagramMarginX;
if (conf.useMaxWidth) {
diagram.attr('height', '100%');
diagram.attr('width', '100%');
diagram.attr('style', 'max-width:' + width + 'px;');
} else {
diagram.attr('height', height);
diagram.attr('width', width);
}
configureSvgSize(diagram, height, width, conf.useMaxWidth);
// Draw activity line
diagram

View File

@ -42,6 +42,7 @@ describe('when using mermaid and ', function() {
beforeEach(function() {
flowParser.parser.yy = flowDb;
flowDb.clear();
flowDb.setGen('gen-2');
});
it('it should handle edges with text', function() {
flowParser.parser.parse('graph TD;A-->|text ex|B;');
@ -50,8 +51,8 @@ describe('when using mermaid and ', function() {
const mockG = {
setEdge: function(start, end, options) {
expect(start).toBe('A');
expect(end).toBe('B');
expect(start).toContain('flowchart-A-');
expect(end).toContain('flowchart-B-');
expect(options.arrowhead).toBe('normal');
expect(options.label.match('text ex')).toBeTruthy();
}
@ -67,8 +68,8 @@ describe('when using mermaid and ', function() {
const mockG = {
setEdge: function(start, end, options) {
expect(start).toBe('A');
expect(end).toBe('B');
expect(start).toContain('flowchart-A-');
expect(end).toContain('flowchart-B-');
expect(options.arrowhead).toBe('normal');
}
};
@ -83,8 +84,8 @@ describe('when using mermaid and ', function() {
const mockG = {
setEdge: function(start, end, options) {
expect(start).toBe('A');
expect(end).toBe('B');
expect(start).toContain('flowchart-A-');
expect(end).toContain('flowchart-B-');
expect(options.arrowhead).toBe('none');
}
};
@ -99,8 +100,8 @@ describe('when using mermaid and ', function() {
const mockG = {
setEdge: function(start, end, options) {
expect(start).toBe('A');
expect(end).toBe('B');
expect(start).toContain('flowchart-A-');
expect(end).toContain('flowchart-B-');
expect(options.arrowhead).toBe('none');
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;');
}
@ -115,8 +116,8 @@ describe('when using mermaid and ', function() {
const mockG = {
setEdge: function(start, end, options) {
expect(start).toBe('A');
expect(end).toBe('B');
expect(start).toContain('flowchart-A-');
expect(end).toContain('flowchart-B-');
expect(options.arrowhead).toBe('none');
expect(options.curve).toBe('basis'); // mocked as string
}
@ -133,8 +134,8 @@ describe('when using mermaid and ', function() {
const mockG = {
setEdge: function(start, end, options) {
expect(start).toBe('A');
expect(end).toBe('B');
expect(start).toContain('flowchart-A-');
expect(end).toContain('flowchart-B-');
expect(options.arrowhead).toBe('none');
expect(options.label.match('the text')).toBeTruthy();
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;');
@ -151,8 +152,8 @@ describe('when using mermaid and ', function() {
const mockG = {
setEdge: function(start, end, options) {
expect(start).toBe('A');
expect(end).toBe('B');
expect(start).toContain('flowchart-A-');
expect(end).toContain('flowchart-B');
expect(options.arrowhead).toBe('none');
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:none;');
}
@ -169,8 +170,8 @@ describe('when using mermaid and ', function() {
const edges = flowParser.parser.yy.getEdges();
const mockG = {
setEdge: function(start, end, options) {
expect(start).toBe('A');
expect(end).toBe('B');
expect(start).toContain('flowchart-A-');
expect(end).toContain('flowchart-B-');
expect(options.arrowhead).toBe('none');
expect(options.style).toBe('stroke:val1;stroke-width:val2;fill:blue;');
}
@ -184,6 +185,7 @@ describe('when using mermaid and ', function() {
beforeEach(function() {
flowParser.parser.yy = flowDb;
flowDb.clear();
flowDb.setGen('gen-2');
});
it('it should throw for an invalid definiton', function() {
expect(() => mermaid.parse('this is not a mermaid diagram definition')).toThrow();

View File

@ -220,7 +220,7 @@ const render = function(id, _txt, cb, container) {
// console.warn('Render fetching config');
const cnf = configApi.getConfig();
console.warn('Render with config after adding new directives', cnf.sequence);
// console.warn('Render with config after adding new directives', cnf.sequence);
// console.warn(
// 'Render with config after adding new directives',
// cnf.fontFamily,
@ -385,7 +385,7 @@ const render = function(id, _txt, cb, container) {
break;
case 'pie':
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
pieRenderer.setConf(cnf.class);
pieRenderer.setConf(cnf.pie);
pieRenderer.draw(txt, id, pkg.version);
break;
case 'er':
@ -437,6 +437,7 @@ const render = function(id, _txt, cb, container) {
cb(svgCode, ganttDb.bindFunctions);
break;
case 'class':
case 'classDiagram':
cb(svgCode, classDb.bindFunctions);
break;
default:
@ -616,9 +617,9 @@ configApi.reset(configApi.getConfig());
export default mermaidAPI;
/**
* ## mermaidAPI configuration defaults
* <pre>
*
* &lt;script>
* ```html
* <script>
* var config = {
* theme:'default',
* logLevel:'fatal',
@ -673,6 +674,6 @@ export default mermaidAPI;
* }
* };
* mermaid.initialize(config);
* &lt;/script>
*</pre>
* </script>
* ```
*/

View File

@ -166,6 +166,5 @@ class Theme {
export const getThemeVariables = userOverrides => {
const theme = new Theme();
theme.calculate(userOverrides);
console.info('Theme(dark)', { userOverrides, theme });
return theme;
};

View File

@ -177,6 +177,5 @@ class Theme {
export const getThemeVariables = userOverrides => {
const theme = new Theme();
theme.calculate(userOverrides);
// console.info('Theme(default)', { userOverrides, theme });
return theme;
};

View File

@ -147,6 +147,5 @@ class Theme {
export const getThemeVariables = userOverrides => {
const theme = new Theme();
theme.calculate(userOverrides);
console.info('Theme(forest)', { userOverrides, theme });
return theme;
};

View File

@ -13,7 +13,6 @@ class Theme {
// this.secondaryColor = adjust(this.primaryColor, { h: 120 });
this.tertiaryColor = adjust(this.primaryColor, { h: -160 });
// console.log('primary color', this.primaryColor, 'tertiary - color', this.tertiaryColor);
this.primaryBorderColor = mkBorder(this.primaryColor, this.darkMode);
this.secondaryBorderColor = mkBorder(this.secondaryColor, this.darkMode);
this.tertiaryBorderColor = mkBorder(this.tertiaryColor, this.darkMode);
@ -185,6 +184,5 @@ class Theme {
export const getThemeVariables = userOverrides => {
const theme = new Theme();
theme.calculate(userOverrides);
console.info('Theme(neutral)', { userOverrides, theme });
return theme;
};

View File

@ -334,6 +334,7 @@ const calcLabelPosition = points => {
const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) => {
let prevPoint;
let totalDistance = 0; // eslint-disable-line
logger.info('our points', points);
if (points[0] !== initialPosition) {
points = points.reverse();
}
@ -380,6 +381,77 @@ const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition)
return cardinalityPosition;
};
/**
* position ['start_left', 'start_right', 'end_left', 'end_right']
*/
const calcTerminalLabelPosition = (terminalMarkerSize, position, _points) => {
// Todo looking to faster cloning method
let points = JSON.parse(JSON.stringify(_points));
let prevPoint;
let totalDistance = 0; // eslint-disable-line
logger.info('our points', points);
if (position !== 'start_left' && position !== 'start_right') {
points = points.reverse();
}
points.forEach(point => {
totalDistance += distance(point, prevPoint);
prevPoint = point;
});
// Traverse only 25 total distance along points to find cardinality point
const distanceToCardinalityPoint = 25;
let remainingDistance = distanceToCardinalityPoint;
let center;
prevPoint = undefined;
points.forEach(point => {
if (prevPoint && !center) {
const vectorDistance = distance(point, prevPoint);
if (vectorDistance < remainingDistance) {
remainingDistance -= vectorDistance;
} else {
// The point is remainingDistance from prevPoint in the vector between prevPoint and point
// Calculate the coordinates
const distanceRatio = remainingDistance / vectorDistance;
if (distanceRatio <= 0) center = prevPoint;
if (distanceRatio >= 1) center = { x: point.x, y: point.y };
if (distanceRatio > 0 && distanceRatio < 1) {
center = {
x: (1 - distanceRatio) * prevPoint.x + distanceRatio * point.x,
y: (1 - distanceRatio) * prevPoint.y + distanceRatio * point.y
};
}
}
}
prevPoint = point;
});
// if relation is present (Arrows will be added), change cardinality point off-set distance (d)
let d = 10;
//Calculate Angle for x and y axis
let angle = Math.atan2(points[0].y - center.y, points[0].x - center.x);
let cardinalityPosition = { x: 0, y: 0 };
//Calculation cardinality position using angle, center point on the line/curve but pendicular and with offset-distance
cardinalityPosition.x = Math.sin(angle) * d + (points[0].x + center.x) / 2;
cardinalityPosition.y = -Math.cos(angle) * d + (points[0].y + center.y) / 2;
if (position === 'start_left') {
cardinalityPosition.x = Math.sin(angle + Math.PI) * d + (points[0].x + center.x) / 2;
cardinalityPosition.y = -Math.cos(angle + Math.PI) * d + (points[0].y + center.y) / 2;
}
if (position === 'end_right') {
cardinalityPosition.x = Math.sin(angle - Math.PI) * d + (points[0].x + center.x) / 2 - 5;
cardinalityPosition.y = -Math.cos(angle - Math.PI) * d + (points[0].y + center.y) / 2 - 5;
}
if (position === 'end_left') {
cardinalityPosition.x = Math.sin(angle) * d + (points[0].x + center.x) / 2 - 5;
cardinalityPosition.y = -Math.cos(angle) * d + (points[0].y + center.y) / 2 - 5;
}
return cardinalityPosition;
};
export const getStylesFromArray = arr => {
let style = '';
let labelStyle = '';
@ -695,12 +767,37 @@ export const calculateTextDimensions = memoize(
(text, config) => `${text}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}`
);
const d3Attrs = function(d3Elem, attrs) {
for (let attr of attrs) {
d3Elem.attr(attr[0], attr[1]);
}
};
export const calculateSvgSizeAttrs = function(height, width, useMaxWidth) {
let attrs = new Map();
attrs.set('height', height);
if (useMaxWidth) {
attrs.set('width', '100%');
attrs.set('style', `max-width: ${width}px;`);
} else {
attrs.set('width', width);
}
return attrs;
};
export const configureSvgSize = function(svgElem, height, width, useMaxWidth) {
const attrs = calculateSvgSizeAttrs(height, width, useMaxWidth);
d3Attrs(svgElem, attrs);
};
export default {
assignWithDepth,
wrapLabel,
calculateTextHeight,
calculateTextWidth,
calculateTextDimensions,
calculateSvgSizeAttrs,
configureSvgSize,
detectInit,
detectDirective,
detectType,
@ -708,6 +805,7 @@ export default {
interpolateToCurve,
calcLabelPosition,
calcCardinalityPosition,
calcTerminalLabelPosition,
formatUrl,
getStylesFromArray,
generateId,

View File

@ -171,7 +171,6 @@ Alice->Bob: hi`;
expect(type).toBe('git');
});
});
describe('when finding substring in array ', function() {
it('should return the array index that contains the substring', function() {
const arr = ['stroke:val1', 'fill:val2'];
@ -184,7 +183,6 @@ describe('when finding substring in array ', function() {
expect(result).toEqual(-1);
});
});
describe('when formatting urls', function() {
it('should handle links', function() {
const url = 'https://mermaid-js.github.io/mermaid/#/';
@ -242,3 +240,16 @@ describe('when formatting urls', function() {
expect(result).toEqual('about:blank');
});
});
describe('when calculating SVG size', function() {
it('should return width 100% when useMaxWidth is true', function () {
const attrs = utils.calculateSvgSizeAttrs(100, 200, true);
expect(attrs.get('height')).toEqual(100);
expect(attrs.get('style')).toEqual('max-width: 200px;');
expect(attrs.get('width')).toEqual('100%');
});
it('should return absolute width when useMaxWidth is false', function () {
const attrs = utils.calculateSvgSizeAttrs(100, 200, false);
expect(attrs.get('height')).toEqual(100);
expect(attrs.get('width')).toEqual(200);
});
});

View File

@ -4411,9 +4411,9 @@ eventemitter2@4.1.2:
integrity sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=
eventemitter3@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
events@^3.0.0:
version "3.1.0"
@ -4882,11 +4882,9 @@ follow-redirects@1.9.0:
debug "^3.0.0"
follow-redirects@^1.0.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb"
integrity sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ==
dependencies:
debug "^3.0.0"
version "1.13.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
for-in@^1.0.2:
version "1.0.2"
@ -5579,9 +5577,9 @@ http-proxy-middleware@0.19.1:
micromatch "^3.1.10"
http-proxy@^1.17.0:
version "1.18.0"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a"
integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
dependencies:
eventemitter3 "^4.0.0"
follow-redirects "^1.0.0"