convert flowDb to class.

This commit is contained in:
Saurabh Gore 2024-12-31 17:40:52 +05:30
parent df636c6d0a
commit 9ef6090c8c
19 changed files with 991 additions and 1008 deletions

View File

@ -1,9 +1,11 @@
import flowDb from './flowDb.js';
import { FlowDb } from './flowDb.js';
import type { FlowSubGraph } from './types.js';
describe('flow db subgraphs', () => {
let flowDb: FlowDb;
let subgraphs: FlowSubGraph[];
beforeEach(() => {
flowDb = new FlowDb();
subgraphs = [
{ nodes: ['a', 'b', 'c', 'e'] },
{ nodes: ['f', 'g', 'h'] },
@ -44,8 +46,9 @@ describe('flow db subgraphs', () => {
});
describe('flow db addClass', () => {
let flowDb: FlowDb;
beforeEach(() => {
flowDb.clear();
flowDb = new FlowDb();
});
it('should detect many classes', () => {
flowDb.addClass('a,b', ['stroke-width: 8px']);

View File

@ -27,43 +27,54 @@ import type {
import type { NodeMetaData } from '../../types.js';
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
let vertexCounter = 0;
let config = getConfig();
let vertices = new Map<string, FlowVertex>();
let edges: FlowEdge[] & { defaultInterpolate?: string; defaultStyle?: string[] } = [];
let classes = new Map<string, FlowClass>();
let subGraphs: FlowSubGraph[] = [];
let subGraphLookup = new Map<string, FlowSubGraph>();
let tooltips = new Map<string, string>();
let subCount = 0;
let firstGraphFlag = true;
let direction: string;
let version: string; // As in graph
export class FlowDb {
private vertexCounter = 0;
private config = getConfig();
private vertices = new Map<string, FlowVertex>();
private edges: FlowEdge[] & { defaultInterpolate?: string; defaultStyle?: string[] } = [];
private classes = new Map<string, FlowClass>();
private subGraphs: FlowSubGraph[] = [];
private subGraphLookup = new Map<string, FlowSubGraph>();
private tooltips = new Map<string, string>();
private subCount = 0;
private firstGraphFlag = true;
private direction: string | undefined;
// Functions to be run after graph rendering
let funs: ((element: Element) => void)[] = []; // cspell:ignore funs
private version: string | undefined; // As in graph
const sanitizeText = (txt: string) => common.sanitizeText(txt, config);
private secCount = -1;
private posCrossRef: number[] = [];
/**
// Functions to be run after graph rendering
private funs: ((element: Element) => void)[] = []; // cspell:ignore funs
constructor() {
this.funs.push(this.setupToolTips);
this.clear();
this.setGen('gen-2');
}
private sanitizeText = (txt: string) => common.sanitizeText(txt, this.config);
/**
* Function to lookup domId from id in the graph definition.
*
* @param id - id of the node
*/
export const lookUpDomId = function (id: string) {
for (const vertex of vertices.values()) {
public lookUpDomId = (id: string) => {
for (const vertex of this.vertices.values()) {
if (vertex.id === id) {
return vertex.domId;
}
}
return id;
};
};
/**
/**
* Function called by parser when a node definition has been found
*/
export const addVertex = function (
public addVertex = (
id: string,
textObj: FlowText,
type: FlowVertexTypeParam,
@ -72,29 +83,29 @@ export const addVertex = function (
dir: string,
props = {},
shapeData: any
) {
) => {
// console.log('addVertex', id, shapeData);
if (!id || id.trim().length === 0) {
return;
}
let txt;
let vertex = vertices.get(id);
let vertex = this.vertices.get(id);
if (vertex === undefined) {
vertex = {
id,
labelType: 'text',
domId: MERMAID_DOM_ID_PREFIX + id + '-' + vertexCounter,
domId: MERMAID_DOM_ID_PREFIX + id + '-' + this.vertexCounter,
styles: [],
classes: [],
};
vertices.set(id, vertex);
this.vertices.set(id, vertex);
}
vertexCounter++;
this.vertexCounter++;
if (textObj !== undefined) {
config = getConfig();
txt = sanitizeText(textObj.text.trim());
this.config = getConfig();
txt = this.sanitizeText(textObj.text.trim());
vertex.labelType = textObj.type;
// strip quotes if string starts and ends with a quote
if (txt.startsWith('"') && txt.endsWith('"')) {
@ -110,12 +121,12 @@ export const addVertex = function (
vertex.type = type;
}
if (style !== undefined && style !== null) {
style.forEach(function (s) {
style.forEach((s) => {
vertex.styles.push(s);
});
}
if (classes !== undefined && classes !== null) {
classes.forEach(function (s) {
classes.forEach((s) => {
vertex.classes.push(s);
});
}
@ -181,13 +192,13 @@ export const addVertex = function (
vertex.assetHeight = Number(doc.h);
}
}
};
};
/**
/**
* Function called by parser when a link/edge definition has been found
*
*/
export const addSingleLink = function (_start: string, _end: string, type: any) {
private addSingleLink = (_start: string, _end: string, type: any) => {
const start = _start;
const end = _end;
@ -196,7 +207,7 @@ export const addSingleLink = function (_start: string, _end: string, type: any)
const linkTextObj = type.text;
if (linkTextObj !== undefined) {
edge.text = sanitizeText(linkTextObj.text.trim());
edge.text = this.sanitizeText(linkTextObj.text.trim());
// strip quotes if string starts and ends with a quote
if (edge.text.startsWith('"') && edge.text.endsWith('"')) {
@ -211,87 +222,84 @@ export const addSingleLink = function (_start: string, _end: string, type: any)
edge.length = type.length > 10 ? 10 : type.length;
}
if (edges.length < (config.maxEdges ?? 500)) {
if (this.edges.length < (this.config.maxEdges ?? 500)) {
log.info('Pushing edge...');
edges.push(edge);
this.edges.push(edge);
} else {
throw new Error(
`Edge limit exceeded. ${edges.length} edges found, but the limit is ${config.maxEdges}.
`Edge limit exceeded. ${this.edges.length} edges found, but the limit is ${this.config.maxEdges}.
Initialize mermaid with maxEdges set to a higher number to allow more edges.
You cannot set this config via configuration inside the diagram as it is a secure config.
You have to call mermaid.initialize.`
);
}
};
};
export const addLink = function (_start: string[], _end: string[], type: unknown) {
public addLink = (_start: string[], _end: string[], type: unknown) => {
log.info('addLink', _start, _end, type);
for (const start of _start) {
for (const end of _end) {
addSingleLink(start, end, type);
this.addSingleLink(start, end, type);
}
}
};
};
/**
/**
* Updates a link's line interpolation algorithm
*
*/
export const updateLinkInterpolate = function (
positions: ('default' | number)[],
interpolate: string
) {
positions.forEach(function (pos) {
public updateLinkInterpolate = (positions: ('default' | number)[], interpolate: string) => {
positions.forEach((pos) => {
if (pos === 'default') {
edges.defaultInterpolate = interpolate;
this.edges.defaultInterpolate = interpolate;
} else {
edges[pos].interpolate = interpolate;
this.edges[pos].interpolate = interpolate;
}
});
};
};
/**
/**
* Updates a link with a style
*
*/
export const updateLink = function (positions: ('default' | number)[], style: string[]) {
positions.forEach(function (pos) {
if (typeof pos === 'number' && pos >= edges.length) {
public updateLink = (positions: ('default' | number)[], style: string[]) => {
positions.forEach((pos) => {
if (typeof pos === 'number' && pos >= this.edges.length) {
throw new Error(
`The index ${pos} for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and ${
edges.length - 1
this.edges.length - 1
}. (Help: Ensure that the index is within the range of existing edges.)`
);
}
if (pos === 'default') {
edges.defaultStyle = style;
this.edges.defaultStyle = style;
} else {
// if (utils.isSubstringInArray('fill', style) === -1) {
// style.push('fill:none');
// }
edges[pos].style = style;
this.edges[pos].style = style;
// if edges[pos].style does have fill not set, set it to none
if (
(edges[pos]?.style?.length ?? 0) > 0 &&
!edges[pos]?.style?.some((s) => s?.startsWith('fill'))
(this.edges[pos]?.style?.length ?? 0) > 0 &&
!this.edges[pos]?.style?.some((s) => s?.startsWith('fill'))
) {
edges[pos]?.style?.push('fill:none');
this.edges[pos]?.style?.push('fill:none');
}
}
});
};
};
export const addClass = function (ids: string, style: string[]) {
ids.split(',').forEach(function (id) {
let classNode = classes.get(id);
public addClass = (ids: string, style: string[]) => {
ids.split(',').forEach((id) => {
let classNode = this.classes.get(id);
if (classNode === undefined) {
classNode = { id, styles: [], textStyles: [] };
classes.set(id, classNode);
this.classes.set(id, classNode);
}
if (style !== undefined && style !== null) {
style.forEach(function (s) {
style.forEach((s) => {
if (/color/.exec(s)) {
const newStyle = s.replace('fill', 'bgFill'); // .replace('color', 'fill');
classNode.textStyles.push(newStyle);
@ -300,62 +308,62 @@ export const addClass = function (ids: string, style: string[]) {
});
}
});
};
};
/**
/**
* Called by parser when a graph definition is found, stores the direction of the chart.
*
*/
export const setDirection = function (dir: string) {
direction = dir;
if (/.*</.exec(direction)) {
direction = 'RL';
public setDirection = (dir: string) => {
this.direction = dir;
if (/.*</.exec(this.direction)) {
this.direction = 'RL';
}
if (/.*\^/.exec(direction)) {
direction = 'BT';
if (/.*\^/.exec(this.direction)) {
this.direction = 'BT';
}
if (/.*>/.exec(direction)) {
direction = 'LR';
if (/.*>/.exec(this.direction)) {
this.direction = 'LR';
}
if (/.*v/.exec(direction)) {
direction = 'TB';
if (/.*v/.exec(this.direction)) {
this.direction = 'TB';
}
if (direction === 'TD') {
direction = 'TB';
if (this.direction === 'TD') {
this.direction = 'TB';
}
};
};
/**
/**
* Called by parser when a special node is found, e.g. a clickable element.
*
* @param ids - Comma separated list of ids
* @param className - Class to add
*/
export const setClass = function (ids: string, className: string) {
public setClass = (ids: string, className: string) => {
for (const id of ids.split(',')) {
const vertex = vertices.get(id);
const vertex = this.vertices.get(id);
if (vertex) {
vertex.classes.push(className);
}
const subGraph = subGraphLookup.get(id);
const subGraph = this.subGraphLookup.get(id);
if (subGraph) {
subGraph.classes.push(className);
}
}
};
};
const setTooltip = function (ids: string, tooltip: string) {
public setTooltip = (ids: string, tooltip: string) => {
if (tooltip === undefined) {
return;
}
tooltip = sanitizeText(tooltip);
tooltip = this.sanitizeText(tooltip);
for (const id of ids.split(',')) {
tooltips.set(version === 'gen-1' ? lookUpDomId(id) : id, tooltip);
this.tooltips.set(this.version === 'gen-1' ? this.lookUpDomId(id) : id, tooltip);
}
};
};
const setClickFun = function (id: string, functionName: string, functionArgs: string) {
const domId = lookUpDomId(id);
private setClickFun = (id: string, functionName: string, functionArgs: string) => {
const domId = this.lookUpDomId(id);
// if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
if (getConfig().securityLevel !== 'loose') {
return;
@ -383,15 +391,15 @@ const setClickFun = function (id: string, functionName: string, functionArgs: st
argList.push(id);
}
const vertex = vertices.get(id);
const vertex = this.vertices.get(id);
if (vertex) {
vertex.haveCallback = true;
funs.push(function () {
this.funs.push(() => {
const elem = document.querySelector(`[id="${domId}"]`);
if (elem !== null) {
elem.addEventListener(
'click',
function () {
() => {
utils.runFunc(functionName, ...argList);
},
false
@ -399,97 +407,100 @@ const setClickFun = function (id: string, functionName: string, functionArgs: st
}
});
}
};
};
/**
/**
* Called by parser when a link is found. Adds the URL to the vertex data.
*
* @param ids - Comma separated list of ids
* @param linkStr - URL to create a link for
* @param target - Target attribute for the link
*/
export const setLink = function (ids: string, linkStr: string, target: string) {
ids.split(',').forEach(function (id) {
const vertex = vertices.get(id);
public setLink = (ids: string, linkStr: string, target: string) => {
ids.split(',').forEach((id) => {
const vertex = this.vertices.get(id);
if (vertex !== undefined) {
vertex.link = utils.formatUrl(linkStr, config);
vertex.link = utils.formatUrl(linkStr, this.config);
vertex.linkTarget = target;
}
});
setClass(ids, 'clickable');
};
this.setClass(ids, 'clickable');
};
export const getTooltip = function (id: string) {
return tooltips.get(id);
};
public getTooltip = (id: string) => {
return this.tooltips.get(id);
};
/**
/**
* Called by parser when a click definition is found. Registers an event handler.
*
* @param ids - Comma separated list of ids
* @param functionName - Function to be called on click
* @param functionArgs - Arguments to be passed to the function
*/
export const setClickEvent = function (ids: string, functionName: string, functionArgs: string) {
ids.split(',').forEach(function (id) {
setClickFun(id, functionName, functionArgs);
public setClickEvent = (ids: string, functionName: string, functionArgs: string) => {
ids.split(',').forEach((id) => {
this.setClickFun(id, functionName, functionArgs);
});
setClass(ids, 'clickable');
};
this.setClass(ids, 'clickable');
};
export const bindFunctions = function (element: Element) {
funs.forEach(function (fun) {
public bindFunctions = (element: Element) => {
this.funs.forEach((fun) => {
fun(element);
});
};
export const getDirection = function () {
return direction.trim();
};
/**
};
public getDirection = () => {
return this.direction?.trim();
};
/**
* Retrieval function for fetching the found nodes after parsing has completed.
*
*/
export const getVertices = function () {
return vertices;
};
public getVertices = () => {
return this.vertices;
};
/**
/**
* Retrieval function for fetching the found links after parsing has completed.
*
*/
export const getEdges = function () {
return edges;
};
public getEdges = () => {
return this.edges;
};
/**
/**
* Retrieval function for fetching the found class definitions after parsing has completed.
*
*/
export const getClasses = function () {
return classes;
};
public getClasses = () => {
return this.classes;
};
const setupToolTips = function (element: Element) {
private setupToolTips = (element: Element) => {
let tooltipElem = select('.mermaidTooltip');
// @ts-ignore TODO: fix this
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
// @ts-ignore TODO: fix this
tooltipElem = select('body').append('div').attr('class', 'mermaidTooltip').style('opacity', 0);
tooltipElem = select('body')
.append('div')
.attr('class', 'mermaidTooltip')
.style('opacity', 0);
}
const svg = select(element).select('svg');
const nodes = svg.selectAll('g.node');
nodes
.on('mouseover', function () {
const el = select(this);
.on('mouseover', (e: MouseEvent) => {
const el = select(e.currentTarget as Element);
const title = el.attr('title');
// Don't try to draw a tooltip if no data is provided
if (title === null) {
return;
}
const rect = (this as Element)?.getBoundingClientRect();
const rect = (e.currentTarget as Element)?.getBoundingClientRect();
tooltipElem.transition().duration(200).style('opacity', '.9');
tooltipElem
@ -499,53 +510,52 @@ const setupToolTips = function (element: Element) {
tooltipElem.html(tooltipElem.html().replace(/&lt;br\/&gt;/g, '<br/>'));
el.classed('hover', true);
})
.on('mouseout', function () {
.on('mouseout', (e: MouseEvent) => {
tooltipElem.transition().duration(500).style('opacity', 0);
const el = select(this);
const el = select(e.currentTarget as Element);
el.classed('hover', false);
});
};
funs.push(setupToolTips);
};
/**
/**
* Clears the internal graph db so that a new graph can be parsed.
*
*/
export const clear = function (ver = 'gen-1') {
vertices = new Map();
classes = new Map();
edges = [];
funs = [setupToolTips];
subGraphs = [];
subGraphLookup = new Map();
subCount = 0;
tooltips = new Map();
firstGraphFlag = true;
version = ver;
config = getConfig();
public clear = (ver = 'gen-1') => {
this.vertices = new Map();
this.classes = new Map();
this.edges = [];
this.funs = [this.setupToolTips];
this.subGraphs = [];
this.subGraphLookup = new Map();
this.subCount = 0;
this.tooltips = new Map();
this.firstGraphFlag = true;
this.version = ver;
this.config = getConfig();
commonClear();
};
};
export const setGen = (ver: string) => {
version = ver || 'gen-2';
};
public setGen = (ver: string) => {
this.version = ver || 'gen-2';
};
export const defaultStyle = function () {
public defaultStyle = () => {
return 'fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;';
};
};
export const addSubGraph = function (
public addSubGraph = (
_id: { text: string },
list: string[],
_title: { text: string; type: string }
) {
) => {
let id: string | undefined = _id.text.trim();
let title = _title.text;
if (_id === _title && /\s/.exec(_title.text)) {
id = undefined;
}
function uniq(a: any[]) {
const uniq = (a: any[]) => {
const prims: any = { boolean: {}, number: {}, string: {} };
const objs: any[] = [];
@ -566,19 +576,19 @@ export const addSubGraph = function (
}
});
return { nodeList, dir };
}
};
const { nodeList, dir } = uniq(list.flat());
if (version === 'gen-1') {
if (this.version === 'gen-1') {
for (let i = 0; i < nodeList.length; i++) {
nodeList[i] = lookUpDomId(nodeList[i]);
nodeList[i] = this.lookUpDomId(nodeList[i]);
}
}
id = id ?? 'subGraph' + subCount;
id = id ?? 'subGraph' + this.subCount;
title = title || '';
title = sanitizeText(title);
subCount = subCount + 1;
title = this.sanitizeText(title);
this.subCount = this.subCount + 1;
const subGraph = {
id: id,
nodes: nodeList,
@ -591,34 +601,33 @@ export const addSubGraph = function (
log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir);
// Remove the members in the new subgraph if they already belong to another subgraph
subGraph.nodes = makeUniq(subGraph, subGraphs).nodes;
subGraphs.push(subGraph);
subGraphLookup.set(id, subGraph);
subGraph.nodes = this.makeUniq(subGraph, this.subGraphs).nodes;
this.subGraphs.push(subGraph);
this.subGraphLookup.set(id, subGraph);
return id;
};
};
const getPosForId = function (id: string) {
for (const [i, subGraph] of subGraphs.entries()) {
private getPosForId = (id: string) => {
for (const [i, subGraph] of this.subGraphs.entries()) {
if (subGraph.id === id) {
return i;
}
}
return -1;
};
let secCount = -1;
const posCrossRef: number[] = [];
const indexNodes2 = function (id: string, pos: number): { result: boolean; count: number } {
const nodes = subGraphs[pos].nodes;
secCount = secCount + 1;
if (secCount > 2000) {
};
private indexNodes2 = (id: string, pos: number): { result: boolean; count: number } => {
const nodes = this.subGraphs[pos].nodes;
this.secCount = this.secCount + 1;
if (this.secCount > 2000) {
return {
result: false,
count: 0,
};
}
posCrossRef[secCount] = pos;
this.posCrossRef[this.secCount] = pos;
// Check if match
if (subGraphs[pos].id === id) {
if (this.subGraphs[pos].id === id) {
return {
result: true,
count: 0,
@ -628,10 +637,10 @@ const indexNodes2 = function (id: string, pos: number): { result: boolean; count
let count = 0;
let posCount = 1;
while (count < nodes.length) {
const childPos = getPosForId(nodes[count]);
const childPos = this.getPosForId(nodes[count]);
// Ignore regular nodes (pos will be -1)
if (childPos >= 0) {
const res = indexNodes2(id, childPos);
const res = this.indexNodes2(id, childPos);
if (res.result) {
return {
result: true,
@ -648,31 +657,31 @@ const indexNodes2 = function (id: string, pos: number): { result: boolean; count
result: false,
count: posCount,
};
};
};
export const getDepthFirstPos = function (pos: number) {
return posCrossRef[pos];
};
export const indexNodes = function () {
secCount = -1;
if (subGraphs.length > 0) {
indexNodes2('none', subGraphs.length - 1);
public getDepthFirstPos = (pos: number) => {
return this.posCrossRef[pos];
};
public indexNodes = () => {
this.secCount = -1;
if (this.subGraphs.length > 0) {
this.indexNodes2('none', this.subGraphs.length - 1);
}
};
};
export const getSubGraphs = function () {
return subGraphs;
};
public getSubGraphs = () => {
return this.subGraphs;
};
export const firstGraph = () => {
if (firstGraphFlag) {
firstGraphFlag = false;
public firstGraph = () => {
if (this.firstGraphFlag) {
this.firstGraphFlag = false;
return true;
}
return false;
};
};
const destructStartLink = (_str: string): FlowLink => {
private destructStartLink = (_str: string): FlowLink => {
let str = _str.trim();
let type = 'arrow_open';
@ -702,9 +711,9 @@ const destructStartLink = (_str: string): FlowLink => {
}
return { type, stroke };
};
};
const countChar = (char: string, str: string) => {
private countChar = (char: string, str: string) => {
const length = str.length;
let count = 0;
for (let i = 0; i < length; ++i) {
@ -713,9 +722,9 @@ const countChar = (char: string, str: string) => {
}
}
return count;
};
};
const destructEndLink = (_str: string) => {
private destructEndLink = (_str: string) => {
const str = _str.trim();
let line = str.slice(0, -1);
let type = 'arrow_open';
@ -755,7 +764,7 @@ const destructEndLink = (_str: string) => {
stroke = 'invisible';
}
const dots = countChar('.', line);
const dots = this.countChar('.', line);
if (dots) {
stroke = 'dotted';
@ -763,13 +772,13 @@ const destructEndLink = (_str: string) => {
}
return { type, stroke, length };
};
};
export const destructLink = (_str: string, _startStr: string) => {
const info = destructEndLink(_str);
public destructLink = (_str: string, _startStr: string) => {
const info = this.destructEndLink(_str);
let startInfo;
if (_startStr) {
startInfo = destructStartLink(_startStr);
startInfo = this.destructStartLink(_startStr);
if (startInfo.stroke !== info.stroke) {
return { type: 'INVALID', stroke: 'INVALID' };
@ -796,36 +805,36 @@ export const destructLink = (_str: string, _startStr: string) => {
}
return info;
};
};
// Todo optimizer this by caching existing nodes
const exists = (allSgs: FlowSubGraph[], _id: string) => {
// Todo optimizer this by caching existing nodes
public exists = (allSgs: FlowSubGraph[], _id: string) => {
for (const sg of allSgs) {
if (sg.nodes.includes(_id)) {
return true;
}
}
return false;
};
/**
};
/**
* Deletes an id from all subgraphs
*
*/
const makeUniq = (sg: FlowSubGraph, allSubgraphs: FlowSubGraph[]) => {
public makeUniq = (sg: FlowSubGraph, allSubgraphs: FlowSubGraph[]) => {
const res: string[] = [];
sg.nodes.forEach((_id, pos) => {
if (!exists(allSubgraphs, _id)) {
if (!this.exists(allSubgraphs, _id)) {
res.push(sg.nodes[pos]);
}
});
return { nodes: res };
};
};
export const lex = {
firstGraph,
};
public lex = {
firstGraph: this.firstGraph,
};
const getTypeFromVertex = (vertex: FlowVertex): ShapeID => {
private getTypeFromVertex = (vertex: FlowVertex): ShapeID => {
if (vertex.img) {
return 'imageSquare';
}
@ -853,10 +862,10 @@ const getTypeFromVertex = (vertex: FlowVertex): ShapeID => {
default:
return vertex.type;
}
};
};
const findNode = (nodes: Node[], id: string) => nodes.find((node) => node.id === id);
const destructEdgeType = (type: string | undefined) => {
private findNode = (nodes: Node[], id: string) => nodes.find((node) => node.id === id);
private destructEdgeType = (type: string | undefined) => {
let arrowTypeStart = 'none';
let arrowTypeEnd = 'arrow_point';
switch (type) {
@ -874,23 +883,23 @@ const destructEdgeType = (type: string | undefined) => {
break;
}
return { arrowTypeStart, arrowTypeEnd };
};
};
const addNodeFromVertex = (
private addNodeFromVertex = (
vertex: FlowVertex,
nodes: Node[],
parentDB: Map<string, string>,
subGraphDB: Map<string, boolean>,
config: any,
look: string
) => {
) => {
const parentId = parentDB.get(vertex.id);
const isGroup = subGraphDB.get(vertex.id) ?? false;
const node = findNode(nodes, vertex.id);
const node = this.findNode(nodes, vertex.id);
if (node) {
node.cssStyles = vertex.styles;
node.cssCompiledStyles = getCompiledStyles(vertex.classes);
node.cssCompiledStyles = this.getCompiledStyles(vertex.classes);
node.cssClasses = vertex.classes.join(' ');
} else {
const baseNode = {
@ -900,14 +909,14 @@ const addNodeFromVertex = (
parentId,
padding: config.flowchart?.padding || 8,
cssStyles: vertex.styles,
cssCompiledStyles: getCompiledStyles(['default', 'node', ...vertex.classes]),
cssCompiledStyles: this.getCompiledStyles(['default', 'node', ...vertex.classes]),
cssClasses: 'default ' + vertex.classes.join(' '),
dir: vertex.dir,
domId: vertex.domId,
look,
link: vertex.link,
linkTarget: vertex.linkTarget,
tooltip: getTooltip(vertex.id),
tooltip: this.getTooltip(vertex.id),
icon: vertex.icon,
pos: vertex.pos,
img: vertex.img,
@ -925,16 +934,16 @@ const addNodeFromVertex = (
nodes.push({
...baseNode,
isGroup: false,
shape: getTypeFromVertex(vertex),
shape: this.getTypeFromVertex(vertex),
});
}
}
};
};
function getCompiledStyles(classDefs: string[]) {
private getCompiledStyles = (classDefs: string[]) => {
let compiledStyles: string[] = [];
for (const customClass of classDefs) {
const cssClass = classes.get(customClass);
const cssClass = this.classes.get(customClass);
if (cssClass?.styles) {
compiledStyles = [...compiledStyles, ...(cssClass.styles ?? [])].map((s) => s.trim());
}
@ -943,14 +952,14 @@ function getCompiledStyles(classDefs: string[]) {
}
}
return compiledStyles;
}
};
export const getData = () => {
public getData = () => {
const config = getConfig();
const nodes: Node[] = [];
const edges: Edge[] = [];
const subGraphs = getSubGraphs();
const subGraphs = this.getSubGraphs();
const parentDB = new Map<string, string>();
const subGraphDB = new Map<string, boolean>();
@ -974,7 +983,7 @@ export const getData = () => {
labelStyle: '',
parentId: parentDB.get(subGraph.id),
padding: 8,
cssCompiledStyles: getCompiledStyles(subGraph.classes),
cssCompiledStyles: this.getCompiledStyles(subGraph.classes),
cssClasses: subGraph.classes.join(' '),
shape: 'rect',
dir: subGraph.dir,
@ -983,14 +992,14 @@ export const getData = () => {
});
}
const n = getVertices();
const n = this.getVertices();
n.forEach((vertex) => {
addNodeFromVertex(vertex, nodes, parentDB, subGraphDB, config, config.look || 'classic');
this.addNodeFromVertex(vertex, nodes, parentDB, subGraphDB, config, config.look || 'classic');
});
const e = getEdges();
const e = this.getEdges();
e.forEach((rawEdge, index) => {
const { arrowTypeStart, arrowTypeEnd } = destructEdgeType(rawEdge.type);
const { arrowTypeStart, arrowTypeEnd } = this.destructEdgeType(rawEdge.type);
const styles = [...(e.defaultStyle ?? [])];
if (rawEdge.style) {
@ -1021,43 +1030,13 @@ export const getData = () => {
});
return { nodes, edges, other: {}, config };
};
};
export default {
defaultConfig: () => defaultConfig.flowchart,
setAccTitle,
getAccTitle,
getAccDescription,
getData,
setAccDescription,
addVertex,
lookUpDomId,
addLink,
updateLinkInterpolate,
updateLink,
addClass,
setDirection,
setClass,
setTooltip,
getTooltip,
setClickEvent,
setLink,
bindFunctions,
getDirection,
getVertices,
getEdges,
getClasses,
clear,
setGen,
defaultStyle,
addSubGraph,
getDepthFirstPos,
indexNodes,
getSubGraphs,
destructLink,
lex,
exists,
makeUniq,
setDiagramTitle,
getDiagramTitle,
};
public defaultConfig = () => defaultConfig.flowchart;
public setAccTitle = setAccTitle;
public setAccDescription = setAccDescription;
public setDiagramTitle = setDiagramTitle;
public getAccTitle = getAccTitle;
public getAccDescription = getAccDescription;
public getDiagramTitle = getDiagramTitle;
}

View File

@ -1,6 +1,6 @@
import type { MermaidConfig } from '../../config.type.js';
import { setConfig } from '../../diagram-api/diagramAPI.js';
import flowDb from './flowDb.js';
import { FlowDb } from './flowDb.js';
import renderer from './flowRenderer-v3-unified.js';
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
@ -8,7 +8,9 @@ import flowStyles from './styles.js';
export const diagram = {
parser: flowParser,
db: flowDb,
get db() {
return new FlowDb();
},
renderer,
styles: flowStyles,
init: (cnf: MermaidConfig) => {
@ -20,7 +22,5 @@ export const diagram = {
}
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
flowDb.clear();
flowDb.setGen('gen-2');
},
};

View File

@ -7,7 +7,6 @@ import { getRegisteredLayoutAlgorithm, render } from '../../rendering-util/rende
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
import type { LayoutData } from '../../rendering-util/types.js';
import utils from '../../utils.js';
import { getDirection } from './flowDb.js';
export const getClasses = function (
text: string,
@ -37,7 +36,7 @@ export const draw = async function (text: string, id: string, _version: string,
log.debug('Data: ', data4Layout);
// Create the root SVG
const svg = getDiagramElement(id, securityLevel);
const direction = getDirection();
const direction = diag.db.getDirection();
data4Layout.type = diag.type;
data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout);

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('[Arrows] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
import { cleanupComments } from '../../../diagram-api/comments.js';
@ -9,7 +9,7 @@ setConfig({
describe('[Comments] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('when parsing directions', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -42,7 +42,7 @@ const doubleEndedEdges = [
describe('[Edges] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('[Text] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
import { vi } from 'vitest';
@ -9,7 +9,9 @@ setConfig({
});
describe('[Interactions] when parsing', () => {
let flowDb;
beforeEach(function () {
flowDb = new FlowDb();
flow.parser.yy = flowDb;
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('[Lines] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('parsing a flow chart with markdown strings', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('when parsing directions', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -31,7 +31,7 @@ const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/', '_
describe('[Singlenodes] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('[Style] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('[Text] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('when parsing flowcharts', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { cleanupComments } from '../../../diagram-api/comments.js';
import { setConfig } from '../../../config.js';
@ -9,7 +9,7 @@ setConfig({
describe('parsing a flow chart', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
});

View File

@ -1,4 +1,4 @@
import flowDb from '../flowDb.js';
import { FlowDb } from '../flowDb.js';
import flow from './flow.jison';
import { setConfig } from '../../../config.js';
@ -8,7 +8,7 @@ setConfig({
describe('when parsing subgraphs', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDb();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});