Merge branch 'develop' into develop

This commit is contained in:
Knut Sveidqvist 2021-11-11 19:09:12 +01:00 committed by GitHub
commit 0db5f9fea7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 22385 additions and 36607 deletions

View File

@ -563,7 +563,8 @@ context('Sequence diagram', () => {
}); });
}); });
context('links', () => { context('links', () => {
it('should support actor links and properties', () => { it('should support actor links and properties EXPERIMENTAL: USE WITH CAUTION', () => {
//Be aware that the syntax for "properties" is likely to be changed.
imgSnapshotTest( imgSnapshotTest(
` `
%%{init: { "config": { "mirrorActors": true, "forceMenus": true }}}%% %%{init: { "config": { "mirrorActors": true, "forceMenus": true }}}%%
@ -583,7 +584,8 @@ context('Sequence diagram', () => {
{ logLevel: 0, sequence: { mirrorActors: true, noteFontSize: 18, noteFontFamily: 'Arial' } } { logLevel: 0, sequence: { mirrorActors: true, noteFontSize: 18, noteFontFamily: 'Arial' } }
); );
}); });
it('should support actor links and properties when not mirrored', () => { it('should support actor links and properties when not mirrored EXPERIMENTAL: USE WITH CAUTION', () => {
//Be aware that the syntax for "properties" is likely to be changed.
imgSnapshotTest( imgSnapshotTest(
` `
%%{init: { "config": { "mirrorActors": false, "forceMenus": true, "wrap": true }}}%% %%{init: { "config": { "mirrorActors": false, "forceMenus": true, "wrap": true }}}%%

686
dist/mermaid.core.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

56780
dist/mermaid.js vendored

File diff suppressed because one or more lines are too long

2
dist/mermaid.js.map vendored

File diff suppressed because one or more lines are too long

2
dist/mermaid.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@ Configuration is the second half of Mermaid, after deployment. Together Deployme
This section will introduce the different methods of configuring of the behaviors and appearances of Mermaid Diagrams. This section will introduce the different methods of configuring of the behaviors and appearances of Mermaid Diagrams.
The Following are the most commonly used methods, and are all tied to Mermaid [Deployment](./n00b-gettingStarted.md) methods. The Following are the most commonly used methods, and are all tied to Mermaid [Deployment](./n00b-gettingStarted.md) methods.
## Configuration Section in the [Live Editor](./Live-Editor.md). ## Configuration Section in the [Live Editor](https://mermaid-js.github.io/mermaid-live-editor).
## The `initialize()` call, for when Mermaid is called via an API, or through a <script> tag. ## The `initialize()` call, for when Mermaid is called via an API, or through a <script> tag.

View File

@ -1005,7 +1005,7 @@ Note: currentConfig is set in this function
- `conf` the base currentConfig to use as siteConfig - `conf` the base currentConfig to use as siteConfig
Returns **any** the siteConfig Returns **[Object][6]** the siteConfig
## getSiteConfig ## getSiteConfig
@ -1018,7 +1018,7 @@ Returns **any** the siteConfig
**Notes**: **Notes**:
Returns **any** values in siteConfig. Returns **any** values in siteConfig.
Returns **any** Returns **[Object][6]** the siteConfig
## setConfig ## setConfig
@ -1067,6 +1067,14 @@ Note: modifies options in-place
- `options` the potential setConfig parameter - `options` the potential setConfig parameter
## addDirective
Pushes in a directive to the configuration
### Parameters
- `directive` **[Object][6]** The directive to push in
## reset ## reset
## reset ## reset
@ -1185,3 +1193,5 @@ mermaidAPI.initialize({
[4]: 8.6.0_docs.md [4]: 8.6.0_docs.md
[5]: #mermaidapi-configuration-defaults [5]: #mermaidapi-configuration-defaults
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object

View File

@ -50,7 +50,7 @@ gantt
Future task in critical line :crit, 5d Future task in critical line :crit, 5d
Create tests for renderer :2d Create tests for renderer :2d
Add to mermaid :1d Add to mermaid :1d
functionality added :milestone Functionality added :milestone, 2014-01-25, 0d
section Documentation section Documentation
Describe gantt syntax :active, a1, after des1, 3d Describe gantt syntax :active, a1, after des1, 3d

View File

@ -107,6 +107,9 @@ They also serve as proof of concept, for the variety of things that can be built
- [Inkdrop](http://inkdrop.app) - [Plugin](https://github.com/inkdropapp/inkdrop-mermaid) - [Inkdrop](http://inkdrop.app) - [Plugin](https://github.com/inkdropapp/inkdrop-mermaid)
- [Vim](https://vim.org) - [Vim](https://vim.org)
- [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram) - [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram)
- [GNU Emacs](https://www.gnu.org/software/emacs/)
- [Major mode for .mmd files](https://github.com/abrochard/mermaid-mode)
- [Org-Mode integration](https://github.com/arnm/ob-mermaid)
- [Brackets](http://brackets.io/) - [Brackets](http://brackets.io/)
- [Mermaid Preview](https://s3.amazonaws.com/extend.brackets/alanhohn.mermaid-preview/alanhohn.mermaid-preview-1.0.2.zip) - [Mermaid Preview](https://s3.amazonaws.com/extend.brackets/alanhohn.mermaid-preview/alanhohn.mermaid-preview-1.0.2.zip)
- [Iodide](https://github.com/iodide-project/iodide) - [Iodide](https://github.com/iodide-project/iodide)
@ -149,6 +152,7 @@ They also serve as proof of concept, for the variety of things that can be built
| Mermaid Diagrams | [🎡🔗](https://chrome.google.com/webstore/detail/mermaid-diagrams/phfcghedmopjadpojhmmaffjmfiakfil) | - | - | - | - | | Mermaid Diagrams | [🎡🔗](https://chrome.google.com/webstore/detail/mermaid-diagrams/phfcghedmopjadpojhmmaffjmfiakfil) | - | - | - | - |
|Mermaid Markdown | [🎡🔗](https://chrome.google.com/webstore/detail/mermaid-markdown/mboeoikjijmjcjgpccghbcoegikliijg) | - | - | - | - | |Mermaid Markdown | [🎡🔗](https://chrome.google.com/webstore/detail/mermaid-markdown/mboeoikjijmjcjgpccghbcoegikliijg) | - | - | - | - |
| Monkeys | [🎡🔗](https://chrome.google.com/webstore/detail/monkeys-mermaid-for-githu/cplfdpoajbclbgphaphphcldamfkjlgi) | - | - | - | - | | Monkeys | [🎡🔗](https://chrome.google.com/webstore/detail/monkeys-mermaid-for-githu/cplfdpoajbclbgphaphphcldamfkjlgi) | - | - | - | - |
| Mermaid Previewer | [🎡🔗](https://chrome.google.com/webstore/detail/mermaid-previewer/oidjnlhbegipkcklbdfnbkikplpghfdl) | - | - | - | - |
## Other ## Other

View File

@ -53,7 +53,8 @@ If you are interested in altering and customizing your Mermaid Diagrams, you wil
This section will introduce the different methods of configuring the behaviors and appearances of Mermaid Diagrams. This section will introduce the different methods of configuring the behaviors and appearances of Mermaid Diagrams.
The following are the most commonly used methods, and they are all tied to Mermaid [Deployment](./n00b-gettingStarted.md) methods. The following are the most commonly used methods, and they are all tied to Mermaid [Deployment](./n00b-gettingStarted.md) methods.
### Configuration Section in the [Live Editor](./Live-Editor.md). ### Configuration Section in the [Live Editor](https://mermaid-js.github.io/mermaid-live-editor).
Here you can edit certain values to change the behavior and appearance of the diagram. Here you can edit certain values to change the behavior and appearance of the diagram.
### [The initialize() call](https://mermaid-js.github.io/mermaid/#/n00b-gettingStarted?id=_3-calling-the-javascript-api), ### [The initialize() call](https://mermaid-js.github.io/mermaid/#/n00b-gettingStarted?id=_3-calling-the-javascript-api),

View File

@ -1,6 +1,6 @@
{ {
"name": "mermaid", "name": "mermaid",
"version": "8.13.2", "version": "8.13.3",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "dist/mermaid.core.js", "main": "dist/mermaid.core.js",
"module": "dist/mermaid.esm.min.mjs", "module": "dist/mermaid.esm.min.mjs",

View File

@ -41,6 +41,7 @@ export const updateCurrentConfig = (siteCfg, _directives) => {
currentConfig = cfg; currentConfig = cfg;
return cfg; return cfg;
}; };
/** /**
*## setSiteConfig *## setSiteConfig
*| Function | Description | Type | Values | *| Function | Description | Type | Values |
@ -53,7 +54,7 @@ export const updateCurrentConfig = (siteCfg, _directives) => {
*Note: currentConfig is set in this function *Note: currentConfig is set in this function
**Default value: At default, will mirror Global Config** **Default value: At default, will mirror Global Config**
* @param conf - the base currentConfig to use as siteConfig * @param conf - the base currentConfig to use as siteConfig
* @returns {*} - the siteConfig * @returns {Object} - the siteConfig
*/ */
export const setSiteConfig = (conf) => { export const setSiteConfig = (conf) => {
siteConfig = assignWithDepth({}, defaultConfig); siteConfig = assignWithDepth({}, defaultConfig);
@ -84,7 +85,7 @@ export const updateSiteConfig = (conf) => {
*| setSiteConfig|Returns the current siteConfig base configuration | Get Request | Returns Any Values in siteConfig| *| setSiteConfig|Returns the current siteConfig base configuration | Get Request | Returns Any Values in siteConfig|
***Notes**: ***Notes**:
*Returns **any** values in siteConfig. *Returns **any** values in siteConfig.
* @returns {*} * @returns {Object} - the siteConfig
*/ */
export const getSiteConfig = () => { export const getSiteConfig = () => {
return assignWithDepth({}, siteConfig); return assignWithDepth({}, siteConfig);
@ -172,6 +173,10 @@ export const sanitize = (options) => {
}); });
}; };
/**
* Pushes in a directive to the configuration
* @param {Object} directive The directive to push in
*/
export const addDirective = (directive) => { export const addDirective = (directive) => {
if (directive.fontFamily) { if (directive.fontFamily) {
if (!directive.themeVariables) { if (!directive.themeVariables) {

View File

@ -25,8 +25,8 @@ const conf = {
/** /**
* Function that adds the vertices found during parsing to the graph to be rendered. * Function that adds the vertices found during parsing to the graph to be rendered.
* @param vert Object containing the vertices. * @param {Object<string, { cssClasses: Array<string>; text: string; id: string; type: string; domId: string; }>} classes Object containing the vertices.
* @param g The graph that is to be drawn. * @param {SVGGElement} g The graph that is to be drawn.
*/ */
export const addClasses = function (classes, g) { export const addClasses = function (classes, g) {
// const svg = select(`[id="${svgId}"]`); // const svg = select(`[id="${svgId}"]`);
@ -226,19 +226,23 @@ export const addRelations = function (relations, g) {
}); });
}; };
// Todo optimize /**
* Gets the ID with the same label as in the cache
* @param {string} label The label to look for
* @returns {string} The resulting ID
*/
const getGraphId = function (label) { const getGraphId = function (label) {
const keys = Object.keys(idCache); const foundEntry = Object.entries(idCache).find((entry) => entry[1].label === label);
for (let i = 0; i < keys.length; i++) { if (foundEntry) {
if (idCache[keys[i]].label === label) { return foundEntry[0];
return keys[i];
}
} }
return undefined;
}; };
/**
* Merges the value of `conf` with the passed `cnf`
* @param {Object} cnf Config to merge
*/
export const setConf = function (cnf) { export const setConf = function (cnf) {
const keys = Object.keys(cnf); const keys = Object.keys(cnf);
@ -249,8 +253,8 @@ export const setConf = function (cnf) {
/** /**
* Draws a flowchart in the tag with id: id based on the graph definition in text. * Draws a flowchart in the tag with id: id based on the graph definition in text.
* @param text * @param {string} text
* @param id * @param {string} id
*/ */
export const drawOld = function (text, id) { export const drawOld = function (text, id) {
idCache = {}; idCache = {};
@ -498,10 +502,11 @@ export const draw = function (text, id) {
// }); // });
}; };
export default { /**
setConf, * Gets the arrow marker for a type index
draw, * @param {number} type The type to look for
}; * @returns {"aggregation" | "extension" | "composition" | "dependency"} The arrow marker
*/
function getArrowMarker(type) { function getArrowMarker(type) {
let marker; let marker;
switch (type) { switch (type) {
@ -522,3 +527,8 @@ function getArrowMarker(type) {
} }
return marker; return marker;
} }
export default {
setConf,
draw,
};

View File

@ -18,21 +18,22 @@ const conf = {
textHeight: 10, textHeight: 10,
}; };
// Todo optimize /**
* Gets the ID with the same label as in the cache
* @param {string} label The label to look for
* @returns {string} The resulting ID
*/
const getGraphId = function (label) { const getGraphId = function (label) {
const keys = Object.keys(idCache); const foundEntry = Object.entries(idCache).find((entry) => entry[1].label === label);
for (let i = 0; i < keys.length; i++) { if (foundEntry) {
if (idCache[keys[i]].label === label) { return foundEntry[0];
return keys[i];
}
} }
return undefined;
}; };
/** /**
* Setup arrow head and define the marker. The result is appended to the svg. * Setup arrow head and define the marker. The result is appended to the svg.
* @param {SVGSVGElement} elem The SVG element to append to
*/ */
const insertMarkers = function (elem) { const insertMarkers = function (elem) {
elem elem
@ -136,6 +137,10 @@ const insertMarkers = function (elem) {
.attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z'); .attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z');
}; };
/**
* Merges the value of `conf` with the passed `cnf`
* @param {Object} cnf Config to merge
*/
export const setConf = function (cnf) { export const setConf = function (cnf) {
const keys = Object.keys(cnf); const keys = Object.keys(cnf);
@ -146,8 +151,8 @@ export const setConf = function (cnf) {
/** /**
* Draws a flowchart in the tag with id: id based on the graph definition in text. * Draws a flowchart in the tag with id: id based on the graph definition in text.
* @param text * @param {string} text
* @param id * @param {string} id
*/ */
export const draw = function (text, id) { export const draw = function (text, id) {
idCache = {}; idCache = {};

View File

@ -144,6 +144,11 @@ export const drawEdge = function (elem, path, relation, conf) {
edgeCount++; edgeCount++;
}; };
/**
* Renders a class diagram
* @param {SVGSVGElement} elem The element to draw it into
* @todo Add more information in the JSDOC here
*/
export const drawClass = function (elem, classDef, conf) { export const drawClass = function (elem, classDef, conf) {
log.info('Rendering class ' + classDef); log.info('Rendering class ' + classDef);
@ -382,6 +387,13 @@ const buildLegacyDisplay = function (text) {
}; };
}; };
/**
* Adds a <tspan> for a member in a diagram
* @param {SVGElement} textEl The element to append to
* @param {string} txt The member
* @param {boolean} isFirst
* @param {{ padding: string; textHeight: string; }} conf The configuration for the member
*/
const addTspan = function (textEl, txt, isFirst, conf) { const addTspan = function (textEl, txt, isFirst, conf) {
let member = parseMember(txt); let member = parseMember(txt);
@ -396,6 +408,14 @@ const addTspan = function (textEl, txt, isFirst, conf) {
} }
}; };
/**
* Makes generics in typescript syntax
* @example <caption>Array of array of strings in typescript syntax</caption>
* // returns "Array<Array<string>>"
* parseGenericTypes("Array~Array~string~~");
* @param {string} text The text to convert
* @returns {string} The converted string
*/
const parseGenericTypes = function (text) { const parseGenericTypes = function (text) {
let cleanedText = text; let cleanedText = text;
@ -409,6 +429,11 @@ const parseGenericTypes = function (text) {
} }
}; };
/**
* Gives the styles for a classifier
* @param {"+" | "-" | "#" | "~" | "*" | "$"} classifier The classifier string
* @returns {string} Styling for the classifier
*/
const parseClassifier = function (classifier) { const parseClassifier = function (classifier) {
switch (classifier) { switch (classifier) {
case '*': case '*':

View File

@ -1,5 +1,10 @@
import DOMPurify from 'dompurify'; import DOMPurify from 'dompurify';
/**
* Gets the number of lines in a string
* @param {string | undefined} s The string to check the lines for
* @returns {number} The number of lines in that string
*/
export const getRows = (s) => { export const getRows = (s) => {
if (!s) return 1; if (!s) return 1;
let str = breakToPlaceholder(s); let str = breakToPlaceholder(s);
@ -7,6 +12,11 @@ export const getRows = (s) => {
return str.split('#br#'); return str.split('#br#');
}; };
/**
* Removes script tags from a text
* @param {string} txt The text to sanitize
* @returns {string} The safer text
*/
export const removeScript = (txt) => { export const removeScript = (txt) => {
var rs = ''; var rs = '';
var idx = 0; var idx = 0;
@ -72,20 +82,47 @@ export const sanitizeText = (text, config) => {
export const lineBreakRegex = /<br\s*\/?>/gi; export const lineBreakRegex = /<br\s*\/?>/gi;
/**
* Whether or not a text has any linebreaks
* @param {string} text The text to test
* @returns {boolean} Whether or not the text has breaks
*/
export const hasBreaks = (text) => { export const hasBreaks = (text) => {
return /<br\s*[/]?>/gi.test(text); return lineBreakRegex.test(text);
}; };
/**
* Splits on <br> tags
* @param {string} text Text to split
* @returns {Array<string>} List of lines as strings
*/
export const splitBreaks = (text) => { export const splitBreaks = (text) => {
return text.split(/<br\s*[/]?>/gi); return text.split(lineBreakRegex);
}; };
/**
* Converts placeholders to linebreaks in HTML
* @param {string} s HTML with placeholders
* @returns {string} HTML with breaks instead of placeholders
*/
const placeholderToBreak = (s) => { const placeholderToBreak = (s) => {
return s.replace(/#br#/g, '<br/>'); return s.replace(/#br#/g, '<br/>');
}; };
/**
* Opposite of `placeholderToBreak`, converts breaks to placeholders
* @param {string} s HTML string
* @returns {string} String with placeholders
*/
const breakToPlaceholder = (s) => { const breakToPlaceholder = (s) => {
return s.replace(lineBreakRegex, '#br#'); return s.replace(lineBreakRegex, '#br#');
}; };
/**
* Gets the current URL
* @param {boolean} useAbsolute Whether to return the absolute URL or not
* @returns {string} The current URL
*/
const getUrl = (useAbsolute) => { const getUrl = (useAbsolute) => {
let url = ''; let url = '';
if (useAbsolute) { if (useAbsolute) {
@ -102,6 +139,11 @@ const getUrl = (useAbsolute) => {
return url; return url;
}; };
/**
* Converts a string/boolean into a boolean
* @param {string | boolean} val String or boolean to convert
* @returns {boolean} The result from the input
*/
export const evaluate = (val) => (val === 'false' || val === false ? false : true); export const evaluate = (val) => (val === 'false' || val === false ? false : true);
export default { export default {

View File

@ -1,3 +1,8 @@
/**
* Returns the styles given options
* @param {{ fontFamily: string; nodeTextColor: string; textColor: string; titleColor: string; mainBkg: string; nodeBorder: string; arrowheadColor: string; lineColor: string; edgeLabelBackground: string; clusterBkg: string; clusterBorder: string; tertiaryColor: string; border2: string; }} options The options for the styles
* @returns {string} The resulting styles
*/
const getStyles = (options) => const getStyles = (options) =>
`.label { `.label {
font-family: ${options.fontFamily}; font-family: ${options.fontFamily};
@ -69,9 +74,9 @@ const getStyles = (options) =>
.cluster span { .cluster span {
color: ${options.titleColor}; color: ${options.titleColor};
} }
// .cluster div { /* .cluster div {
// color: ${options.titleColor}; color: ${options.titleColor};
// } } */
div.mermaidTooltip { div.mermaidTooltip {
position: absolute; position: absolute;

View File

@ -1,6 +1,7 @@
import mermaidAPI from '../../mermaidAPI'; import mermaidAPI from '../../mermaidAPI';
import * as configApi from '../../config'; import * as configApi from '../../config';
import { log } from '../../logger'; import { log } from '../../logger';
import { sanitizeText } from '../common/common';
let prevActor = undefined; let prevActor = undefined;
let actors = {}; let actors = {};
@ -219,7 +220,10 @@ export const addLinks = function (actorId, text) {
const actor = getActor(actorId); const actor = getActor(actorId);
// JSON.parse the text // JSON.parse the text
try { try {
const links = JSON.parse(text.text); let sanitizedText = sanitizeText(text.text, configApi.getConfig());
sanitizedText = sanitizedText.replace(/&amp;/g, '&');
sanitizedText = sanitizedText.replace(/&equals;/g, '=');
const links = JSON.parse(sanitizedText);
// add the deserialized text to the actor's links field. // add the deserialized text to the actor's links field.
insertLinks(actor, links); insertLinks(actor, links);
} catch (e) { } catch (e) {
@ -232,9 +236,12 @@ export const addALink = function (actorId, text) {
const actor = getActor(actorId); const actor = getActor(actorId);
try { try {
const links = {}; const links = {};
var sep = text.text.indexOf('@'); let sanitizedText = sanitizeText(text.text, configApi.getConfig());
var label = text.text.slice(0, sep - 1).trim(); var sep = sanitizedText.indexOf('@');
var link = text.text.slice(sep + 1).trim(); sanitizedText = sanitizedText.replace(/&amp;/g, '&');
sanitizedText = sanitizedText.replace(/&equals;/g, '=');
var label = sanitizedText.slice(0, sep - 1).trim();
var link = sanitizedText.slice(sep + 1).trim();
links[label] = link; links[label] = link;
// add the deserialized text to the actor's links field. // add the deserialized text to the actor's links field.
@ -259,7 +266,8 @@ export const addProperties = function (actorId, text) {
const actor = getActor(actorId); const actor = getActor(actorId);
// JSON.parse the text // JSON.parse the text
try { try {
const properties = JSON.parse(text.text); let sanitizedText = sanitizeText(text.text, configApi.getConfig());
const properties = JSON.parse(sanitizedText);
// add the deserialized text to the actor's property field. // add the deserialized text to the actor's property field.
insertProperties(actor, properties); insertProperties(actor, properties);
} catch (e) { } catch (e) {

View File

@ -976,7 +976,8 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com
expect(actors.a.links["Tests"]).toBe("https://tests.contoso.com/?svc=alice@contoso.com"); expect(actors.a.links["Tests"]).toBe("https://tests.contoso.com/?svc=alice@contoso.com");
}); });
it('it should handle properties', function () { it('it should handle properties EXPERIMENTAL: USE WITH CAUTION', function () {
//Be aware that the syntax for "properties" is likely to be changed.
const str = ` const str = `
sequenceDiagram sequenceDiagram
participant a as Alice participant a as Alice

View File

@ -1,4 +1,5 @@
import common from '../common/common'; import common from '../common/common';
import { addFunction } from '../../interactionDb';
export const drawRect = function (elem, rectData) { export const drawRect = function (elem, rectData) {
const rectElem = elem.append('rect'); const rectElem = elem.append('rect');
@ -25,6 +26,17 @@ const sanitizeUrl = function (s) {
.replace(/javascript:/g, ''); .replace(/javascript:/g, '');
}; };
const addPopupInteraction = (id, actorCnt) => {
addFunction(() => {
const arr = document.querySelectorAll(id);
arr[0].addEventListener('mouseover', function () {
popupMenuUpFunc('actor' + actorCnt + '_popup');
});
arr[0].addEventListener('mouseout', function () {
popupMenuDownFunc('actor' + actorCnt + '_popup');
});
});
};
export const drawPopup = function (elem, actor, minMenuWidth, textAttrs, forceMenus) { export const drawPopup = function (elem, actor, minMenuWidth, textAttrs, forceMenus) {
if (actor.links === undefined || actor.links === null || Object.keys(actor.links).length === 0) { if (actor.links === undefined || actor.links === null || Object.keys(actor.links).length === 0) {
return { height: 0, width: 0 }; return { height: 0, width: 0 };
@ -43,9 +55,7 @@ export const drawPopup = function (elem, actor, minMenuWidth, textAttrs, forceMe
g.attr('id', 'actor' + actorCnt + '_popup'); g.attr('id', 'actor' + actorCnt + '_popup');
g.attr('class', 'actorPopupMenu'); g.attr('class', 'actorPopupMenu');
g.attr('display', displayValue); g.attr('display', displayValue);
g.attr('onmouseover', popupMenu('actor' + actorCnt + '_popup')); addPopupInteraction('#actor' + actorCnt + '_popup', actorCnt);
g.attr('onmouseout', popdownMenu('actor' + actorCnt + '_popup'));
var actorClass = ''; var actorClass = '';
if (typeof rectData.class !== 'undefined') { if (typeof rectData.class !== 'undefined') {
actorClass = ' ' + rectData.class; actorClass = ' ' + rectData.class;
@ -123,6 +133,19 @@ export const popdownMenu = function (popid) {
); );
}; };
const popupMenuUpFunc = function (popupId) {
var pu = document.getElementById(popupId);
if (pu != null) {
pu.style.display = 'block';
}
};
const popupMenuDownFunc = function (popupId) {
var pu = document.getElementById(popupId);
if (pu != null) {
pu.style.display = 'none';
}
};
export const drawText = function (elem, textData) { export const drawText = function (elem, textData) {
let prevTextHeight = 0, let prevTextHeight = 0,
textHeight = 0; textHeight = 0;
@ -321,9 +344,10 @@ const drawActorTypeParticipant = function (elem, actor, conf) {
g = boxpluslineGroup.append('g'); g = boxpluslineGroup.append('g');
actor.actorCnt = actorCnt; actor.actorCnt = actorCnt;
if (actor.links != null) { if (actor.links != null) {
g.attr('onmouseover', popupMenu('actor' + actorCnt + '_popup')); g.attr('id', 'root-' + actorCnt);
g.attr('onmouseout', popdownMenu('actor' + actorCnt + '_popup')); addPopupInteraction('#root-' + actorCnt, actorCnt);
} }
} }
@ -370,6 +394,7 @@ const drawActorTypeParticipant = function (elem, actor, conf) {
actor.height = bounds.height; actor.height = bounds.height;
height = bounds.height; height = bounds.height;
} }
return height; return height;
}; };

View File

@ -5,6 +5,11 @@ import { select } from 'd3';
import { log } from './logger'; import { log } from './logger';
const conf = {}; const conf = {};
/**
* Merges the value of `conf` with the passed `cnf`
* @param {Object} cnf Config to merge
*/
export const setConf = function (cnf) { export const setConf = function (cnf) {
const keys = Object.keys(cnf); const keys = Object.keys(cnf);
@ -15,8 +20,8 @@ export const setConf = function (cnf) {
/** /**
* Draws a an info picture in the tag with id: id based on the graph definition in text. * Draws a an info picture in the tag with id: id based on the graph definition in text.
* @param text * @param id The text for the error
* @param id * @param ver The version
*/ */
export const draw = (id, ver) => { export const draw = (id, ver) => {
try { try {

10
src/interactionDb.js Normal file
View File

@ -0,0 +1,10 @@
let interactionFunctions = [];
export const addFunction = (func) => {
interactionFunctions.push(func);
};
export const attachFunctions = () => {
interactionFunctions.forEach((f) => {
f();
});
interactionFunctions = [];
};

View File

@ -1,5 +1,12 @@
import moment from 'moment-mini'; import moment from 'moment-mini';
/**
* @typedef {"debug" | "info" | "warn" | "error" | "fatal"} LogLevel A log level
*/
/**
* @type {Object<LogLevel, number>}
*/
export const LEVELS = { export const LEVELS = {
debug: 1, debug: 1,
info: 2, info: 2,
@ -16,6 +23,10 @@ export const log = {
fatal: () => {}, fatal: () => {},
}; };
/**
* Sets a log level
* @param {LogLevel} [level="fatal"] The level to set the logging to
*/
export const setLogLevel = function (level = 'fatal') { export const setLogLevel = function (level = 'fatal') {
if (isNaN(level)) { if (isNaN(level)) {
level = level.toLowerCase(); level = level.toLowerCase();
@ -56,6 +67,11 @@ export const setLogLevel = function (level = 'fatal') {
} }
}; };
/**
* Returns a format with the timestamp and the log level
* @param {LogLevel} level The level for the log format
* @returns {string} The format with the timestamp and log level
*/
const format = (level) => { const format = (level) => {
const time = moment().format('ss.SSS'); const time = moment().format('ss.SSS');
return `%c${time} : ${level} : `; return `%c${time} : ${level} : `;

View File

@ -54,6 +54,7 @@ import journeyDb from './diagrams/user-journey/journeyDb';
import journeyRenderer from './diagrams/user-journey/journeyRenderer'; import journeyRenderer from './diagrams/user-journey/journeyRenderer';
import journeyParser from './diagrams/user-journey/parser/journey'; import journeyParser from './diagrams/user-journey/parser/journey';
import errorRenderer from './errorRenderer'; import errorRenderer from './errorRenderer';
import { attachFunctions } from './interactionDb';
// import * as configApi from './config'; // import * as configApi from './config';
// // , { // // , {
@ -483,6 +484,7 @@ const render = function (id, _txt, cb, container) {
} else { } else {
log.debug('CB = undefined!'); log.debug('CB = undefined!');
} }
attachFunctions();
const node = select('#d' + id).node(); const node = select('#d' + id).node();
if (node !== null && typeof node.remove === 'function') { if (node !== null && typeof node.remove === 'function') {

View File

@ -175,6 +175,7 @@ export const detectDirective = function (text, type = null) {
* ``` * ```
* *
* @param {string} text The text defining the graph * @param {string} text The text defining the graph
* @param {{ class: { defaultRenderer: string } | undefined; state: { defaultRenderer: string } | undefined; flowchart: { defaultRenderer: string } | undefined; }} [cnf]
* @returns {string} A graph definition key * @returns {string} A graph definition key
*/ */
export const detectType = function (text, cnf) { export const detectType = function (text, cnf) {
@ -234,6 +235,12 @@ export const detectType = function (text, cnf) {
return 'flowchart'; return 'flowchart';
}; };
/**
* Caches results of functions based on input
* @param {Function} fn Function to run
* @param {Function} resolver Function that resolves to an ID given arguments the `fn` takes
* @returns {Function} An optimized caching function
*/
const memoize = (fn, resolver) => { const memoize = (fn, resolver) => {
let cache = {}; let cache = {};
return (...args) => { return (...args) => {
@ -262,6 +269,12 @@ export const isSubstringInArray = function (str, arr) {
return -1; return -1;
}; };
/**
* Returns a d3 curve given a curve name
* @param {string | undefined} interpolate The interpolation name
* @param {*} defaultCurve The default curve to return
* @returns {import('d3-shape').CurveFactory} The curve factory to use
*/
export const interpolateToCurve = (interpolate, defaultCurve) => { export const interpolateToCurve = (interpolate, defaultCurve) => {
if (!interpolate) { if (!interpolate) {
return defaultCurve; return defaultCurve;
@ -270,6 +283,12 @@ export const interpolateToCurve = (interpolate, defaultCurve) => {
return d3CurveTypes[curveName] || defaultCurve; return d3CurveTypes[curveName] || defaultCurve;
}; };
/**
* Formats a URL string
* @param {string} linkStr String of the URL
* @param {{ securityLevel: string; }} config Configuration passed to MermaidJS
* @returns {string | undefined} The formatted URL
*/
export const formatUrl = (linkStr, config) => { export const formatUrl = (linkStr, config) => {
let url = linkStr.trim(); let url = linkStr.trim();
@ -282,6 +301,11 @@ export const formatUrl = (linkStr, config) => {
} }
}; };
/**
* Runs a function
* @param {string} functionName A dot seperated path to the function relative to the `window`
* @param {...any} params Parameters to pass to the function
*/
export const runFunc = (functionName, ...params) => { export const runFunc = (functionName, ...params) => {
const arrPaths = functionName.split('.'); const arrPaths = functionName.split('.');
@ -297,9 +321,26 @@ export const runFunc = (functionName, ...params) => {
obj[fnName](...params); obj[fnName](...params);
}; };
/**
* @typedef {Object} Point A (x, y) point
* @property {number} x The x value
* @property {number} y The y value
*/
/**
* Finds the distance between two points using the Distance Formula
* @param {Point} p1 The first point
* @param {Point} p2 The second point
* @returns {number} The distance
*/
const distance = (p1, p2) => const distance = (p1, p2) =>
p1 && p2 ? Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)) : 0; p1 && p2 ? Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)) : 0;
/**
* @todo Give this a description
* @param {Array<Point>} points List of points
* @returns {Point}
*/
const traverseEdge = (points) => { const traverseEdge = (points) => {
let prevPoint; let prevPoint;
let totalDistance = 0; let totalDistance = 0;
@ -337,6 +378,11 @@ const traverseEdge = (points) => {
return center; return center;
}; };
/**
* Alias for `traverseEdge`
* @param {Point[]} points List of points
* @returns {Point} Return result of `transverseEdge`
*/
const calcLabelPosition = (points) => { const calcLabelPosition = (points) => {
return traverseEdge(points); return traverseEdge(points);
}; };
@ -462,6 +508,11 @@ const calcTerminalLabelPosition = (terminalMarkerSize, position, _points) => {
return cardinalityPosition; return cardinalityPosition;
}; };
/**
* Gets styles from an array of declarations
* @param {Array<string>} arr Declarations
* @returns {{ style: string; labelStyle: string; }} The styles grouped as strings
*/
export const getStylesFromArray = (arr) => { export const getStylesFromArray = (arr) => {
let style = ''; let style = '';
let labelStyle = ''; let labelStyle = '';
@ -520,9 +571,9 @@ export const random = (options) => {
* <p> * <p>
* If src is a destructured array of objects and dst is not an array, assignWithDepth will apply each element of src to dst * If src is a destructured array of objects and dst is not an array, assignWithDepth will apply each element of src to dst
* in order. * in order.
* @param dst:any - the destination of the merge * @param {any} dst - the destination of the merge
* @param src:any - the source object(s) to merge into destination * @param {any} src - the source object(s) to merge into destination
* @param config:{ depth: number, clobber: boolean } - depth: depth to traverse within src and dst for merging - * @param {{ depth: number, clobber: boolean }} [config={ depth: 2, clobber: false }] - depth: depth to traverse within src and dst for merging -
* clobber: should dissimilar types clobber (default: { depth: 2, clobber: false }) * clobber: should dissimilar types clobber (default: { depth: 2, clobber: false })
* @returns {*} * @returns {*}
*/ */
@ -580,6 +631,12 @@ export const getTextObj = function () {
}; };
}; };
/**
* Adds text to an element
* @param {SVGElement} elem Element to add text to
* @param {{ text: string; x: number; y: number; anchor: "start" | "middle" | "end"; fontFamily: string; fontSize: string | number; fontWeight: string | number; fill: string; class: string | undefined; textMargin: number; }} textData
* @returns {SVGTextElement} Text element with given styling and content
*/
export const drawSimpleText = function (elem, textData) { export const drawSimpleText = function (elem, textData) {
// Remove and ignore br:s // Remove and ignore br:s
const nText = textData.text.replace(common.lineBreakRegex, ' '); const nText = textData.text.replace(common.lineBreakRegex, ' ');
@ -770,12 +827,24 @@ export const calculateTextDimensions = memoize(
(text, config) => `${text}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}` (text, config) => `${text}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}`
); );
/**
* Applys d3 attributes
* @param {*} d3Elem d3 Element to apply the attributes onto
* @param {Array<[string, string]>} attrs Object.keys equivalent format of key to value mapping of attributes
*/
const d3Attrs = function (d3Elem, attrs) { const d3Attrs = function (d3Elem, attrs) {
for (let attr of attrs) { for (let attr of attrs) {
d3Elem.attr(attr[0], attr[1]); d3Elem.attr(attr[0], attr[1]);
} }
}; };
/**
* Gives attributes for an SVG's size given arguments
* @param {number} height The height of the SVG
* @param {number} width The width of the SVG
* @param {boolean} useMaxWidth Whether or not to use max-width and set width to 100%
* @returns {Map<'height' | 'width' | 'style', string>} Attributes for the SVG
*/
export const calculateSvgSizeAttrs = function (height, width, useMaxWidth) { export const calculateSvgSizeAttrs = function (height, width, useMaxWidth) {
let attrs = new Map(); let attrs = new Map();
attrs.set('height', height); attrs.set('height', height);
@ -788,6 +857,13 @@ export const calculateSvgSizeAttrs = function (height, width, useMaxWidth) {
return attrs; return attrs;
}; };
/**
* Applies attributes from `calculateSvgSizeAttrs`
* @param {SVGSVGElement} svgElem The SVG Element to configure
* @param {number} height The height of the SVG
* @param {number} width The width of the SVG
* @param {boolean} useMaxWidth Whether or not to use max-width and set width to 100%
*/
export const configureSvgSize = function (svgElem, height, width, useMaxWidth) { export const configureSvgSize = function (svgElem, height, width, useMaxWidth) {
const attrs = calculateSvgSizeAttrs(height, width, useMaxWidth); const attrs = calculateSvgSizeAttrs(height, width, useMaxWidth);
d3Attrs(svgElem, attrs); d3Attrs(svgElem, attrs);
@ -808,8 +884,13 @@ export const initIdGeneratior = class iterator {
} }
}; };
// Source https://github.com/shrpne/entity-decode/blob/master/browser.js
let decoder; let decoder;
/**
* Decodes HTML, source: {@link https://github.com/shrpne/entity-decode/blob/v2.0.1/browser.js}
* @param {string} html HTML as a string
* @returns Unescaped HTML
*/
export const entityDecode = function (html) { export const entityDecode = function (html) {
decoder = decoder || document.createElement('div'); decoder = decoder || document.createElement('div');
// Escape HTML before decoding for HTML Entities // Escape HTML before decoding for HTML Entities
@ -819,6 +900,10 @@ export const entityDecode = function (html) {
return unescape(decoder.textContent); return unescape(decoder.textContent);
}; };
/**
* Sanitizes directive objects
* @param {Object} args Directive's JSON
*/
export const directiveSanitizer = (args) => { export const directiveSanitizer = (args) => {
log.debug('directiveSanitizer called with', args); log.debug('directiveSanitizer called with', args);
if (typeof args === 'object') { if (typeof args === 'object') {

1137
yarn.lock

File diff suppressed because it is too large Load Diff