diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js
index 4513cc87d..836d8ad79 100644
--- a/cypress/integration/rendering/flowchart-v2.spec.js
+++ b/cypress/integration/rendering/flowchart-v2.spec.js
@@ -685,6 +685,16 @@ A ~~~ B
{ titleTopMargin: 0 }
);
});
+ it('4023: Should render html labels with images and-or text correctly', () => {
+ imgSnapshotTest(
+ `flowchart TD
+ B[
]
+ B-->C[
more text
]
+ B-->D(
some text)
+ B-->E(plain)`,
+ {}
+ );
+ });
describe('Markdown strings flowchart (#4220)', () => {
describe('html labels', () => {
it('With styling and classes', () => {
diff --git a/docs/config/setup/modules/mermaidAPI.md b/docs/config/setup/modules/mermaidAPI.md
index c09402dbd..683850fd3 100644
--- a/docs/config/setup/modules/mermaidAPI.md
+++ b/docs/config/setup/modules/mermaidAPI.md
@@ -96,7 +96,7 @@ mermaid.initialize(config);
#### Defined in
-[mermaidAPI.ts:667](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L667)
+[mermaidAPI.ts:673](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L673)
## Functions
diff --git a/packages/mermaid/src/dagre-wrapper/index.js b/packages/mermaid/src/dagre-wrapper/index.js
index ce3ef6014..f569fc8c5 100644
--- a/packages/mermaid/src/dagre-wrapper/index.js
+++ b/packages/mermaid/src/dagre-wrapper/index.js
@@ -14,7 +14,7 @@ import { insertCluster, clear as clearClusters } from './clusters';
import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges';
import { log } from '../logger';
-const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
+const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => {
log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster);
const dir = graph.graph().rankdir;
log.trace('Dir in recursive render - dir:', dir);
@@ -35,44 +35,46 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
// Insert nodes, this will insert them into the dom and each node will get a size. The size is updated
// to the abstract node and is later used by dagre for the layout
- graph.nodes().forEach(function (v) {
- const node = graph.node(v);
- if (parentCluster !== undefined) {
- const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
- // data.clusterPositioning = true;
- log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
- graph.setNode(parentCluster.id, data);
- if (!graph.parent(v)) {
- log.trace('Setting parent', v, parentCluster.id);
- graph.setParent(v, parentCluster.id, data);
+ await Promise.all(
+ graph.nodes().map(async function (v) {
+ const node = graph.node(v);
+ if (parentCluster !== undefined) {
+ const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
+ // data.clusterPositioning = true;
+ log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
+ graph.setNode(parentCluster.id, data);
+ if (!graph.parent(v)) {
+ log.trace('Setting parent', v, parentCluster.id);
+ graph.setParent(v, parentCluster.id, data);
+ }
}
- }
- log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v)));
- if (node && node.clusterNode) {
- // const children = graph.children(v);
- log.info('Cluster identified', v, node.width, graph.node(v));
- const o = recursiveRender(nodes, node.graph, diagramtype, graph.node(v));
- const newEl = o.elem;
- updateNodeBounds(node, newEl);
- node.diff = o.diff || 0;
- log.info('Node bounds (abc123)', v, node, node.width, node.x, node.y);
- setNodeElem(newEl, node);
+ log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v)));
+ if (node && node.clusterNode) {
+ // const children = graph.children(v);
+ log.info('Cluster identified', v, node.width, graph.node(v));
+ const o = await recursiveRender(nodes, node.graph, diagramtype, graph.node(v));
+ const newEl = o.elem;
+ updateNodeBounds(node, newEl);
+ node.diff = o.diff || 0;
+ log.info('Node bounds (abc123)', v, node, node.width, node.x, node.y);
+ setNodeElem(newEl, node);
- log.warn('Recursive render complete ', newEl, node);
- } else {
- if (graph.children(v).length > 0) {
- // This is a cluster but not to be rendered recursively
- // Render as before
- log.info('Cluster - the non recursive path XXX', v, node.id, node, graph);
- log.info(findNonClusterChild(node.id, graph));
- clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
- // insertCluster(clusters, graph.node(v));
+ log.warn('Recursive render complete ', newEl, node);
} else {
- log.info('Node - the non recursive path', v, node.id, node);
- insertNode(nodes, graph.node(v), dir);
+ if (graph.children(v).length > 0) {
+ // This is a cluster but not to be rendered recursively
+ // Render as before
+ log.info('Cluster - the non recursive path XXX', v, node.id, node, graph);
+ log.info(findNonClusterChild(node.id, graph));
+ clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
+ // insertCluster(clusters, graph.node(v));
+ } else {
+ log.info('Node - the non recursive path', v, node.id, node);
+ await insertNode(nodes, graph.node(v), dir);
+ }
}
- }
- });
+ })
+ );
// Insert labels, this will insert them into the dom so that the width can be calculated
// Also figure out which edges point to/from clusters and adjust them accordingly
@@ -146,7 +148,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
return { elem, diff };
};
-export const render = (elem, graph, markers, diagramtype, id) => {
+export const render = async (elem, graph, markers, diagramtype, id) => {
insertMarkers(elem, markers, diagramtype, id);
clearNodes();
clearEdges();
@@ -157,7 +159,7 @@ export const render = (elem, graph, markers, diagramtype, id) => {
adjustClustersAndEdges(graph);
log.warn('Graph after:', graphlibJson.write(graph));
// log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph));
- recursiveRender(elem, graph, diagramtype);
+ await recursiveRender(elem, graph, diagramtype);
};
// const shapeDefinitions = {};
diff --git a/packages/mermaid/src/dagre-wrapper/nodes.js b/packages/mermaid/src/dagre-wrapper/nodes.js
index d5af81c1b..e0eafe032 100644
--- a/packages/mermaid/src/dagre-wrapper/nodes.js
+++ b/packages/mermaid/src/dagre-wrapper/nodes.js
@@ -8,8 +8,8 @@ import note from './shapes/note';
import { parseMember } from '../diagrams/class/svgDraw';
import { evaluate } from '../diagrams/common/common';
-const question = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const question = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding;
const h = bbox.height + node.padding;
@@ -69,8 +69,8 @@ const choice = (parent, node) => {
return shapeSvg;
};
-const hexagon = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const hexagon = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const f = 4;
const h = bbox.height + node.padding;
@@ -96,8 +96,8 @@ const hexagon = (parent, node) => {
return shapeSvg;
};
-const rect_left_inv_arrow = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const rect_left_inv_arrow = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding;
const h = bbox.height + node.padding;
@@ -122,8 +122,8 @@ const rect_left_inv_arrow = (parent, node) => {
return shapeSvg;
};
-const lean_right = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const lean_right = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding;
const h = bbox.height + node.padding;
@@ -145,8 +145,8 @@ const lean_right = (parent, node) => {
return shapeSvg;
};
-const lean_left = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const lean_left = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding;
const h = bbox.height + node.padding;
@@ -168,8 +168,8 @@ const lean_left = (parent, node) => {
return shapeSvg;
};
-const trapezoid = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const trapezoid = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding;
const h = bbox.height + node.padding;
@@ -191,8 +191,8 @@ const trapezoid = (parent, node) => {
return shapeSvg;
};
-const inv_trapezoid = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const inv_trapezoid = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding;
const h = bbox.height + node.padding;
@@ -214,8 +214,8 @@ const inv_trapezoid = (parent, node) => {
return shapeSvg;
};
-const rect_right_inv_arrow = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const rect_right_inv_arrow = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding;
const h = bbox.height + node.padding;
@@ -238,8 +238,8 @@ const rect_right_inv_arrow = (parent, node) => {
return shapeSvg;
};
-const cylinder = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const cylinder = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding;
const rx = w / 2;
@@ -310,8 +310,13 @@ const cylinder = (parent, node) => {
return shapeSvg;
};
-const rect = (parent, node) => {
- const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true);
+const rect = async (parent, node) => {
+ const { shapeSvg, bbox, halfPadding } = await labelHelper(
+ parent,
+ node,
+ 'node ' + node.classes,
+ true
+ );
// add the rect
const rect = shapeSvg.insert('rect', ':first-child');
@@ -352,8 +357,8 @@ const rect = (parent, node) => {
return shapeSvg;
};
-const labelRect = (parent, node) => {
- const { shapeSvg } = labelHelper(parent, node, 'label', true);
+const labelRect = async (parent, node) => {
+ const { shapeSvg } = await labelHelper(parent, node, 'label', true);
log.trace('Classes = ', node.classes);
// add the rect
@@ -539,8 +544,8 @@ const rectWithTitle = (parent, node) => {
return shapeSvg;
};
-const stadium = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const stadium = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const h = bbox.height + node.padding;
const w = bbox.width + h / 4 + node.padding;
@@ -565,8 +570,8 @@ const stadium = (parent, node) => {
return shapeSvg;
};
-const circle = (parent, node) => {
- const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, undefined, true);
+const circle = async (parent, node) => {
+ const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, undefined, true);
const circle = shapeSvg.insert('circle', ':first-child');
// center the circle around its coordinate
@@ -590,8 +595,8 @@ const circle = (parent, node) => {
return shapeSvg;
};
-const doublecircle = (parent, node) => {
- const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, undefined, true);
+const doublecircle = async (parent, node) => {
+ const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, undefined, true);
const gap = 5;
const circleGroup = shapeSvg.insert('g', ':first-child');
const outerCircle = circleGroup.insert('circle');
@@ -626,8 +631,8 @@ const doublecircle = (parent, node) => {
return shapeSvg;
};
-const subroutine = (parent, node) => {
- const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true);
+const subroutine = async (parent, node) => {
+ const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding;
const h = bbox.height + node.padding;
@@ -976,7 +981,7 @@ const shapes = {
let nodeElems = {};
-export const insertNode = (elem, node, dir) => {
+export const insertNode = async (elem, node, dir) => {
let newEl;
let el;
@@ -989,9 +994,9 @@ export const insertNode = (elem, node, dir) => {
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 = await shapes[node.shape](newEl, node, dir);
} else {
- el = shapes[node.shape](elem, node, dir);
+ el = await shapes[node.shape](elem, node, dir);
newEl = el;
}
if (node.tooltip) {
@@ -1017,6 +1022,7 @@ export const clear = () => {
export const positionNode = (node) => {
const el = nodeElems[node.id];
+
log.trace(
'Transforming node',
node.diff,
diff --git a/packages/mermaid/src/dagre-wrapper/shapes/note.js b/packages/mermaid/src/dagre-wrapper/shapes/note.js
index a39450d54..415df13e3 100644
--- a/packages/mermaid/src/dagre-wrapper/shapes/note.js
+++ b/packages/mermaid/src/dagre-wrapper/shapes/note.js
@@ -3,12 +3,17 @@ import { log } from '../../logger';
import { getConfig } from '../../config';
import intersect from '../intersect/index.js';
-const note = (parent, node) => {
+const note = async (parent, node) => {
const useHtmlLabels = node.useHtmlLabels || getConfig().flowchart.htmlLabels;
if (!useHtmlLabels) {
node.centerLabel = true;
}
- const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true);
+ const { shapeSvg, bbox, halfPadding } = await labelHelper(
+ parent,
+ node,
+ 'node ' + node.classes,
+ true
+ );
log.info('Classes = ', node.classes);
// add the rect
diff --git a/packages/mermaid/src/dagre-wrapper/shapes/util.js b/packages/mermaid/src/dagre-wrapper/shapes/util.js
index 1eeeebb72..d6cb4cd8d 100644
--- a/packages/mermaid/src/dagre-wrapper/shapes/util.js
+++ b/packages/mermaid/src/dagre-wrapper/shapes/util.js
@@ -4,7 +4,8 @@ import { getConfig } from '../../config';
import { decodeEntities } from '../../mermaidAPI';
import { select } from 'd3';
import { evaluate, sanitizeText } from '../../diagrams/common/common';
-export const labelHelper = (parent, node, _classes, isNode) => {
+
+export const labelHelper = async (parent, node, _classes, isNode) => {
let classes;
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels);
if (!_classes) {
@@ -51,17 +52,47 @@ export const labelHelper = (parent, node, _classes, isNode) => {
// Get the size of the label
let bbox = text.getBBox();
+ const halfPadding = node.padding / 2;
if (evaluate(getConfig().flowchart.htmlLabels)) {
const div = text.children[0];
const dv = select(text);
+
+ // if there are images, need to wait for them to load before getting the bounding box
+ const images = div.getElementsByTagName('img');
+ if (images) {
+ const noImgText = labelText.replace(/
]*>/g, '').trim() === '';
+
+ await Promise.all(
+ [...images].map(
+ (img) =>
+ new Promise((res) =>
+ img.addEventListener('load', function () {
+ img.style.display = 'flex';
+ img.style.flexDirection = 'column';
+
+ if (noImgText) {
+ // default size if no text
+ const bodyFontSize = getConfig().fontSize
+ ? getConfig().fontSize
+ : window.getComputedStyle(document.body).fontSize;
+ const enlargingFactor = 5;
+ img.style.width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px';
+ } else {
+ img.style.width = '100%';
+ }
+ res(img);
+ })
+ )
+ )
+ );
+ }
+
bbox = div.getBoundingClientRect();
dv.attr('width', bbox.width);
dv.attr('height', bbox.height);
}
- const halfPadding = node.padding / 2;
-
// Center the label
if (useHtmlLabels) {
label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
diff --git a/packages/mermaid/src/diagrams/class/classRenderer-v2.ts b/packages/mermaid/src/diagrams/class/classRenderer-v2.ts
index e308990c6..e2cd3ddf7 100644
--- a/packages/mermaid/src/diagrams/class/classRenderer-v2.ts
+++ b/packages/mermaid/src/diagrams/class/classRenderer-v2.ts
@@ -248,7 +248,7 @@ export const setConf = function (cnf: any) {
* @param _version -
* @param diagObj -
*/
-export const draw = function (text: string, id: string, _version: string, diagObj: any) {
+export const draw = async function (text: string, id: string, _version: string, diagObj: any) {
log.info('Drawing class - ', id);
// TODO V10: Why flowchart? Might be a mistake when copying.
@@ -300,7 +300,7 @@ export const draw = function (text: string, id: string, _version: string, diagOb
// Run the renderer. This is what draws the final graph.
// @ts-ignore Ignore type error for now
const element = root.select('#' + id + ' g');
- render(
+ await render(
element,
g,
['aggregation', 'extension', 'composition', 'dependency', 'lollipop'],
diff --git a/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js b/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js
index 4748807d1..efadb4199 100644
--- a/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js
+++ b/packages/mermaid/src/diagrams/flowchart/elk/flowRenderer-elk.js
@@ -35,229 +35,231 @@ let nodeDb = {};
// * @param doc
// * @param diagObj
// */
-export const addVertices = function (vert, svgId, root, doc, diagObj, parentLookupDb, graph) {
+export const addVertices = async function (vert, svgId, root, doc, diagObj, parentLookupDb, graph) {
const svg = root.select(`[id="${svgId}"]`);
const nodes = svg.insert('g').attr('class', 'nodes');
const keys = Object.keys(vert);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
- keys.forEach(function (id) {
- const vertex = vert[id];
+ await Promise.all(
+ keys.map(async function (id) {
+ const vertex = vert[id];
- /**
- * Variable for storing the classes for the vertex
- *
- * @type {string}
- */
- let classStr = 'default';
- if (vertex.classes.length > 0) {
- classStr = vertex.classes.join(' ');
- }
- classStr = classStr + ' flowchart-label';
- const styles = getStylesFromArray(vertex.styles);
+ /**
+ * Variable for storing the classes for the vertex
+ *
+ * @type {string}
+ */
+ let classStr = 'default';
+ if (vertex.classes.length > 0) {
+ classStr = vertex.classes.join(' ');
+ }
+ classStr = classStr + ' flowchart-label';
+ const styles = getStylesFromArray(vertex.styles);
- // Use vertex id as text in the box if no text is provided by the graph definition
- let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
+ // Use vertex id as text in the box if no text is provided by the graph definition
+ let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
- // We create a SVG label, either by delegating to addHtmlLabel or manually
- let vertexNode;
- const labelData = { width: 0, height: 0 };
+ // We create a SVG label, either by delegating to addHtmlLabel or manually
+ let vertexNode;
+ const labelData = { width: 0, height: 0 };
- const ports = [
- {
- id: vertex.id + '-west',
- layoutOptions: {
- 'port.side': 'WEST',
+ const ports = [
+ {
+ id: vertex.id + '-west',
+ layoutOptions: {
+ 'port.side': 'WEST',
+ },
},
- },
- {
- id: vertex.id + '-east',
- layoutOptions: {
- 'port.side': 'EAST',
+ {
+ id: vertex.id + '-east',
+ layoutOptions: {
+ 'port.side': 'EAST',
+ },
},
- },
- {
- id: vertex.id + '-south',
- layoutOptions: {
- 'port.side': 'SOUTH',
+ {
+ id: vertex.id + '-south',
+ layoutOptions: {
+ 'port.side': 'SOUTH',
+ },
},
- },
- {
- id: vertex.id + '-north',
- layoutOptions: {
- 'port.side': 'NORTH',
+ {
+ id: vertex.id + '-north',
+ layoutOptions: {
+ 'port.side': 'NORTH',
+ },
},
- },
- ];
+ ];
- let radious = 0;
- let _shape = '';
- let layoutOptions = {};
- // Set the shape based parameters
- switch (vertex.type) {
- case 'round':
- radious = 5;
- _shape = 'rect';
- break;
- case 'square':
- _shape = 'rect';
- break;
- case 'diamond':
- _shape = 'question';
- layoutOptions = {
- portConstraints: 'FIXED_SIDE',
- };
- break;
- case 'hexagon':
- _shape = 'hexagon';
- break;
- case 'odd':
- _shape = 'rect_left_inv_arrow';
- break;
- case 'lean_right':
- _shape = 'lean_right';
- break;
- case 'lean_left':
- _shape = 'lean_left';
- break;
- case 'trapezoid':
- _shape = 'trapezoid';
- break;
- case 'inv_trapezoid':
- _shape = 'inv_trapezoid';
- break;
- case 'odd_right':
- _shape = 'rect_left_inv_arrow';
- break;
- case 'circle':
- _shape = 'circle';
- break;
- case 'ellipse':
- _shape = 'ellipse';
- break;
- case 'stadium':
- _shape = 'stadium';
- break;
- case 'subroutine':
- _shape = 'subroutine';
- break;
- case 'cylinder':
- _shape = 'cylinder';
- break;
- case 'group':
- _shape = 'rect';
- break;
- case 'doublecircle':
- _shape = 'doublecircle';
- break;
- default:
- _shape = 'rect';
- }
+ let radious = 0;
+ let _shape = '';
+ let layoutOptions = {};
+ // Set the shape based parameters
+ switch (vertex.type) {
+ case 'round':
+ radious = 5;
+ _shape = 'rect';
+ break;
+ case 'square':
+ _shape = 'rect';
+ break;
+ case 'diamond':
+ _shape = 'question';
+ layoutOptions = {
+ portConstraints: 'FIXED_SIDE',
+ };
+ break;
+ case 'hexagon':
+ _shape = 'hexagon';
+ break;
+ case 'odd':
+ _shape = 'rect_left_inv_arrow';
+ break;
+ case 'lean_right':
+ _shape = 'lean_right';
+ break;
+ case 'lean_left':
+ _shape = 'lean_left';
+ break;
+ case 'trapezoid':
+ _shape = 'trapezoid';
+ break;
+ case 'inv_trapezoid':
+ _shape = 'inv_trapezoid';
+ break;
+ case 'odd_right':
+ _shape = 'rect_left_inv_arrow';
+ break;
+ case 'circle':
+ _shape = 'circle';
+ break;
+ case 'ellipse':
+ _shape = 'ellipse';
+ break;
+ case 'stadium':
+ _shape = 'stadium';
+ break;
+ case 'subroutine':
+ _shape = 'subroutine';
+ break;
+ case 'cylinder':
+ _shape = 'cylinder';
+ break;
+ case 'group':
+ _shape = 'rect';
+ break;
+ case 'doublecircle':
+ _shape = 'doublecircle';
+ break;
+ default:
+ _shape = 'rect';
+ }
- // Add the node
- const node = {
- labelStyle: styles.labelStyle,
- shape: _shape,
- labelText: vertexText,
- labelType: vertex.labelType,
- rx: radious,
- ry: radious,
- class: classStr,
- style: styles.style,
- id: vertex.id,
- link: vertex.link,
- linkTarget: vertex.linkTarget,
- tooltip: diagObj.db.getTooltip(vertex.id) || '',
- domId: diagObj.db.lookUpDomId(vertex.id),
- haveCallback: vertex.haveCallback,
- width: vertex.type === 'group' ? 500 : undefined,
- dir: vertex.dir,
- type: vertex.type,
- props: vertex.props,
- padding: getConfig().flowchart.padding,
- };
- let boundingBox;
- let nodeEl;
+ // Add the node
+ const node = {
+ labelStyle: styles.labelStyle,
+ shape: _shape,
+ labelText: vertexText,
+ labelType: vertex.labelType,
+ rx: radious,
+ ry: radious,
+ class: classStr,
+ style: styles.style,
+ id: vertex.id,
+ link: vertex.link,
+ linkTarget: vertex.linkTarget,
+ tooltip: diagObj.db.getTooltip(vertex.id) || '',
+ domId: diagObj.db.lookUpDomId(vertex.id),
+ haveCallback: vertex.haveCallback,
+ width: vertex.type === 'group' ? 500 : undefined,
+ dir: vertex.dir,
+ type: vertex.type,
+ props: vertex.props,
+ padding: getConfig().flowchart.padding,
+ };
+ let boundingBox;
+ let nodeEl;
- // Add the element to the DOM
- if (node.type !== 'group') {
- nodeEl = insertNode(nodes, node, vertex.dir);
- boundingBox = nodeEl.node().getBBox();
- } else {
- 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 (const row of rows) {
- // 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');
- // tspan.textContent = row;
- // svgLabel.appendChild(tspan);
+ // Add the element to the DOM
+ if (node.type !== 'group') {
+ nodeEl = insertNode(nodes, node, vertex.dir);
+ boundingBox = nodeEl.node().getBBox();
+ } else {
+ 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 (const row of rows) {
+ // 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');
+ // tspan.textContent = row;
+ // svgLabel.appendChild(tspan);
+ // }
+ // vertexNode = svgLabel;
+ // const bbox = vertexNode.getBBox();
+ const { shapeSvg, bbox } = await labelHelper(nodes, node, undefined, true);
+ labelData.width = bbox.width;
+ labelData.wrappingWidth = getConfig().flowchart.wrappingWidth;
+ labelData.height = bbox.height;
+ labelData.labelNode = shapeSvg.node();
+ node.labelData = labelData;
+ }
+ // const { shapeSvg, bbox } = await labelHelper(svg, node, undefined, true);
+
+ const data = {
+ id: vertex.id,
+ ports: vertex.type === 'diamond' ? ports : [],
+ // labelStyle: styles.labelStyle,
+ // shape: _shape,
+ layoutOptions,
+ labelText: vertexText,
+ labelData,
+ // labels: [{ text: vertexText }],
+ // rx: radius,
+ // ry: radius,
+ // class: classStr,
+ // style: styles.style,
+ // link: vertex.link,
+ // linkTarget: vertex.linkTarget,
+ // tooltip: diagObj.db.getTooltip(vertex.id) || '',
+ domId: diagObj.db.lookUpDomId(vertex.id),
+ // haveCallback: vertex.haveCallback,
+ width: boundingBox?.width,
+ height: boundingBox?.height,
+ // dir: vertex.dir,
+ type: vertex.type,
+ // props: vertex.props,
+ // padding: getConfig().flowchart.padding,
+ // boundingBox,
+ el: nodeEl,
+ parent: parentLookupDb.parentById[vertex.id],
+ };
+ // if (!Object.keys(parentLookupDb.childrenById).includes(vertex.id)) {
+ // graph.children.push({
+ // ...data,
+ // });
// }
- // vertexNode = svgLabel;
- // const bbox = vertexNode.getBBox();
- const { shapeSvg, bbox } = labelHelper(nodes, node, undefined, true);
- labelData.width = bbox.width;
- labelData.wrappingWidth = getConfig().flowchart.wrappingWidth;
- labelData.height = bbox.height;
- labelData.labelNode = shapeSvg.node();
- node.labelData = labelData;
- }
- // const { shapeSvg, bbox } = labelHelper(svg, node, undefined, true);
-
- const data = {
- id: vertex.id,
- ports: vertex.type === 'diamond' ? ports : [],
- // labelStyle: styles.labelStyle,
- // shape: _shape,
- layoutOptions,
- labelText: vertexText,
- labelData,
- // labels: [{ text: vertexText }],
- // rx: radius,
- // ry: radius,
- // class: classStr,
- // style: styles.style,
- // link: vertex.link,
- // linkTarget: vertex.linkTarget,
- // tooltip: diagObj.db.getTooltip(vertex.id) || '',
- domId: diagObj.db.lookUpDomId(vertex.id),
- // haveCallback: vertex.haveCallback,
- width: boundingBox?.width,
- height: boundingBox?.height,
- // dir: vertex.dir,
- type: vertex.type,
- // props: vertex.props,
- // padding: getConfig().flowchart.padding,
- // boundingBox,
- el: nodeEl,
- parent: parentLookupDb.parentById[vertex.id],
- };
- // if (!Object.keys(parentLookupDb.childrenById).includes(vertex.id)) {
- // graph.children.push({
- // ...data,
- // });
- // }
- nodeDb[node.id] = data;
- // log.trace('setNode', {
- // labelStyle: styles.labelStyle,
- // shape: _shape,
- // labelText: vertexText,
- // rx: radius,
- // ry: radius,
- // class: classStr,
- // style: styles.style,
- // id: vertex.id,
- // domId: diagObj.db.lookUpDomId(vertex.id),
- // width: vertex.type === 'group' ? 500 : undefined,
- // type: vertex.type,
- // dir: vertex.dir,
- // props: vertex.props,
- // padding: getConfig().flowchart.padding,
- // parent: parentLookupDb.parentById[vertex.id],
- // });
- });
+ nodeDb[node.id] = data;
+ // log.trace('setNode', {
+ // labelStyle: styles.labelStyle,
+ // shape: _shape,
+ // labelText: vertexText,
+ // rx: radius,
+ // ry: radius,
+ // class: classStr,
+ // style: styles.style,
+ // id: vertex.id,
+ // domId: diagObj.db.lookUpDomId(vertex.id),
+ // width: vertex.type === 'group' ? 500 : undefined,
+ // type: vertex.type,
+ // dir: vertex.dir,
+ // props: vertex.props,
+ // padding: getConfig().flowchart.padding,
+ // parent: parentLookupDb.parentById[vertex.id],
+ // });
+ })
+ );
return graph;
};
@@ -861,7 +863,7 @@ export const draw = async function (text, id, _version, diagObj) {
// in order to get the size of the node. You can't get the size of a node
// that is not in the dom so we need to add it to the dom, get the size
// we will position the nodes when we get the layout from elkjs
- graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph, svg);
+ graph = await addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph);
// Time for the edges, we start with adding an element in the node to hold the edges
const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js
index a87f23e04..ce7aebb5c 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js
+++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js
@@ -362,7 +362,7 @@ export const getClasses = function (text, diagObj) {
* @param id
*/
-export const draw = function (text, id, _version, diagObj) {
+export const draw = async function (text, id, _version, diagObj) {
log.info('Drawing flowchart');
diagObj.db.clear();
flowDb.setGen('gen-2');
@@ -451,7 +451,7 @@ export const draw = function (text, id, _version, diagObj) {
// Run the renderer. This is what draws the final graph.
const element = root.select('#' + id + ' g');
- render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
+ await render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
diff --git a/packages/mermaid/src/diagrams/state/stateRenderer-v2.js b/packages/mermaid/src/diagrams/state/stateRenderer-v2.js
index c2b1a9b6d..1c617cfb4 100644
--- a/packages/mermaid/src/diagrams/state/stateRenderer-v2.js
+++ b/packages/mermaid/src/diagrams/state/stateRenderer-v2.js
@@ -382,7 +382,7 @@ const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
* @param _version
* @param diag
*/
-export const draw = function (text, id, _version, diag) {
+export const draw = async function (text, id, _version, diag) {
log.info('Drawing state diagram (v2)', id);
// diag.sb.clear();
nodeDb = {};
@@ -436,7 +436,7 @@ export const draw = function (text, id, _version, diag) {
// Run the renderer. This is what draws the final graph.
const element = root.select('#' + id + ' g');
- render(element, g, ['barb'], CSS_DIAGRAM, id);
+ await render(element, g, ['barb'], CSS_DIAGRAM, id);
const padding = 8;
diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts
index b015994ac..a037995df 100644
--- a/packages/mermaid/src/mermaidAPI.ts
+++ b/packages/mermaid/src/mermaidAPI.ts
@@ -406,6 +406,12 @@ const render = async function (
// clean up text CRLFs
text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;;
+ // clean up html tags so that all attributes use single quotes, parser throws error on double quotes
+ text = text.replace(
+ /<(\w+)([^>]*)>/g,
+ (match, tag, attributes) => '<' + tag + attributes.replace(/="([^"]*)"/g, "='$1'") + '>'
+ );
+
const idSelector = '#' + id;
const iFrameID = 'i' + id;
const iFrameID_selector = '#' + iFrameID;