Merge branch 'master' into develop

This commit is contained in:
Knut Sveidqvist
2022-02-17 18:25:00 +01:00
33 changed files with 907 additions and 261 deletions

View File

@@ -215,6 +215,14 @@ Mermaid is a growing community and is always accepting new contributors. There's
Detailed information about how to contribute can be found in the [contribution guide](CONTRIBUTING.md)
## Security and safe diagrams
For public sites, it can be precarious to retrieve text from users on the internet, storing that content for presentation in a browser at a later stage. The reason is that the user content can contain embedded malicious scripts that will run when the data is presented. For Mermaid this is a risk, specially as mermaid diagrams contain many characters that are used in html which makes the standard sanitation unusable as it also breaks the diagrams. We still make an effort to sanitise the incoming code and keep refining the process but it is hard to guarantee that there are no loop holes.
As an extra level of security for sites with external users we are happy to introduce a new security level in which the diagram is rendered in a sandboxed iframe preventing javascript in the code from being executed. This is a great step forward for better security.
*Unfortunately you can not have a cake and eat it at the same time which in this case means that some of the interactive functionality gets blocked along with the possible malicious code.*
## Reporting vulnerabilities
To report a vulnerability, please e-mail security@mermaid.live with a description of the issue, the steps you took to create the issue, affected versions, and if known, mitigations for the issue.

View File

