mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-28 20:56:48 +02:00
feat: add support for links in sanbox mode
This commit is contained in:
@@ -25,14 +25,13 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="apa" style="background: aliceblue; width: 150px; height: 75px;">APA</div>
|
|
||||||
<div class="mermaid2" style="width: 100%;">
|
<div class="mermaid2" style="width: 100%;">
|
||||||
pie title Pets adopted by volunteers
|
pie title Pets adopted by volunteers
|
||||||
"Dogs" : 386
|
"Dogs" : 386
|
||||||
"Cats" : 85
|
"Cats" : 85
|
||||||
"Rats" : 15
|
"Rats" : 15
|
||||||
</div>
|
</div>
|
||||||
<div class="mermaid" style="width: 100%;">
|
<div class="mermaid2" style="width: 100%;">
|
||||||
gantt
|
gantt
|
||||||
title Adding GANTT diagram functionality to mermaid
|
title Adding GANTT diagram functionality to mermaid
|
||||||
excludes :excludes the named dates/days from being included in a charted task..
|
excludes :excludes the named dates/days from being included in a charted task..
|
||||||
@@ -64,7 +63,7 @@ commit
|
|||||||
commit
|
commit
|
||||||
merge newbranch
|
merge newbranch
|
||||||
</div>
|
</div>
|
||||||
<div class="mermaid" style="width: 100%;">
|
<div class="mermaid2" style="width: 100%;">
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant a as Alice
|
participant a as Alice
|
||||||
participant j as John
|
participant j as John
|
||||||
@@ -146,6 +145,76 @@ requirementDiagram
|
|||||||
end
|
end
|
||||||
</div>
|
</div>
|
||||||
<div class="mermaid2" style="width: 100%;">
|
<div class="mermaid2" style="width: 100%;">
|
||||||
|
flowchart TB
|
||||||
|
Function-->URL
|
||||||
|
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%;">
|
||||||
|
classDiagram-v2
|
||||||
|
class Test
|
||||||
|
class ShapeLink
|
||||||
|
link ShapeLink "https://mermaid-js.github.io/mermaid/#/" "This is a tooltip for a link"
|
||||||
|
class ShapeCallback
|
||||||
|
callback ShapeCallback "clickByClass" "This is a tooltip for a callback"
|
||||||
|
</div>
|
||||||
|
<div class="mermaid" 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
|
stateDiagram
|
||||||
[*] --> S1
|
[*] --> S1
|
||||||
state "Some long name" as S1
|
state "Some long name" as S1
|
||||||
@@ -181,7 +250,7 @@ requirementDiagram
|
|||||||
};
|
};
|
||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
// theme: 'dark',
|
// theme: 'dark',
|
||||||
theme: 'forest',
|
// theme: 'forest',
|
||||||
// arrowMarkerAbsolute: true,
|
// arrowMarkerAbsolute: true,
|
||||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||||
flowchart: {
|
flowchart: {
|
||||||
@@ -192,6 +261,7 @@ requirementDiagram
|
|||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
defaultRenderer: 'dagre-d3',
|
defaultRenderer: 'dagre-d3',
|
||||||
|
htmlLabels: true,
|
||||||
},
|
},
|
||||||
// gantt: { axisFormat: '%m/%d/%Y' },
|
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||||
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
|
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
|
||||||
@@ -212,6 +282,15 @@ requirementDiagram
|
|||||||
function callback() {
|
function callback() {
|
||||||
alert('It worked');
|
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -967,10 +967,13 @@ export const insertNode = (elem, node, dir) => {
|
|||||||
|
|
||||||
// Add link when appropriate
|
// Add link when appropriate
|
||||||
if (node.link) {
|
if (node.link) {
|
||||||
newEl = elem
|
let target;
|
||||||
.insert('svg:a')
|
if (getConfig().securityLevel === 'sandbox') {
|
||||||
.attr('xlink:href', node.link)
|
target = '_top';
|
||||||
.attr('target', node.linkTarget || '_blank');
|
} 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);
|
el = shapes[node.shape](newEl, node, dir);
|
||||||
} else {
|
} else {
|
||||||
el = shapes[node.shape](elem, node, dir);
|
el = shapes[node.shape](elem, node, dir);
|
||||||
|
@@ -213,7 +213,9 @@ export const setLink = function (ids, linkStr, target) {
|
|||||||
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||||
if (typeof classes[id] !== 'undefined') {
|
if (typeof classes[id] !== 'undefined') {
|
||||||
classes[id].link = utils.formatUrl(linkStr, config);
|
classes[id].link = utils.formatUrl(linkStr, config);
|
||||||
if (typeof target === 'string') {
|
if (config.securityLevel === 'sandbox') {
|
||||||
|
classes[id].linkTarget = '_top';
|
||||||
|
} else if (typeof target === 'string') {
|
||||||
classes[id].linkTarget = target;
|
classes[id].linkTarget = target;
|
||||||
} else {
|
} else {
|
||||||
classes[id].linkTarget = '_blank';
|
classes[id].linkTarget = '_blank';
|
||||||
|
@@ -496,7 +496,9 @@ export const draw = function (text, id) {
|
|||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
|
||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
|
||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
|
||||||
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);
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'target', vertex.linkTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -503,7 +503,9 @@ export const draw = function (text, id) {
|
|||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'class', vertex.classes.join(' '));
|
||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
|
||||||
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
|
||||||
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);
|
link.setAttributeNS('http://www.w3.org/2000/svg', 'target', vertex.linkTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ let axisFormat = '';
|
|||||||
let todayMarker = '';
|
let todayMarker = '';
|
||||||
let includes = [];
|
let includes = [];
|
||||||
let excludes = [];
|
let excludes = [];
|
||||||
|
let links = {};
|
||||||
let title = '';
|
let title = '';
|
||||||
let sections = [];
|
let sections = [];
|
||||||
let tasks = [];
|
let tasks = [];
|
||||||
@@ -44,6 +45,7 @@ export const clear = function () {
|
|||||||
inclusiveEndDates = false;
|
inclusiveEndDates = false;
|
||||||
topAxis = false;
|
topAxis = false;
|
||||||
lastOrder = 0;
|
lastOrder = 0;
|
||||||
|
links = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setAxisFormat = function (txt) {
|
export const setAxisFormat = function (txt) {
|
||||||
@@ -101,6 +103,10 @@ export const getExcludes = function () {
|
|||||||
return excludes;
|
return excludes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getLinks = function () {
|
||||||
|
return links;
|
||||||
|
};
|
||||||
|
|
||||||
export const setTitle = function (txt) {
|
export const setTitle = function (txt) {
|
||||||
title = txt;
|
title = txt;
|
||||||
};
|
};
|
||||||
@@ -505,6 +511,7 @@ export const setLink = function (ids, _linkStr) {
|
|||||||
pushFun(id, () => {
|
pushFun(id, () => {
|
||||||
window.open(linkStr, '_self');
|
window.open(linkStr, '_self');
|
||||||
});
|
});
|
||||||
|
links[id] = linkStr;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setClass(ids, 'clickable');
|
setClass(ids, 'clickable');
|
||||||
@@ -642,6 +649,7 @@ export default {
|
|||||||
getExcludes,
|
getExcludes,
|
||||||
setClickEvent,
|
setClickEvent,
|
||||||
setLink,
|
setLink,
|
||||||
|
getLinks,
|
||||||
bindFunctions,
|
bindFunctions,
|
||||||
durationToDate,
|
durationToDate,
|
||||||
isInvalidDate,
|
isInvalidDate,
|
||||||
|
@@ -185,6 +185,10 @@ export const draw = function (text, id) {
|
|||||||
// Draw the rects representing the tasks
|
// Draw the rects representing the tasks
|
||||||
const rectangles = svg.append('g').selectAll('rect').data(theArray).enter();
|
const rectangles = svg.append('g').selectAll('rect').data(theArray).enter();
|
||||||
|
|
||||||
|
const links = ganttDb.getLinks();
|
||||||
|
|
||||||
|
// Render the tasks with links
|
||||||
|
// Render the other tasks
|
||||||
rectangles
|
rectangles
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('id', function (d) {
|
.attr('id', function (d) {
|
||||||
@@ -381,6 +385,32 @@ export const draw = function (text, id) {
|
|||||||
return classStr + ' taskText taskText' + secNum + ' ' + taskType + ' width-' + textWidth;
|
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
|
* @param theGap
|
||||||
|
@@ -518,32 +518,12 @@ const render = function (id, _txt, cb, container) {
|
|||||||
svgCode = svgCode.replace(/marker-end="url\(.*?#/g, 'marker-end="url(#', 'g');
|
svgCode = svgCode.replace(/marker-end="url\(.*?#/g, 'marker-end="url(#', 'g');
|
||||||
}
|
}
|
||||||
|
|
||||||
// const iframe = document.createElement('iframe');
|
|
||||||
// iframe.setAttribute('frameBorder', '0');
|
|
||||||
// iframe.setAttribute('id', id);
|
|
||||||
// iframe.setAttribute('sanbox', '');
|
|
||||||
// iframe.setAttribute('src', 'about:blank');
|
|
||||||
// iframe.contentWindow.document.body = svgCode;
|
|
||||||
// element.innerHTML = '';
|
|
||||||
// // element.appendChild(iframe);
|
|
||||||
// // element.innerHTML = '<iframe sandbox>' + svgCode + '</iframe>';
|
|
||||||
|
|
||||||
svgCode = decodeEntities(svgCode);
|
svgCode = decodeEntities(svgCode);
|
||||||
|
|
||||||
// Fix for when the br tag is used
|
// Fix for when the br tag is used
|
||||||
svgCode = svgCode.replace(/<br>/g, '<br/>');
|
svgCode = svgCode.replace(/<br>/g, '<br/>');
|
||||||
|
|
||||||
if (cnf.securityLevel === 'sandbox') {
|
if (cnf.securityLevel === 'sandbox') {
|
||||||
// const newSvgCode =
|
|
||||||
// '<iframe id="' +
|
|
||||||
// id +
|
|
||||||
// '" sandbox src="data:text/html;base64,' +
|
|
||||||
// btoa(btoa(unescape(encodeURIComponent(svgCode)))) +
|
|
||||||
// '"></iframe>';
|
|
||||||
// svgCode = newSvgCode;
|
|
||||||
// svgCode = `<iframe src="data:text/html;base64,V2VsY29tZSB0byA8Yj5iYXNlNjQuZ3VydTwvYj4h">
|
|
||||||
// The “iframe” tag is not supported by your browser.
|
|
||||||
// </iframe>`;
|
|
||||||
let svgEl = root.select('#d' + id + ' svg').node();
|
let svgEl = root.select('#d' + id + ' svg').node();
|
||||||
let width = '100%';
|
let width = '100%';
|
||||||
let height = '100%';
|
let height = '100%';
|
||||||
@@ -553,7 +533,7 @@ const render = function (id, _txt, cb, container) {
|
|||||||
}
|
}
|
||||||
svgCode = `<iframe style="width:${width};height:${height};border:0;margin:0;" src="data:text/html;base64,${btoa(
|
svgCode = `<iframe style="width:${width};height:${height};border:0;margin:0;" src="data:text/html;base64,${btoa(
|
||||||
'<body style="margin:0">' + svgCode + '</body>'
|
'<body style="margin:0">' + svgCode + '</body>'
|
||||||
)}" sandbox>
|
)}" sandbox="allow-top-navigation-by-user-activation allow-popups">
|
||||||
The “iframe” tag is not supported by your browser.
|
The “iframe” tag is not supported by your browser.
|
||||||
</iframe>`;
|
</iframe>`;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user