#931 Aligning with code standard

This commit is contained in:
knsv 2019-09-12 12:58:04 -07:00
parent cf05a8d8fa
commit 34de31195f
5 changed files with 2415 additions and 2327 deletions

View File

@ -1,33 +1,33 @@
import * as d3 from 'd3'
import { sanitizeUrl } from '@braintree/sanitize-url'
import { logger } from '../../logger'
import utils from '../../utils'
import { getConfig } from '../../config'
import * as d3 from 'd3';
import { sanitizeUrl } from '@braintree/sanitize-url';
import { logger } from '../../logger';
import utils from '../../utils';
import { getConfig } from '../../config';
const config = getConfig()
let vertices = {}
let edges = []
let classes = []
let subGraphs = []
let subGraphLookup = {}
let tooltips = {}
let subCount = 0
let direction
const config = getConfig();
let vertices = {};
let edges = [];
let classes = [];
let subGraphs = [];
let subGraphLookup = {};
let tooltips = {};
let subCount = 0;
let direction;
// Functions to be run after graph rendering
let funs = []
let funs = [];
const sanitize = text => {
let txt = text
let txt = text;
if (config.securityLevel !== 'loose') {
txt = txt.replace(/<br>/g, '#br#')
txt = txt.replace(/<br\S*?\/>/g, '#br#')
txt = txt.replace(/</g, '&lt;').replace(/>/g, '&gt;')
txt = txt.replace(/=/g, '&equals;')
txt = txt.replace(/#br#/g, '<br/>')
txt = txt.replace(/<br>/g, '#br#');
txt = txt.replace(/<br\S*?\/>/g, '#br#');
txt = txt.replace(/</g, '&lt;').replace(/>/g, '&gt;');
txt = txt.replace(/=/g, '&equals;');
txt = txt.replace(/#br#/g, '<br/>');
}
return txt
}
return txt;
};
/**
* Function called by parser when a node definition has been found
@ -37,53 +37,53 @@ const sanitize = text => {
* @param style
* @param classes
*/
export const addVertex = function (_id, text, type, style, classes) {
let txt
let id = _id
export const addVertex = function(_id, text, type, style, classes) {
let txt;
let id = _id;
if (typeof id === 'undefined') {
return
return;
}
if (id.trim().length === 0) {
return
return;
}
if (id[0].match(/\d/)) id = 's' + id
if (id[0].match(/\d/)) id = 's' + id;
if (typeof vertices[id] === 'undefined') {
vertices[id] = { id: id, styles: [], classes: [] }
vertices[id] = { id: id, styles: [], classes: [] };
}
if (typeof text !== 'undefined') {
txt = sanitize(text.trim())
txt = sanitize(text.trim());
// strip quotes if string starts and exnds with a quote
if (txt[0] === '"' && txt[txt.length - 1] === '"') {
txt = txt.substring(1, txt.length - 1)
txt = txt.substring(1, txt.length - 1);
}
vertices[id].text = txt
vertices[id].text = txt;
} else {
if (!vertices[id].text) {
vertices[id].text = _id
vertices[id].text = _id;
}
}
if (typeof type !== 'undefined') {
vertices[id].type = type
vertices[id].type = type;
}
if (typeof style !== 'undefined') {
if (style !== null) {
style.forEach(function (s) {
vertices[id].styles.push(s)
})
style.forEach(function(s) {
vertices[id].styles.push(s);
});
}
}
if (typeof classes !== 'undefined') {
if (classes !== null) {
classes.forEach(function (s) {
vertices[id].classes.push(s)
})
classes.forEach(function(s) {
vertices[id].classes.push(s);
});
}
}
}
};
/**
* Function called by parser when a link/edge definition has been found
@ -92,134 +92,138 @@ export const addVertex = function (_id, text, type, style, classes) {
* @param type
* @param linktext
*/
export const addLink = function (_start, _end, type, linktext) {
let start = _start
let end = _end
if (start[0].match(/\d/)) start = 's' + start
if (end[0].match(/\d/)) end = 's' + end
logger.info('Got edge...', start, end)
export const addLink = function(_start, _end, type, linktext) {
let start = _start;
let end = _end;
if (start[0].match(/\d/)) start = 's' + start;
if (end[0].match(/\d/)) end = 's' + end;
logger.info('Got edge...', start, end);
const edge = { start: start, end: end, type: undefined, text: '' }
linktext = type.text
const edge = { start: start, end: end, type: undefined, text: '' };
linktext = type.text;
if (typeof linktext !== 'undefined') {
edge.text = sanitize(linktext.trim())
edge.text = sanitize(linktext.trim());
// strip quotes if string starts and exnds with a quote
if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
edge.text = edge.text.substring(1, edge.text.length - 1)
edge.text = edge.text.substring(1, edge.text.length - 1);
}
}
if (typeof type !== 'undefined') {
edge.type = type.type
edge.stroke = type.stroke
edge.type = type.type;
edge.stroke = type.stroke;
}
edges.push(edge)
}
edges.push(edge);
};
/**
* Updates a link's line interpolation algorithm
* @param pos
* @param interpolate
*/
export const updateLinkInterpolate = function (positions, interp) {
positions.forEach(function (pos) {
export const updateLinkInterpolate = function(positions, interp) {
positions.forEach(function(pos) {
if (pos === 'default') {
edges.defaultInterpolate = interp
edges.defaultInterpolate = interp;
} else {
edges[pos].interpolate = interp
edges[pos].interpolate = interp;
}
})
}
});
};
/**
* Updates a link with a style
* @param pos
* @param style
*/
export const updateLink = function (positions, style) {
positions.forEach(function (pos) {
export const updateLink = function(positions, style) {
positions.forEach(function(pos) {
if (pos === 'default') {
edges.defaultStyle = style
edges.defaultStyle = style;
} else {
if (utils.isSubstringInArray('fill', style) === -1) {
style.push('fill:none')
style.push('fill:none');
}
edges[pos].style = style
edges[pos].style = style;
}
})
}
});
};
export const addClass = function (id, style) {
export const addClass = function(id, style) {
if (typeof classes[id] === 'undefined') {
classes[id] = { id: id, styles: [] }
classes[id] = { id: id, styles: [] };
}
if (typeof style !== 'undefined') {
if (style !== null) {
style.forEach(function (s) {
classes[id].styles.push(s)
})
style.forEach(function(s) {
classes[id].styles.push(s);
});
}
}
}
};
/**
* Called by parser when a graph definition is found, stores the direction of the chart.
* @param dir
*/
export const setDirection = function (dir) {
direction = dir
}
export const setDirection = function(dir) {
direction = dir;
};
/**
* 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, className) {
ids.split(',').forEach(function (_id) {
let id = _id
if (_id[0].match(/\d/)) id = 's' + id
export const setClass = function(ids, className) {
ids.split(',').forEach(function(_id) {
let id = _id;
if (_id[0].match(/\d/)) id = 's' + id;
if (typeof vertices[id] !== 'undefined') {
vertices[id].classes.push(className)
vertices[id].classes.push(className);
}
if (typeof subGraphLookup[id] !== 'undefined') {
subGraphLookup[id].classes.push(className)
subGraphLookup[id].classes.push(className);
}
})
}
});
};
const setTooltip = function (ids, tooltip) {
ids.split(',').forEach(function (id) {
const setTooltip = function(ids, tooltip) {
ids.split(',').forEach(function(id) {
if (typeof tooltip !== 'undefined') {
tooltips[id] = sanitize(tooltip)
tooltips[id] = sanitize(tooltip);
}
})
}
});
};
const setClickFun = function (_id, functionName) {
let id = _id
if (_id[0].match(/\d/)) id = 's' + id
const setClickFun = function(_id, functionName) {
let id = _id;
if (_id[0].match(/\d/)) id = 's' + id;
if (config.securityLevel !== 'loose') {
return
return;
}
if (typeof functionName === 'undefined') {
return
return;
}
if (typeof vertices[id] !== 'undefined') {
funs.push(function (element) {
const elem = document.querySelector(`[id="${id}"]`)
funs.push(function(element) {
const elem = document.querySelector(`[id="${id}"]`);
if (elem !== null) {
elem.addEventListener('click', function () {
window[functionName](id)
}, false)
elem.addEventListener(
'click',
function() {
window[functionName](id);
},
false
);
}
})
});
}
}
};
/**
* Called by parser when a link is found. Adds the URL to the vertex data.
@ -227,24 +231,24 @@ const setClickFun = function (_id, functionName) {
* @param linkStr URL to create a link for
* @param tooltip Tooltip for the clickable element
*/
export const setLink = function (ids, linkStr, tooltip) {
ids.split(',').forEach(function (_id) {
let id = _id
if (_id[0].match(/\d/)) id = 's' + id
export const setLink = function(ids, linkStr, tooltip) {
ids.split(',').forEach(function(_id) {
let id = _id;
if (_id[0].match(/\d/)) id = 's' + id;
if (typeof vertices[id] !== 'undefined') {
if (config.securityLevel !== 'loose') {
vertices[id].link = sanitizeUrl(linkStr) // .replace(/javascript:.*/g, '')
vertices[id].link = sanitizeUrl(linkStr); // .replace(/javascript:.*/g, '')
} else {
vertices[id].link = linkStr
vertices[id].link = linkStr;
}
}
})
setTooltip(ids, tooltip)
setClass(ids, 'clickable')
}
export const getTooltip = function (id) {
return tooltips[id]
}
});
setTooltip(ids, tooltip);
setClass(ids, 'clickable');
};
export const getTooltip = function(id) {
return tooltips[id];
};
/**
* Called by parser when a click definition is found. Registers an event handler.
@ -252,209 +256,219 @@ export const getTooltip = function (id) {
* @param functionName Function to be called on click
* @param tooltip Tooltip for the clickable element
*/
export const setClickEvent = function (ids, functionName, tooltip) {
ids.split(',').forEach(function (id) { setClickFun(id, functionName) })
setTooltip(ids, tooltip)
setClass(ids, 'clickable')
}
export const setClickEvent = function(ids, functionName, tooltip) {
ids.split(',').forEach(function(id) {
setClickFun(id, functionName);
});
setTooltip(ids, tooltip);
setClass(ids, 'clickable');
};
export const bindFunctions = function (element) {
funs.forEach(function (fun) {
fun(element)
})
}
export const getDirection = function () {
return direction
}
export const bindFunctions = function(element) {
funs.forEach(function(fun) {
fun(element);
});
};
export const getDirection = function() {
return direction;
};
/**
* Retrieval function for fetching the found nodes after parsing has completed.
* @returns {{}|*|vertices}
*/
export const getVertices = function () {
return vertices
}
export const getVertices = function() {
return vertices;
};
/**
* Retrieval function for fetching the found links after parsing has completed.
* @returns {{}|*|edges}
*/
export const getEdges = function () {
return edges
}
export const getEdges = function() {
return edges;
};
/**
* Retrieval function for fetching the found class definitions after parsing has completed.
* @returns {{}|*|classes}
*/
export const getClasses = function () {
return classes
}
export const getClasses = function() {
return classes;
};
const setupToolTips = function (element) {
let tooltipElem = d3.select('.mermaidTooltip')
const setupToolTips = function(element) {
let tooltipElem = d3.select('.mermaidTooltip');
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
tooltipElem = d3.select('body')
tooltipElem = d3
.select('body')
.append('div')
.attr('class', 'mermaidTooltip')
.style('opacity', 0)
.style('opacity', 0);
}
const svg = d3.select(element).select('svg')
const svg = d3.select(element).select('svg');
const nodes = svg.selectAll('g.node')
const nodes = svg.selectAll('g.node');
nodes
.on('mouseover', function () {
const el = d3.select(this)
const title = el.attr('title')
.on('mouseover', function() {
const el = d3.select(this);
const title = el.attr('title');
// Dont try to draw a tooltip if no data is provided
if (title === null) {
return
return;
}
const rect = this.getBoundingClientRect()
const rect = this.getBoundingClientRect();
tooltipElem.transition()
tooltipElem
.transition()
.duration(200)
.style('opacity', '.9')
tooltipElem.html(el.attr('title'))
.style('left', (rect.left + (rect.right - rect.left) / 2) + 'px')
.style('top', (rect.top - 14 + document.body.scrollTop) + 'px')
el.classed('hover', true)
.style('opacity', '.9');
tooltipElem
.html(el.attr('title'))
.style('left', rect.left + (rect.right - rect.left) / 2 + 'px')
.style('top', rect.top - 14 + document.body.scrollTop + 'px');
el.classed('hover', true);
})
.on('mouseout', function () {
tooltipElem.transition()
.on('mouseout', function() {
tooltipElem
.transition()
.duration(500)
.style('opacity', 0)
const el = d3.select(this)
el.classed('hover', false)
})
}
funs.push(setupToolTips)
.style('opacity', 0);
const el = d3.select(this);
el.classed('hover', false);
});
};
funs.push(setupToolTips);
/**
* Clears the internal graph db so that a new graph can be parsed.
*/
export const clear = function () {
vertices = {}
classes = {}
edges = []
funs = []
funs.push(setupToolTips)
subGraphs = []
subGraphLookup = {}
subCount = 0
tooltips = []
}
export const clear = function() {
vertices = {};
classes = {};
edges = [];
funs = [];
funs.push(setupToolTips);
subGraphs = [];
subGraphLookup = {};
subCount = 0;
tooltips = [];
};
/**
*
* @returns {string}
*/
export const defaultStyle = function () {
return 'fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;'
}
export const defaultStyle = function() {
return 'fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;';
};
/**
* Clears the internal graph db so that a new graph can be parsed.
*/
export const addSubGraph = function (_id, list, _title) {
let id = _id
let title = _title
export const addSubGraph = function(_id, list, _title) {
let id = _id;
let title = _title;
if (_id === _title && _title.match(/\s/)) {
id = undefined
id = undefined;
}
function uniq (a) {
const prims = { 'boolean': {}, 'number': {}, 'string': {} }
const objs = []
function uniq(a) {
const prims = { boolean: {}, number: {}, string: {} };
const objs = [];
return a.filter(function (item) {
const type = typeof item
return a.filter(function(item) {
const type = typeof item;
if (item.trim() === '') {
return false
return false;
}
if (type in prims) { return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true) } else { return objs.indexOf(item) >= 0 ? false : objs.push(item) }
})
if (type in prims) {
return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
} else {
return objs.indexOf(item) >= 0 ? false : objs.push(item);
}
});
}
let nodeList = []
let nodeList = [];
nodeList = uniq(nodeList.concat.apply(nodeList, list))
nodeList = uniq(nodeList.concat.apply(nodeList, list));
for (let i = 0; i < nodeList.length; i++) {
if (nodeList[i][0].match(/\d/)) nodeList[i] = 's' + nodeList[i]
if (nodeList[i][0].match(/\d/)) nodeList[i] = 's' + nodeList[i];
}
id = id || ('subGraph' + subCount)
if (id[0].match(/\d/)) id = 's' + id
title = title || ''
title = sanitize(title)
subCount = subCount + 1
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] }
subGraphs.push(subGraph)
subGraphLookup[id] = subGraph
return id
}
id = id || 'subGraph' + subCount;
if (id[0].match(/\d/)) id = 's' + id;
title = title || '';
title = sanitize(title);
subCount = subCount + 1;
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [] };
subGraphs.push(subGraph);
subGraphLookup[id] = subGraph;
return id;
};
const getPosForId = function (id) {
const getPosForId = function(id) {
for (let i = 0; i < subGraphs.length; i++) {
if (subGraphs[i].id === id) {
return i
return i;
}
}
return -1
}
let secCount = -1
const posCrossRef = []
const indexNodes2 = function (id, pos) {
const nodes = subGraphs[pos].nodes
secCount = secCount + 1
return -1;
};
let secCount = -1;
const posCrossRef = [];
const indexNodes2 = function(id, pos) {
const nodes = subGraphs[pos].nodes;
secCount = secCount + 1;
if (secCount > 2000) {
return
return;
}
posCrossRef[secCount] = pos
posCrossRef[secCount] = pos;
// Check if match
if (subGraphs[pos].id === id) {
return {
result: true,
count: 0
}
};
}
let count = 0
let posCount = 1
let count = 0;
let posCount = 1;
while (count < nodes.length) {
const childPos = getPosForId(nodes[count])
const childPos = getPosForId(nodes[count]);
// Ignore regular nodes (pos will be -1)
if (childPos >= 0) {
const res = indexNodes2(id, childPos)
const res = indexNodes2(id, childPos);
if (res.result) {
return {
result: true,
count: posCount + res.count
}
};
} else {
posCount = posCount + res.count
posCount = posCount + res.count;
}
}
count = count + 1
count = count + 1;
}
return {
result: false,
count: posCount
}
}
};
};
export const getDepthFirstPos = function (pos) {
return posCrossRef[pos]
}
export const indexNodes = function () {
secCount = -1
export const getDepthFirstPos = function(pos) {
return posCrossRef[pos];
};
export const indexNodes = function() {
secCount = -1;
if (subGraphs.length > 0) {
indexNodes2('none', subGraphs.length - 1, 0)
indexNodes2('none', subGraphs.length - 1, 0);
}
}
};
export const getSubGraphs = function () {
return subGraphs
}
export const getSubGraphs = function() {
return subGraphs;
};
export default {
addVertex,
@ -478,4 +492,4 @@ export default {
getDepthFirstPos,
indexNodes,
getSubGraphs
}
};