@@ -0,0 +1,170 @@
<!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="mermaid">
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="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">
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>
<div style="display: flex">
<div id="FirstLine" class="mermaid">
graph TB
FunctionArg-->URL
click FunctionArg call clickByFlowArg(ARGUMENT) "Add a div"
click URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>"
</div>
<div id="FirstLine" class="mermaid">
flowchart TB
FunctionArg-->URL
click FunctionArg call clickByFlowArg(ARGUMENT) "Add a div"
click URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>"
</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
click ShapeCallback call clickByClass(123) "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
click ShapeCallback2 call clickByClass(123) "This is a tooltip for a callback"
</div>
</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 clickByFlowArg(argument) {
const div = document.createElement('div');
div.className = 'created-by-click-2';
div.style = 'padding: 20px; background: green; color: white;';
div.innerText = 'Clicked By Flow: ' + argument;
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(arg) {
const div = document.createElement('div');
div.className = 'created-by-class-click';
div.style = 'padding: 20px; background: purple; color: white;';
div.innerText = 'Clicked By Class' + (arg ? arg : '');
document.getElementsByTagName('body')[0].appendChild(div);
}
mermaid.initialize({ startOnLoad: true, securityLevel: 'sandbox', logLevel: 1 });
</script>
</body>
</html>

View File

@@ -14,6 +14,7 @@
font-family: 'Arial';
/* font-size: 18px !important; */
width: 100%;
display: flex;
}
h1 { color: grey;}
.mermaid2,.mermaid3 {
@@ -25,15 +26,214 @@
</style>
</head>
<body>
<div>info below</div>
<div class="flex flex-wrap">
<div class="mermaid2" style="width: 50%;">
pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15
</div>
<div class="mermaid2" style="width: 100%;">
gantt
title Adding GANTT diagram functionality to mermaid
excludes :excludes the named dates/days from being included in a charted task..
section Screening
Lexplore :active, des1, 2023-01-06,2023-01-08
H4 :active, des2, 2024-01-09, 3d
Future task : des3, after des2, 5d
Future task2 : des4, after des3, 5d
<div class="mermaid" style="width: 100%; height: 20%;">
stateDiagram-v2
</div>
<div class="mermaid2" style="width: 100%;">
info
</div>
<div class="mermaid2" style="width: 100%;">
gitGraph:
options
{
"nodeSpacing": 150,
"nodeRadius": 10
}
end
commit
branch newbranch
checkout newbranch
commit
commit
checkout master
commit
commit
merge newbranch
</div>
<div class="mermaid" style="width: 50%;">
sequenceDiagram
participant a as Alice
participant j as John
note right of a: Hello world!
properties a: {"class": "internal-service-actor", "type": "@clock"}
properties j: {"class": "external-service-actor", "type": "@computer"}
links a: {"Repo": "https://www.contoso.com/repo", "Swagger": "https://www.contoso.com/swagger"}
links j: {"Repo": "https://www.contoso.com/repo"}
links a: {"Dashboard": "https://www.contoso.com/dashboard", "On-Call": "https://www.contoso.com/oncall"}
link a: Contacts @ https://contacts.contoso.com/?contact=alice@contoso.com
a->>j: Hello John, how are you?
j-->>a: Great! </div>
<div class="mermaid2" style="width: 100%;">
journey
title My working day
section Go to work
Make tea: 5: Me
Go upstairs: 3: Me
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 5: Me
</div>
<div class="mermaid2" style="width: 100%;">
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
functionalRequirement test_req2 {
id: 1.1
text: the second test text.
risk: low
verifymethod: inspection
}
performanceRequirement test_req3 {
id: 1.2
text: the third test text.
risk: medium
verifymethod: demonstration
}
element test_entity {
type: simulation
}
element test_entity2 {
type: word doc
docRef: reqs/test_entity
}
test_entity - satisfies -> test_req2
test_req - traces -> test_req2
test_req - contains -> test_req3
test_req <- copies - test_entity2
</div>
<div class="mermaid2" style="width: 100%;">
erDiagram
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
CUSTOMER ||--o{ ORDER : places
CUSTOMER ||--o{ INVOICE : "liable for"
DELIVERY-ADDRESS ||--o{ ORDER : receives
INVOICE ||--|{ ORDER : covers
ORDER ||--|{ ORDER-ITEM : includes
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
</div>
<div class="mermaid2" style="width: 100%;">
graph TB
subgraph One
a1-->a2-->a3
end
</div>
<div class="mermaid" style="width: 50%;">
flowchart LR
Function-->URL-->A-->B-->C
click Function clickByFlow "Add a div"
click URL "https://mermaid-js.github.io/mermaid/#/" "Visit <strong>mermaid docs</strong>" _blank
</div>
<div class="mermaid2" style="width: 100%;">
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 "https://mermaid-js.github.io/mermaid/#/"
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>
<div class="mermaid2" style="width: 100%;">
classDiagram
Class01 <|-- AveryLongClass : Cool
Class09 --> C2 : Where am i?
Class09 --* C3
Class09 --|> Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
class Class10 {
int id
size()
}
</div>
<div class="mermaid2" style="width: 100%;">
stateDiagram
[*] --> S1
state "Some long name" as S1
</div>
</div>
<div class="mermaid2" style="width: 100%;">
classDiagram
Animal "1" <|-- Duck
Animal <|-- Fish
Animal <--o Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}
</div>
<script src="./mermaid.js"></script>
<script>
@@ -42,29 +242,52 @@ stateDiagram-v2
};
mermaid.initialize({
// theme: 'dark',
theme: 'forest',
arrowMarkerAbsolute: true,
// theme: 'forest',
// arrowMarkerAbsolute: true,
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
flowchart: { nodeSpacing: 10, curve: 'cardinal', htmlLabels: false },
flowchart: {
nodeSpacing: 10,
curve: 'cardinal',
htmlLabels: true,
defaultRenderer: 'dagre-d3',
},
class: {
defaultRenderer: 'dagre-d3',
htmlLabels: true,
},
// gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
// sequenceDiagram: { actorMargin: 300 } // deprecated
sequence: {
actorFontFamily: 'courier',
actorMargin: 50,
showSequenceNumbers: false,
// forceMenus: true,
},
// sequenceDiagram: { actorMargin: 300, forceMenus: false }, // deprecated
// fontFamily: '"times", sans-serif',
// fontFamily: 'courier',
state: {
nodeSpacing: 50,
rankSpacing: 50,
defaultRenderer: 'dagre-wrapper',
defaultRenderer: 'dagre-d3',
},
logLevel: 0,
fontSize: 18,
curve: 'cardinal',
securityLevel: 'strict',
// securityLevel: 'sandbox',
// themeVariables: {relationLabelColor: 'red'}
});
function callback() {
alert('It worked');
}
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);
}
</script>
</body>
</html>

View File

@@ -12,9 +12,11 @@
<script src="./mermaid.js"></script>
<script>mermaid.init({ startOnLoad: false });
mermaid.mermaidAPI.initialize();
mermaid.mermaidAPI.initialize({ securityLevel: 'strict' });
try {
console.log('rendering');
mermaid.mermaidAPI.render('graphDiv', `>`);
} catch (e) {}

View File

@@ -70,7 +70,7 @@
// fontFamily: 'courier',
fontSize: 18,
curve: 'basis',
// securityLevel: 'loose',
securityLevel: 'sandbox',
startOnLoad: false,
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
// themeVariables: {relationLabelColor: 'red'}

View File

@@ -197,7 +197,8 @@ To Deploy Mermaid:
```html
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad:true});</script>
<script>mermaid.initialize({startOnLoad:true});
</script>
```
**Doing so will command the mermaid parser to look for the `<div>` tags with `class="mermaid"`. From these tags mermaid will try to read the diagram/chart definitions and render them into svg charts.**
@@ -267,6 +268,15 @@ npm publish
The above command generates files into the `dist` folder and publishes them to npmjs.org.
## Security and safe diagrams
For public sites, it can be precarious to retrieve text from users on the internet, storing that content for presentation in a browser at a later stage. The reason is that the user content can contain embedded malicious scripts that will run when the data is presented. For Mermaid this is a risk, specially as mermaid diagrams contain many characters that are used in html which makes the standard sanitation unusable as it also breaks the diagrams. We still make an effort to sanitise the incoming code and keep refining the process but it is hard to guarantee that there are no loop holes.
As an extra level of security for sites with external users we are happy to introduce a new security level in which the diagram is rendered in a sandboxed iframe preventing javascript in the code from being executed. This is a great step forward for better security.
*Unfortunately you can not have a cake and eat it at the same time which in this case means that some of the interactive functionality gets blocked along with the possible malicious code.*
## Credits
Many thanks to the [d3](http://d3js.org/) and [dagre-d3](https://github.com/cpettitt/dagre-d3) projects for providing the graphical layout and drawing libraries!

View File

@@ -1057,6 +1057,10 @@ Returns **any**
- `conf` **any**
## reinitialize
To be removed
## initialize
### Parameters

View File

@@ -255,7 +255,7 @@ graph TB
B --> D
```
### Beta: New arrow types
### New arrow types
When using flowchart instead of graph there are new types of arrows supported as per below:
@@ -266,7 +266,7 @@ flowchart LR
```
### Beta: Multi directional arrows
### Multi directional arrows
When using flowchart instead of graph there is the possibility to use multidirectional arrows.
@@ -377,9 +377,9 @@ graph TB
end
```
## Beta: flowcharts
## flowcharts
With the graphtype flowcharts it is also possible to set edges to and from subgraphs as in the graph below.
With the graphtype flowchart it is also possible to set edges to and from subgraphs as in the graph below.
```mermaid-example
flowchart TB

View File

@@ -281,11 +281,11 @@ to node _E_, so that it spans two more ranks than regular links:
```mermaid-example
flowchart TD
A[Start] --> B{Is it?};
B -->|Yes| C[OK];
C --> D[Rethink];
D --> B;
B ---->|No| E[End];
A[Start] --> B{Is it?}
B -->|Yes| C[OK]
C --> D[Rethink]
D --> B
B ---->|No| E[End]
```
> **Note** Links may still be made longer than the requested number of ranks
@@ -297,11 +297,11 @@ the previous one:
```mermaid-example
flowchart TD
A[Start] --> B{Is it?};
B -- Yes --> C[OK];
C --> D[Rethink];
D --> B;
B -- No ----> E[End];
A[Start] --> B{Is it?}
B -- Yes --> C[OK]
C --> D[Rethink]
D --> B
B -- No ----> E[End]
```
For dotted or thick links, the characters to add are equals signs or dots,
@@ -370,9 +370,9 @@ flowchart TB
end
```
## Beta: flowcharts
## flowcharts
With the graphtype flowcharts it is also possible to set edges to and from subgraphs as in the flowchart below.
With the graphtype flowchart it is also possible to set edges to and from subgraphs as in the flowchart below.
```mermaid-example
flowchart TB
@@ -437,10 +437,10 @@ Examples of tooltip usage below:
The tooltip text is surrounded in double quotes. The styles of the tooltip are set by the class `.mermaidTooltip`.
```mermaid-example
flowchart LR;
A-->B;
B-->C;
C-->D;
flowchart LR
A-->B
B-->C
C-->D
click A callback "Tooltip for a callback"
click B "http://www.github.com" "This is a tooltip for a link"
click A call callback() "Tooltip for a callback"
@@ -453,11 +453,11 @@ flowchart LR;
Links are opened in the same browser tab/window by default. It is possible to change this by adding a link target to the click definition (`_self`, `_blank`, `_parent` and `_top` are supported):
```mermaid-example
flowchart LR;
A-->B;
B-->C;
C-->D;
D-->E;
flowchart LR
A-->B
B-->C
C-->D
D-->E
click A "http://www.github.com" _blank
click B "http://www.github.com" "Open this in a new tab" _blank
click C href "http://www.github.com" _blank
@@ -468,10 +468,10 @@ Beginners tip, a full example using interactive links in a html context:
```html
<body>
<div class="mermaid">
flowchart LR;
A-->B;
B-->C;
C-->D;
flowchart LR
A-->B
B-->C
C-->D
click A callback "Tooltip"
click B "http://www.github.com" "This is a link"
click C call callback() "Tooltip"
@@ -587,9 +587,9 @@ below:
```mermaid-example
flowchart LR;
A-->B[AAA<span>BBB</span>];
B-->D;
class A cssClass;
A-->B[AAA<span>BBB</span>]
B-->D
class A cssClass
```
@@ -613,7 +613,7 @@ flowchart TD
B["fa:fa-twitter for peace"]
B-->C[fa:fa-ban forbidden]
B-->D(fa:fa-spinner);
B-->E(A fa:fa-camera-retro perhaps?);
B-->E(A fa:fa-camera-retro perhaps?)
```

View File

@@ -50,7 +50,8 @@ The easiest way to integrate mermaid on a web page requires three elements:
2. The `mermaidAPI` call, in a separate `script` tag. Example:
```html
<script>mermaid.initialize({startOnLoad:true});</script>
<script>mermaid.initialize({startOnLoad:true});
</script>
```
3. A graph definition, inside `<div>` tags labeled `class=mermaid`. Example:
@@ -82,7 +83,8 @@ locate the graph definitions inside the `div` tags with `class="mermaid"` and re
B-->D(fa:fa-spinner);
</div>
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad:true});</script>
<script>mermaid.initialize({startOnLoad:true});
</script>
</body>
</html>
```
@@ -105,17 +107,19 @@ Mermaid can load multiple diagrams, in the same page.
| Parameter | Description | Type | Required | Values |
| ------------- | --------------------------------- | ------ | -------- | ------------------------- |
| securityLevel | Level of trust for parsed diagram | String | Required | Strict, Loose, antiscript |
| securityLevel | Level of trust for parsed diagram | String | Required | Strict, Loose, antiscript , sandbox|
Values:
- **strict**: (**default**) tags in text are encoded, click functionality is disabled
- **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
- **sandbox**: With this security level all rendering takes place in a sandboxed iframe. This prevent any javascript running in the context. This may hinder interactive functionality of the diagram like scripts, popups in sequence diagram or links to other tabs/targets etc.
```note
This changes the default behaviour of mermaid so that after upgrade to 8.2,unless the `securityLevel` is not changed, tags in flowcharts are encoded as tags and clicking is disabled.
**sandbox** security level is still in the beta version.
```
**If you are taking responsibility for the diagram source security you can set the `securityLevel` to a value of your choosing . This allows clicks and tags are allowed.**
@@ -203,20 +207,10 @@ The example below show an outline of how this could be used. The example just lo
<script src="mermaid.js"></script>
<script>
mermaid.mermaidAPI.initialize({
startOnLoad:false
});
$(function(){
// Example of using the API
var element = document.querySelector("#graphDiv");
var insertSvg = function(svgCode, bindFunctions){
element.innerHTML = svgCode;
};
var graphDefinition = 'graph TB\na-->b';
var graph = mermaid.mermaidAPI.render('graphDiv', graphDefinition, insertSvg);
});
mermaid.mermaidAPI.initialize({ startOnLoad:false }); $(function(){ // Example of using the API var
element = document.querySelector("#graphDiv"); var insertSvg = function(svgCode, bindFunctions){
element.innerHTML = svgCode; }; var graphDefinition = 'graph TB\na-->b'; var graph =
mermaid.mermaidAPI.render('graphDiv', graphDefinition, insertSvg); });
</script>
```
@@ -345,13 +339,7 @@ on what kind of integration you use.
```html
<script src="../dist/mermaid.js"></script>
<script>
var config = {
startOnLoad:true,
flowchart:{
useMaxWidth:false,
htmlLabels:true
}
};
var config = { startOnLoad:true, flowchart:{ useMaxWidth:false, htmlLabels:true } };
mermaid.initialize(config);
</script>
```

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "8.13.10",
"version": "8.14.0",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "dist/mermaid.core.js",
"module": "dist/mermaid.esm.min.mjs",

View File

@@ -6,7 +6,9 @@ import intersect from './intersect/index.js';
import createLabel from './createLabel';
import note from './shapes/note';
import { parseMember } from '../diagrams/class/svgDraw';
import { evaluate } from '../diagrams/common/common';
import { evaluate, sanitizeText as sanitize } from '../diagrams/common/common';
const sanitizeText = (txt) => sanitize(txt, getConfig());
const question = (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
@@ -967,10 +969,13 @@ export const insertNode = (elem, node, dir) => {
// Add link when appropriate
if (node.link) {
newEl = elem
.insert('svg:a')
.attr('xlink:href', node.link)
.attr('target', node.linkTarget || '_blank');
let target;
if (getConfig().securityLevel === 'sandbox') {
target = '_top';
} else if (node.linkTarget) {
target = node.linkTarget || '_blank';
}
newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target);
el = shapes[node.shape](newEl, node, dir);
} else {
el = shapes[node.shape](elem, node, dir);

View File

@@ -143,6 +143,7 @@ export const addMember = function (className, member) {
if (memberString.startsWith('<<') && memberString.endsWith('>>')) {
// Remove leading and trailing brackets
// theClass.annotations.push(memberString.substring(2, memberString.length - 2));
theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2)));
} else if (memberString.indexOf(')') > 0) {
theClass.methods.push(sanitizeText(memberString));
@@ -212,8 +213,10 @@ export const setLink = function (ids, linkStr, target) {
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
if (typeof classes[id] !== 'undefined') {
classes[id].link = utils.formatUrl(linkStr, config);
if (typeof target === 'string') {
classes[id].linkTarget = target;
if (config.securityLevel === 'sandbox') {
classes[id].linkTarget = '_top';
} else if (typeof target === 'string') {
classes[id].linkTarget = sanitizeText(target);
} else {
classes[id].linkTarget = '_blank';
}

View File

@@ -269,101 +269,8 @@ export const setConf = function (cnf) {
* @param {string} text
* @param {string} id
*/
export const drawOld = function (text, id) {
idCache = {};
parser.yy.clear();
parser.parse(text);
log.info('Rendering diagram ' + text);
// Fetch the default direction, use TD if none was found
const diagram = select(`[id='${id}']`);
// insertMarkers(diagram);
// Layout graph, Create a new directed graph
const g = new graphlib.Graph({
multigraph: true,
});
// Set an object for the graph label
g.setGraph({
isMultiGraph: true,
});
// Default to assigning a new object as a label for each new edge.
g.setDefaultEdgeLabel(function () {
return {};
});
const classes = classDb.getClasses();
log.info('classes:');
log.info(classes);
const keys = Object.keys(classes);
for (let i = 0; i < keys.length; i++) {
const classDef = classes[keys[i]];
const node = svgDraw.drawClass(diagram, classDef, conf);
idCache[node.id] = node;
// Add nodes to the graph. The first argument is the node id. The second is
// metadata about the node. In this case we're going to add labels to each of
// our nodes.
g.setNode(node.id, node);
log.info('Org height: ' + node.height);
}
const relations = classDb.getRelations();
log.info('relations:', relations);
relations.forEach(function (relation) {
log.info(
'tjoho' + getGraphId(relation.id1) + getGraphId(relation.id2) + JSON.stringify(relation)
);
g.setEdge(
getGraphId(relation.id1),
getGraphId(relation.id2),
{
relation: relation,
},
relation.title || 'DEFAULT'
);
});
dagre.layout(g);
g.nodes().forEach(function (v) {
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
log.debug('Node ' + v + ': ' + JSON.stringify(g.node(v)));
select('#' + lookUpDomId(v)).attr(
'transform',
'translate(' +
(g.node(v).x - g.node(v).width / 2) +
',' +
(g.node(v).y - g.node(v).height / 2) +
' )'
);
}
});
g.edges().forEach(function (e) {
if (typeof e !== 'undefined' && typeof g.edge(e) !== 'undefined') {
log.debug('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e)));
svgDraw.drawEdge(diagram, g.edge(e), g.edge(e).relation, conf);
}
});
const svgBounds = diagram.node().getBBox();
const width = svgBounds.width + padding * 2;
const height = svgBounds.height + padding * 2;
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}`;
log.debug(`viewBox ${vBox}`);
diagram.attr('viewBox', vBox);
};
export const draw = function (text, id) {
log.info('Drawing class');
log.info('Drawing class - ', id);
classDb.clear();
// const parser = classDb.parser;
// parser.yy = classDb;
@@ -379,6 +286,7 @@ export const draw = function (text, id) {
//let dir = 'TD';
const conf = getConfig().flowchart;
const securityLevel = getConfig().securityLevel;
log.info('config:', conf);
const nodeSpacing = conf.nodeSpacing || 50;
const rankSpacing = conf.rankSpacing || 50;
@@ -430,11 +338,19 @@ export const draw = function (text, id) {
// flowChartShapes.addToRenderV2(addShape);
// Set up an SVG group so that we can translate the final graph.
const svg = select(`[id="${id}"]`);
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const svg = root.select(`[id="${id}"]`);
svg.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
// Run the renderer. This is what draws the final graph.
const element = select('#' + id + ' g');
const element = root.select('#' + id + ' g');
render(element, g, ['aggregation', 'extension', 'composition', 'dependency'], 'classDiagram', id);
// element.selectAll('g.node').attr('title', function() {
@@ -462,14 +378,15 @@ export const draw = function (text, id) {
// Add label rects for non html labels
if (!conf.htmlLabels) {
const labels = document.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
for (let k = 0; k < labels.length; k++) {
const label = labels[k];
// Get dimensions of label
const dim = label.getBBox();
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
const rect = doc.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('rx', 0);
rect.setAttribute('ry', 0);
rect.setAttribute('width', dim.width);

View File

@@ -6,6 +6,7 @@ import classDb, { lookUpDomId } from './classDb';
import { parser } from './parser/classDiagram';
import svgDraw from './svgDraw';
import { configureSvgSize } from '../../utils';
import { getConfig } from '../../config';
parser.yy = classDb;
@@ -165,8 +166,20 @@ export const draw = function (text, id) {
log.info('Rendering diagram ' + text);
const securityLevel = getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Fetch the default direction, use TD if none was found
const diagram = select(`[id='${id}']`);
const diagram = root.select(`[id='${id}']`);
diagram.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
insertMarkers(diagram);
@@ -220,14 +233,16 @@ export const draw = function (text, id) {
g.nodes().forEach(function (v) {
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
log.debug('Node ' + v + ': ' + JSON.stringify(g.node(v)));
select('#' + lookUpDomId(v)).attr(
'transform',
'translate(' +
(g.node(v).x - g.node(v).width / 2) +
',' +
(g.node(v).y - g.node(v).height / 2) +
' )'
);
root
.select('#' + lookUpDomId(v))
.attr(
'transform',
'translate(' +
(g.node(v).x - g.node(v).width / 2) +
',' +
(g.node(v).y - g.node(v).height / 2) +
' )'
);
}
});

View File

@@ -545,6 +545,17 @@ export const draw = function (text, id) {
erDb.clear();
const parser = erParser.parser;
parser.yy = erDb;
const securityLevel = getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Parse the text to populate erDb
try {
@@ -554,7 +565,7 @@ export const draw = function (text, id) {
}
// Get a reference to the svg node that contains the text
const svg = select(`[id='${id}']`);
const svg = root.select(`[id='${id}']`);
// Add cardinality marker definitions to the svg
erMarkers.insertMarkers(svg, conf);

View File

@@ -25,9 +25,11 @@ export const setConf = function (cnf) {
* @param vert Object containing the vertices.
* @param g The graph that is to be drawn.
* @param svgId
* @param root
* @param doc
*/
export const addVertices = function (vert, g, svgId) {
const svg = select(`[id="${svgId}"]`);
export const addVertices = function (vert, g, svgId, root, doc) {
const svg = root.select(`[id="${svgId}"]`);
const keys = Object.keys(vert);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
@@ -62,13 +64,13 @@ export const addVertices = function (vert, g, svgId) {
vertexNode = addHtmlLabel(svg, node).node();
vertexNode.parentNode.removeChild(vertexNode);
} else {
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
const rows = vertexText.split(common.lineBreakRegex);
for (let j = 0; j < rows.length; j++) {
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
tspan.setAttribute('dy', '1em');
tspan.setAttribute('x', '1');
@@ -374,6 +376,18 @@ export const draw = function (text, id) {
const nodeSpacing = conf.nodeSpacing || 50;
const rankSpacing = conf.rankSpacing || 50;
const securityLevel = getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Create the input mermaid.graph
const g = new graphlib.Graph({
multigraph: true,
@@ -417,18 +431,18 @@ export const draw = function (text, id) {
g.setParent(subG.nodes[j], subG.id);
}
}
addVertices(vert, g, id);
addVertices(vert, g, id, root, doc);
addEdges(edges, g);
// Add custom shapes
// flowChartShapes.addToRenderV2(addShape);
// Set up an SVG group so that we can translate the final graph.
const svg = select(`[id="${id}"]`);
const svg = root.select(`[id="${id}"]`);
svg.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
// Run the renderer. This is what draws the final graph.
const element = select('#' + id + ' g');
const element = root.select('#' + id + ' g');
render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
const padding = conf.diagramPadding;
@@ -452,14 +466,14 @@ export const draw = function (text, id) {
// Add label rects for non html labels
if (!conf.htmlLabels) {
const labels = document.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
for (let k = 0; k < labels.length; k++) {
const label = labels[k];
// Get dimensions of label
const dim = label.getBBox();
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
const rect = doc.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('rx', 0);
rect.setAttribute('ry', 0);
rect.setAttribute('width', dim.width);
@@ -478,11 +492,13 @@ export const draw = function (text, id) {
if (vertex.link) {
const node = select('#' + id + ' [id="' + key + '"]');
if (node) {
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
const link = doc.createElementNS('http://www.w3.org/2000/svg', 'a');
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
if (vertex.linkTarget) {
if (securityLevel === 'sandbox') {
link.setAttributeNS('http://www.w3.org/2000/svg', 'target', '_top');
} else if (vertex.linkTarget) {
link.setAttributeNS('http://www.w3.org/2000/svg', 'target', vertex.linkTarget);
}

View File

@@ -26,9 +26,15 @@ export const setConf = function (cnf) {
* @param vert Object containing the vertices.
* @param g The graph that is to be drawn.
* @param svgId
* @param root
* @param doc
* @param _doc
*/
export const addVertices = function (vert, g, svgId) {
const svg = select(`[id="${svgId}"]`);
export const addVertices = function (vert, g, svgId, root, _doc) {
const securityLevel = getConfig().securityLevel;
const svg = !root ? select(`[id="${svgId}"]`) : root.select(`[id="${svgId}"]`);
const doc = !_doc ? document : _doc;
const keys = Object.keys(vert);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
@@ -63,13 +69,13 @@ export const addVertices = function (vert, g, svgId) {
vertexNode = addHtmlLabel(svg, node).node();
vertexNode.parentNode.removeChild(vertexNode);
} else {
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
const rows = vertexText.split(common.lineBreakRegex);
for (let j = 0; j < rows.length; j++) {
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
tspan.setAttribute('dy', '1em');
tspan.setAttribute('x', '1');
@@ -293,6 +299,17 @@ export const draw = function (text, id) {
const parser = flow.parser;
parser.yy = flowDb;
const securityLevel = getConfig().securityLevel;
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Parse the graph definition
// try {
parser.parse(text);
@@ -355,7 +372,7 @@ export const draw = function (text, id) {
g.setParent(flowDb.lookUpDomId(subG.nodes[j]), flowDb.lookUpDomId(subG.id));
}
}
addVertices(vert, g, id);
addVertices(vert, g, id, root, doc);
addEdges(edges, g);
// Create the renderer
@@ -404,13 +421,13 @@ export const draw = function (text, id) {
};
// Set up an SVG group so that we can translate the final graph.
const svg = select(`[id="${id}"]`);
const svg = root.select(`[id="${id}"]`);
svg.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
log.warn(g);
// Run the renderer. This is what draws the final graph.
const element = select('#' + id + ' g');
const element = root.select('#' + id + ' g');
render(element, g);
element.selectAll('g.node').attr('title', function () {
@@ -436,10 +453,10 @@ export const draw = function (text, id) {
for (i = 0; i < subGraphs.length; i++) {
subG = subGraphs[i];
if (subG.title !== 'undefined') {
const clusterRects = document.querySelectorAll(
const clusterRects = doc.querySelectorAll(
'#' + id + ' [id="' + flowDb.lookUpDomId(subG.id) + '"] rect'
);
const clusterEl = document.querySelectorAll(
const clusterEl = doc.querySelectorAll(
'#' + id + ' [id="' + flowDb.lookUpDomId(subG.id) + '"]'
);
@@ -459,14 +476,14 @@ export const draw = function (text, id) {
// Add label rects for non html labels
if (!evaluate(conf.htmlLabels) || true) { // eslint-disable-line
const labels = document.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
for (let k = 0; k < labels.length; k++) {
const label = labels[k];
// Get dimensions of label
const dim = label.getBBox();
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
const rect = doc.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('rx', 0);
rect.setAttribute('ry', 0);
rect.setAttribute('width', dim.width);
@@ -483,13 +500,15 @@ export const draw = function (text, id) {
const vertex = vert[key];
if (vertex.link) {
const node = select('#' + id + ' [id="' + flowDb.lookUpDomId(key) + '"]');
const node = root.select('#' + id + ' [id="' + flowDb.lookUpDomId(key) + '"]');
if (node) {
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
const link = doc.createElementNS('http://www.w3.org/2000/svg', 'a');
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
if (vertex.linkTarget) {
if (securityLevel === 'sandbox') {
link.setAttributeNS('http://www.w3.org/2000/svg', 'target', '_top');
} else if (vertex.linkTarget) {
link.setAttributeNS('http://www.w3.org/2000/svg', 'target', vertex.linkTarget);
}

View File

@@ -10,6 +10,7 @@ let axisFormat = '';
let todayMarker = '';
let includes = [];
let excludes = [];
let links = {};
let title = '';
let sections = [];
let tasks = [];
@@ -44,6 +45,7 @@ export const clear = function () {
inclusiveEndDates = false;
topAxis = false;
lastOrder = 0;
links = {};
};
export const setAxisFormat = function (txt) {
@@ -101,6 +103,10 @@ export const getExcludes = function () {
return excludes;
};
export const getLinks = function () {
return links;
};
export const setTitle = function (txt) {
title = txt;
};
@@ -505,6 +511,7 @@ export const setLink = function (ids, _linkStr) {
pushFun(id, () => {
window.open(linkStr, '_self');
});
links[id] = linkStr;
}
});
setClass(ids, 'clickable');
@@ -642,6 +649,7 @@ export default {
getExcludes,
setClickEvent,
setLink,
getLinks,
bindFunctions,
durationToDate,
isInvalidDate,

View File

@@ -29,7 +29,19 @@ export const draw = function (text, id) {
parser.yy.clear();
parser.parse(text);
const elem = document.getElementById(id);
const securityLevel = getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const elem = doc.getElementById(id);
w = elem.parentElement.offsetWidth;
if (typeof w === 'undefined') {
@@ -47,7 +59,7 @@ export const draw = function (text, id) {
// Set viewBox
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
const svg = select(`[id="${id}"]`);
const svg = root.select(`[id="${id}"]`);
// Set timescale
const timeScale = scaleTime()
@@ -173,6 +185,10 @@ export const draw = function (text, id) {
// Draw the rects representing the tasks
const rectangles = svg.append('g').selectAll('rect').data(theArray).enter();
const links = ganttDb.getLinks();
// Render the tasks with links
// Render the other tasks
rectangles
.append('rect')
.attr('id', function (d) {
@@ -369,6 +385,32 @@ export const draw = function (text, id) {
return classStr + ' taskText taskText' + secNum + ' ' + taskType + ' width-' + textWidth;
}
});
const securityLevel = getConfig().securityLevel;
// Wrap the tasks in an a tag for working links without javascript
if (securityLevel === 'sandbox') {
let sandboxElement;
sandboxElement = select('#i' + id);
const root = select(sandboxElement.nodes()[0].contentDocument.body);
const doc = sandboxElement.nodes()[0].contentDocument;
rectangles
.filter(function (d) {
return typeof links[d.id] !== 'undefined';
})
.each(function (o) {
var taskRect = doc.querySelector('#' + o.id);
var taskText = doc.querySelector('#' + o.id + '-text');
const oldParent = taskRect.parentNode;
var Link = doc.createElement('a');
Link.setAttribute('xlink:href', links[o.id]);
Link.setAttribute('target', '_top');
oldParent.appendChild(Link);
Link.appendChild(taskRect);
Link.appendChild(taskText);
});
}
}
/**
* @param theGap
@@ -505,11 +547,11 @@ export const draw = function (text, id) {
const rows = d[0].split(common.lineBreakRegex);
const dy = -(rows.length - 1) / 2;
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
svgLabel.setAttribute('dy', dy + 'em');
for (let j = 0; j < rows.length; j++) {
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttribute('alignment-baseline', 'central');
tspan.setAttribute('x', '10');
if (j > 0) tspan.setAttribute('dy', '1em');

View File

@@ -4,6 +4,7 @@ import db from './gitGraphAst';
import gitGraphParser from './parser/gitGraph';
import { log } from '../../logger';
import { interpolateToCurve } from '../../utils';
import { getConfig } from '../../config';
let allCommitsDict = {};
let branchNum;
@@ -338,6 +339,18 @@ export const draw = function (txt, id, ver) {
parser.yy = db;
parser.yy.clear();
const securityLevel = getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
log.debug('in gitgraph renderer', txt + '\n', 'id:', id, ver);
// Parse the graph definition
parser.parse(txt + '\n');
@@ -352,7 +365,7 @@ export const draw = function (txt, id, ver) {
config.nodeLabel.width = '100%';
config.nodeLabel.y = -1 * 2 * config.nodeRadius;
}
const svg = select(`[id="${id}"]`);
const svg = root.select(`[id="${id}"]`);
svgCreateDefs(svg);
branchNum = 1;
for (let branch in branches) {

View File

@@ -3,6 +3,7 @@ import { select } from 'd3';
import db from './infoDb';
import infoParser from './parser/info';
import { log } from '../../logger';
import { getConfig } from '../../config';
const conf = {};
export const setConf = function (cnf) {
@@ -25,11 +26,24 @@ export const draw = (text, id, version) => {
const parser = infoParser.parser;
parser.yy = db;
log.debug('Renering info diagram\n' + text);
const securityLevel = getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Parse the graph definition
parser.parse(text);
log.debug('Parsed info diagram');
// Fetch the default direction, use TD if none was found
const svg = select('#' + id);
const svg = root.select('#' + id);
const g = svg.append('g');

View File

@@ -22,11 +22,24 @@ export const draw = (txt, id) => {
const parser = pieParser.parser;
parser.yy = pieData;
log.debug('Rendering info diagram\n' + txt);
const securityLevel = configApi.getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Parse the Pie Chart definition
parser.yy.clear();
parser.parse(txt);
log.debug('Parsed info diagram');
const elem = document.getElementById(id);
const elem = doc.getElementById(id);
width = elem.parentElement.offsetWidth;
if (typeof width === 'undefined') {
@@ -40,7 +53,7 @@ export const draw = (txt, id) => {
width = conf.pie.useWidth;
}
const diagram = select('#' + id);
const diagram = root.select('#' + id);
configureSvgSize(diagram, height, width, conf.pie.useMaxWidth);
// Set viewBox

View File

@@ -8,6 +8,7 @@ import common from '../common/common';
import { parser } from './parser/requirementDiagram';
import requirementDb from './requirementDb';
import markers from './requirementMarkers';
import { getConfig } from '../../config';
const conf = {};
let relCnt = 0;
@@ -321,7 +322,19 @@ export const draw = (text, id) => {
parser.yy.clear();
parser.parse(text);
const svg = select(`[id='${id}']`);
const securityLevel = getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const svg = root.select(`[id='${id}']`);
markers.insertLineEndings(svg, conf);
const g = new graphlib.Graph({

View File

@@ -452,13 +452,20 @@ export const drawActors = function (diagram, actors, actorKeys, verticalPos) {
bounds.bumpVerticalPos(maxHeight);
};
export const drawActorsPopup = function (diagram, actors, actorKeys) {
export const drawActorsPopup = function (diagram, actors, actorKeys, doc) {
var maxHeight = 0;
var maxWidth = 0;
for (let i = 0; i < actorKeys.length; i++) {
const actor = actors[actorKeys[i]];
const minMenuWidth = getRequiredPopupWidth(actor);
var menuDimensions = svgDraw.drawPopup(diagram, actor, minMenuWidth, conf, conf.forceMenus);
var menuDimensions = svgDraw.drawPopup(
diagram,
actor,
minMenuWidth,
conf,
conf.forceMenus,
doc
);
if (menuDimensions.height > maxHeight) {
maxHeight = menuDimensions.height;
}
@@ -539,13 +546,26 @@ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoop
*/
export const draw = function (text, id) {
conf = configApi.getConfig().sequence;
const securityLevel = configApi.getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
parser.yy.clear();
parser.yy.setWrap(conf.wrap);
parser.parse(text + '\n');
bounds.init();
log.debug(`C:${JSON.stringify(conf, null, 2)}`);
const diagram = select(`[id="${id}"]`);
const diagram =
securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : select(`[id="${id}"]`);
// Fetch data from the parsing
const actors = parser.yy.getActors();
@@ -733,7 +753,7 @@ export const draw = function (text, id) {
}
// only draw popups for the top row of actors.
var requiredBoxSize = drawActorsPopup(diagram, actors, actorKeys);
var requiredBoxSize = drawActorsPopup(diagram, actors, actorKeys, doc);
const { bounds: box } = bounds.getBounds();

View File

@@ -30,6 +30,8 @@ export const drawRect = function (elem, rectData) {
const addPopupInteraction = (id, actorCnt) => {
addFunction(() => {
const arr = document.querySelectorAll(id);
// This will be the case when running in sandboxed mode
if (arr.length === 0) return;
arr[0].addEventListener('mouseover', function () {
popupMenuUpFunc('actor' + actorCnt + '_popup');
});

View File

@@ -258,6 +258,8 @@ export const draw = function (text, id) {
const nodeSpacing = conf.nodeSpacing || 50;
const rankSpacing = conf.rankSpacing || 50;
const securityLevel = getConfig().securityLevel;
log.info(stateDb.getRootDocV2());
stateDb.extract(stateDb.getRootDocV2());
log.info(stateDb.getRootDocV2());
@@ -281,10 +283,20 @@ export const draw = function (text, id) {
setupNode(g, undefined, stateDb.getRootDocV2(), true);
// Set up an SVG group so that we can translate the final graph.
const svg = select(`[id="${id}"]`);
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const svg = root.select(`[id="${id}"]`);
// Run the renderer. This is what draws the final graph.
const element = select('#' + id + ' g');
const element = root.select('#' + id + ' g');
render(element, g, ['barb'], 'statediagram', id);
const padding = 8;

View File

@@ -46,12 +46,24 @@ const insertMarkers = function (elem) {
*/
export const draw = function (text, id) {
conf = getConfig().state;
const securityLevel = getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
parser.yy.clear();
parser.parse(text);
log.debug('Rendering diagram ' + text);
// Fetch the default direction, use TD if none was found
const diagram = select(`[id='${id}']`);
const diagram = root.select(`[id='${id}']`);
insertMarkers(diagram);
// Layout graph, Create a new directed graph
@@ -69,7 +81,7 @@ export const draw = function (text, id) {
});
const rootDoc = stateDb.getRootDoc();
renderDoc(rootDoc, diagram, undefined, false);
renderDoc(rootDoc, diagram, undefined, false, root, doc);
const padding = conf.padding;
const bounds = diagram.node().getBBox();
@@ -90,7 +102,7 @@ const getLabelWidth = (text) => {
return text ? text.length * conf.fontSizeFactor : 1;
};
const renderDoc = (doc, diagram, parentId, altBkg) => {
const renderDoc = (doc, diagram, parentId, altBkg, root, domDocument) => {
// Layout graph, Create a new directed graph
const graph = new graphlib.Graph({
compound: true,
@@ -159,7 +171,7 @@ const renderDoc = (doc, diagram, parentId, altBkg) => {
let node;
if (stateDef.doc) {
let sub = diagram.append('g').attr('id', stateDef.id).attr('class', 'stateGroup');
node = renderDoc(stateDef.doc, sub, stateDef.id, !altBkg);
node = renderDoc(stateDef.doc, sub, stateDef.id, !altBkg, root, domDocument);
if (first) {
// first = false;
@@ -234,21 +246,22 @@ const renderDoc = (doc, diagram, parentId, altBkg) => {
graph.nodes().forEach(function (v) {
if (typeof v !== 'undefined' && typeof graph.node(v) !== 'undefined') {
log.warn('Node ' + v + ': ' + JSON.stringify(graph.node(v)));
select('#' + svgElem.id + ' #' + v).attr(
'transform',
'translate(' +
(graph.node(v).x - graph.node(v).width / 2) +
',' +
(graph.node(v).y +
(transformationLog[v] ? transformationLog[v].y : 0) -
graph.node(v).height / 2) +
' )'
);
select('#' + svgElem.id + ' #' + v).attr(
'data-x-shift',
graph.node(v).x - graph.node(v).width / 2
);
const dividers = document.querySelectorAll('#' + svgElem.id + ' #' + v + ' .divider');
root
.select('#' + svgElem.id + ' #' + v)
.attr(
'transform',
'translate(' +
(graph.node(v).x - graph.node(v).width / 2) +
',' +
(graph.node(v).y +
(transformationLog[v] ? transformationLog[v].y : 0) -
graph.node(v).height / 2) +
' )'
);
root
.select('#' + svgElem.id + ' #' + v)
.attr('data-x-shift', graph.node(v).x - graph.node(v).width / 2);
const dividers = domDocument.querySelectorAll('#' + svgElem.id + ' #' + v + ' .divider');
dividers.forEach((divider) => {
const parent = divider.parentElement;
let pWidth = 0;

View File

@@ -54,8 +54,20 @@ export const draw = function (text, id) {
parser.yy.clear();
parser.parse(text + '\n');
const securityLevel = getConfig().securityLevel;
// Handle root and ocument for when rendering in sanbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
bounds.init();
const diagram = select('#' + id);
const diagram = root.select('#' + id);
diagram.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink');
svgDraw.initGraphics(diagram);

View File

@@ -83,6 +83,7 @@ const init = function () {
let txt;
for (let i = 0; i < nodes.length; i++) {
// element is the current div with mermaid class
const element = nodes[i];
/*! Check if previously processed */

View File

@@ -232,10 +232,43 @@ const render = function (id, _txt, cb, container) {
txt = 'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';
}
// let d3Iframe;
let root = select('body');
// In regular execurtion the container will be the div with a mermaid class
if (typeof container !== 'undefined') {
if (cnf.securityLevel === 'sandbox') {
// IF we are in sandboxed mode, we do everyting mermaid related
// in a sandboxed div
const iframe = select('body')
.append('iframe')
.attr('id', 'i' + id)
.attr('style', 'width: 100%; height: 100%;')
.attr('sandbox', '');
// const iframeBody = ;
root = select(iframe.nodes()[0].contentDocument.body);
root.node().style.margin = 0;
}
// A container was provided by the caller
container.innerHTML = '';
select(container)
if (cnf.securityLevel === 'sandbox') {
// IF we are in sandboxed mode, we do everyting mermaid related
// in a sandboxed div
const iframe = select(container)
.append('iframe')
.attr('id', 'i' + id)
.attr('style', 'width: 100%; height: 100%;')
.attr('sandbox', '');
// const iframeBody = ;
root = select(iframe.nodes()[0].contentDocument.body);
root.node().style.margin = 0;
} else {
root = select(container);
}
root
.append('div')
.attr('id', 'd' + id)
.attr('style', 'font-family: ' + cnf.fontFamily)
@@ -245,18 +278,57 @@ const render = function (id, _txt, cb, container) {
.attr('xmlns', 'http://www.w3.org/2000/svg')
.append('g');
} else {
// No container was provided
// If there is an existsing element with the id, we remove it
// this likley a previously rendered diagram
const existingSvg = document.getElementById(id);
if (existingSvg) {
existingSvg.remove();
}
const element = document.querySelector('#' + 'd' + id);
// Remove previous tpm element if it exists
let element;
if (cnf.securityLevel !== 'sandbox') {
element = document.querySelector('#' + 'd' + id);
} else {
element = document.querySelector('#' + 'i' + id);
}
if (element) {
element.remove();
}
select('body')
// if (cnf.securityLevel === 'sandbox') {
// const iframe = select('body')
// .append('iframe')
// .attr('id', 'i' + id)
// .attr('sandbox', '');
// // const iframeBody = ;
// root = select(iframe.nodes()[0].contentDocument.body);
// }
// Add the tmp div used for rendering with the id `d${id}`
// d+id it will contain a svg with the id "id"
if (cnf.securityLevel === 'sandbox') {
// IF we are in sandboxed mode, we do everyting mermaid related
// in a sandboxed div
const iframe = select('body')
.append('iframe')
.attr('id', 'i' + id)
.attr('style', 'width: 100%; height: 100%;')
.attr('sandbox', '');
// const iframeBody = ;
root = select(iframe.nodes()[0].contentDocument.body);
root.node().style.margin = 0;
} else {
root = select('body');
}
// This is the temporary div
root
.append('div')
.attr('id', 'd' + id)
// this is the seed of the svg to be rendered
.append('svg')
.attr('id', id)
.attr('width', '100%')
@@ -264,10 +336,10 @@ const render = function (id, _txt, cb, container) {
.append('g');
}
window.txt = txt;
txt = encodeEntities(txt);
const element = select('#d' + id).node();
// Get the tmp element containing the the svg
const element = root.select('#d' + id).node();
const graphType = utils.detectType(txt, cnf);
// insert inline style into svg
@@ -430,14 +502,19 @@ const render = function (id, _txt, cb, container) {
throw e;
}
select(`[id="${id}"]`)
root
.select(`[id="${id}"]`)
.selectAll('foreignobject > *')
.attr('xmlns', 'http://www.w3.org/1999/xhtml');
// Fix for when the base tag is used
let svgCode = select('#d' + id).node().innerHTML;
let svgCode = root.select('#d' + id).node().innerHTML;
log.debug('cnf.arrowMarkerAbsolute', cnf.arrowMarkerAbsolute);
if (!cnf.arrowMarkerAbsolute || cnf.arrowMarkerAbsolute === 'false') {
if (
(!cnf.arrowMarkerAbsolute || cnf.arrowMarkerAbsolute === 'false') &&
cnf.arrowMarkerAbsolute !== 'sandbox'
) {
svgCode = svgCode.replace(/marker-end="url\(.*?#/g, 'marker-end="url(#', 'g');
}
@@ -446,6 +523,21 @@ const render = function (id, _txt, cb, container) {
// Fix for when the br tag is used
svgCode = svgCode.replace(/<br>/g, '<br/>');
if (cnf.securityLevel === 'sandbox') {
let svgEl = root.select('#d' + id + ' svg').node();
let width = '100%';
let height = '100%';
if (svgEl) {
// width = svgEl.viewBox.baseVal.width + 'px';
height = svgEl.viewBox.baseVal.height + 'px';
}
svgCode = `<iframe style="width:${width};height:${height};border:0;margin:0;" src="data:text/html;base64,${btoa(
'<body style="margin:0">' + svgCode + '</body>'
)}" sandbox="allow-top-navigation-by-user-activation allow-popups">
The “iframe” tag is not supported by your browser.
</iframe>`;
}
if (typeof cb !== 'undefined') {
switch (graphType) {
case 'flowchart':
@@ -467,11 +559,10 @@ const render = function (id, _txt, cb, container) {
}
attachFunctions();
const node = select('#d' + id).node();
const tmpElementSelector = cnf.securityLevel === 'sandbox' ? '#i' + id : '#d' + id;
const node = select(tmpElementSelector).node();
if (node !== null && typeof node.remove === 'function') {
select('#d' + id)
.node()
.remove();
select(tmpElementSelector).node().remove();
}
return svgCode;
@@ -570,6 +661,7 @@ function updateRendererConfigs(conf) {
errorRenderer.setConf(conf.class);
}
/** To be removed */
function reinitialize() {
// `mermaidAPI.reinitialize: v${pkg.version}`,
// JSON.stringify(options),