mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-18 03:34:12 +01:00
Merge branch 'develop' into 1179-SupportGenericTypesForMembers
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,3 +18,4 @@ dist/classTest.html
|
|||||||
dist/sequenceTest.html
|
dist/sequenceTest.html
|
||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
|
cypress/platform/current.html
|
||||||
@@ -512,7 +512,7 @@ describe('Flowchart', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('24: Keep node label text (if already defined) when a style is applied', () => {
|
it('24.1: Keep node label text (if already defined) when a style is applied', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`graph LR
|
`graph LR
|
||||||
A(( )) -->|step 1| B(( ))
|
A(( )) -->|step 1| B(( ))
|
||||||
@@ -524,4 +524,55 @@ describe('Flowchart', () => {
|
|||||||
{ flowchart: { htmlLabels: false } }
|
{ flowchart: { htmlLabels: false } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('24.2: Handle link click events (link, anchor, mailto, other protocol, script)', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph TB
|
||||||
|
TITLE["Link Click Events<br>(click the nodes below)"]
|
||||||
|
A[link test]
|
||||||
|
B[anchor test]
|
||||||
|
C[mailto test]
|
||||||
|
D[other protocol test]
|
||||||
|
E[script test]
|
||||||
|
TITLE --> A & B & C & D & E
|
||||||
|
click A "https://mermaid-js.github.io/mermaid/#/" "link test"
|
||||||
|
click B "#link-clicked" "anchor test"
|
||||||
|
click C "mailto:user@user.user" "mailto test"
|
||||||
|
click D "notes://do-your-thing/id" "other protocol test"
|
||||||
|
click E "javascript:alert('test')" "script test"
|
||||||
|
`,
|
||||||
|
{ securityLevel: 'loose' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('25: Set node text color according to style when html labels are enabled', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph LR
|
||||||
|
A[red<br>text] --> B(blue<br>text)
|
||||||
|
C[/red<br/>text/] --> D{blue<br/>text}
|
||||||
|
style A color:red;
|
||||||
|
style B color:blue;
|
||||||
|
style C stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||||
|
style D stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||||
|
click B "index.html#link-clicked" "link test"
|
||||||
|
click D testClick "click test"
|
||||||
|
`,
|
||||||
|
{ flowchart: { htmlLabels: true } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('26: Set node text color according to style when html labels are disabled', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph LR
|
||||||
|
A[red<br>text] --> B(blue<br>text)
|
||||||
|
C[/red<br/>text/] --> D{blue<br/>text}
|
||||||
|
style A color:red;
|
||||||
|
style B color:blue;
|
||||||
|
style C stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||||
|
style D stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||||
|
click B "index.html#link-clicked" "link test"
|
||||||
|
click D testClick "click test"
|
||||||
|
`,
|
||||||
|
{ flowchart: { htmlLabels: false } }
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ describe('State diagram', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('Simplest compone state', () => {
|
it('Simplest composit state', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
stateDiagram
|
stateDiagram
|
||||||
@@ -332,5 +332,17 @@ describe('State diagram', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('should handle multiple arrows from one node to another', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
stateDiagram
|
||||||
|
a --> b: Start
|
||||||
|
a --> b: Stop
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
logLevel: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,33 +5,28 @@
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<style>
|
<style>
|
||||||
body {background: black}
|
body {background: white}
|
||||||
h1 { color: white;}
|
h1 { color: white;}
|
||||||
.arrowheadPath {fill: red;}
|
.arrowheadPath {fill: red;}
|
||||||
|
|
||||||
.edgePath .path {stroke: red;}
|
.edgePath .path {stroke: red;}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>info below</h1>
|
<h1>info below</h1>
|
||||||
<div style="display: flex;width: 100%; height: 100%">
|
<div style="display: flex;width: 100%; height: 100%">
|
||||||
<div class="mermaid" style="width: 100%; height: 100%">
|
<div class="mermaid" style="width: 100%; height: 100%">
|
||||||
graph TB
|
stateDiagram
|
||||||
A --> B
|
|
||||||
A ==> C
|
NotFound --> NotFound: Status
|
||||||
A .-> D
|
NotFound --> NotFound: Stop
|
||||||
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>
|
||||||
<script>
|
<script>
|
||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
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: 0,
|
logLevel: 0,
|
||||||
|
|||||||
39
cypress/platform/huge.html
Normal file
39
cypress/platform/huge.html
Normal file
File diff suppressed because one or more lines are too long
30
dist/index.html
vendored
30
dist/index.html
vendored
@@ -353,6 +353,33 @@ graph TB
|
|||||||
linkStyle 1 stroke:greenyellow,stroke-width:2px
|
linkStyle 1 stroke:greenyellow,stroke-width:2px
|
||||||
style C fill:greenyellow,stroke:green,stroke-width:4px
|
style C fill:greenyellow,stroke:green,stroke-width:4px
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mermaid">
|
||||||
|
graph TB
|
||||||
|
TITLE["Link Click Events<br>(click the nodes below)"]
|
||||||
|
A[link test]
|
||||||
|
B[anchor test]
|
||||||
|
C[mailto test]
|
||||||
|
D[other protocol test]
|
||||||
|
E[script test]
|
||||||
|
TITLE --> A & B & C & D & E
|
||||||
|
click A "https://mermaid-js.github.io/mermaid/#/" "link test"
|
||||||
|
click B "#link-clicked" "anchor test"
|
||||||
|
click C "mailto:user@user.user" "mailto test"
|
||||||
|
click D "notes://do-your-thing/id" "other protocol test"
|
||||||
|
click E "javascript:alert('test')" "script test"
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<div class="mermaid">
|
||||||
|
graph LR
|
||||||
|
A[red<br>text] --> B(blue<br>text)
|
||||||
|
C[/red<br/>text/] --> D{blue<br/>text}
|
||||||
|
style A color:red;
|
||||||
|
style B color:blue;
|
||||||
|
style C stroke:#ff0000,fill:#ffcccc,color:#ff0000
|
||||||
|
style D stroke:#0000ff,fill:#ccccff,color:#0000ff
|
||||||
|
click B "index.html#link-clicked" "link test"
|
||||||
|
click D testClick "click test"
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
@@ -587,12 +614,15 @@ class Class10 {
|
|||||||
end note
|
end note
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h1 id="link-clicked">Anchor for "link-clicked" test</h1>
|
||||||
|
|
||||||
<script src="./mermaid.js"></script>
|
<script src="./mermaid.js"></script>
|
||||||
<script>
|
<script>
|
||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
theme: 'forest',
|
theme: 'forest',
|
||||||
// themeCSS: '.node rect { fill: red; }',
|
// themeCSS: '.node rect { fill: red; }',
|
||||||
logLevel: 3,
|
logLevel: 3,
|
||||||
|
securityLevel: 'loose',
|
||||||
flowchart: { curve: 'basis' },
|
flowchart: { curve: 'basis' },
|
||||||
gantt: { axisFormat: '%m/%d/%Y' },
|
gantt: { axisFormat: '%m/%d/%Y' },
|
||||||
sequence: { actorMargin: 50 },
|
sequence: { actorMargin: 50 },
|
||||||
|
|||||||
@@ -509,13 +509,13 @@ It is possible to apply specific styles such as a thicker border or a different
|
|||||||
graph LR
|
graph LR
|
||||||
id1(Start)-->id2(Stop)
|
id1(Start)-->id2(Stop)
|
||||||
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
style id2 fill:#ccf,stroke:#f66,stroke-width:2px,stroke-dasharray: 5, 5
|
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5, 5
|
||||||
```
|
```
|
||||||
```mermaid
|
```mermaid
|
||||||
graph LR
|
graph LR
|
||||||
id1(Start)-->id2(Stop)
|
id1(Start)-->id2(Stop)
|
||||||
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
style id2 fill:#ccf,stroke:#f66,stroke-width:2px,stroke-dasharray: 5, 5
|
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5, 5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -275,11 +275,12 @@ mermaidAPI.initialize({
|
|||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `id` the id of the element to be rendered
|
- `id` the id of the element to be rendered
|
||||||
- `txt` the graph definition
|
- `_txt`
|
||||||
- `cb` callback which is called after rendering is finished with the svg code as inparam.
|
- `cb` callback which is called after rendering is finished with the svg code as inparam.
|
||||||
- `container` selector to element in which a div with the graph temporarily will be inserted. In one is
|
- `container` selector to element in which a div with the graph temporarily will be inserted. In one is
|
||||||
provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
|
provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
|
||||||
completed.
|
completed.
|
||||||
|
- `txt` the graph definition
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
||||||
|
|||||||
@@ -502,8 +502,8 @@ export const draw = function(text, id) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
diagram.attr('height', '100%');
|
diagram.attr('height', g.graph().height + 40);
|
||||||
diagram.attr('width', `${g.graph().width * 1.5 + 20}`);
|
diagram.attr('width', g.graph().width * 1.5 + 20);
|
||||||
diagram.attr('viewBox', '-10 -10 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20));
|
diagram.attr('viewBox', '-10 -10 ' + (g.graph().width + 20) + ' ' + (g.graph().height + 20));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export const addVertices = function(vert, g, svgId) {
|
|||||||
vertexNode.parentNode.removeChild(vertexNode);
|
vertexNode.parentNode.removeChild(vertexNode);
|
||||||
} else {
|
} else {
|
||||||
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||||
|
svgLabel.setAttribute('style', labelStyle.replace('color:', 'fill:'));
|
||||||
|
|
||||||
const rows = vertexText.split(/<br\s*\/?>/gi);
|
const rows = vertexText.split(/<br\s*\/?>/gi);
|
||||||
|
|
||||||
|
|||||||
29
src/diagrams/flowchart/parser/flow-huge.spec.js
Normal file
29
src/diagrams/flowchart/parser/flow-huge.spec.js
Normal file
File diff suppressed because one or more lines are too long
@@ -48,7 +48,7 @@
|
|||||||
<STATE>.*"[[fork]]" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Fork: ',yytext);*/return 'FORK';}
|
<STATE>.*"[[fork]]" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Fork: ',yytext);*/return 'FORK';}
|
||||||
<STATE>.*"[[join]]" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Join: ',yytext);*/return 'JOIN';}
|
<STATE>.*"[[join]]" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Join: ',yytext);*/return 'JOIN';}
|
||||||
<STATE>["] this.begin("STATE_STRING");
|
<STATE>["] this.begin("STATE_STRING");
|
||||||
<STATE>"as"\s* {this.popState();this.pushState('STATE_ID');return "AS";}
|
<STATE>\s*"as"\s+ {this.popState();this.pushState('STATE_ID');return "AS";}
|
||||||
<STATE_ID>[^\n\{]* {this.popState();/* console.log('STATE_ID', yytext);*/return "ID";}
|
<STATE_ID>[^\n\{]* {this.popState();/* console.log('STATE_ID', yytext);*/return "ID";}
|
||||||
<STATE_STRING>["] this.popState();
|
<STATE_STRING>["] this.popState();
|
||||||
<STATE_STRING>[^"]* { /*console.log('Long description:', yytext);*/return "STATE_DESCR";}
|
<STATE_STRING>[^"]* { /*console.log('Long description:', yytext);*/return "STATE_DESCR";}
|
||||||
|
|||||||
@@ -53,6 +53,39 @@ describe('state diagram, ', function() {
|
|||||||
|
|
||||||
parser.parse(str);
|
parser.parse(str);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handle "as" in state names', function() {
|
||||||
|
const str = `stateDiagram
|
||||||
|
assemble
|
||||||
|
state assemble
|
||||||
|
`;
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
});
|
||||||
|
it('handle "as" in state names 1', function() {
|
||||||
|
const str = `stateDiagram
|
||||||
|
assemble
|
||||||
|
state assemble
|
||||||
|
`;
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
});
|
||||||
|
it('handle "as" in state names 2', function() {
|
||||||
|
const str = `stateDiagram
|
||||||
|
assembleas
|
||||||
|
state assembleas
|
||||||
|
`;
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
});
|
||||||
|
it('handle "as" in state names 3', function() {
|
||||||
|
const str = `stateDiagram
|
||||||
|
state "as" as as
|
||||||
|
`;
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
});
|
||||||
|
|
||||||
it('scale', function() {
|
it('scale', function() {
|
||||||
const str = `stateDiagram\n
|
const str = `stateDiagram\n
|
||||||
scale 350 width
|
scale 350 width
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export const draw = function(text, id) {
|
|||||||
|
|
||||||
// Layout graph, Create a new directed graph
|
// Layout graph, Create a new directed graph
|
||||||
const graph = new graphlib.Graph({
|
const graph = new graphlib.Graph({
|
||||||
multigraph: false,
|
multigraph: true,
|
||||||
compound: true,
|
compound: true,
|
||||||
// acyclicer: 'greedy',
|
// acyclicer: 'greedy',
|
||||||
rankdir: 'RL'
|
rankdir: 'RL'
|
||||||
@@ -110,7 +110,8 @@ const getRows = s => {
|
|||||||
const renderDoc = (doc, diagram, parentId, altBkg) => {
|
const renderDoc = (doc, diagram, parentId, altBkg) => {
|
||||||
// // Layout graph, Create a new directed graph
|
// // Layout graph, Create a new directed graph
|
||||||
const graph = new graphlib.Graph({
|
const graph = new graphlib.Graph({
|
||||||
compound: true
|
compound: true,
|
||||||
|
multigraph: true
|
||||||
});
|
});
|
||||||
|
|
||||||
let i;
|
let i;
|
||||||
@@ -126,28 +127,29 @@ const renderDoc = (doc, diagram, parentId, altBkg) => {
|
|||||||
if (parentId)
|
if (parentId)
|
||||||
graph.setGraph({
|
graph.setGraph({
|
||||||
rankdir: 'LR',
|
rankdir: 'LR',
|
||||||
// multigraph: false,
|
multigraph: true,
|
||||||
compound: true,
|
compound: true,
|
||||||
// acyclicer: 'greedy',
|
// acyclicer: 'greedy',
|
||||||
ranker: 'tight-tree',
|
ranker: 'tight-tree',
|
||||||
ranksep: edgeFreeDoc ? 1 : conf.edgeLengthFactor,
|
ranksep: edgeFreeDoc ? 1 : conf.edgeLengthFactor,
|
||||||
nodeSep: edgeFreeDoc ? 1 : 50
|
nodeSep: edgeFreeDoc ? 1 : 50,
|
||||||
// isMultiGraph: false
|
isMultiGraph: true
|
||||||
// ranksep: 5,
|
// ranksep: 5,
|
||||||
// nodesep: 1
|
// nodesep: 1
|
||||||
});
|
});
|
||||||
else {
|
else {
|
||||||
graph.setGraph({
|
graph.setGraph({
|
||||||
rankdir: 'TB',
|
rankdir: 'TB',
|
||||||
|
multigraph: true,
|
||||||
compound: true,
|
compound: true,
|
||||||
// isCompound: true,
|
// isCompound: true,
|
||||||
// acyclicer: 'greedy',
|
// acyclicer: 'greedy',
|
||||||
// ranker: 'longest-path'
|
// ranker: 'longest-path'
|
||||||
ranksep: edgeFreeDoc ? 1 : conf.edgeLengthFactor,
|
ranksep: edgeFreeDoc ? 1 : conf.edgeLengthFactor,
|
||||||
nodeSep: edgeFreeDoc ? 1 : 50,
|
nodeSep: edgeFreeDoc ? 1 : 50,
|
||||||
ranker: 'tight-tree'
|
ranker: 'tight-tree',
|
||||||
// ranker: 'network-simplex'
|
// ranker: 'network-simplex'
|
||||||
// isMultiGraph: false
|
isMultiGraph: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,14 +228,22 @@ const renderDoc = (doc, diagram, parentId, altBkg) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Count=', graph.nodeCount());
|
logger.debug('Count=', graph.nodeCount(), graph);
|
||||||
|
let cnt = 0;
|
||||||
relations.forEach(function(relation) {
|
relations.forEach(function(relation) {
|
||||||
graph.setEdge(relation.id1, relation.id2, {
|
cnt++;
|
||||||
relation: relation,
|
logger.debug('Setting edge', relation);
|
||||||
width: getLabelWidth(relation.title),
|
graph.setEdge(
|
||||||
height: conf.labelHeight * getRows(relation.title).length,
|
relation.id1,
|
||||||
labelpos: 'c'
|
relation.id2,
|
||||||
});
|
{
|
||||||
|
relation: relation,
|
||||||
|
width: getLabelWidth(relation.title),
|
||||||
|
height: conf.labelHeight * getRows(relation.title).length,
|
||||||
|
labelpos: 'c'
|
||||||
|
},
|
||||||
|
'id' + cnt
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
dagre.layout(graph);
|
dagre.layout(graph);
|
||||||
@@ -299,7 +309,7 @@ const renderDoc = (doc, diagram, parentId, altBkg) => {
|
|||||||
stateInfo.width = stateBox.width + 2 * conf.padding;
|
stateInfo.width = stateBox.width + 2 * conf.padding;
|
||||||
stateInfo.height = stateBox.height + 2 * conf.padding;
|
stateInfo.height = stateBox.height + 2 * conf.padding;
|
||||||
|
|
||||||
logger.info('Doc rendered', stateInfo, graph);
|
logger.debug('Doc rendered', stateInfo, graph);
|
||||||
return stateInfo;
|
return stateInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ const config = {
|
|||||||
*/
|
*/
|
||||||
theme: 'default',
|
theme: 'default',
|
||||||
themeCSS: undefined,
|
themeCSS: undefined,
|
||||||
|
/* **maxTextSize** - The maximum allowed size of the users text diamgram */
|
||||||
|
maxTextSize: 50000,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* **fontFamily** The font to be used for the rendered diagrams. Default value is \"trebuchet ms\", verdana, arial;
|
* **fontFamily** The font to be used for the rendered diagrams. Default value is \"trebuchet ms\", verdana, arial;
|
||||||
@@ -460,7 +462,13 @@ export const decodeEntities = function(text) {
|
|||||||
* provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
|
* provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
|
||||||
* completed.
|
* completed.
|
||||||
*/
|
*/
|
||||||
const render = function(id, txt, cb, container) {
|
const render = function(id, _txt, cb, container) {
|
||||||
|
// Check the maximum allowed text size
|
||||||
|
let txt = _txt;
|
||||||
|
if (_txt.length > config.maxTextSize) {
|
||||||
|
txt = 'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof container !== 'undefined') {
|
if (typeof container !== 'undefined') {
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
|||||||
@@ -99,10 +99,6 @@ export const formatUrl = (linkStr, config) => {
|
|||||||
if (url) {
|
if (url) {
|
||||||
if (config.securityLevel !== 'loose') {
|
if (config.securityLevel !== 'loose') {
|
||||||
return sanitizeUrl(url);
|
return sanitizeUrl(url);
|
||||||
} else {
|
|
||||||
if (!/^(https?:)?\/\//i.test(url)) {
|
|
||||||
url = 'http://' + url;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
|
|||||||
@@ -37,3 +37,61 @@ describe('when finding substring in array ', function() {
|
|||||||
expect(result).toEqual(-1);
|
expect(result).toEqual(-1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when formatting urls', function() {
|
||||||
|
it('should handle links', function() {
|
||||||
|
const url = 'https://mermaid-js.github.io/mermaid/#/';
|
||||||
|
|
||||||
|
let config = { securityLevel: 'loose' };
|
||||||
|
let result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
|
config.securityLevel = 'strict';
|
||||||
|
result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual(url);
|
||||||
|
});
|
||||||
|
it('should handle anchors', function() {
|
||||||
|
const url = '#interaction';
|
||||||
|
|
||||||
|
let config = { securityLevel: 'loose' };
|
||||||
|
let result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
|
config.securityLevel = 'strict';
|
||||||
|
result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual('about:blank');
|
||||||
|
});
|
||||||
|
it('should handle mailto', function() {
|
||||||
|
const url = 'mailto:user@user.user';
|
||||||
|
|
||||||
|
let config = { securityLevel: 'loose' };
|
||||||
|
let result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
|
config.securityLevel = 'strict';
|
||||||
|
result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual(url);
|
||||||
|
});
|
||||||
|
it('should handle other protocols', function() {
|
||||||
|
const url = 'notes://do-your-thing/id';
|
||||||
|
|
||||||
|
let config = { securityLevel: 'loose' };
|
||||||
|
let result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
|
config.securityLevel = 'strict';
|
||||||
|
result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual(url);
|
||||||
|
});
|
||||||
|
it('should handle scripts', function() {
|
||||||
|
const url = 'javascript:alert("test")';
|
||||||
|
|
||||||
|
let config = { securityLevel: 'loose' };
|
||||||
|
let result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
|
config.securityLevel = 'strict';
|
||||||
|
result = utils.formatUrl(url, config);
|
||||||
|
expect(result).toEqual('about:blank');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
1
todo.md
1
todo.md
@@ -1,4 +1,3 @@
|
|||||||
- Get familar with jison
|
|
||||||
- git graph requires a blank line at the end. why?
|
- git graph requires a blank line at the end. why?
|
||||||
- Create a desktop client
|
- Create a desktop client
|
||||||
- Do the rendering in an iframe to avoid global CSS to affect rendering.
|
- Do the rendering in an iframe to avoid global CSS to affect rendering.
|
||||||
|
|||||||
Reference in New Issue
Block a user