View File

@ -1,274 +1,288 @@
import graphlib from 'graphlibrary'
import * as d3 from 'd3'
import graphlib from 'graphlibrary';
import * as d3 from 'd3';
import flowDb from './flowDb'
import flow from './parser/flow'
import { getConfig } from '../../config'
import dagreD3 from 'dagre-d3-renderer'
import addHtmlLabel from 'dagre-d3-renderer/lib/label/add-html-label.js'
import { logger } from '../../logger'
import { interpolateToCurve } from '../../utils'
import flowDb from './flowDb';
import flow from './parser/flow';
import { getConfig } from '../../config';
import dagreD3 from 'dagre-d3-renderer';
import addHtmlLabel from 'dagre-d3-renderer/lib/label/add-html-label.js';
import { logger } from '../../logger';
import { interpolateToCurve } from '../../utils';
const conf = {
}
export const setConf = function (cnf) {
const keys = Object.keys(cnf)
const conf = {};
export const setConf = function(cnf) {
const keys = Object.keys(cnf);
for (let i = 0; i < keys.length; i++) {
conf[keys[i]] = cnf[keys[i]]
conf[keys[i]] = cnf[keys[i]];
}
}
};
/**
* Function that adds the vertices found in the graph definition to the graph to be rendered.
* @param vert Object containing the vertices.
* @param g The graph that is to be drawn.
*/
export const addVertices = function (vert, g, svgId) {
const svg = d3.select(`[id="${svgId}"]`)
const keys = Object.keys(vert)
export const addVertices = function(vert, g, svgId) {
const svg = d3.select(`[id="${svgId}"]`);
const keys = Object.keys(vert);
const styleFromStyleArr = function (styleStr, arr, { label }) {
const styleFromStyleArr = function(styleStr, arr, { label }) {
if (!label) {
// Create a compound style definition from the style definitions found for the node in the graph definition
// Create a compound style definition from the style definitions found for the node in the graph definition
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] !== 'undefined') {
styleStr = styleStr + arr[i] + ';'
styleStr = styleStr + arr[i] + ';';
}
}
} else {
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] !== 'undefined') {
if (arr[i].match('^color:')) styleStr = styleStr + arr[i] + ';'
if (arr[i].match('^color:')) styleStr = styleStr + arr[i] + ';';
}
}
}
return styleStr
}
return styleStr;
};
// 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]
keys.forEach(function(id) {
const vertex = vert[id];
/**
* Variable for storing the classes for the vertex
* @type {string}
*/
let classStr = ''
let classStr = '';
if (vertex.classes.length > 0) {
classStr = vertex.classes.join(' ')
classStr = vertex.classes.join(' ');
}
/**
* Variable for storing the extracted style for the vertex
* @type {string}
*/
let style = ''
let style = '';
// Create a compound style definition from the style definitions found for the node in the graph definition
style = styleFromStyleArr(style, vertex.styles, { label: false })
let labelStyle = ''
labelStyle = styleFromStyleArr(labelStyle, vertex.styles, { label: true })
style = styleFromStyleArr(style, vertex.styles, { label: false });
let labelStyle = '';
labelStyle = styleFromStyleArr(labelStyle, vertex.styles, { label: true });
// 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
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
// We create a SVG label, either by delegating to addHtmlLabel or manually
let vertexNode
let vertexNode;
if (getConfig().flowchart.htmlLabels) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
const node = { label: vertexText.replace(/fa[lrsb]?:fa-[\w-]+/g, s => `<i class='${s.replace(':', ' ')}'></i>`) }
vertexNode = addHtmlLabel(svg, node).node()
vertexNode.parentNode.removeChild(vertexNode)
const node = {
label: vertexText.replace(
/fa[lrsb]?:fa-[\w-]+/g,
s => `<i class='${s.replace(':', ' ')}'></i>`
)
};
vertexNode = addHtmlLabel(svg, node).node();
vertexNode.parentNode.removeChild(vertexNode);
} else {
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text')
const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
const rows = vertexText.split(/<br[/]{0,1}>/)
const rows = vertexText.split(/<br[/]{0,1}>/);
for (let j = 0; j < rows.length; j++) {
const tspan = document.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 = rows[j]
svgLabel.appendChild(tspan)
const tspan = document.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 = rows[j];
svgLabel.appendChild(tspan);
}
vertexNode = svgLabel
vertexNode = svgLabel;
}
// If the node has a link, we wrap it in a SVG link
if (vertex.link) {
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a')
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link)
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener')
link.appendChild(vertexNode)
vertexNode = link
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
link.setAttributeNS('http://www.w3.org/2000/svg', 'href', vertex.link);
link.setAttributeNS('http://www.w3.org/2000/svg', 'rel', 'noopener');
link.appendChild(vertexNode);
vertexNode = link;
}
let radious = 0
let _shape = ''
let radious = 0;
let _shape = '';
// Set the shape based parameters
switch (vertex.type) {
case 'round':
radious = 5
_shape = 'rect'
break
radious = 5;
_shape = 'rect';
break;
case 'square':
_shape = 'rect'
break
_shape = 'rect';
break;
case 'diamond':
_shape = 'question'
break
_shape = 'question';
break;
case 'odd':
_shape = 'rect_left_inv_arrow'
break
_shape = 'rect_left_inv_arrow';
break;
case 'lean_right':
_shape = 'lean_right'
break
_shape = 'lean_right';
break;
case 'lean_left':
_shape = 'lean_left'
break
_shape = 'lean_left';
break;
case 'trapezoid':
_shape = 'trapezoid'
break
_shape = 'trapezoid';
break;
case 'inv_trapezoid':
_shape = 'inv_trapezoid'
break
_shape = 'inv_trapezoid';
break;
case 'odd_right':
_shape = 'rect_left_inv_arrow'
break
_shape = 'rect_left_inv_arrow';
break;
case 'circle':
_shape = 'circle'
break
_shape = 'circle';
break;
case 'ellipse':
_shape = 'ellipse'
break
_shape = 'ellipse';
break;
case 'group':
_shape = 'rect'
break
_shape = 'rect';
break;
default:
_shape = 'rect'
_shape = 'rect';
}
// Add the node
g.setNode(vertex.id, { labelType: 'svg', labelStyle: labelStyle, shape: _shape, label: vertexNode, rx: radious, ry: radious, 'class': classStr, style: style, id: vertex.id })
})
}
g.setNode(vertex.id, {
labelType: 'svg',
labelStyle: labelStyle,
shape: _shape,
label: vertexNode,
rx: radious,
ry: radious,
class: classStr,
style: style,
id: vertex.id
});
});
};
/**
* Add edges to graph based on parsed graph defninition
* @param {Object} edges The edges to add to the graph
* @param {Object} g The graph object
*/
export const addEdges = function (edges, g) {
let cnt = 0
export const addEdges = function(edges, g) {
let cnt = 0;
let defaultStyle
let defaultStyle;
if (typeof edges.defaultStyle !== 'undefined') {
defaultStyle = edges.defaultStyle.toString().replace(/,/g, ';')
defaultStyle = edges.defaultStyle.toString().replace(/,/g, ';');
}
edges.forEach(function (edge) {
cnt++
const edgeData = {}
edges.forEach(function(edge) {
cnt++;
const edgeData = {};
// Set link type for rendering
if (edge.type === 'arrow_open') {
edgeData.arrowhead = 'none'
edgeData.arrowhead = 'none';
} else {
edgeData.arrowhead = 'normal'
edgeData.arrowhead = 'normal';
}
let style = ''
let style = '';
if (typeof edge.style !== 'undefined') {
edge.style.forEach(function (s) {
style = style + s + ';'
})
edge.style.forEach(function(s) {
style = style + s + ';';
});
} else {
switch (edge.stroke) {
case 'normal':
style = 'fill:none'
style = 'fill:none';
if (typeof defaultStyle !== 'undefined') {
style = defaultStyle
style = defaultStyle;
}
break
break;
case 'dotted':
style = 'stroke: #333; fill:none;stroke-width:2px;stroke-dasharray:3;'
break
style = 'stroke: #333; fill:none;stroke-width:2px;stroke-dasharray:3;';
break;
case 'thick':
style = 'stroke: #333; stroke-width: 3.5px;fill:none'
break
style = 'stroke: #333; stroke-width: 3.5px;fill:none';
break;
}
}
edgeData.style = style
edgeData.style = style;
if (typeof edge.interpolate !== 'undefined') {
edgeData.curve = interpolateToCurve(edge.interpolate, d3.curveLinear)
edgeData.curve = interpolateToCurve(edge.interpolate, d3.curveLinear);
} else if (typeof edges.defaultInterpolate !== 'undefined') {
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, d3.curveLinear)
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, d3.curveLinear);
} else {
edgeData.curve = interpolateToCurve(conf.curve, d3.curveLinear)
edgeData.curve = interpolateToCurve(conf.curve, d3.curveLinear);
}
if (typeof edge.text === 'undefined') {
if (typeof edge.style !== 'undefined') {
edgeData.arrowheadStyle = 'fill: #333'
edgeData.arrowheadStyle = 'fill: #333';
}
} else {
edgeData.arrowheadStyle = 'fill: #333'
edgeData.arrowheadStyle = 'fill: #333';
if (typeof edge.style === 'undefined') {
edgeData.labelpos = 'c'
edgeData.labelpos = 'c';
if (getConfig().flowchart.htmlLabels) {
edgeData.labelType = 'html'
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>'
edgeData.labelType = 'html';
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
} else {
edgeData.labelType = 'text'
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none'
edgeData.label = edge.text.replace(/<br>/g, '\n')
edgeData.labelType = 'text';
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
edgeData.label = edge.text.replace(/<br>/g, '\n');
}
} else {
edgeData.label = edge.text.replace(/<br>/g, '\n')
edgeData.label = edge.text.replace(/<br>/g, '\n');
}
}
// Add the edge to the graph
g.setEdge(edge.start, edge.end, edgeData, cnt)
})
}
g.setEdge(edge.start, edge.end, edgeData, cnt);
});
};
/**
* Returns the all the styles from classDef statements in the graph definition.
* @returns {object} classDef styles
*/
export const getClasses = function (text) {
logger.info('Extracting classes')
flowDb.clear()
const parser = flow.parser
parser.yy = flowDb
export const getClasses = function(text) {
logger.info('Extracting classes');
flowDb.clear();
const parser = flow.parser;
parser.yy = flowDb;
// Parse the graph definition
parser.parse(text)
return flowDb.getClasses()
}
parser.parse(text);
return flowDb.getClasses();
};
/**
* Draws a flowchart in the tag with id: id based on the graph definition in text.
* @param text
* @param id
*/
export const draw = function (text, id) {
logger.info('Drawing flowchart')
flowDb.clear()
const parser = flow.parser
parser.yy = flowDb
export const draw = function(text, id) {
logger.info('Drawing flowchart');
flowDb.clear();
const parser = flow.parser;
parser.yy = flowDb;
// Parse the graph definition
try {
parser.parse(text)
parser.parse(text);
} catch (err) {
logger.debug('Parsing failed')
logger.debug('Parsing failed');
}
// Fetch the default direction, use TD if none was found
let dir = flowDb.getDirection()
let dir = flowDb.getDirection();
if (typeof dir === 'undefined') {
dir = 'TD'
dir = 'TD';
}
// Create the input mermaid.graph
@ -280,196 +294,238 @@ export const draw = function (text, id) {
rankdir: dir,
marginx: 20,
marginy: 20
})
.setDefaultEdgeLabel(function () {
return {}
})
.setDefaultEdgeLabel(function() {
return {};
});
let subG
const subGraphs = flowDb.getSubGraphs()
let subG;
const subGraphs = flowDb.getSubGraphs();
for (let i = subGraphs.length - 1; i >= 0; i--) {
subG = subGraphs[i]
flowDb.addVertex(subG.id, subG.title, 'group', undefined, subG.classes)
subG = subGraphs[i];
flowDb.addVertex(subG.id, subG.title, 'group', undefined, subG.classes);
}
// Fetch the verices/nodes and edges/links from the parsed graph definition
const vert = flowDb.getVertices()
const vert = flowDb.getVertices();
const edges = flowDb.getEdges()
const edges = flowDb.getEdges();
let i = 0
let i = 0;
for (i = subGraphs.length - 1; i >= 0; i--) {
subG = subGraphs[i]
subG = subGraphs[i];
d3.selectAll('cluster').append('text')
d3.selectAll('cluster').append('text');
for (let j = 0; j < subG.nodes.length; j++) {
g.setParent(subG.nodes[j], subG.id)
g.setParent(subG.nodes[j], subG.id);
}
}
addVertices(vert, g, id)
addEdges(edges, g)
addVertices(vert, g, id);
addEdges(edges, g);
// Create the renderer
const Render = dagreD3.render
const render = new Render()
const Render = dagreD3.render;
const render = new Render();
// Add custom shape for rhombus type of boc (decision)
render.shapes().question = function (parent, bbox, node) {
const w = bbox.width
const h = bbox.height
const s = (w + h) * 0.9
render.shapes().question = function(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const s = (w + h) * 0.9;
const points = [
{ x: s / 2, y: 0 },
{ x: s, y: -s / 2 },
{ x: s / 2, y: -s },
{ x: 0, y: -s / 2 }
]
const shapeSvg = parent.insert('polygon', ':first-child')
.attr('points', points.map(function (d) {
return d.x + ',' + d.y
}).join(' '))
];
const shapeSvg = parent
.insert('polygon', ':first-child')
.attr(
'points',
points
.map(function(d) {
return d.x + ',' + d.y;
})
.join(' ')
)
.attr('rx', 5)
.attr('ry', 5)
.attr('transform', 'translate(' + (-s / 2) + ',' + (s * 2 / 4) + ')')
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point)
}
return shapeSvg
}
.attr('transform', 'translate(' + -s / 2 + ',' + (s * 2) / 4 + ')');
node.intersect = function(point) {
return dagreD3.intersect.polygon(node, points, point);
};
return shapeSvg;
};
// Add custom shape for box with inverted arrow on left side
render.shapes().rect_left_inv_arrow = function (parent, bbox, node) {
const w = bbox.width
const h = bbox.height
render.shapes().rect_left_inv_arrow = function(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
{ x: -h / 2, y: 0 },
{ x: w, y: 0 },
{ x: w, y: -h },
{ x: -h / 2, y: -h },
{ x: 0, y: -h / 2 }
]
const shapeSvg = parent.insert('polygon', ':first-child')
.attr('points', points.map(function (d) {
return d.x + ',' + d.y
}).join(' '))
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point)
}
return shapeSvg
}
];
const shapeSvg = parent
.insert('polygon', ':first-child')
.attr(
'points',
points
.map(function(d) {
return d.x + ',' + d.y;
})
.join(' ')
)
.attr('transform', 'translate(' + -w / 2 + ',' + (h * 2) / 4 + ')');
node.intersect = function(point) {
return dagreD3.intersect.polygon(node, points, point);
};
return shapeSvg;
};
// Add custom shape for box with inverted arrow on left side
render.shapes().lean_right = function (parent, bbox, node) {
const w = bbox.width
const h = bbox.height
render.shapes().lean_right = function(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
{ x: -2 * h / 6, y: 0 },
{ x: (-2 * h) / 6, y: 0 },
{ x: w - h / 6, y: 0 },
{ x: w + 2 * h / 6, y: -h },
{ x: w + (2 * h) / 6, y: -h },
{ x: h / 6, y: -h }
]
const shapeSvg = parent.insert('polygon', ':first-child')
.attr('points', points.map(function (d) {
return d.x + ',' + d.y
}).join(' '))
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point)
}
return shapeSvg
}
];
const shapeSvg = parent
.insert('polygon', ':first-child')
.attr(
'points',
points
.map(function(d) {
return d.x + ',' + d.y;
})
.join(' ')
)
.attr('transform', 'translate(' + -w / 2 + ',' + (h * 2) / 4 + ')');
node.intersect = function(point) {
return dagreD3.intersect.polygon(node, points, point);
};
return shapeSvg;
};
// Add custom shape for box with inverted arrow on left side
render.shapes().lean_left = function (parent, bbox, node) {
const w = bbox.width
const h = bbox.height
render.shapes().lean_left = function(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
{ x: 2 * h / 6, y: 0 },
{ x: (2 * h) / 6, y: 0 },
{ x: w + h / 6, y: 0 },
{ x: w - 2 * h / 6, y: -h },
{ x: w - (2 * h) / 6, y: -h },
{ x: -h / 6, y: -h }
]
const shapeSvg = parent.insert('polygon', ':first-child')
.attr('points', points.map(function (d) {
return d.x + ',' + d.y
}).join(' '))
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point)
}
return shapeSvg
}
];
const shapeSvg = parent
.insert('polygon', ':first-child')
.attr(
'points',
points
.map(function(d) {
return d.x + ',' + d.y;
})
.join(' ')
)
.attr('transform', 'translate(' + -w / 2 + ',' + (h * 2) / 4 + ')');
node.intersect = function(point) {
return dagreD3.intersect.polygon(node, points, point);
};
return shapeSvg;
};
// Add custom shape for box with inverted arrow on left side
render.shapes().trapezoid = function (parent, bbox, node) {
const w = bbox.width
const h = bbox.height
render.shapes().trapezoid = function(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
{ x: -2 * h / 6, y: 0 },
{ x: w + 2 * h / 6, y: 0 },
{ x: (-2 * h) / 6, y: 0 },
{ x: w + (2 * h) / 6, y: 0 },
{ x: w - h / 6, y: -h },
{ x: h / 6, y: -h }
]
const shapeSvg = parent.insert('polygon', ':first-child')
.attr('points', points.map(function (d) {
return d.x + ',' + d.y
}).join(' '))
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point)
}
return shapeSvg
}
];
const shapeSvg = parent
.insert('polygon', ':first-child')
.attr(
'points',
points
.map(function(d) {
return d.x + ',' + d.y;
})
.join(' ')
)
.attr('transform', 'translate(' + -w / 2 + ',' + (h * 2) / 4 + ')');
node.intersect = function(point) {
return dagreD3.intersect.polygon(node, points, point);
};
return shapeSvg;
};
// Add custom shape for box with inverted arrow on left side
render.shapes().inv_trapezoid = function (parent, bbox, node) {
const w = bbox.width
const h = bbox.height
render.shapes().inv_trapezoid = function(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
{ x: h / 6, y: 0 },
{ x: w - h / 6, y: 0 },
{ x: w + 2 * h / 6, y: -h },
{ x: -2 * h / 6, y: -h }
]
const shapeSvg = parent.insert('polygon', ':first-child')
.attr('points', points.map(function (d) {
return d.x + ',' + d.y
}).join(' '))
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point)
}
return shapeSvg
}
{ x: w + (2 * h) / 6, y: -h },
{ x: (-2 * h) / 6, y: -h }
];
const shapeSvg = parent
.insert('polygon', ':first-child')
.attr(
'points',
points
.map(function(d) {
return d.x + ',' + d.y;
})
.join(' ')
)
.attr('transform', 'translate(' + -w / 2 + ',' + (h * 2) / 4 + ')');
node.intersect = function(point) {
return dagreD3.intersect.polygon(node, points, point);
};
return shapeSvg;
};
// Add custom shape for box with inverted arrow on right side
render.shapes().rect_right_inv_arrow = function (parent, bbox, node) {
const w = bbox.width
const h = bbox.height
render.shapes().rect_right_inv_arrow = function(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
{ x: 0, y: 0 },
{ x: w + h / 2, y: 0 },
{ x: w, y: -h / 2 },
{ x: w + h / 2, y: -h },
{ x: 0, y: -h }
]
const shapeSvg = parent.insert('polygon', ':first-child')
.attr('points', points.map(function (d) {
return d.x + ',' + d.y
}).join(' '))
.attr('transform', 'translate(' + (-w / 2) + ',' + (h * 2 / 4) + ')')
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point)
}
return shapeSvg
}
];
const shapeSvg = parent
.insert('polygon', ':first-child')
.attr(
'points',
points
.map(function(d) {
return d.x + ',' + d.y;
})
.join(' ')
)
.attr('transform', 'translate(' + -w / 2 + ',' + (h * 2) / 4 + ')');
node.intersect = function(point) {
return dagreD3.intersect.polygon(node, points, point);
};
return shapeSvg;
};
// Add our custom arrow - an empty arrowhead
render.arrows().none = function normal (parent, id, edge, type) {
const marker = parent.append('marker')
render.arrows().none = function normal(parent, id, edge, type) {
const marker = parent
.append('marker')
.attr('id', id)
.attr('viewBox', '0 0 10 10')
.attr('refX', 9)
@ -477,16 +533,16 @@ export const draw = function (text, id) {
.attr('markerUnits', 'strokeWidth')
.attr('markerWidth', 8)
.attr('markerHeight', 6)
.attr('orient', 'auto')
.attr('orient', 'auto');
const path = marker.append('path')
.attr('d', 'M 0 0 L 0 0 L 0 0 z')
dagreD3.util.applyStyle(path, edge[type + 'Style'])
}
const path = marker.append('path').attr('d', 'M 0 0 L 0 0 L 0 0 z');
dagreD3.util.applyStyle(path, edge[type + 'Style']);
};
// Override normal arrowhead defined in d3. Remove style & add class to allow css styling.
render.arrows().normal = function normal (parent, id, edge, type) {
const marker = parent.append('marker')
render.arrows().normal = function normal(parent, id, edge, type) {
const marker = parent
.append('marker')
.attr('id', id)
.attr('viewBox', '0 0 10 10')
.attr('refX', 9)
@ -494,76 +550,76 @@ export const draw = function (text, id) {
.attr('markerUnits', 'strokeWidth')
.attr('markerWidth', 8)
.attr('markerHeight', 6)
.attr('orient', 'auto')
.attr('orient', 'auto');
marker.append('path')
marker
.append('path')
.attr('d', 'M 0 0 L 10 5 L 0 10 z')
.attr('class', 'arrowheadPath')
.style('stroke-width', 1)
.style('stroke-dasharray', '1,0')
}
.style('stroke-dasharray', '1,0');
};
// Set up an SVG group so that we can translate the final graph.
const svg = d3.select(`[id="${id}"]`)
const svg = d3.select(`[id="${id}"]`);
// Run the renderer. This is what draws the final graph.
const element = d3.select('#' + id + ' g')
render(element, g)
const element = d3.select('#' + id + ' g');
render(element, g);
element.selectAll('g.node')
.attr('title', function () {
return flowDb.getTooltip(this.id)
})
element.selectAll('g.node').attr('title', function() {
return flowDb.getTooltip(this.id);
});
const padding = 8
const width = g.maxX - g.minX + padding * 2
const height = g.maxY - g.minY + padding * 2
svg.attr('width', '100%')
svg.attr('style', `max-width: ${width}px;`)
svg.attr('viewBox', `0 0 ${width} ${height}`)
svg.select('g').attr('transform', `translate(${padding - g.minX}, ${padding - g.minY})`)
const padding = 8;
const width = g.maxX - g.minX + padding * 2;
const height = g.maxY - g.minY + padding * 2;
svg.attr('width', '100%');
svg.attr('style', `max-width: ${width}px;`);
svg.attr('viewBox', `0 0 ${width} ${height}`);
svg.select('g').attr('transform', `translate(${padding - g.minX}, ${padding - g.minY})`);
// Index nodes
flowDb.indexNodes('subGraph' + i)
flowDb.indexNodes('subGraph' + i);
// reposition labels
for (i = 0; i < subGraphs.length; i++) {
subG = subGraphs[i]
subG = subGraphs[i];
if (subG.title !== 'undefined') {
const clusterRects = document.querySelectorAll('#' + id + ' #' + subG.id + ' rect')
const clusterEl = document.querySelectorAll('#' + id + ' #' + subG.id)
const clusterRects = document.querySelectorAll('#' + id + ' #' + subG.id + ' rect');
const clusterEl = document.querySelectorAll('#' + id + ' #' + subG.id);
const xPos = clusterRects[0].x.baseVal.value
const yPos = clusterRects[0].y.baseVal.value
const width = clusterRects[0].width.baseVal.value
const cluster = d3.select(clusterEl[0])
const te = cluster.select('.label')
te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`)
te.attr('id', id + 'Text')
const xPos = clusterRects[0].x.baseVal.value;
const yPos = clusterRects[0].y.baseVal.value;
const width = clusterRects[0].width.baseVal.value;
const cluster = d3.select(clusterEl[0]);
const te = cluster.select('.label');
te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`);
te.attr('id', id + 'Text');
}
}
// Add label rects for non html labels
if (!getConfig().flowchart.htmlLabels) {
const labels = document.querySelectorAll('#' + id + ' .edgeLabel .label')
const labels = document.querySelectorAll('#' + id + ' .edgeLabel .label');
for (let k = 0; k < labels.length; k++) {
const label = labels[k]
const label = labels[k];
// Get dimensions of label
const dim = label.getBBox()
const dim = label.getBBox();
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
rect.setAttribute('rx', 0)
rect.setAttribute('ry', 0)
rect.setAttribute('width', dim.width)
rect.setAttribute('height', dim.height)
rect.setAttribute('style', 'fill:#e8e8e8;')
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('rx', 0);
rect.setAttribute('ry', 0);
rect.setAttribute('width', dim.width);
rect.setAttribute('height', dim.height);
rect.setAttribute('style', 'fill:#e8e8e8;');
label.insertBefore(rect, label.firstChild)
label.insertBefore(rect, label.firstChild);
}
}
}
};
export default {
setConf,
@ -571,4 +627,4 @@ export default {
addEdges,
getClasses,
draw
}
};

