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', () => {
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(
`
%%{init: { "config": { "mirrorActors": true, "forceMenus": true }}}%%
@ -583,7 +584,8 @@ context('Sequence diagram', () => {
{ 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(
`
%%{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.
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.

View File

@ -1005,7 +1005,7 @@ Note: currentConfig is set in this function
- `conf` the base currentConfig to use as siteConfig
Returns **any** the siteConfig
Returns **[Object][6]** the siteConfig
## getSiteConfig
@ -1018,7 +1018,7 @@ Returns **any** the siteConfig
**Notes**:
Returns **any** values in siteConfig.
Returns **any**
Returns **[Object][6]** the siteConfig
## setConfig
@ -1067,6 +1067,14 @@ Note: modifies options in-place
- `options` the potential setConfig parameter
## addDirective
Pushes in a directive to the configuration
### Parameters
- `directive` **[Object][6]** The directive to push in
## reset
## reset
@ -1185,3 +1193,5 @@ mermaidAPI.initialize({
[4]: 8.6.0_docs.md
[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
Create tests for renderer :2d
Add to mermaid :1d
functionality added :milestone
Functionality added :milestone, 2014-01-25, 0d
section Documentation
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)
- [Vim](https://vim.org)
- [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/)
- [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)
@ -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 Markdown | [🎡🔗](https://chrome.google.com/webstore/detail/mermaid-markdown/mboeoikjijmjcjgpccghbcoegikliijg) | - | - | - | - |
| Monkeys | [🎡🔗](https://chrome.google.com/webstore/detail/monkeys-mermaid-for-githu/cplfdpoajbclbgphaphphcldamfkjlgi) | - | - | - | - |
| Mermaid Previewer | [🎡🔗](https://chrome.google.com/webstore/detail/mermaid-previewer/oidjnlhbegipkcklbdfnbkikplpghfdl) | - | - | - | - |
## Other

View File

@ -51,9 +51,10 @@ Configuration is the third part of Mermaid, after deployment and syntax. It deal
If you are interested in altering and customizing your Mermaid Diagrams, you will find the methods and values available for [Configuration](./Setup.md) here. It includes themes.
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](https://mermaid-js.github.io/mermaid-live-editor).
### Configuration Section in the [Live Editor](./Live-Editor.md).
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),

View File

@ -1,6 +1,6 @@
{
"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.",
"main": "dist/mermaid.core.js",
"module": "dist/mermaid.esm.min.mjs",

View File

@ -41,6 +41,7 @@ export const updateCurrentConfig = (siteCfg, _directives) => {
currentConfig = cfg;
return cfg;
};
/**
*## setSiteConfig
*| Function | Description | Type | Values |
@ -53,7 +54,7 @@ export const updateCurrentConfig = (siteCfg, _directives) => {
*Note: currentConfig is set in this function
**Default value: At default, will mirror Global Config**
* @param conf - the base currentConfig to use as siteConfig
* @returns {*} - the siteConfig
* @returns {Object} - the siteConfig
*/
export const setSiteConfig = (conf) => {
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|
***Notes**:
*Returns **any** values in siteConfig.
* @returns {*}
* @returns {Object} - the siteConfig
*/
export const getSiteConfig = () => {
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) => {
if (directive.fontFamily) {
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.
* @param vert Object containing the vertices.
* @param g The graph that is to be drawn.
* @param {Object<string, { cssClasses: Array<string>; text: string; id: string; type: string; domId: string; }>} classes Object containing the vertices.
* @param {SVGGElement} g The graph that is to be drawn.
*/
export const addClasses = function (classes, g) {
// 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 keys = Object.keys(idCache);
const foundEntry = Object.entries(idCache).find((entry) => entry[1].label === label);
for (let i = 0; i < keys.length; i++) {
if (idCache[keys[i]].label === label) {
return keys[i];
}
if (foundEntry) {
return foundEntry[0];
}
return undefined;
};
/**
* Merges the value of `conf` with the passed `cnf`
* @param {Object} cnf Config to merge
*/
export const setConf = function (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.
* @param text
* @param id
* @param {string} text
* @param {string} id
*/
export const drawOld = function (text, id) {
idCache = {};
@ -498,10 +502,11 @@ export const draw = function (text, id) {
// });
};
export default {
setConf,
draw,
};
/**
* Gets the arrow marker for a type index
* @param {number} type The type to look for
* @returns {"aggregation" | "extension" | "composition" | "dependency"} The arrow marker
*/
function getArrowMarker(type) {
let marker;
switch (type) {
@ -522,3 +527,8 @@ function getArrowMarker(type) {
}
return marker;
}
export default {
setConf,
draw,
};

View File

@ -18,21 +18,22 @@ const conf = {
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 keys = Object.keys(idCache);
const foundEntry = Object.entries(idCache).find((entry) => entry[1].label === label);
for (let i = 0; i < keys.length; i++) {
if (idCache[keys[i]].label === label) {
return keys[i];
}
if (foundEntry) {
return foundEntry[0];
}
return undefined;
};
/**
* 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) {
elem
@ -136,6 +137,10 @@ const insertMarkers = function (elem) {
.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) {
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.
* @param text
* @param id
* @param {string} text
* @param {string} id
*/
export const draw = function (text, id) {
idCache = {};

View File

@ -144,6 +144,11 @@ export const drawEdge = function (elem, path, relation, conf) {
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) {
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) {
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) {
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) {
switch (classifier) {
case '*':

View File

@ -1,5 +1,10 @@
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) => {
if (!s) return 1;
let str = breakToPlaceholder(s);
@ -7,6 +12,11 @@ export const getRows = (s) => {
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) => {
var rs = '';
var idx = 0;
@ -72,20 +82,47 @@ export const sanitizeText = (text, config) => {
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) => {
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) => {
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) => {
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) => {
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) => {
let url = '';
if (useAbsolute) {
@ -102,6 +139,11 @@ const getUrl = (useAbsolute) => {
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 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) =>
`.label {
font-family: ${options.fontFamily};
@ -69,9 +74,9 @@ const getStyles = (options) =>
.cluster span {
color: ${options.titleColor};
}
// .cluster div {
// color: ${options.titleColor};
// }
/* .cluster div {
color: ${options.titleColor};
} */
div.mermaidTooltip {
position: absolute;

View File

@ -1,6 +1,7 @@
import mermaidAPI from '../../mermaidAPI';
import * as configApi from '../../config';
import { log } from '../../logger';
import { sanitizeText } from '../common/common';
let prevActor = undefined;
let actors = {};
@ -219,7 +220,10 @@ export const addLinks = function (actorId, text) {
const actor = getActor(actorId);
// JSON.parse the text
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.
insertLinks(actor, links);
} catch (e) {
@ -232,9 +236,12 @@ export const addALink = function (actorId, text) {
const actor = getActor(actorId);
try {
const links = {};
var sep = text.text.indexOf('@');
var label = text.text.slice(0, sep - 1).trim();
var link = text.text.slice(sep + 1).trim();
let sanitizedText = sanitizeText(text.text, configApi.getConfig());
var sep = sanitizedText.indexOf('@');
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;
// add the deserialized text to the actor's links field.
@ -259,7 +266,8 @@ export const addProperties = function (actorId, text) {
const actor = getActor(actorId);
// JSON.parse the text
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.
insertProperties(actor, properties);
} 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");
});
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 = `
sequenceDiagram
participant a as Alice

View File

@ -1,4 +1,5 @@
import common from '../common/common';
import { addFunction } from '../../interactionDb';
export const drawRect = function (elem, rectData) {
const rectElem = elem.append('rect');
@ -25,6 +26,17 @@ const sanitizeUrl = function (s) {
.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) {
if (actor.links === undefined || actor.links === null || Object.keys(actor.links).length === 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('class', 'actorPopupMenu');
g.attr('display', displayValue);
g.attr('onmouseover', popupMenu('actor' + actorCnt + '_popup'));
g.attr('onmouseout', popdownMenu('actor' + actorCnt + '_popup'));
addPopupInteraction('#actor' + actorCnt + '_popup', actorCnt);
var actorClass = '';
if (typeof rectData.class !== 'undefined') {
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) {
let prevTextHeight = 0,
textHeight = 0;
@ -321,9 +344,10 @@ const drawActorTypeParticipant = function (elem, actor, conf) {
g = boxpluslineGroup.append('g');
actor.actorCnt = actorCnt;
if (actor.links != null) {
g.attr('onmouseover', popupMenu('actor' + actorCnt + '_popup'));
g.attr('onmouseout', popdownMenu('actor' + actorCnt + '_popup'));
g.attr('id', 'root-' + actorCnt);
addPopupInteraction('#root-' + actorCnt, actorCnt);
}
}
@ -370,6 +394,7 @@ const drawActorTypeParticipant = function (elem, actor, conf) {
actor.height = bounds.height;
height = bounds.height;
}
return height;
};

View File

@ -5,6 +5,11 @@ import { select } from 'd3';
import { log } from './logger';
const conf = {};
/**
* Merges the value of `conf` with the passed `cnf`
* @param {Object} cnf Config to merge
*/
export const setConf = function (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.
* @param text
* @param id
* @param id The text for the error
* @param ver The version
*/
export const draw = (id, ver) => {
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';
/**
* @typedef {"debug" | "info" | "warn" | "error" | "fatal"} LogLevel A log level
*/
/**
* @type {Object<LogLevel, number>}
*/
export const LEVELS = {
debug: 1,
info: 2,
@ -16,6 +23,10 @@ export const log = {
fatal: () => {},
};
/**
* Sets a log level
* @param {LogLevel} [level="fatal"] The level to set the logging to
*/
export const setLogLevel = function (level = 'fatal') {
if (isNaN(level)) {
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 time = moment().format('ss.SSS');
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 journeyParser from './diagrams/user-journey/parser/journey';
import errorRenderer from './errorRenderer';
import { attachFunctions } from './interactionDb';
// import * as configApi from './config';
// // , {
@ -483,6 +484,7 @@ const render = function (id, _txt, cb, container) {
} else {
log.debug('CB = undefined!');
}
attachFunctions();
const node = select('#d' + id).node();
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 {{ class: { defaultRenderer: string } | undefined; state: { defaultRenderer: string } | undefined; flowchart: { defaultRenderer: string } | undefined; }} [cnf]
* @returns {string} A graph definition key
*/
export const detectType = function (text, cnf) {
@ -234,6 +235,12 @@ export const detectType = function (text, cnf) {
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) => {
let cache = {};
return (...args) => {
@ -262,6 +269,12 @@ export const isSubstringInArray = function (str, arr) {
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) => {
if (!interpolate) {
return defaultCurve;
@ -270,6 +283,12 @@ export const interpolateToCurve = (interpolate, 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) => {
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) => {
const arrPaths = functionName.split('.');
@ -297,9 +321,26 @@ export const runFunc = (functionName, ...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) =>
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) => {
let prevPoint;
let totalDistance = 0;
@ -337,6 +378,11 @@ const traverseEdge = (points) => {
return center;
};
/**
* Alias for `traverseEdge`
* @param {Point[]} points List of points
* @returns {Point} Return result of `transverseEdge`
*/
const calcLabelPosition = (points) => {
return traverseEdge(points);
};
@ -462,6 +508,11 @@ const calcTerminalLabelPosition = (terminalMarkerSize, position, _points) => {
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) => {
let style = '';
let labelStyle = '';
@ -520,9 +571,9 @@ export const random = (options) => {
* <p>
* 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.
* @param dst:any - the destination of the merge
* @param src:any - 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 {any} dst - the destination of the merge
* @param {any} src - the source object(s) to merge into destination
* @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 })
* @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) {
// Remove and ignore br:s
const nText = textData.text.replace(common.lineBreakRegex, ' ');
@ -770,12 +827,24 @@ export const calculateTextDimensions = memoize(
(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) {
for (let attr of attrs) {
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) {
let attrs = new Map();
attrs.set('height', height);
@ -788,6 +857,13 @@ export const calculateSvgSizeAttrs = function (height, width, useMaxWidth) {
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) {
const attrs = calculateSvgSizeAttrs(height, width, useMaxWidth);
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;
/**
* 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) {
decoder = decoder || document.createElement('div');
// Escape HTML before decoding for HTML Entities
@ -819,6 +900,10 @@ export const entityDecode = function (html) {
return unescape(decoder.textContent);
};
/**
* Sanitizes directive objects
* @param {Object} args Directive's JSON
*/
export const directiveSanitizer = (args) => {
log.debug('directiveSanitizer called with', args);
if (typeof args === 'object') {

1137
yarn.lock

File diff suppressed because it is too large Load Diff