mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-18 11:44:07 +01:00
7
.github/pull_request_template.md
vendored
Normal file
7
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
## Summary
|
||||||
|
Brief description about the content of your PR.
|
||||||
|
|
||||||
|
## Design Decisions
|
||||||
|
Describe the way your implementation works or what design decisions you made if applicable.
|
||||||
|
|
||||||
|
Resolves #<your issue id here>
|
||||||
@@ -164,7 +164,31 @@ describe('Class diagram', () => {
|
|||||||
cy.get('svg');
|
cy.get('svg');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('5: should render a simple class diagram with Generic class', () => {
|
it('5: should render a simple class diagram with abstract method', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
classDiagram
|
||||||
|
Class01 <|-- AveryLongClass : Cool
|
||||||
|
Class01 : someMethod()*
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
cy.get('svg');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('6: should render a simple class diagram with static method', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
classDiagram
|
||||||
|
Class01 <|-- AveryLongClass : Cool
|
||||||
|
Class01 : someMethod()$
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
cy.get('svg');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('7: should render a simple class diagram with Generic class', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
classDiagram
|
classDiagram
|
||||||
@@ -184,7 +208,7 @@ describe('Class diagram', () => {
|
|||||||
cy.get('svg');
|
cy.get('svg');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('6: should render a simple class diagram with Generic class and relations', () => {
|
it('8: should render a simple class diagram with Generic class and relations', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
classDiagram
|
classDiagram
|
||||||
|
|||||||
@@ -396,4 +396,61 @@ describe('Flowcart', () => {
|
|||||||
{ flowchart: { htmlLabels: false } }
|
{ flowchart: { htmlLabels: false } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('15: Render Stadium shape', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
` graph 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;`,
|
||||||
|
{ flowchart: { htmlLabels: false } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('16: Render Stadium shape', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph LR
|
||||||
|
A1[Multi<br>Line] -->|Multi<br>Line| B1(Multi<br>Line)
|
||||||
|
C1[Multi<br/>Line] -->|Multi<br/>Line| D1(Multi<br/>Line)
|
||||||
|
E1[Multi<br />Line] -->|Multi<br />Line| F1(Multi<br />Line)
|
||||||
|
A2[Multi<br>Line] -->|Multi<br>Line| B2(Multi<br>Line)
|
||||||
|
C2[Multi<br/>Line] -->|Multi<br/>Line| D2(Multi<br/>Line)
|
||||||
|
E2[Multi<br />Line] -->|Multi<br />Line| F2(Multi<br />Line)
|
||||||
|
linkStyle 0 stroke:DarkGray,stroke-width:2px
|
||||||
|
linkStyle 1 stroke:DarkGray,stroke-width:2px
|
||||||
|
linkStyle 2 stroke:DarkGray,stroke-width:2px
|
||||||
|
`,
|
||||||
|
{ flowchart: { htmlLabels: false } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('17: Chaining of nodes', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph LR
|
||||||
|
a --> b --> c
|
||||||
|
`,
|
||||||
|
{ flowchart: { htmlLabels: false } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('18: Multiple nodes and chaining in one statement', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph LR
|
||||||
|
a --> b c--> d
|
||||||
|
`,
|
||||||
|
{ flowchart: { htmlLabels: false } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('19: Multiple nodes and chaining in one statement', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph TD
|
||||||
|
A[ h ] -- hello --> B[" test "]:::exClass C --> D;
|
||||||
|
classDef exClass background:#bbb,border:1px solid red;
|
||||||
|
`,
|
||||||
|
{ flowchart: { htmlLabels: false } }
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,4 +38,20 @@ describe('Sequencediagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('Multiple dependencies syntax', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
gantt
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d/%m
|
||||||
|
title Adding GANTT diagram to mermaid
|
||||||
|
excludes weekdays 2014-01-10
|
||||||
|
|
||||||
|
apple :a, 2017-07-20, 1w
|
||||||
|
banana :crit, b, 2017-07-23, 1d
|
||||||
|
cherry :active, c, after b a, 1d
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import { imgSnapshotTest } from '../../helpers/util';
|
import { imgSnapshotTest } from '../../helpers/util';
|
||||||
|
|
||||||
context('Aliasing', () => {
|
context('Sequence diagram', () => {
|
||||||
it('should render a simple sequence diagrams', () => {
|
it('should render a simple sequence diagram', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
@@ -32,6 +32,23 @@ context('Aliasing', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('should handle different line breaks', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
sequenceDiagram
|
||||||
|
participant 1 as multiline<br>using #lt;br#gt;
|
||||||
|
participant 2 as multiline<br/>using #lt;br/#gt;
|
||||||
|
participant 3 as multiline<br />using #lt;br /#gt;
|
||||||
|
1->>2: multiline<br>using #lt;br#gt;
|
||||||
|
note right of 2: multiline<br>using #lt;br#gt;
|
||||||
|
2->>3: multiline<br/>using #lt;br/#gt;
|
||||||
|
note right of 3: multiline<br/>using #lt;br/#gt;
|
||||||
|
3->>1: multiline<br />using #lt;br /#gt;
|
||||||
|
note right of 1: multiline<br />using #lt;br /#gt;
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
});
|
||||||
context('background rects', () => {
|
context('background rects', () => {
|
||||||
it('should render a single and nested rects', () => {
|
it('should render a single and nested rects', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
|
|||||||
@@ -16,11 +16,16 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>info below</h1>
|
<h1>info below</h1>
|
||||||
<div style="display: flex;">
|
<div style="display: flex;width: 100%; height: 100%">
|
||||||
<div class="mermaid">graph TD
|
<div class="mermaid" style="width: 100%; height: 100%">
|
||||||
A ==> B
|
graph TB
|
||||||
A --> C
|
A --> B
|
||||||
A -.-> D
|
A ==> C
|
||||||
|
A .-> D
|
||||||
|
A === E
|
||||||
|
A -.- F
|
||||||
|
D -- Hello --> a
|
||||||
|
D-- text including R TD space --xb
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="./mermaid.js"></script>
|
<script src="./mermaid.js"></script>
|
||||||
@@ -29,9 +34,9 @@
|
|||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
// arrowMarkerAbsolute: true,
|
// arrowMarkerAbsolute: true,
|
||||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||||
logLevel: 3,
|
logLevel: 0,
|
||||||
flowchart: { curve: 'linear', "htmlLabels": false },
|
flowchart: { curve: 'linear', "htmlLabels": false },
|
||||||
gantt: { axisFormat: '%m/%d/%Y' },
|
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||||
sequence: { actorMargin: 50 },
|
sequence: { actorMargin: 50 },
|
||||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||||
});
|
});
|
||||||
|
|||||||
37
dist/index.html
vendored
37
dist/index.html
vendored
@@ -300,6 +300,31 @@ click B testClick "click test"
|
|||||||
classDef someclass fill:#f96;
|
classDef someclass fill:#f96;
|
||||||
class A someclass;
|
class A someclass;
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mermaid">
|
||||||
|
graph 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;
|
||||||
|
</div>
|
||||||
|
<div class="mermaid">
|
||||||
|
graph LR
|
||||||
|
A1[Multi<br>Line] -->|Multi<br>Line| B1(Multi<br>Line)
|
||||||
|
C1[Multi<br/>Line] -->|Multi<br/>Line| D1(Multi<br/>Line)
|
||||||
|
E1[Multi<br />Line] -->|Multi<br />Line| F1(Multi<br />Line)
|
||||||
|
A2[Multi<br>Line] -->|Multi<br>Line| B2(Multi<br>Line)
|
||||||
|
C2[Multi<br/>Line] -->|Multi<br/>Line| D2(Multi<br/>Line)
|
||||||
|
E2[Multi<br />Line] -->|Multi<br />Line| F2(Multi<br />Line)
|
||||||
|
linkStyle 0 stroke:DarkGray,stroke-width:2px
|
||||||
|
linkStyle 1 stroke:DarkGray,stroke-width:2px
|
||||||
|
linkStyle 2 stroke:DarkGray,stroke-width:2px
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
@@ -331,6 +356,18 @@ and
|
|||||||
Alice -->> John: Parallel message 2
|
Alice -->> John: Parallel message 2
|
||||||
end
|
end
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mermaid">
|
||||||
|
sequenceDiagram
|
||||||
|
participant 1 as multiline<br>using #lt;br#gt;
|
||||||
|
participant 2 as multiline<br/>using #lt;br/#gt;
|
||||||
|
participant 3 as multiline<br />using #lt;br /#gt;
|
||||||
|
1->>2: multiline<br>using #lt;br#gt;
|
||||||
|
note right of 2: multiline<br>using #lt;br#gt;
|
||||||
|
2->>3: multiline<br/>using #lt;br/#gt;
|
||||||
|
note right of 3: multiline<br/>using #lt;br/#gt;
|
||||||
|
3->>1: multiline<br />using #lt;br /#gt;
|
||||||
|
note right of 1: multiline<br />using #lt;br /#gt;
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[](https://travis-ci.org/knsv/mermaid)
|
[](https://travis-ci.org/mermaid-js/mermaid)
|
||||||
[](https://coveralls.io/github/knsv/mermaid?branch=master)
|
[](https://coveralls.io/github/knsv/mermaid?branch=master)
|
||||||
[](https://gitter.im/knsv/mermaid?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/knsv/mermaid?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
|
|||||||
@@ -105,17 +105,10 @@ Naming convention: a class name should be composed of alphanumeric (unicode allo
|
|||||||
|
|
||||||
UML provides mechanisms to represent class members, such as attributes and methods, and additional information about them.
|
UML provides mechanisms to represent class members, such as attributes and methods, and additional information about them.
|
||||||
|
|
||||||
#### Visibility
|
Mermaid distinguishes between attributes and functions/methods based on if the **parenthesis** `()` are present or not. The ones with `()` are treated as functions/methods, and others as attributes.
|
||||||
To specify the visibility of a class member (i.e. any attribute or method), these notations may be placed before the member's name, but is it optional:
|
|
||||||
|
|
||||||
- `+` Public
|
|
||||||
- `-` Private
|
|
||||||
- `#` Protected
|
|
||||||
- `~` Package
|
|
||||||
|
|
||||||
Mermaid distinguishes between attributes and functions/methods based on if the **parenthesis** `()` are present or not. The one with `()` are treated as functions/methods, and others as attributes.
|
There are two ways to define the members of a class, and regardless of whichever syntax is used to define the members, the output will still be same. The two different ways are :
|
||||||
|
|
||||||
There are two ways to define the members of a class, and regardless of the whichever syntax is used to define the members, the output will still be same. The two different ways are :
|
|
||||||
- Associate a member of a class using **:** (colon) followed by member name, useful to define one member at a time. For example:
|
- Associate a member of a class using **:** (colon) followed by member name, useful to define one member at a time. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -150,7 +143,22 @@ class BankAccount{
|
|||||||
+BigDecimal balance
|
+BigDecimal balance
|
||||||
+deposit(amount)
|
+deposit(amount)
|
||||||
+withdrawl(amount)
|
+withdrawl(amount)
|
||||||
}```
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Visibility
|
||||||
|
To specify the visibility of a class member (i.e. any attribute or method), these notations may be placed before the member's name, but it is optional:
|
||||||
|
|
||||||
|
- `+` Public
|
||||||
|
- `-` Private
|
||||||
|
- `#` Protected
|
||||||
|
- `~` Package
|
||||||
|
|
||||||
|
>_note_ you can also include additional _classifers_ to a method definition by adding the following notations to the end of the method, i.e.: after the `()`:
|
||||||
|
> - `*` Abstract e.g.: `someAbstractMethod()*`
|
||||||
|
> - `$` Static e.g.: `someStaticMethod()$`
|
||||||
|
|
||||||
|
|
||||||
## Defining Relationship
|
## Defining Relationship
|
||||||
A relationship is a general term covering the specific types of logical connections found on class and object diagrams.
|
A relationship is a general term covering the specific types of logical connections found on class and object diagrams.
|
||||||
|
|||||||
@@ -78,6 +78,17 @@ graph LR
|
|||||||
id1(This is the text in the box)
|
id1(This is the text in the box)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### A stadium-shaped node
|
||||||
|
|
||||||
|
```
|
||||||
|
graph LR
|
||||||
|
id1([This is the text in the box])
|
||||||
|
```
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
id1([This is the text in the box])
|
||||||
|
```
|
||||||
|
|
||||||
### A node in the form of a circle
|
### A node in the form of a circle
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -87,6 +87,20 @@ gantt
|
|||||||
Add another diagram to demo page :48h
|
Add another diagram to demo page :48h
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It is possible to set multiple depenendenies separated by space:
|
||||||
|
```
|
||||||
|
gantt
|
||||||
|
apple :a, 2017-07-20, 1w
|
||||||
|
banana :crit, b, 2017-07-23, 1d
|
||||||
|
cherry :active, c, after b a, 1d
|
||||||
|
```
|
||||||
|
```
|
||||||
|
gantt
|
||||||
|
apple :a, 2017-07-20, 1w
|
||||||
|
banana :crit, b, 2017-07-23, 1d
|
||||||
|
cherry :active, c, after b a, 1d
|
||||||
|
```
|
||||||
|
|
||||||
### Title
|
### Title
|
||||||
|
|
||||||
Tbd
|
Tbd
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<meta name="description" content="Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.">
|
<meta name="description" content="Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.">
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
|
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
|
||||||
<script src="//cdn.jsdelivr.net/npm/mermaid@8.4.3/dist/mermaid.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/mermaid@8.4.4/dist/mermaid.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
|
|||||||
@@ -159,14 +159,13 @@ The main idea of the API is to be able to call a render function with the graph
|
|||||||
will render the graph and call a callback with the resulting svg code. With this approach it is up to the site creator to
|
will render the graph and call a callback with the resulting svg code. With this approach it is up to the site creator to
|
||||||
fetch the graph definition from the site (perhaps from a textarea), render it and place the graph somewhere in the site.
|
fetch the graph definition from the site (perhaps from a textarea), render it and place the graph somewhere in the site.
|
||||||
|
|
||||||
To do this, include mermaidAPI on your web website instead of mermaid.js. The example below show an outline of how this
|
The example below show an outline of how this could be used. The example just logs the resulting svg to the javascript console.
|
||||||
could be used. The example just logs the resulting svg to the javascript console.
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="mermaidAPI.js"></script>
|
<script src="mermaid.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
mermaidAPI.initialize({
|
mermaid.mermaidAPI.initialize({
|
||||||
startOnLoad:false
|
startOnLoad:false
|
||||||
});
|
});
|
||||||
$(function(){
|
$(function(){
|
||||||
@@ -178,7 +177,7 @@ could be used. The example just logs the resulting svg to the javascript console
|
|||||||
};
|
};
|
||||||
|
|
||||||
var graphDefinition = 'graph TB\na-->b';
|
var graphDefinition = 'graph TB\na-->b';
|
||||||
var graph = mermaidAPI.render('graphDiv', graphDefinition, insertSvg);
|
var graph = mermaid.mermaidAPI.render('graphDiv', graphDefinition, insertSvg);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mermaid",
|
"name": "mermaid",
|
||||||
"version": "8.4.3",
|
"version": "8.4.4",
|
||||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||||
"main": "dist/mermaid.core.js",
|
"main": "dist/mermaid.core.js",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export const addMember = function(className, member) {
|
|||||||
if (memberString.startsWith('<<') && memberString.endsWith('>>')) {
|
if (memberString.startsWith('<<') && memberString.endsWith('>>')) {
|
||||||
// Remove leading and trailing brackets
|
// Remove leading and trailing brackets
|
||||||
theClass.annotations.push(memberString.substring(2, memberString.length - 2));
|
theClass.annotations.push(memberString.substring(2, memberString.length - 2));
|
||||||
} else if (memberString.endsWith(')')) {
|
} else if (memberString.indexOf(')') > 0) {
|
||||||
theClass.methods.push(memberString);
|
theClass.methods.push(memberString);
|
||||||
} else if (memberString) {
|
} else if (memberString) {
|
||||||
theClass.members.push(memberString);
|
theClass.members.push(memberString);
|
||||||
|
|||||||
@@ -442,5 +442,27 @@ describe('class diagram, ', function () {
|
|||||||
expect(testClass.methods[0]).toBe('test()');
|
expect(testClass.methods[0]).toBe('test()');
|
||||||
expect(testClass.methods[1]).toBe('foo()');
|
expect(testClass.methods[1]).toBe('foo()');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle abstract methods', function () {
|
||||||
|
const str = 'classDiagram\n' + 'class Class1\n' + 'Class1 : someMethod()*';
|
||||||
|
parser.parse(str);
|
||||||
|
|
||||||
|
const testClass = parser.yy.getClass('Class1');
|
||||||
|
expect(testClass.annotations.length).toBe(0);
|
||||||
|
expect(testClass.members.length).toBe(0);
|
||||||
|
expect(testClass.methods.length).toBe(1);
|
||||||
|
expect(testClass.methods[0]).toBe('someMethod()*');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle static methods', function () {
|
||||||
|
const str = 'classDiagram\n' + 'class Class1\n' + 'Class1 : someMethod()$';
|
||||||
|
parser.parse(str);
|
||||||
|
|
||||||
|
const testClass = parser.yy.getClass('Class1');
|
||||||
|
expect(testClass.annotations.length).toBe(0);
|
||||||
|
expect(testClass.members.length).toBe(0);
|
||||||
|
expect(testClass.methods.length).toBe(1);
|
||||||
|
expect(testClass.methods[0]).toBe('someMethod()$');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -281,10 +281,34 @@ const drawClass = function(elem, classDef) {
|
|||||||
logger.info('Rendering class ' + classDef);
|
logger.info('Rendering class ' + classDef);
|
||||||
|
|
||||||
const addTspan = function(textEl, txt, isFirst) {
|
const addTspan = function(textEl, txt, isFirst) {
|
||||||
|
let displayText = txt;
|
||||||
|
let cssStyle = '';
|
||||||
|
let methodEnd = txt.indexOf(')') + 1;
|
||||||
|
|
||||||
|
if (methodEnd > 1 && methodEnd <= txt.length) {
|
||||||
|
let classifier = txt.substring(methodEnd);
|
||||||
|
|
||||||
|
switch (classifier) {
|
||||||
|
case '*':
|
||||||
|
cssStyle = 'font-style:italic;';
|
||||||
|
break;
|
||||||
|
case '$':
|
||||||
|
cssStyle = 'text-decoration:underline;';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayText = txt.substring(0, methodEnd);
|
||||||
|
}
|
||||||
|
|
||||||
const tSpan = textEl
|
const tSpan = textEl
|
||||||
.append('tspan')
|
.append('tspan')
|
||||||
.attr('x', conf.padding)
|
.attr('x', conf.padding)
|
||||||
.text(txt);
|
.text(displayText);
|
||||||
|
|
||||||
|
if (cssStyle !== '') {
|
||||||
|
tSpan.attr('style', cssStyle);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isFirst) {
|
if (!isFirst) {
|
||||||
tSpan.attr('dy', conf.textHeight);
|
tSpan.attr('dy', conf.textHeight);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,9 +135,29 @@ function rect_right_inv_arrow(parent, bbox, node) {
|
|||||||
return shapeSvg;
|
return shapeSvg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stadium(parent, bbox, node) {
|
||||||
|
const h = bbox.height;
|
||||||
|
const w = bbox.width + h / 4;
|
||||||
|
|
||||||
|
const shapeSvg = parent
|
||||||
|
.insert('rect', ':first-child')
|
||||||
|
.attr('rx', h / 2)
|
||||||
|
.attr('ry', h / 2)
|
||||||
|
.attr('x', -w / 2)
|
||||||
|
.attr('y', -h / 2)
|
||||||
|
.attr('width', w)
|
||||||
|
.attr('height', h);
|
||||||
|
|
||||||
|
node.intersect = function(point) {
|
||||||
|
return dagreD3.intersect.rect(node, point);
|
||||||
|
};
|
||||||
|
return shapeSvg;
|
||||||
|
}
|
||||||
|
|
||||||
export function addToRender(render) {
|
export function addToRender(render) {
|
||||||
render.shapes().question = question;
|
render.shapes().question = question;
|
||||||
render.shapes().hexagon = hexagon;
|
render.shapes().hexagon = hexagon;
|
||||||
|
render.shapes().stadium = stadium;
|
||||||
|
|
||||||
// Add custom shape for box with inverted arrow on left side
|
// Add custom shape for box with inverted arrow on left side
|
||||||
render.shapes().rect_left_inv_arrow = rect_left_inv_arrow;
|
render.shapes().rect_left_inv_arrow = rect_left_inv_arrow;
|
||||||
|
|||||||
@@ -1,6 +1,29 @@
|
|||||||
import { addToRender } from './flowChartShapes';
|
import { addToRender } from './flowChartShapes';
|
||||||
|
|
||||||
describe('flowchart shapes', function() {
|
describe('flowchart shapes', function() {
|
||||||
|
// rect-based shapes
|
||||||
|
[
|
||||||
|
['stadium', useWidth, useHeight]
|
||||||
|
].forEach(function([shapeType, getW, getH]) {
|
||||||
|
it(`should add a ${shapeType} shape that renders a properly positioned rect element`, function() {
|
||||||
|
const mockRender = MockRender();
|
||||||
|
const mockSvg = MockSvg();
|
||||||
|
addToRender(mockRender);
|
||||||
|
|
||||||
|
[[100, 100], [123, 45], [71, 300]].forEach(function([width, height]) {
|
||||||
|
const shape = mockRender.shapes()[shapeType](mockSvg, { width, height }, {});
|
||||||
|
const w = width + height / 4;
|
||||||
|
const h = height;
|
||||||
|
const dx = -getW(w, h) / 2;
|
||||||
|
const dy = -getH(w, h) / 2;
|
||||||
|
expect(shape.__tag).toEqual('rect');
|
||||||
|
expect(shape.__attrs).toHaveProperty('x', dx);
|
||||||
|
expect(shape.__attrs).toHaveProperty('y', dy);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// polygon-based shapes
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
'question',
|
'question',
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export const addVertex = function(_id, text, type, style, classes) {
|
|||||||
* @param type
|
* @param type
|
||||||
* @param linktext
|
* @param linktext
|
||||||
*/
|
*/
|
||||||
export const addLink = function(_start, _end, type, linktext) {
|
export const addSingleLink = function(_start, _end, type, linktext) {
|
||||||
let start = _start;
|
let start = _start;
|
||||||
let end = _end;
|
let end = _end;
|
||||||
if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
|
if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
|
||||||
@@ -127,6 +127,14 @@ export const addLink = function(_start, _end, type, linktext) {
|
|||||||
}
|
}
|
||||||
edges.push(edge);
|
edges.push(edge);
|
||||||
};
|
};
|
||||||
|
export const addLink = function(_start, _end, type, linktext) {
|
||||||
|
let i, j;
|
||||||
|
for (i = 0; i < _start.length; i++) {
|
||||||
|
for (j = 0; j < _end.length; j++) {
|
||||||
|
addSingleLink(_start[i], _end[j], type, linktext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a link's line interpolation algorithm
|
* Updates a link's line interpolation algorithm
|
||||||
@@ -501,6 +509,130 @@ export const firstGraph = () => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const destructStartLink = _str => {
|
||||||
|
const str = _str.trim();
|
||||||
|
|
||||||
|
switch (str) {
|
||||||
|
case '<--':
|
||||||
|
return { type: 'arrow', stroke: 'normal' };
|
||||||
|
case 'x--':
|
||||||
|
return { type: 'arrow_cross', stroke: 'normal' };
|
||||||
|
case 'o--':
|
||||||
|
return { type: 'arrow_circle', stroke: 'normal' };
|
||||||
|
case '<-.':
|
||||||
|
return { type: 'arrow', stroke: 'dotted' };
|
||||||
|
case 'x-.':
|
||||||
|
return { type: 'arrow_cross', stroke: 'dotted' };
|
||||||
|
case 'o-.':
|
||||||
|
return { type: 'arrow_circle', stroke: 'dotted' };
|
||||||
|
case '<==':
|
||||||
|
return { type: 'arrow', stroke: 'thick' };
|
||||||
|
case 'x==':
|
||||||
|
return { type: 'arrow_cross', stroke: 'thick' };
|
||||||
|
case 'o==':
|
||||||
|
return { type: 'arrow_circle', stroke: 'thick' };
|
||||||
|
case '--':
|
||||||
|
return { type: 'arrow_open', stroke: 'normal' };
|
||||||
|
case '==':
|
||||||
|
return { type: 'arrow_open', stroke: 'thick' };
|
||||||
|
case '-.':
|
||||||
|
return { type: 'arrow_open', stroke: 'dotted' };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const destructEndLink = _str => {
|
||||||
|
const str = _str.trim();
|
||||||
|
|
||||||
|
switch (str) {
|
||||||
|
case '--x':
|
||||||
|
return { type: 'arrow_cross', stroke: 'normal' };
|
||||||
|
case '-->':
|
||||||
|
return { type: 'arrow', stroke: 'normal' };
|
||||||
|
case '<-->':
|
||||||
|
return { type: 'double_arrow_point', stroke: 'normal' };
|
||||||
|
case 'x--x':
|
||||||
|
return { type: 'double_arrow_cross', stroke: 'normal' };
|
||||||
|
case 'o--o':
|
||||||
|
return { type: 'double_arrow_circle', stroke: 'normal' };
|
||||||
|
case 'o.-o':
|
||||||
|
return { type: 'double_arrow_circle', stroke: 'dotted' };
|
||||||
|
case '<==>':
|
||||||
|
return { type: 'double_arrow_point', stroke: 'thick' };
|
||||||
|
case 'o==o':
|
||||||
|
return { type: 'double_arrow_circle', stroke: 'thick' };
|
||||||
|
case 'x==x':
|
||||||
|
return { type: 'double_arrow_cross', stroke: 'thick' };
|
||||||
|
case 'x.-x':
|
||||||
|
return { type: 'double_arrow_cross', stroke: 'dotted' };
|
||||||
|
case 'x-.-x':
|
||||||
|
return { type: 'double_arrow_cross', stroke: 'dotted' };
|
||||||
|
case '<.->':
|
||||||
|
return { type: 'double_arrow_point', stroke: 'dotted' };
|
||||||
|
case '<-.->':
|
||||||
|
return { type: 'double_arrow_point', stroke: 'dotted' };
|
||||||
|
case 'o-.-o':
|
||||||
|
return { type: 'double_arrow_circle', stroke: 'dotted' };
|
||||||
|
case '--o':
|
||||||
|
return { type: 'arrow_circle', stroke: 'normal' };
|
||||||
|
case '---':
|
||||||
|
return { type: 'arrow_open', stroke: 'normal' };
|
||||||
|
case '-.-x':
|
||||||
|
return { type: 'arrow_cross', stroke: 'dotted' };
|
||||||
|
case '-.->':
|
||||||
|
return { type: 'arrow', stroke: 'dotted' };
|
||||||
|
case '-.-o':
|
||||||
|
return { type: 'arrow_circle', stroke: 'dotted' };
|
||||||
|
case '-.-':
|
||||||
|
return { type: 'arrow_open', stroke: 'dotted' };
|
||||||
|
case '.-x':
|
||||||
|
return { type: 'arrow_cross', stroke: 'dotted' };
|
||||||
|
case '.->':
|
||||||
|
return { type: 'arrow', stroke: 'dotted' };
|
||||||
|
case '.-o':
|
||||||
|
return { type: 'arrow_circle', stroke: 'dotted' };
|
||||||
|
case '.-':
|
||||||
|
return { type: 'arrow_open', stroke: 'dotted' };
|
||||||
|
case '==x':
|
||||||
|
return { type: 'arrow_cross', stroke: 'thick' };
|
||||||
|
case '==>':
|
||||||
|
return { type: 'arrow', stroke: 'thick' };
|
||||||
|
case '==o':
|
||||||
|
return { type: 'arrow_circle', stroke: 'thick' };
|
||||||
|
case '===':
|
||||||
|
return { type: 'arrow_open', stroke: 'thick' };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const destructLink = (_str, _startStr) => {
|
||||||
|
const info = destructEndLink(_str);
|
||||||
|
let startInfo;
|
||||||
|
if (_startStr) {
|
||||||
|
startInfo = destructStartLink(_startStr);
|
||||||
|
console.log(startInfo, info);
|
||||||
|
if (startInfo.stroke !== info.stroke) {
|
||||||
|
return { type: 'INVALID', stroke: 'INVALID' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startInfo.type === 'arrow_open') {
|
||||||
|
// -- xyz --> - take arrow type form ending
|
||||||
|
startInfo.type = info.type;
|
||||||
|
} else {
|
||||||
|
// x-- xyz --> - not supported
|
||||||
|
if (startInfo.type !== info.type) return { type: 'INVALID', stroke: 'INVALID' };
|
||||||
|
|
||||||
|
startInfo.type = 'double_' + startInfo.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startInfo.type === 'double_arrow') {
|
||||||
|
startInfo.type = 'double_arrow_point';
|
||||||
|
}
|
||||||
|
|
||||||
|
return startInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
addVertex,
|
addVertex,
|
||||||
addLink,
|
addLink,
|
||||||
@@ -523,6 +655,7 @@ export default {
|
|||||||
getDepthFirstPos,
|
getDepthFirstPos,
|
||||||
indexNodes,
|
indexNodes,
|
||||||
getSubGraphs,
|
getSubGraphs,
|
||||||
|
destructLink,
|
||||||
lex: {
|
lex: {
|
||||||
firstGraph
|
firstGraph
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,9 @@ export const addVertices = function(vert, g, svgId) {
|
|||||||
case 'ellipse':
|
case 'ellipse':
|
||||||
_shape = 'ellipse';
|
_shape = 'ellipse';
|
||||||
break;
|
break;
|
||||||
|
case 'stadium':
|
||||||
|
_shape = 'stadium';
|
||||||
|
break;
|
||||||
case 'group':
|
case 'group':
|
||||||
_shape = 'rect';
|
_shape = 'rect';
|
||||||
break;
|
break;
|
||||||
@@ -236,18 +239,18 @@ export const addEdges = function(edges, g) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
edgeData.arrowheadStyle = 'fill: #333';
|
edgeData.arrowheadStyle = 'fill: #333';
|
||||||
if (typeof edge.style === 'undefined') {
|
|
||||||
edgeData.labelpos = 'c';
|
edgeData.labelpos = 'c';
|
||||||
|
|
||||||
if (getConfig().flowchart.htmlLabels) {
|
if (getConfig().flowchart.htmlLabels) {
|
||||||
edgeData.labelType = 'html';
|
edgeData.labelType = 'html';
|
||||||
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
|
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
|
||||||
} else {
|
} else {
|
||||||
edgeData.labelType = 'text';
|
edgeData.labelType = 'text';
|
||||||
|
edgeData.label = edge.text.replace(/<br ?\/?>/g, '\n');
|
||||||
|
|
||||||
|
if (typeof edge.style === 'undefined') {
|
||||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
|
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
|
||||||
edgeData.label = edge.text.replace(/<br>/g, '\n');
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
edgeData.label = edge.text.replace(/<br>/g, '\n');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add the edge to the graph
|
// Add the edge to the graph
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { addVertices } from './flowRenderer';
|
import { addVertices, addEdges } from './flowRenderer';
|
||||||
import { setConfig } from '../../config';
|
import { setConfig } from '../../config';
|
||||||
|
|
||||||
setConfig({
|
setConfig({
|
||||||
@@ -22,6 +22,7 @@ describe('the flowchart renderer', function() {
|
|||||||
['odd_right', 'rect_left_inv_arrow'],
|
['odd_right', 'rect_left_inv_arrow'],
|
||||||
['circle', 'circle'],
|
['circle', 'circle'],
|
||||||
['ellipse', 'ellipse'],
|
['ellipse', 'ellipse'],
|
||||||
|
['stadium', 'stadium'],
|
||||||
['group', 'rect']
|
['group', 'rect']
|
||||||
].forEach(function([type, expectedShape, expectedRadios = 0]) {
|
].forEach(function([type, expectedShape, expectedRadios = 0]) {
|
||||||
it(`should add the correct shaped node to the graph for vertex type ${type}`, function() {
|
it(`should add the correct shaped node to the graph for vertex type ${type}`, function() {
|
||||||
@@ -93,4 +94,32 @@ describe('the flowchart renderer', function() {
|
|||||||
expect(addedNodes[0][1]).toHaveProperty('labelStyle', expectedLabelStyle);
|
expect(addedNodes[0][1]).toHaveProperty('labelStyle', expectedLabelStyle);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when adding edges to a graph', function() {
|
||||||
|
it('should handle multiline texts and set centered label position', function() {
|
||||||
|
const addedEdges = [];
|
||||||
|
const mockG = {
|
||||||
|
setEdge: function(s, e, data, c) {
|
||||||
|
addedEdges.push(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addEdges(
|
||||||
|
[
|
||||||
|
{ text: 'Multi<br>Line' },
|
||||||
|
{ text: 'Multi<br/>Line' },
|
||||||
|
{ text: 'Multi<br />Line' },
|
||||||
|
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br>Line' },
|
||||||
|
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br/>Line' },
|
||||||
|
{ style: ['stroke:DarkGray', 'stroke-width:2px'], text: 'Multi<br />Line' }
|
||||||
|
],
|
||||||
|
mockG,
|
||||||
|
'svg-id'
|
||||||
|
);
|
||||||
|
|
||||||
|
addedEdges.forEach(function(edge) {
|
||||||
|
expect(edge).toHaveProperty('label', 'Multi\nLine');
|
||||||
|
expect(edge).toHaveProperty('labelpos', 'c');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ describe('[Edges] when parsing', () => {
|
|||||||
expect(edges[0].text).toBe('');
|
expect(edges[0].text).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle double edged nodes with text on thick arrows', function() {
|
it('should handle double edged nodes with text on thick arrows XYZ1', function() {
|
||||||
const res = flow.parser.parse('graph TD;\nA x== text ==x B;');
|
const res = flow.parser.parse('graph TD;\nA x== text ==x B;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
|||||||
@@ -22,6 +22,16 @@ describe('[Singlenodes] when parsing', () => {
|
|||||||
expect(edges.length).toBe(0);
|
expect(edges.length).toBe(0);
|
||||||
expect(vert['A'].styles.length).toBe(0);
|
expect(vert['A'].styles.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
it('should handle a single node with white space after it (SN1)', function() {
|
||||||
|
// Silly but syntactically correct
|
||||||
|
const res = flow.parser.parse('graph TD;A ;');
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
expect(edges.length).toBe(0);
|
||||||
|
expect(vert['A'].styles.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle a single square node', function() {
|
it('should handle a single square node', function() {
|
||||||
// Silly but syntactically correct
|
// Silly but syntactically correct
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ describe('[Text] when parsing', () => {
|
|||||||
|
|
||||||
expect(edges[0].stroke).toBe('normal');
|
expect(edges[0].stroke).toBe('normal');
|
||||||
});
|
});
|
||||||
it('it should handle dotted text on lines', function() {
|
it('it should handle dotted text on lines (TD3)', function() {
|
||||||
const res = flow.parser.parse('graph TD;A-. test text with == .->B;');
|
const res = flow.parser.parse('graph TD;A-. test text with == .->B;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
@@ -265,7 +265,7 @@ describe('[Text] when parsing', () => {
|
|||||||
expect(edges[0].text).toBe('text including URL space');
|
expect(edges[0].text).toBe('text including URL space');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle space and dir (TD)', function() {
|
it('should handle space and dir (TD2)', function() {
|
||||||
const res = flow.parser.parse('graph TD;A-- text including R TD space --xB;');
|
const res = flow.parser.parse('graph TD;A-- text including R TD space --xB;');
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
|||||||
@@ -34,4 +34,179 @@ describe('when parsing flowcharts', function() {
|
|||||||
expect(edges[1].type).toBe('arrow');
|
expect(edges[1].type).toBe('arrow');
|
||||||
expect(edges[1].text).toBe('');
|
expect(edges[1].text).toBe('');
|
||||||
});
|
});
|
||||||
|
it('should handle chaining of vertices', function() {
|
||||||
|
const res = flow.parser.parse(`
|
||||||
|
graph TD
|
||||||
|
A B --> C;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
expect(vert['A'].id).toBe('A');
|
||||||
|
expect(vert['B'].id).toBe('B');
|
||||||
|
expect(vert['C'].id).toBe('C');
|
||||||
|
expect(edges.length).toBe(2);
|
||||||
|
expect(edges[0].start).toBe('A');
|
||||||
|
expect(edges[0].end).toBe('C');
|
||||||
|
expect(edges[0].type).toBe('arrow');
|
||||||
|
expect(edges[0].text).toBe('');
|
||||||
|
expect(edges[1].start).toBe('B');
|
||||||
|
expect(edges[1].end).toBe('C');
|
||||||
|
expect(edges[1].type).toBe('arrow');
|
||||||
|
expect(edges[1].text).toBe('');
|
||||||
|
});
|
||||||
|
it('should multiple vertices in link statement in the begining', function() {
|
||||||
|
const res = flow.parser.parse(`
|
||||||
|
graph TD
|
||||||
|
A-->B C;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
expect(vert['A'].id).toBe('A');
|
||||||
|
expect(vert['B'].id).toBe('B');
|
||||||
|
expect(vert['C'].id).toBe('C');
|
||||||
|
expect(edges.length).toBe(2);
|
||||||
|
expect(edges[0].start).toBe('A');
|
||||||
|
expect(edges[0].end).toBe('B');
|
||||||
|
expect(edges[0].type).toBe('arrow');
|
||||||
|
expect(edges[0].text).toBe('');
|
||||||
|
expect(edges[1].start).toBe('A');
|
||||||
|
expect(edges[1].end).toBe('C');
|
||||||
|
expect(edges[1].type).toBe('arrow');
|
||||||
|
expect(edges[1].text).toBe('');
|
||||||
|
});
|
||||||
|
it('should multiple vertices in link statement at the end', function() {
|
||||||
|
const res = flow.parser.parse(`
|
||||||
|
graph TD
|
||||||
|
A B--> C D;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
expect(vert['A'].id).toBe('A');
|
||||||
|
expect(vert['B'].id).toBe('B');
|
||||||
|
expect(vert['C'].id).toBe('C');
|
||||||
|
expect(vert['D'].id).toBe('D');
|
||||||
|
expect(edges.length).toBe(4);
|
||||||
|
expect(edges[0].start).toBe('A');
|
||||||
|
expect(edges[0].end).toBe('C');
|
||||||
|
expect(edges[0].type).toBe('arrow');
|
||||||
|
expect(edges[0].text).toBe('');
|
||||||
|
expect(edges[1].start).toBe('A');
|
||||||
|
expect(edges[1].end).toBe('D');
|
||||||
|
expect(edges[1].type).toBe('arrow');
|
||||||
|
expect(edges[1].text).toBe('');
|
||||||
|
expect(edges[2].start).toBe('B');
|
||||||
|
expect(edges[2].end).toBe('C');
|
||||||
|
expect(edges[2].type).toBe('arrow');
|
||||||
|
expect(edges[2].text).toBe('');
|
||||||
|
expect(edges[3].start).toBe('B');
|
||||||
|
expect(edges[3].end).toBe('D');
|
||||||
|
expect(edges[3].type).toBe('arrow');
|
||||||
|
expect(edges[3].text).toBe('');
|
||||||
|
});
|
||||||
|
it('should handle chaining of vertices at both ends at once', function() {
|
||||||
|
const res = flow.parser.parse(`
|
||||||
|
graph TD
|
||||||
|
A B--> C D;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
expect(vert['A'].id).toBe('A');
|
||||||
|
expect(vert['B'].id).toBe('B');
|
||||||
|
expect(vert['C'].id).toBe('C');
|
||||||
|
expect(vert['D'].id).toBe('D');
|
||||||
|
expect(edges.length).toBe(4);
|
||||||
|
expect(edges[0].start).toBe('A');
|
||||||
|
expect(edges[0].end).toBe('C');
|
||||||
|
expect(edges[0].type).toBe('arrow');
|
||||||
|
expect(edges[0].text).toBe('');
|
||||||
|
expect(edges[1].start).toBe('A');
|
||||||
|
expect(edges[1].end).toBe('D');
|
||||||
|
expect(edges[1].type).toBe('arrow');
|
||||||
|
expect(edges[1].text).toBe('');
|
||||||
|
expect(edges[2].start).toBe('B');
|
||||||
|
expect(edges[2].end).toBe('C');
|
||||||
|
expect(edges[2].type).toBe('arrow');
|
||||||
|
expect(edges[2].text).toBe('');
|
||||||
|
expect(edges[3].start).toBe('B');
|
||||||
|
expect(edges[3].end).toBe('D');
|
||||||
|
expect(edges[3].type).toBe('arrow');
|
||||||
|
expect(edges[3].text).toBe('');
|
||||||
|
});
|
||||||
|
it('should handle chaining and multiple nodes in in link statement', function() {
|
||||||
|
const res = flow.parser.parse(`
|
||||||
|
graph TD
|
||||||
|
A --> B C --> D;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
expect(vert['A'].id).toBe('A');
|
||||||
|
expect(vert['B'].id).toBe('B');
|
||||||
|
expect(vert['C'].id).toBe('C');
|
||||||
|
expect(vert['D'].id).toBe('D');
|
||||||
|
expect(edges.length).toBe(4);
|
||||||
|
expect(edges[0].start).toBe('A');
|
||||||
|
expect(edges[0].end).toBe('B');
|
||||||
|
expect(edges[0].type).toBe('arrow');
|
||||||
|
expect(edges[0].text).toBe('');
|
||||||
|
expect(edges[1].start).toBe('A');
|
||||||
|
expect(edges[1].end).toBe('C');
|
||||||
|
expect(edges[1].type).toBe('arrow');
|
||||||
|
expect(edges[1].text).toBe('');
|
||||||
|
expect(edges[2].start).toBe('B');
|
||||||
|
expect(edges[2].end).toBe('D');
|
||||||
|
expect(edges[2].type).toBe('arrow');
|
||||||
|
expect(edges[2].text).toBe('');
|
||||||
|
expect(edges[3].start).toBe('C');
|
||||||
|
expect(edges[3].end).toBe('D');
|
||||||
|
expect(edges[3].type).toBe('arrow');
|
||||||
|
expect(edges[3].text).toBe('');
|
||||||
|
});
|
||||||
|
it('should handle chaining and multiple nodes in in link statement with extra info in statements', function() {
|
||||||
|
const res = flow.parser.parse(`
|
||||||
|
graph TD
|
||||||
|
A[ h ] -- hello --> B[" test "]:::exClass C --> D;
|
||||||
|
classDef exClass background:#bbb,border:1px solid red;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
const classes = flow.parser.yy.getClasses();
|
||||||
|
|
||||||
|
expect(classes['exClass'].styles.length).toBe(2);
|
||||||
|
expect(classes['exClass'].styles[0]).toBe('background:#bbb');
|
||||||
|
expect(classes['exClass'].styles[1]).toBe('border:1px solid red');
|
||||||
|
expect(vert['A'].id).toBe('A');
|
||||||
|
expect(vert['B'].id).toBe('B');
|
||||||
|
expect(vert['B'].classes[0]).toBe('exClass');
|
||||||
|
expect(vert['C'].id).toBe('C');
|
||||||
|
expect(vert['D'].id).toBe('D');
|
||||||
|
expect(edges.length).toBe(4);
|
||||||
|
expect(edges[0].start).toBe('A');
|
||||||
|
expect(edges[0].end).toBe('B');
|
||||||
|
expect(edges[0].type).toBe('arrow');
|
||||||
|
expect(edges[0].text).toBe('hello');
|
||||||
|
expect(edges[1].start).toBe('A');
|
||||||
|
expect(edges[1].end).toBe('C');
|
||||||
|
expect(edges[1].type).toBe('arrow');
|
||||||
|
expect(edges[1].text).toBe('hello');
|
||||||
|
expect(edges[2].start).toBe('B');
|
||||||
|
expect(edges[2].end).toBe('D');
|
||||||
|
expect(edges[2].type).toBe('arrow');
|
||||||
|
expect(edges[2].text).toBe('');
|
||||||
|
expect(edges[3].start).toBe('C');
|
||||||
|
expect(edges[3].end).toBe('D');
|
||||||
|
expect(edges[3].type).toBe('arrow');
|
||||||
|
expect(edges[3].text).toBe('');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
%lex
|
%lex
|
||||||
%x string
|
%x string
|
||||||
%x dir
|
%x dir
|
||||||
|
%x vertex
|
||||||
%%
|
%%
|
||||||
\%\%[^\n]*\n* /* do nothing */
|
\%\%[^\n]*\n* /* do nothing */
|
||||||
["] this.begin("string");
|
["] this.begin("string");
|
||||||
@@ -40,48 +41,50 @@
|
|||||||
";" return 'SEMI';
|
";" return 'SEMI';
|
||||||
"," return 'COMMA';
|
"," return 'COMMA';
|
||||||
"*" return 'MULT';
|
"*" return 'MULT';
|
||||||
\s*\-\-[x]\s* return 'ARROW_CROSS';
|
\s*\-\-[x]\s* return 'LINK';
|
||||||
\s*\-\-\>\s* return 'ARROW_POINT';
|
\s*\-\-\>\s* return 'LINK';
|
||||||
\s*\<\-\-\>\s* return 'DOUBLE_ARROW_POINT';
|
\s*\<\-\-\>\s* return 'LINK';
|
||||||
\s*[x]\-\-[x]\s* return 'DOUBLE_ARROW_CROSS';
|
\s*[x]\-\-[x]\s* return 'LINK';
|
||||||
\s*[o]\-\-[o]\s* return 'DOUBLE_ARROW_CIRCLE';
|
\s*[o]\-\-[o]\s* return 'LINK';
|
||||||
\s*[o]\.\-[o]\s* return 'DOUBLE_DOTTED_ARROW_CIRCLE';
|
\s*[o]\.\-[o]\s* return 'LINK';
|
||||||
\s*\<\=\=\>\s* return 'DOUBLE_THICK_ARROW_POINT';
|
\s*\<\=\=\>\s* return 'LINK';
|
||||||
\s*[o]\=\=[o]\s* return 'DOUBLE_THICK_ARROW_CIRCLE';
|
\s*[o]\=\=[o]\s* return 'LINK';
|
||||||
\s*[x]\=\=[x]\s* return 'DOUBLE_THICK_ARROW_CROSS';
|
\s*[x]\=\=[x]\s* return 'LINK';
|
||||||
\s*[x].\-[x]\s* return 'DOUBLE_DOTTED_ARROW_CROSS';
|
\s*[x].\-[x]\s* return 'LINK';
|
||||||
\s*[x]\-\.\-[x]\s* return 'DOUBLE_DOTTED_ARROW_CROSS';
|
\s*[x]\-\.\-[x]\s* return 'LINK';
|
||||||
\s*\<\.\-\>\s* return 'DOUBLE_DOTTED_ARROW_POINT';
|
\s*\<\.\-\>\s* return 'LINK';
|
||||||
\s*\<\-\.\-\>\s* return 'DOUBLE_DOTTED_ARROW_POINT';
|
\s*\<\-\.\-\>\s* return 'LINK';
|
||||||
\s*[o]\-\.\-[o]\s* return 'DOUBLE_DOTTED_ARROW_CIRCLE';
|
\s*[o]\-\.\-[o]\s* return 'LINK';
|
||||||
\s*\-\-[o]\s* return 'ARROW_CIRCLE';
|
\s*\-\-[o]\s* return 'LINK';
|
||||||
\s*\-\-\-\s* return 'ARROW_OPEN';
|
\s*\-\-\-\s* return 'LINK';
|
||||||
\s*\-\.\-[x]\s* return 'DOTTED_ARROW_CROSS';
|
\s*\-\.\-[x]\s* return 'LINK';
|
||||||
\s*\-\.\-\>\s* return 'DOTTED_ARROW_POINT';
|
\s*\-\.\-\>\s* return 'LINK';
|
||||||
\s*\-\.\-[o]\s* return 'DOTTED_ARROW_CIRCLE';
|
\s*\-\.\-[o]\s* return 'LINK';
|
||||||
\s*\-\.\-\s* return 'DOTTED_ARROW_OPEN';
|
\s*\-\.\-\s* return 'LINK';
|
||||||
\s*.\-[x]\s* return 'DOTTED_ARROW_CROSS';
|
\s*.\-[x]\s* return 'LINK';
|
||||||
\s*\.\-\>\s* return 'DOTTED_ARROW_POINT';
|
\s*\.\-\>\s* return 'LINK';
|
||||||
\s*\.\-[o]\s* return 'DOTTED_ARROW_CIRCLE';
|
\s*\.\-[o]\s* return 'LINK';
|
||||||
\s*\.\-\s* return 'DOTTED_ARROW_OPEN';
|
\s*\.\-\s* return 'LINK';
|
||||||
\s*\=\=[x]\s* return 'THICK_ARROW_CROSS';
|
\s*\=\=[x]\s* return 'LINK';
|
||||||
\s*\=\=\>\s* return 'THICK_ARROW_POINT';
|
\s*\=\=\>\s* return 'LINK';
|
||||||
\s*\=\=[o]\s* return 'THICK_ARROW_CIRCLE';
|
\s*\=\=[o]\s* return 'LINK';
|
||||||
\s*\=\=[\=]\s* return 'THICK_ARROW_OPEN';
|
\s*\=\=[\=]\s* return 'LINK';
|
||||||
\s*\<\-\-\s* return 'START_DOUBLE_ARROW_POINT';
|
\s*\<\-\-\s* return 'START_LINK';
|
||||||
\s*[x]\-\-\s* return 'START_DOUBLE_ARROW_CROSS';
|
\s*[x]\-\-\s* return 'START_LINK';
|
||||||
\s*[o]\-\-\s* return 'START_DOUBLE_ARROW_CIRCLE';
|
\s*[o]\-\-\s* return 'START_LINK';
|
||||||
\s*\<\-\.\s* return 'START_DOUBLE_DOTTED_ARROW_POINT';
|
\s*\<\-\.\s* return 'START_LINK';
|
||||||
\s*[x]\-\.\s* return 'START_DOUBLE_DOTTED_ARROW_CROSS';
|
\s*[x]\-\.\s* return 'START_LINK';
|
||||||
\s*[o]\-\.\s* return 'START_DOUBLE_DOTTED_ARROW_CIRCLE';
|
\s*[o]\-\.\s* return 'START_LINK';
|
||||||
\s*\<\=\=\s* return 'START_DOUBLE_THICK_ARROW_POINT';
|
\s*\<\=\=\s* return 'START_LINK';
|
||||||
\s*[x]\=\=\s* return 'START_DOUBLE_THICK_ARROW_CROSS';
|
\s*[x]\=\=\s* return 'START_LINK';
|
||||||
\s*[o]\=\=\s* return 'START_DOUBLE_THICK_ARROW_CIRCLE';
|
\s*[o]\=\=\s* return 'START_LINK';
|
||||||
\s*\-\-\s* return '--';
|
\s*\-\-\s* return 'START_LINK';
|
||||||
\s*\-\.\s* return '-.';
|
\s*\-\.\s* return 'START_LINK';
|
||||||
\s*\=\=\s* return '==';
|
\s*\=\=\s* return 'START_LINK';
|
||||||
"(-" return '(-';
|
"(-" return '(-';
|
||||||
"-)" return '-)';
|
"-)" return '-)';
|
||||||
|
"([" return 'STADIUMSTART';
|
||||||
|
"])" return 'STADIUMEND';
|
||||||
\- return 'MINUS';
|
\- return 'MINUS';
|
||||||
"." return 'DOT';
|
"." return 'DOT';
|
||||||
[\_] return 'UNDERSCORE';
|
[\_] return 'UNDERSCORE';
|
||||||
@@ -92,6 +95,7 @@
|
|||||||
"<" return 'TAGSTART';
|
"<" return 'TAGSTART';
|
||||||
">" return 'TAGEND';
|
">" return 'TAGEND';
|
||||||
"^" return 'UP';
|
"^" return 'UP';
|
||||||
|
"\|" return 'SEP';
|
||||||
"v" return 'DOWN';
|
"v" return 'DOWN';
|
||||||
[A-Za-z]+ return 'ALPHA';
|
[A-Za-z]+ return 'ALPHA';
|
||||||
"\\]" return 'TRAPEND';
|
"\\]" return 'TRAPEND';
|
||||||
@@ -245,7 +249,7 @@ spaceList
|
|||||||
|
|
||||||
statement
|
statement
|
||||||
: verticeStatement separator
|
: verticeStatement separator
|
||||||
{ $$=$1}
|
{ /* console.warn('finat vs', $1.nodes); */ $$=$1.nodes}
|
||||||
| styleStatement separator
|
| styleStatement separator
|
||||||
{$$=[];}
|
{$$=[];}
|
||||||
| linkStyleStatement separator
|
| linkStyleStatement separator
|
||||||
@@ -283,68 +287,49 @@ separator: NEWLINE | SEMI | EOF ;
|
|||||||
// {$$ = [$1];yy.setClass($1,$3)}
|
// {$$ = [$1];yy.setClass($1,$3)}
|
||||||
// ;
|
// ;
|
||||||
|
|
||||||
verticeStatement: verticeStatement link node { yy.addLink($1[0],$3[0],$2); $$ = $3.concat($1) }
|
|
||||||
|node { $$ = $1 }
|
verticeStatement: verticeStatement link node
|
||||||
|
{ /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } }
|
||||||
|
| verticeStatement link node spaceList
|
||||||
|
{ /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } }
|
||||||
|
|node spaceList {/*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }}
|
||||||
|
|node { /*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }}
|
||||||
;
|
;
|
||||||
|
|
||||||
node: vertex
|
node: vertex
|
||||||
{ $$ = [$1];}
|
{ /* console.warn('nod', $1); */ $$ = [$1];}
|
||||||
|
| node spaceList vertex
|
||||||
|
{ $$ = [$1[0], $3]; /*console.warn('pip', $1, $3, $$);*/ }
|
||||||
| vertex STYLE_SEPARATOR idString
|
| vertex STYLE_SEPARATOR idString
|
||||||
{$$ = [$1];yy.setClass($1,$3)}
|
{$$ = [$1];yy.setClass($1,$3)}
|
||||||
;
|
;
|
||||||
|
|
||||||
vertex: idString SQS text SQE
|
vertex: idString SQS text SQE
|
||||||
{$$ = $1;yy.addVertex($1,$3,'square');}
|
{$$ = $1;yy.addVertex($1,$3,'square');}
|
||||||
| idString SQS text SQE spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'square');}
|
|
||||||
| idString PS PS text PE PE
|
| idString PS PS text PE PE
|
||||||
{$$ = $1;yy.addVertex($1,$4,'circle');}
|
{$$ = $1;yy.addVertex($1,$4,'circle');}
|
||||||
| idString PS PS text PE PE spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$4,'circle');}
|
|
||||||
| idString '(-' text '-)'
|
| idString '(-' text '-)'
|
||||||
{$$ = $1;yy.addVertex($1,$3,'ellipse');}
|
{$$ = $1;yy.addVertex($1,$3,'ellipse');}
|
||||||
| idString '(-' text '-)' spaceList
|
| idString STADIUMSTART text STADIUMEND
|
||||||
{$$ = $1;yy.addVertex($1,$3,'ellipse');}
|
{$$ = $1;yy.addVertex($1,$3,'stadium');}
|
||||||
| idString PS text PE
|
| idString PS text PE
|
||||||
{$$ = $1;yy.addVertex($1,$3,'round');}
|
{$$ = $1;yy.addVertex($1,$3,'round');}
|
||||||
| idString PS text PE spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'round');}
|
|
||||||
| idString DIAMOND_START text DIAMOND_STOP
|
| idString DIAMOND_START text DIAMOND_STOP
|
||||||
{$$ = $1;yy.addVertex($1,$3,'diamond');}
|
{$$ = $1;yy.addVertex($1,$3,'diamond');}
|
||||||
| idString DIAMOND_START text DIAMOND_STOP spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'diamond');}
|
|
||||||
| idString DIAMOND_START DIAMOND_START text DIAMOND_STOP DIAMOND_STOP
|
| idString DIAMOND_START DIAMOND_START text DIAMOND_STOP DIAMOND_STOP
|
||||||
{$$ = $1;yy.addVertex($1,$4,'hexagon');}
|
{$$ = $1;yy.addVertex($1,$4,'hexagon');}
|
||||||
| idString DIAMOND_START DIAMOND_START text DIAMOND_STOP DIAMOND_STOP spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$4,'hexagon');}
|
|
||||||
| idString TAGEND text SQE
|
| idString TAGEND text SQE
|
||||||
{$$ = $1;yy.addVertex($1,$3,'odd');}
|
{$$ = $1;yy.addVertex($1,$3,'odd');}
|
||||||
| idString TAGEND text SQE spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'odd');}
|
|
||||||
| idString TRAPSTART text TRAPEND
|
| idString TRAPSTART text TRAPEND
|
||||||
{$$ = $1;yy.addVertex($1,$3,'trapezoid');}
|
{$$ = $1;yy.addVertex($1,$3,'trapezoid');}
|
||||||
| idString TRAPSTART text TRAPEND spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'trapezoid');}
|
|
||||||
| idString INVTRAPSTART text INVTRAPEND
|
| idString INVTRAPSTART text INVTRAPEND
|
||||||
{$$ = $1;yy.addVertex($1,$3,'inv_trapezoid');}
|
{$$ = $1;yy.addVertex($1,$3,'inv_trapezoid');}
|
||||||
| idString INVTRAPSTART text INVTRAPEND spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'inv_trapezoid');}
|
|
||||||
| idString TRAPSTART text INVTRAPEND
|
| idString TRAPSTART text INVTRAPEND
|
||||||
{$$ = $1;yy.addVertex($1,$3,'lean_right');}
|
{$$ = $1;yy.addVertex($1,$3,'lean_right');}
|
||||||
| idString TRAPSTART text INVTRAPEND spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'lean_right');}
|
|
||||||
| idString INVTRAPSTART text TRAPEND
|
| idString INVTRAPSTART text TRAPEND
|
||||||
{$$ = $1;yy.addVertex($1,$3,'lean_left');}
|
{$$ = $1;yy.addVertex($1,$3,'lean_left');}
|
||||||
| idString INVTRAPSTART text TRAPEND spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'lean_left');}
|
|
||||||
/* | idString SQS text TAGSTART
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'odd_right');}
|
|
||||||
| idString SQS text TAGSTART spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1,$3,'odd_right');} */
|
|
||||||
| idString
|
| idString
|
||||||
{$$ = $1;yy.addVertex($1);}
|
{ /*console.warn('h: ', $1);*/$$ = $1;yy.addVertex($1);}
|
||||||
| idString spaceList
|
|
||||||
{$$ = $1;yy.addVertex($1);}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
@@ -357,92 +342,12 @@ link: linkStatement arrowText
|
|||||||
{$1.text = $2;$$ = $1;}
|
{$1.text = $2;$$ = $1;}
|
||||||
| linkStatement
|
| linkStatement
|
||||||
{$$ = $1;}
|
{$$ = $1;}
|
||||||
| '--' text ARROW_POINT
|
| START_LINK text LINK
|
||||||
{$$ = {"type":"arrow","stroke":"normal","text":$2};}
|
{var inf = yy.destructLink($3, $1); $$ = {"type":inf.type,"stroke":inf.stroke,"text":$2};}
|
||||||
| 'START_DOUBLE_ARROW_POINT' text ARROW_POINT
|
|
||||||
{$$ = {"type":"double_arrow_point","stroke":"normal","text":$2};}
|
|
||||||
| '--' text ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"arrow_circle","stroke":"normal","text":$2};}
|
|
||||||
| 'START_DOUBLE_ARROW_CIRCLE' text ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"double_arrow_circle","stroke":"normal","text":$2};}
|
|
||||||
| '--' text ARROW_CROSS
|
|
||||||
{$$ = {"type":"arrow_cross","stroke":"normal","text":$2};}
|
|
||||||
| 'START_DOUBLE_ARROW_CROSS' text ARROW_CROSS
|
|
||||||
{$$ = {"type":"double_arrow_cross","stroke":"normal","text":$2};}
|
|
||||||
| '--' text ARROW_OPEN
|
|
||||||
{$$ = {"type":"arrow_open","stroke":"normal","text":$2};}
|
|
||||||
| '-.' text DOTTED_ARROW_POINT
|
|
||||||
{$$ = {"type":"arrow","stroke":"dotted","text":$2};}
|
|
||||||
| 'START_DOUBLE_DOTTED_ARROW_POINT' text DOTTED_ARROW_POINT
|
|
||||||
{$$ = {"type":"double_arrow_point","stroke":"dotted","text":$2};}
|
|
||||||
| '-.' text DOTTED_ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"arrow_circle","stroke":"dotted","text":$2};}
|
|
||||||
| 'START_DOUBLE_DOTTED_ARROW_CIRCLE' text DOTTED_ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"double_arrow_circle","stroke":"dotted","text":$2};}
|
|
||||||
| '-.' text DOTTED_ARROW_CROSS
|
|
||||||
{$$ = {"type":"arrow_cross","stroke":"dotted","text":$2};}
|
|
||||||
| 'START_DOUBLE_DOTTED_ARROW_CROSS' text DOTTED_ARROW_CROSS
|
|
||||||
{$$ = {"type":"double_arrow_cross","stroke":"dotted","text":$2};}
|
|
||||||
| '-.' text DOTTED_ARROW_OPEN
|
|
||||||
{$$ = {"type":"arrow_open","stroke":"dotted","text":$2};}
|
|
||||||
| '==' text THICK_ARROW_POINT
|
|
||||||
{$$ = {"type":"arrow","stroke":"thick","text":$2};}
|
|
||||||
| 'START_DOUBLE_THICK_ARROW_POINT' text THICK_ARROW_POINT
|
|
||||||
{$$ = {"type":"double_arrow_point","stroke":"thick","text":$2};}
|
|
||||||
| '==' text THICK_ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"arrow_circle","stroke":"thick","text":$2};}
|
|
||||||
| 'START_DOUBLE_THICK_ARROW_CIRCLE' text THICK_ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"double_arrow_circle","stroke":"thick","text":$2};}
|
|
||||||
| '==' text THICK_ARROW_CROSS
|
|
||||||
{$$ = {"type":"arrow_cross","stroke":"thick","text":$2};}
|
|
||||||
| 'START_DOUBLE_THICK_ARROW_CROSS' text THICK_ARROW_CROSS
|
|
||||||
{$$ = {"type":"double_arrow_cross","stroke":"thick","text":$2};}
|
|
||||||
| '==' text THICK_ARROW_OPEN
|
|
||||||
{$$ = {"type":"arrow_open","stroke":"thick","text":$2};}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
linkStatement: ARROW_POINT
|
linkStatement: LINK
|
||||||
{$$ = {"type":"arrow","stroke":"normal"};}
|
{var inf = yy.destructLink($1);$$ = {"type":inf.type,"stroke":inf.stroke};}
|
||||||
| DOUBLE_ARROW_POINT
|
|
||||||
{$$ = {"type":"double_arrow_point","stroke":"normal"};}
|
|
||||||
| ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"arrow_circle","stroke":"normal"};}
|
|
||||||
| DOUBLE_ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"double_arrow_circle","stroke":"normal"};}
|
|
||||||
| ARROW_CROSS
|
|
||||||
{$$ = {"type":"arrow_cross","stroke":"normal"};}
|
|
||||||
| DOUBLE_ARROW_CROSS
|
|
||||||
{$$ = {"type":"double_arrow_cross","stroke":"normal"};}
|
|
||||||
| ARROW_OPEN
|
|
||||||
{$$ = {"type":"arrow_open","stroke":"normal"};}
|
|
||||||
| DOTTED_ARROW_POINT
|
|
||||||
{$$ = {"type":"arrow","stroke":"dotted"};}
|
|
||||||
| DOUBLE_DOTTED_ARROW_POINT
|
|
||||||
{$$ = {"type":"double_arrow_point","stroke":"dotted"};}
|
|
||||||
| DOTTED_ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"arrow_circle","stroke":"dotted"};}
|
|
||||||
| DOUBLE_DOTTED_ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"double_arrow_circle","stroke":"dotted"};}
|
|
||||||
| DOTTED_ARROW_CROSS
|
|
||||||
{$$ = {"type":"arrow_cross","stroke":"dotted"};}
|
|
||||||
| DOUBLE_DOTTED_ARROW_CROSS
|
|
||||||
{$$ = {"type":"double_arrow_cross","stroke":"dotted"};}
|
|
||||||
| DOTTED_ARROW_OPEN
|
|
||||||
{$$ = {"type":"arrow_open","stroke":"dotted"};}
|
|
||||||
| THICK_ARROW_POINT
|
|
||||||
{$$ = {"type":"arrow","stroke":"thick"};}
|
|
||||||
| DOUBLE_THICK_ARROW_POINT
|
|
||||||
{$$ = {"type":"double_arrow_point","stroke":"thick"};}
|
|
||||||
| THICK_ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"arrow_circle","stroke":"thick"};}
|
|
||||||
| DOUBLE_THICK_ARROW_CIRCLE
|
|
||||||
{$$ = {"type":"double_arrow_circle","stroke":"thick"};}
|
|
||||||
| THICK_ARROW_CROSS
|
|
||||||
{$$ = {"type":"arrow_cross","stroke":"thick"};}
|
|
||||||
| DOUBLE_THICK_ARROW_CROSS
|
|
||||||
{$$ = {"type":"double_arrow_cross","stroke":"thick"};}
|
|
||||||
| THICK_ARROW_OPEN
|
|
||||||
{$$ = {"type":"arrow_open","stroke":"thick"};}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
arrowText:
|
arrowText:
|
||||||
@@ -530,7 +435,7 @@ styleComponent: ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT |
|
|||||||
|
|
||||||
/* Token lists */
|
/* Token lists */
|
||||||
|
|
||||||
textToken : textNoTagsToken | TAGSTART | TAGEND | '==' | '--' | PCT | DEFAULT;
|
textToken : textNoTagsToken | TAGSTART | TAGEND | START_LINK | PCT | DEFAULT;
|
||||||
|
|
||||||
textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ;
|
textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ;
|
||||||
|
|
||||||
@@ -563,5 +468,5 @@ alphaNumToken : PUNCTUATION | UNICODE_TEXT | NUM| ALPHA | COLON | COMMA | PLUS
|
|||||||
|
|
||||||
idStringToken : ALPHA|UNDERSCORE |UNICODE_TEXT | NUM| COLON | COMMA | PLUS | MINUS | DOWN |EQUALS | MULT | BRKT | DOT | PUNCTUATION;
|
idStringToken : ALPHA|UNDERSCORE |UNICODE_TEXT | NUM| COLON | COMMA | PLUS | MINUS | DOWN |EQUALS | MULT | BRKT | DOT | PUNCTUATION;
|
||||||
|
|
||||||
graphCodeTokens: TRAPSTART | TRAPEND | INVTRAPSTART | INVTRAPEND | PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAGSTART | TAGEND | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI ;
|
graphCodeTokens: STADIUMSTART | STADIUMEND | TRAPSTART | TRAPEND | INVTRAPSTART | INVTRAPEND | PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAGSTART | TAGEND | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI;
|
||||||
%%
|
%%
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ describe('when parsing subgraphs', function() {
|
|||||||
const subgraphs = flow.parser.yy.getSubGraphs();
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
expect(subgraphs.length).toBe(1);
|
expect(subgraphs.length).toBe(1);
|
||||||
const subgraph = subgraphs[0];
|
const subgraph = subgraphs[0];
|
||||||
|
|
||||||
expect(subgraph.nodes.length).toBe(2);
|
expect(subgraph.nodes.length).toBe(2);
|
||||||
expect(subgraph.nodes[0]).toBe('a2');
|
expect(subgraph.nodes[0]).toBe('a2');
|
||||||
expect(subgraph.nodes[1]).toBe('a1');
|
expect(subgraph.nodes[1]).toBe('a1');
|
||||||
@@ -191,6 +192,15 @@ describe('when parsing subgraphs', function() {
|
|||||||
expect(edges[0].type).toBe('arrow');
|
expect(edges[0].type).toBe('arrow');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle subgraphs3', function() {
|
||||||
|
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle \n\n c-->d \nend\n');
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
expect(edges[0].type).toBe('arrow');
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle nested subgraphs', function() {
|
it('should handle nested subgraphs', function() {
|
||||||
const str =
|
const str =
|
||||||
'graph TD\n' +
|
'graph TD\n' +
|
||||||
@@ -218,6 +228,14 @@ describe('when parsing subgraphs', function() {
|
|||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
expect(edges[0].type).toBe('arrow');
|
||||||
|
});
|
||||||
|
it('should handle subgraphs with multi node statements in it', function() {
|
||||||
|
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\na b --> c e\n end;');
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
expect(edges[0].type).toBe('arrow');
|
expect(edges[0].type).toBe('arrow');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -138,14 +138,28 @@ const getStartDate = function(prevTime, dateFormat, str) {
|
|||||||
const afterStatement = re.exec(str.trim());
|
const afterStatement = re.exec(str.trim());
|
||||||
|
|
||||||
if (afterStatement !== null) {
|
if (afterStatement !== null) {
|
||||||
const task = findTaskById(afterStatement[1]);
|
// check all after ids and take the latest
|
||||||
|
let latestEndingTask = null;
|
||||||
|
afterStatement[1].split(' ').forEach(function(id) {
|
||||||
|
let task = findTaskById(id);
|
||||||
|
if (typeof task !== 'undefined') {
|
||||||
|
if (!latestEndingTask) {
|
||||||
|
latestEndingTask = task;
|
||||||
|
} else {
|
||||||
|
if (task.endTime > latestEndingTask.endTime) {
|
||||||
|
latestEndingTask = task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (typeof task === 'undefined') {
|
if (!latestEndingTask) {
|
||||||
const dt = new Date();
|
const dt = new Date();
|
||||||
dt.setHours(0, 0, 0, 0);
|
dt.setHours(0, 0, 0, 0);
|
||||||
return dt;
|
return dt;
|
||||||
|
} else {
|
||||||
|
return latestEndingTask.endTime;
|
||||||
}
|
}
|
||||||
return task.endTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for actual date set
|
// Check for actual date set
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ export const draw = function(txt, id, ver) {
|
|||||||
try {
|
try {
|
||||||
const parser = gitGraphParser.parser;
|
const parser = gitGraphParser.parser;
|
||||||
parser.yy = db;
|
parser.yy = db;
|
||||||
|
parser.yy.clear();
|
||||||
|
|
||||||
logger.debug('in gitgraph renderer', txt + '\n', 'id:', id, ver);
|
logger.debug('in gitgraph renderer', txt + '\n', 'id:', id, ver);
|
||||||
// Parse the graph definition
|
// Parse the graph definition
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ line
|
|||||||
|
|
||||||
statement
|
statement
|
||||||
: STR VALUE {
|
: STR VALUE {
|
||||||
console.log('str:'+$1+' value: '+$2)
|
/*console.log('str:'+$1+' value: '+$2)*/
|
||||||
yy.addSection($1,yy.cleanupValue($2)); }
|
yy.addSection($1,yy.cleanupValue($2)); }
|
||||||
| title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
|
| title {yy.setTitle($1.substr(6));$$=$1.substr(6);}
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -333,6 +333,34 @@ describe('when parsing a sequenceDiagram', function() {
|
|||||||
expect(messages[0].from).toBe('Alice');
|
expect(messages[0].from).toBe('Alice');
|
||||||
expect(messages[2].from).toBe('John');
|
expect(messages[2].from).toBe('John');
|
||||||
});
|
});
|
||||||
|
it('it should handle different line breaks', function() {
|
||||||
|
const str =
|
||||||
|
'sequenceDiagram\n' +
|
||||||
|
'participant 1 as multiline<br>text\n' +
|
||||||
|
'participant 2 as multiline<br/>text\n' +
|
||||||
|
'participant 3 as multiline<br />text\n' +
|
||||||
|
'1->>2: multiline<br>text\n' +
|
||||||
|
'note right of 2: multiline<br>text\n' +
|
||||||
|
'2->>3: multiline<br/>text\n' +
|
||||||
|
'note right of 3: multiline<br/>text\n' +
|
||||||
|
'3->>1: multiline<br />text\n' +
|
||||||
|
'note right of 1: multiline<br />text\n';
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
|
||||||
|
const actors = parser.yy.getActors();
|
||||||
|
expect(actors['1'].description).toBe('multiline<br>text');
|
||||||
|
expect(actors['2'].description).toBe('multiline<br/>text');
|
||||||
|
expect(actors['3'].description).toBe('multiline<br />text');
|
||||||
|
|
||||||
|
const messages = parser.yy.getMessages();
|
||||||
|
expect(messages[0].message).toBe('multiline<br>text');
|
||||||
|
expect(messages[1].message).toBe('multiline<br>text');
|
||||||
|
expect(messages[2].message).toBe('multiline<br/>text');
|
||||||
|
expect(messages[3].message).toBe('multiline<br/>text');
|
||||||
|
expect(messages[4].message).toBe('multiline<br />text');
|
||||||
|
expect(messages[5].message).toBe('multiline<br />text');
|
||||||
|
});
|
||||||
it('it should handle notes over a single actor', function() {
|
it('it should handle notes over a single actor', function() {
|
||||||
const str =
|
const str =
|
||||||
'sequenceDiagram\n' + 'Alice->Bob: Hello Bob, how are you?\n' + 'Note over Bob: Bob thinks\n';
|
'sequenceDiagram\n' + 'Alice->Bob: Hello Bob, how are you?\n' + 'Note over Bob: Bob thinks\n';
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ export const bounds = {
|
|||||||
|
|
||||||
const _drawLongText = (text, x, y, g, width) => {
|
const _drawLongText = (text, x, y, g, width) => {
|
||||||
let textHeight = 0;
|
let textHeight = 0;
|
||||||
const lines = text.split(/<br\/?>/gi);
|
const lines = text.split(/<br ?\/?>/gi);
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const textObj = svgDraw.getTextObj();
|
const textObj = svgDraw.getTextObj();
|
||||||
textObj.x = x;
|
textObj.x = x;
|
||||||
@@ -233,7 +233,7 @@ const drawMessage = function(elem, startx, stopx, verticalPos, msg, sequenceInde
|
|||||||
let textElem;
|
let textElem;
|
||||||
let counterBreaklines = 0;
|
let counterBreaklines = 0;
|
||||||
let breaklineOffset = 17;
|
let breaklineOffset = 17;
|
||||||
const breaklines = msg.message.split(/<br\/?>/gi);
|
const breaklines = msg.message.split(/<br ?\/?>/gi);
|
||||||
for (const breakline of breaklines) {
|
for (const breakline of breaklines) {
|
||||||
textElem = g
|
textElem = g
|
||||||
.append('text') // text label for the x axis
|
.append('text') // text label for the x axis
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const drawRect = function(elem, rectData) {
|
|||||||
|
|
||||||
export const drawText = function(elem, textData) {
|
export const drawText = function(elem, textData) {
|
||||||
// Remove and ignore br:s
|
// Remove and ignore br:s
|
||||||
const nText = textData.text.replace(/<br\/?>/gi, ' ');
|
const nText = textData.text.replace(/<br ?\/?>/gi, ' ');
|
||||||
|
|
||||||
const textElem = elem.append('text');
|
const textElem = elem.append('text');
|
||||||
textElem.attr('x', textData.x);
|
textElem.attr('x', textData.x);
|
||||||
@@ -321,7 +321,7 @@ const _drawTextCandidateFunc = (function() {
|
|||||||
function byTspan(content, g, x, y, width, height, textAttrs, conf) {
|
function byTspan(content, g, x, y, width, height, textAttrs, conf) {
|
||||||
const { actorFontSize, actorFontFamily } = conf;
|
const { actorFontSize, actorFontFamily } = conf;
|
||||||
|
|
||||||
const lines = content.split(/<br\/?>/gi);
|
const lines = content.split(/<br ?\/?>/gi);
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const dy = i * actorFontSize - (actorFontSize * (lines.length - 1)) / 2;
|
const dy = i * actorFontSize - (actorFontSize * (lines.length - 1)) / 2;
|
||||||
const text = g
|
const text = g
|
||||||
|
|||||||
@@ -23,23 +23,34 @@ export const setLogLevel = function(level) {
|
|||||||
logger.error = () => {};
|
logger.error = () => {};
|
||||||
logger.fatal = () => {};
|
logger.fatal = () => {};
|
||||||
if (level <= LEVELS.fatal) {
|
if (level <= LEVELS.fatal) {
|
||||||
logger.fatal = console.log.bind(console, '\x1b[35m', format('FATAL'));
|
logger.fatal = console.error
|
||||||
|
? console.error.bind(console, format('FATAL'), 'color: orange')
|
||||||
|
: console.log.bind(console, '\x1b[35m', format('FATAL'));
|
||||||
}
|
}
|
||||||
if (level <= LEVELS.error) {
|
if (level <= LEVELS.error) {
|
||||||
logger.error = console.log.bind(console, '\x1b[31m', format('ERROR'));
|
logger.error = console.error
|
||||||
|
? console.error.bind(console, format('ERROR'), 'color: orange')
|
||||||
|
: console.log.bind(console, '\x1b[31m', format('ERROR'));
|
||||||
}
|
}
|
||||||
if (level <= LEVELS.warn) {
|
if (level <= LEVELS.warn) {
|
||||||
logger.warn = console.log.bind(console, `\x1b[33m`, format('WARN'));
|
logger.warn = console.warn
|
||||||
|
? console.warn.bind(console, format('WARN'), 'color: orange')
|
||||||
|
: console.log.bind(console, `\x1b[33m`, format('WARN'));
|
||||||
}
|
}
|
||||||
if (level <= LEVELS.info) {
|
if (level <= LEVELS.info) {
|
||||||
logger.info = console.log.bind(console, '\x1b[34m', format('INFO'));
|
logger.info = console.info
|
||||||
|
? // ? console.info.bind(console, '\x1b[34m', format('INFO'), 'color: blue')
|
||||||
|
console.info.bind(console, format('INFO'), 'color: lightblue')
|
||||||
|
: console.log.bind(console, '\x1b[34m', format('INFO'));
|
||||||
}
|
}
|
||||||
if (level <= LEVELS.debug) {
|
if (level <= LEVELS.debug) {
|
||||||
logger.debug = console.log.bind(console, '\x1b[32m', format('DEBUG'));
|
logger.debug = console.debug
|
||||||
|
? console.debug.bind(console, format('DEBUG'), 'color: lightgreen')
|
||||||
|
: console.log.bind(console, '\x1b[32m', format('DEBUG'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const format = level => {
|
const format = level => {
|
||||||
const time = moment().format('HH:mm:ss.SSS');
|
const time = moment().format('ss.SSS');
|
||||||
return `${time} : ${level} : `;
|
return `%c${time} : ${level} : `;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ const init = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const initialize = function(config) {
|
const initialize = function(config) {
|
||||||
logger.debug('Initializing mermaid ');
|
|
||||||
if (typeof config.mermaid !== 'undefined') {
|
if (typeof config.mermaid !== 'undefined') {
|
||||||
if (typeof config.mermaid.startOnLoad !== 'undefined') {
|
if (typeof config.mermaid.startOnLoad !== 'undefined') {
|
||||||
mermaid.startOnLoad = config.mermaid.startOnLoad;
|
mermaid.startOnLoad = config.mermaid.startOnLoad;
|
||||||
@@ -126,6 +125,7 @@ const initialize = function(config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mermaidAPI.initialize(config);
|
mermaidAPI.initialize(config);
|
||||||
|
logger.debug('Initializing mermaid ');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
.grid .tick {
|
.grid .tick {
|
||||||
stroke: $gridColor;
|
stroke: $gridColor;
|
||||||
opacity: 0.3;
|
opacity: 0.8;
|
||||||
shape-rendering: crispEdges;
|
shape-rendering: crispEdges;
|
||||||
text {
|
text {
|
||||||
font-family: 'trebuchet ms', verdana, arial;
|
font-family: 'trebuchet ms', verdana, arial;
|
||||||
|
|||||||
Reference in New Issue
Block a user