View File

@ -1,38 +1,37 @@
import flowDb from '../flowDb'
import flow from './flow'
import { setConfig } from '../../../config'
import flowDb from '../flowDb';
import flow from './flow';
import { setConfig } from '../../../config';
setConfig({
securityLevel: 'strict',
})
securityLevel: 'strict'
});
describe('when parsing flowcharts', function () {
beforeEach(function () {
flow.parser.yy = flowDb
flow.parser.yy.clear()
})
describe('when parsing flowcharts', function() {
beforeEach(function() {
flow.parser.yy = flowDb;
flow.parser.yy.clear();
});
it('should handle chaining of vertices', function () {
it('should handle chaining of vertices', function() {
const res = flow.parser.parse(`
graph TD
A-->B-->C;
`);
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A')
expect(vert['B'].id).toBe('B')
expect(vert['C'].id).toBe('C')
expect(edges.length).toBe(2)
expect(edges[0].start).toBe('A')
expect(edges[0].end).toBe('B')
expect(edges[0].type).toBe('arrow')
expect(edges[0].text).toBe('')
expect(edges[1].start).toBe('B')
expect(edges[1].end).toBe('C')
expect(edges[1].type).toBe('arrow')
expect(edges[1].text).toBe('')
})
})
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(vert['C'].id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow');
expect(edges[0].text).toBe('');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
expect(edges[1].type).toBe('arrow');
expect(edges[1].text).toBe('');
});
});

File diff suppressed because it is too large Load Diff

View File

@ -1,223 +1,223 @@
import flowDb from '../flowDb'
import flow from './flow'
import { setConfig } from '../../../config'
import flowDb from '../flowDb';
import flow from './flow';
import { setConfig } from '../../../config';
setConfig({
securityLevel: 'strict',
})
securityLevel: 'strict'
});
describe('when parsing subgraphs', function () {
beforeEach(function () {
flow.parser.yy = flowDb
flow.parser.yy.clear()
})
it('should handle subgraph with tab indentation', function () {
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2\nend')
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
expect(subgraph.nodes.length).toBe(2)
expect(subgraph.nodes[0]).toBe('a2')
expect(subgraph.nodes[1]).toBe('a1')
expect(subgraph.title).toBe('One')
expect(subgraph.id).toBe('One')
})
it('should handle subgraph with chaining nodes indentation', function () {
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2-->a3\nend')
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
expect(subgraph.nodes.length).toBe(3)
expect(subgraph.nodes[0]).toBe('a3')
expect(subgraph.nodes[1]).toBe('a2')
expect(subgraph.nodes[2]).toBe('a1')
expect(subgraph.title).toBe('One')
expect(subgraph.id).toBe('One')
})
it('should handle subgraph with multiple words in title', function () {
const res = flow.parser.parse('graph TB\nsubgraph "Some Title"\n\ta1-->a2\nend')
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
expect(subgraph.nodes.length).toBe(2)
expect(subgraph.nodes[0]).toBe('a2')
expect(subgraph.nodes[1]).toBe('a1')
expect(subgraph.title).toBe('Some Title')
expect(subgraph.id).toBe('subGraph0')
describe('when parsing subgraphs', function() {
beforeEach(function() {
flow.parser.yy = flowDb;
flow.parser.yy.clear();
});
it('should handle subgraph with tab indentation', function() {
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2\nend');
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
expect(subgraph.nodes[0]).toBe('a2');
expect(subgraph.nodes[1]).toBe('a1');
expect(subgraph.title).toBe('One');
expect(subgraph.id).toBe('One');
});
it('should handle subgraph with chaining nodes indentation', function() {
const res = flow.parser.parse('graph TB\nsubgraph One\n\ta1-->a2-->a3\nend');
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(3);
expect(subgraph.nodes[0]).toBe('a3');
expect(subgraph.nodes[1]).toBe('a2');
expect(subgraph.nodes[2]).toBe('a1');
expect(subgraph.title).toBe('One');
expect(subgraph.id).toBe('One');
});
it('should handle subgraph with id and title notation', function () {
const res = flow.parser.parse('graph TB\nsubgraph some-id[Some Title]\n\ta1-->a2\nend')
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
expect(subgraph.nodes.length).toBe(2)
expect(subgraph.nodes[0]).toBe('a2')
expect(subgraph.nodes[1]).toBe('a1')
expect(subgraph.title).toBe('Some Title')
expect(subgraph.id).toBe('some-id')
it('should handle subgraph with multiple words in title', function() {
const res = flow.parser.parse('graph TB\nsubgraph "Some Title"\n\ta1-->a2\nend');
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
expect(subgraph.nodes[0]).toBe('a2');
expect(subgraph.nodes[1]).toBe('a1');
expect(subgraph.title).toBe('Some Title');
expect(subgraph.id).toBe('subGraph0');
});
xit('should handle subgraph without id and space in title', function () {
const res = flow.parser.parse('graph TB\nsubgraph Some Title\n\ta1-->a2\nend')
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
expect(subgraph.nodes.length).toBe(2)
expect(subgraph.nodes[0]).toBe('a1')
expect(subgraph.nodes[1]).toBe('a2')
expect(subgraph.title).toBe('Some Title')
expect(subgraph.id).toBe('some-id')
it('should handle subgraph with id and title notation', function() {
const res = flow.parser.parse('graph TB\nsubgraph some-id[Some Title]\n\ta1-->a2\nend');
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
expect(subgraph.nodes[0]).toBe('a2');
expect(subgraph.nodes[1]).toBe('a1');
expect(subgraph.title).toBe('Some Title');
expect(subgraph.id).toBe('some-id');
});
it('should handle subgraph id starting with a number', function () {
xit('should handle subgraph without id and space in title', function() {
const res = flow.parser.parse('graph TB\nsubgraph Some Title\n\ta1-->a2\nend');
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
expect(subgraph.nodes[0]).toBe('a1');
expect(subgraph.nodes[1]).toBe('a2');
expect(subgraph.title).toBe('Some Title');
expect(subgraph.id).toBe('some-id');
});
it('should handle subgraph id starting with a number', function() {
const res = flow.parser.parse(`graph TD
A[Christmas] -->|Get money| B(Go shopping)
subgraph 1test
A
end`)
end`);
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
expect(subgraph.nodes.length).toBe(1)
expect(subgraph.nodes[0]).toBe('A')
expect(subgraph.id).toBe('s1test')
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(1);
expect(subgraph.nodes[0]).toBe('A');
expect(subgraph.id).toBe('s1test');
});
it('should handle subgraphs1', function () {
const res = flow.parser.parse('graph TD;A-->B;subgraph myTitle;c-->d;end;')
it('should handle subgraphs1', function() {
const res = flow.parser.parse('graph TD;A-->B;subgraph myTitle;c-->d;end;');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow')
})
it('should handle subgraphs with title in quotes', function () {
const res = flow.parser.parse('graph TD;A-->B;subgraph "title in quotes";c-->d;end;')
expect(edges[0].type).toBe('arrow');
});
it('should handle subgraphs with title in quotes', function() {
const res = flow.parser.parse('graph TD;A-->B;subgraph "title in quotes";c-->d;end;');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('title in quotes')
expect(subgraph.title).toBe('title in quotes');
expect(edges[0].type).toBe('arrow')
})
it('should handle subgraphs in old style that was broken', function () {
const res = flow.parser.parse('graph TD;A-->B;subgraph old style that is broken;c-->d;end;')
expect(edges[0].type).toBe('arrow');
});
it('should handle subgraphs in old style that was broken', function() {
const res = flow.parser.parse('graph TD;A-->B;subgraph old style that is broken;c-->d;end;');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('old style that is broken')
expect(subgraph.title).toBe('old style that is broken');
expect(edges[0].type).toBe('arrow')
})
it('should handle subgraphs with dashes in the title', function () {
const res = flow.parser.parse('graph TD;A-->B;subgraph a-b-c;c-->d;end;')
expect(edges[0].type).toBe('arrow');
});
it('should handle subgraphs with dashes in the title', function() {
const res = flow.parser.parse('graph TD;A-->B;subgraph a-b-c;c-->d;end;');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('a-b-c')
expect(subgraph.title).toBe('a-b-c');
expect(edges[0].type).toBe('arrow')
})
it('should handle subgraphs with id and title in brackets', function () {
const res = flow.parser.parse('graph TD;A-->B;subgraph uid1[text of doom];c-->d;end;')
expect(edges[0].type).toBe('arrow');
});
it('should handle subgraphs with id and title in brackets', function() {
const res = flow.parser.parse('graph TD;A-->B;subgraph uid1[text of doom];c-->d;end;');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('text of doom')
expect(subgraph.id).toBe('uid1')
expect(subgraph.title).toBe('text of doom');
expect(subgraph.id).toBe('uid1');
expect(edges[0].type).toBe('arrow')
})
it('should handle subgraphs with id and title in brackets and quotes', function () {
const res = flow.parser.parse('graph TD;A-->B;subgraph uid2["text of doom"];c-->d;end;')
expect(edges[0].type).toBe('arrow');
});
it('should handle subgraphs with id and title in brackets and quotes', function() {
const res = flow.parser.parse('graph TD;A-->B;subgraph uid2["text of doom"];c-->d;end;');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('text of doom')
expect(subgraph.id).toBe('uid2')
expect(subgraph.title).toBe('text of doom');
expect(subgraph.id).toBe('uid2');
expect(edges[0].type).toBe('arrow')
})
it('should handle subgraphs with id and title in brackets without spaces', function () {
const res = flow.parser.parse('graph TD;A-->B;subgraph uid2[textofdoom];c-->d;end;')
expect(edges[0].type).toBe('arrow');
});
it('should handle subgraphs with id and title in brackets without spaces', function() {
const res = flow.parser.parse('graph TD;A-->B;subgraph uid2[textofdoom];c-->d;end;');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
const subgraphs = flow.parser.yy.getSubGraphs()
expect(subgraphs.length).toBe(1)
const subgraph = subgraphs[0]
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('textofdoom')
expect(subgraph.id).toBe('uid2')
expect(subgraph.title).toBe('textofdoom');
expect(subgraph.id).toBe('uid2');
expect(edges[0].type).toBe('arrow')
})
expect(edges[0].type).toBe('arrow');
});
it('should handle subgraphs2', function () {
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\n\n c-->d \nend\n')
it('should handle subgraphs2', function() {
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\n\n c-->d \nend\n');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow')
})
expect(edges[0].type).toBe('arrow');
});
it('should handle nested subgraphs', function () {
const str = 'graph TD\n' +
'A-->B\n' +
'subgraph myTitle\n\n' +
' c-->d \n\n' +
' subgraph inner\n\n e-->f \n end \n\n' +
' subgraph inner\n\n h-->i \n end \n\n' +
'end\n'
const res = flow.parser.parse(str)
})
it('should handle nested subgraphs', function() {
const str =
'graph TD\n' +
'A-->B\n' +
'subgraph myTitle\n\n' +
' c-->d \n\n' +
' subgraph inner\n\n e-->f \n end \n\n' +
' subgraph inner\n\n h-->i \n end \n\n' +
'end\n';
const res = flow.parser.parse(str);
});
it('should handle subgraphs4', function () {
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-->d\nend;')
it('should handle subgraphs4', function() {
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-->d\nend;');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow')
})
expect(edges[0].type).toBe('arrow');
});
it('should handle subgraphs5', function () {
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-- text -->d\nd-->e\n end;')
it('should handle subgraphs5', function() {
const res = flow.parser.parse('graph TD\nA-->B\nsubgraph myTitle\nc-- text -->d\nd-->e\n end;');
const vert = flow.parser.yy.getVertices()
const edges = flow.parser.yy.getEdges()
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow')
})
})
expect(edges[0].type).toBe('arrow');
});
});