Fixed test cases for sequence diagrams

Updated config to match a conversation knut and i had about the relationship between global, site, and integrator configuration
(Will update docs)
Renamed wrapEnabled to wrap
Poor man's caching for calculateTextDimensions, wrapLabel, and breakString (actually makes a huge difference)
This commit is contained in:
chris moran
2020-06-26 09:26:56 -04:00
parent 7d9bf83f66
commit 217bd1f4bf
13 changed files with 1156 additions and 1370 deletions

View File

@@ -144,7 +144,7 @@ context('Sequence diagram', () => {
it('should wrap (directive) long actor descriptions', () => {
imgSnapshotTest(
`
%%{init: {'config': {'wrapEnabled': true }}}%%
%%{init: {'config': {'wrap': true }}}%%
sequenceDiagram
participant A as Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
A->>Bob: Hola
@@ -326,7 +326,7 @@ context('Sequence diagram', () => {
it('should render a single and nested opt with long test wrapping', () => {
imgSnapshotTest(
`
%%{init: { 'config': { 'wrapEnabled': true } } }%%
%%{init: { 'config': { 'wrap': true } } }%%
sequenceDiagram
participant A
participant B
@@ -482,7 +482,7 @@ context('Sequence diagram', () => {
it('should render with wrapping enabled', () => {
imgSnapshotTest(
`
%%{init: { 'config': { 'wrapEnabled': true }}}%%
%%{init: { 'config': { 'wrap': true }}}%%
sequenceDiagram
participant A as Alice, the talkative one
A->>John: Hello John, how are you today? I'm feeling quite verbose today.
@@ -495,7 +495,7 @@ context('Sequence diagram', () => {
});
it('should render with an init directive', () => {
imgSnapshotTest(
`%%{init: { "theme": "dark", 'config': { "fontFamily": "Menlo", "fontSize": 18, "fontWeight": 400, "wrapEnabled": true }}}%%
`%%{init: { "theme": "dark", 'config': { "fontFamily": "Menlo", "fontSize": 18, "fontWeight": 400, "wrap": true }}}%%
sequenceDiagram
Alice->>Bob: Hello Bob, how are you? If you are not available right now, I can leave you a message. Please get back to me as soon as you can!
Note left of Alice: Bob thinks
@@ -520,11 +520,10 @@ context('Sequence diagram', () => {
it('should overide config with directive settings', () => {
imgSnapshotTest(
`
%%{init: { "config": { "mirrorActors": false }}}%%
%%{init: { "config": { "mirrorActors": false, "wrap": true }}}%%
sequenceDiagram
%%{config: { "mirrorActors": false} }%%
Alice->>Bob: I'm short
note left of Alice: config set to mirrorActors: true<br/>directive set to mirrorActors: false
note left of Alice: config: mirrorActors=true<br/>directive: mirrorActors=false
Bob->>Alice: Short as well
`,
{ logLevel:0, sequence: { mirrorActors: true, noteFontSize: 18, noteFontFamily: 'Arial' } }

View File

@@ -11,429 +11,6 @@ It is is then up to the user of the API to make use of the svg, either insert it
In addition to the render function, a number of behavioral configuration options are available.
## Configuration
These are the default options which can be overridden with the initialization call like so:
**Example 1:**
<pre>
mermaid.initialize({
flowchart:{
htmlLabels: false
}
});
</pre>
**Example 2:**
<pre>
&lt;script>
var config = {
startOnLoad:true,
flowchart:{
useMaxWidth:true,
htmlLabels:true,
curve:'cardinal',
},
securityLevel:'loose',
};
mermaid.initialize(config);
&lt;/script>
</pre>
A summary of all options and their defaults is found [here][2]. A description of each option follows below.
## theme
theme , the CSS style sheet
**theme** - Choose one of the built-in themes:
- default
- forest
- dark
- neutral.
To disable any pre-defined mermaid theme, use "null".
**themeCSS** - Use your own CSS. This overrides **theme**.
<pre>
"theme": "forest",
"themeCSS": ".node rect { fill: red; }"
</pre>
## fontFamily
**fontFamily** The font to be used for the rendered diagrams. Default value is \\"trebuchet ms\\", verdana, arial;
## logLevel
This option decides the amount of logging to be used.
- debug: 1
- info: 2
- warn: 3
- error: 4
- fatal: (**default**) 5
## securityLevel
Sets the level of trust to be used on the parsed diagrams.
- **strict**: (**default**) tags in text are encoded, click functionality is disabeled
- **loose**: tags in text are allowed, click functionality is enabled
## startOnLoad
This options controls whether or mermaid starts when the page loads
**Default value true**.
## arrowMarkerAbsolute
This options controls whether or arrow markers in html code will be absolute paths or
an anchor, #. This matters if you are using base tag settings.
**Default value false**.
## flowchart
The object containing configurations specific for flowcharts
### htmlLabels
Flag for setting whether or not a html tag should be used for rendering labels
on the edges.
**Default value true**.
### nodeSpacing
Defines the spacing between nodes on the same level (meaning horizontal spacing for
TB or BT graphs, and the vertical spacing for LR as well as RL graphs).
**Default value 50**.
### rankSpacing
Defines the spacing between nodes on different levels (meaning vertical spacing for
TB or BT graphs, and the horizontal spacing for LR as well as RL graphs).
**Default value 50**.
### curve
How mermaid renders curves for flowcharts. Possible values are
- basis
- linear **default**
- cardinal
## sequence
The object containing configurations specific for sequence diagrams
### diagramMarginX
margin to the right and left of the sequence diagram.
**Default value 50**.
### diagramMarginY
margin to the over and under the sequence diagram.
**Default value 10**.
### actorMargin
Margin between actors.
**Default value 50**.
### width
Width of actor boxes
**Default value 150**.
### height
Height of actor boxes
**Default value 65**.
### boxMargin
Margin around loop boxes
**Default value 10**.
### boxTextMargin
margin around the text in loop/alt/opt boxes
**Default value 5**.
### noteMargin
margin around notes.
**Default value 10**.
### messageMargin
Space between messages.
**Default value 35**.
### messageAlign
Multiline message alignment. Possible values are:
- left
- center **default**
- right
### mirrorActors
mirror actors under diagram.
**Default value true**.
### bottomMarginAdj
Depending on css styling this might need adjustment.
Prolongs the edge of the diagram downwards.
**Default value 1**.
### useMaxWidth
when this flag is set the height and width is set to 100% and is then scaling with the
available space if not the absolute space required is used.
**Default value true**.
### rightAngles
This will display arrows that start and begin at the same node as right angles, rather than a curve
**Default value false**.
### showSequenceNumbers
This will show the node numbers
**Default value false**.
### actorFontSize
This sets the font size of the actor's description
**Default value 14**.
### actorFontFamily
This sets the font family of the actor's description
**Default value "Open-Sans", "sans-serif"**.
### actorFontWeight
This sets the font weight of the actor's description
\*\*Default value 400.
### noteFontSize
This sets the font size of actor-attached notes.
**Default value 14**.
### noteFontFamily
This sets the font family of actor-attached notes.
**Default value "trebuchet ms", verdana, arial**.
### noteFontWeight
This sets the font weight of the note's description
\*\*Default value 400.
### noteAlign
This sets the text alignment of actor-attached notes.
**Default value center**.
### messageFontSize
This sets the font size of actor messages.
**Default value 16**.
### messageFontFamily
This sets the font family of actor messages.
**Default value "trebuchet ms", verdana, arial**.
### messageFontWeight
This sets the font weight of the message's description
\*\*Default value 400.
### wrapEnabled
This sets the auto-wrap state for the diagram
\*\*Default value false.
### wrapPadding
This sets the auto-wrap padding for the diagram (sides only)
\*\*Default value 15.
## gantt
The object containing configurations specific for gantt diagrams\*
### titleTopMargin
Margin top for the text over the gantt diagram
**Default value 25**.
### barHeight
The height of the bars in the graph
**Default value 20**.
### barGap
The margin between the different activities in the gantt diagram.
**Default value 4**.
### topPadding
Margin between title and gantt diagram and between axis and gantt diagram.
**Default value 50**.
### leftPadding
The space allocated for the section name to the left of the activities.
**Default value 75**.
### gridLineStartPadding
Vertical starting position of the grid lines.
**Default value 35**.
### fontSize
Font size ...
**Default value 11**.
### fontFamily
font family ...
**Default value '"Open-Sans", "sans-serif"'**.
### numberSectionStyles
The number of alternating section styles.
**Default value 4**.
### axisFormat
Datetime format of the axis. This might need adjustment to match your locale and preferences
**Default value '%Y-%m-%d'**.
## journey
The object containing configurations specific for sequence diagrams
### diagramMarginX
margin to the right and left of the sequence diagram.
**Default value 50**.
### diagramMarginY
margin to the over and under the sequence diagram.
**Default value 10**.
### actorMargin
Margin between actors.
**Default value 50**.
### width
Width of actor boxes
**Default value 150**.
### height
Height of actor boxes
**Default value 65**.
### boxMargin
Margin around loop boxes
**Default value 10**.
### boxTextMargin
margin around the text in loop/alt/opt boxes
**Default value 5**.
### noteMargin
margin around notes.
**Default value 10**.
### messageMargin
Space between messages.
**Default value 35**.
### messageAlign
Multiline message alignment. Possible values are:
- left
- center **default**
- right
### bottomMarginAdj
Depending on css styling this might need adjustment.
Prolongs the edge of the diagram downwards.
**Default value 1**.
### useMaxWidth
when this flag is set the height and width is set to 100% and is then scaling with the
available space if not the absolute space required is used.
**Default value true**.
### rightAngles
This will display arrows that start and begin at the same node as right angles, rather than a curve
**Default value false**.
## er
The object containing configurations specific for entity relationship diagrams
### diagramPadding
The amount of padding around the diagram as a whole so that embedded diagrams have margins, expressed in pixels
### layoutDirection
Directional bias for layout of entities. Can be either 'TB', 'BT', 'LR', or 'RL',
where T = top, B = bottom, L = left, and R = right.
### minEntityWidth
The mimimum width of an entity box, expressed in pixels
### minEntityHeight
The minimum height of an entity box, expressed in pixels
### entityPadding
The minimum internal padding between the text in an entity box and the enclosing box borders, expressed in pixels
### stroke
Stroke color of box edges and lines
### fill
Fill color of entity boxes
### fontSize
Font size (expressed as an integer representing a number of pixels)
## render
Function that renders an svg with a graph from a chart definition. Usage example below.
@@ -513,5 +90,3 @@ mermaidAPI.initialize({
</pre>
[1]: https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#render
[2]: https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults

View File

@@ -1,19 +1,632 @@
import { assignWithDepth } from './utils';
const config = {};
import { logger } from './logger';
/**
* These are the default options which can be overridden with the initialization call like so:
* **Example 1:**
* <pre>
* mermaid.initialize({
* flowchart:{
* htmlLabels: false
* }
* });
* </pre>
*
* **Example 2:**
* <pre>
* &lt;script>
* var currentConfig = {
* startOnLoad:true,
* flowchart:{
* useMaxWidth:true,
* htmlLabels:true,
* curve:'cardinal',
* },
*
* securityLevel:'loose',
* };
* mermaid.initialize(currentConfig);
* &lt;/script>
* </pre>
* A summary of all options and their defaults is found [here](https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults). A description of each option follows below.
*
* @name Configuration
*/
const config = {
/** theme , the CSS style sheet
*
* **theme** - Choose one of the built-in themes:
* * default
* * forest
* * dark
* * neutral.
* To disable any pre-defined mermaid theme, use "null".
*
* **themeCSS** - Use your own CSS. This overrides **theme**.
* <pre>
* "theme": "forest",
* "themeCSS": ".node rect { fill: red; }"
* </pre>
*/
theme: 'default',
themeCSS: undefined,
/* **maxTextSize** - The maximum allowed size of the users text diamgram */
maxTextSize: 50000,
/**
* **fontFamily** The font to be used for the rendered diagrams. Default value is \"trebuchet ms\", verdana, arial;
*/
fontFamily: '"trebuchet ms", verdana, arial;',
/**
* This option decides the amount of logging to be used.
* * debug: 1
* * info: 2
* * warn: 3
* * error: 4
* * fatal: (**default**) 5
*/
logLevel: 5,
/**
* Sets the level of trust to be used on the parsed diagrams.
* * **strict**: (**default**) tags in text are encoded, click functionality is disabeled
* * **loose**: tags in text are allowed, click functionality is enabled
*/
securityLevel: 'strict',
/**
* This options controls whether or mermaid starts when the page loads
* **Default value true**.
*/
startOnLoad: true,
/**
* This options controls whether or arrow markers in html code will be absolute paths or
* an anchor, #. This matters if you are using base tag settings.
* **Default value false**.
*/
arrowMarkerAbsolute: false,
/**
* This option controls which currentConfig keys are considered _secure_ and can only be changed via
* call to mermaidAPI.initialize. Calls to mermaidAPI.reinitialize cannot make changes to
* the `secure` keys in the current currentConfig. This prevents malicious graph directives from
* overriding a site's default security.
*/
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
/**
* The object containing configurations specific for flowcharts
*/
flowchart: {
/**
* Flag for setting whether or not a html tag should be used for rendering labels
* on the edges.
* **Default value true**.
*/
htmlLabels: true,
/**
* Defines the spacing between nodes on the same level (meaning horizontal spacing for
* TB or BT graphs, and the vertical spacing for LR as well as RL graphs).
* **Default value 50**.
*/
nodeSpacing: 50,
/**
* Defines the spacing between nodes on different levels (meaning vertical spacing for
* TB or BT graphs, and the horizontal spacing for LR as well as RL graphs).
* **Default value 50**.
*/
rankSpacing: 50,
/**
* How mermaid renders curves for flowcharts. Possible values are
* * basis
* * linear **default**
* * cardinal
*/
curve: 'linear',
// Only used in new experimental rendering
// repreesents the padding between the labels and the shape
padding: 15
},
/**
* The object containing configurations specific for sequence diagrams
*/
sequence: {
/**
* margin to the right and left of the sequence diagram.
* **Default value 50**.
*/
diagramMarginX: 50,
/**
* margin to the over and under the sequence diagram.
* **Default value 10**.
*/
diagramMarginY: 10,
/**
* Margin between actors.
* **Default value 50**.
*/
actorMargin: 50,
/**
* Width of actor boxes
* **Default value 150**.
*/
width: 150,
/**
* Height of actor boxes
* **Default value 65**.
*/
height: 65,
/**
* Margin around loop boxes
* **Default value 10**.
*/
boxMargin: 10,
/**
* margin around the text in loop/alt/opt boxes
* **Default value 5**.
*/
boxTextMargin: 5,
/**
* margin around notes.
* **Default value 10**.
*/
noteMargin: 10,
/**
* Space between messages.
* **Default value 35**.
*/
messageMargin: 35,
/**
* Multiline message alignment. Possible values are:
* * left
* * center **default**
* * right
*/
messageAlign: 'center',
/**
* mirror actors under diagram.
* **Default value true**.
*/
mirrorActors: true,
/**
* Depending on css styling this might need adjustment.
* Prolongs the edge of the diagram downwards.
* **Default value 1**.
*/
bottomMarginAdj: 1,
/**
* when this flag is set the height and width is set to 100% and is then scaling with the
* available space if not the absolute space required is used.
* **Default value true**.
*/
useMaxWidth: true,
/**
* This will display arrows that start and begin at the same node as right angles, rather than a curve
* **Default value false**.
*/
rightAngles: false,
/**
* This will show the node numbers
* **Default value false**.
*/
showSequenceNumbers: false,
/**
* This sets the font size of the actor's description
* **Default value 14**.
*/
actorFontSize: 14,
/**
* This sets the font family of the actor's description
* **Default value "Open-Sans", "sans-serif"**.
*/
actorFontFamily: '"Open-Sans", "sans-serif"',
/**
* This sets the font weight of the actor's description
* **Default value 400.
*/
actorFontWeight: 400,
/**
* This sets the font size of actor-attached notes.
* **Default value 14**.
*/
noteFontSize: 14,
/**
* This sets the font family of actor-attached notes.
* **Default value "trebuchet ms", verdana, arial**.
*/
noteFontFamily: '"trebuchet ms", verdana, arial',
/**
* This sets the font weight of the note's description
* **Default value 400.
*/
noteFontWeight: 400,
/**
* This sets the text alignment of actor-attached notes.
* **Default value center**.
*/
noteAlign: 'center',
/**
* This sets the font size of actor messages.
* **Default value 16**.
*/
messageFontSize: 16,
/**
* This sets the font family of actor messages.
* **Default value "trebuchet ms", verdana, arial**.
*/
messageFontFamily: '"trebuchet ms", verdana, arial',
/**
* This sets the font weight of the message's description
* **Default value 400.
*/
messageFontWeight: 400,
/**
* This sets the auto-wrap state for the diagram
* **Default value false.
*/
wrap: false,
/**
* This sets the auto-wrap padding for the diagram (sides only)
* **Default value 15.
*/
wrapPadding: 15,
/**
* This sets the width of the loop-box (loop, alt, opt, par)
* **Default value 50.
*/
labelBoxWidth: 50,
/**
* This sets the height of the loop-box (loop, alt, opt, par)
* **Default value 20.
*/
labelBoxHeight: 20,
messageFont: () => {
return {
fontFamily: config.messageFontFamily,
fontSize: config.messageFontSize,
fontWeight: config.messageFontWeight
};
},
noteFont: () => {
return {
fontFamily: config.noteFontFamily,
fontSize: config.noteFontSize,
fontWeight: config.noteFontWeight
};
},
actorFont: () => {
return {
fontFamily: config.actorFontFamily,
fontSize: config.actorFontSize,
fontWeight: config.actorFontWeight
};
}
},
/**
* The object containing configurations specific for gantt diagrams*
*/
gantt: {
/**
* Margin top for the text over the gantt diagram
* **Default value 25**.
*/
titleTopMargin: 25,
/**
* The height of the bars in the graph
* **Default value 20**.
*/
barHeight: 20,
/**
* The margin between the different activities in the gantt diagram.
* **Default value 4**.
*/
barGap: 4,
/**
* Margin between title and gantt diagram and between axis and gantt diagram.
* **Default value 50**.
*/
topPadding: 50,
/**
* The space allocated for the section name to the left of the activities.
* **Default value 75**.
*/
leftPadding: 75,
/**
* Vertical starting position of the grid lines.
* **Default value 35**.
*/
gridLineStartPadding: 35,
/**
* Font size ...
* **Default value 11**.
*/
fontSize: 11,
/**
* font family ...
* **Default value '"Open-Sans", "sans-serif"'**.
*/
fontFamily: '"Open-Sans", "sans-serif"',
/**
* The number of alternating section styles.
* **Default value 4**.
*/
numberSectionStyles: 4,
/**
* Datetime format of the axis. This might need adjustment to match your locale and preferences
* **Default value '%Y-%m-%d'**.
*/
axisFormat: '%Y-%m-%d'
},
/**
* The object containing configurations specific for sequence diagrams
*/
journey: {
/**
* margin to the right and left of the sequence diagram.
* **Default value 50**.
*/
diagramMarginX: 50,
/**
* margin to the over and under the sequence diagram.
* **Default value 10**.
*/
diagramMarginY: 10,
/**
* Margin between actors.
* **Default value 50**.
*/
actorMargin: 50,
/**
* Width of actor boxes
* **Default value 150**.
*/
width: 150,
/**
* Height of actor boxes
* **Default value 65**.
*/
height: 65,
/**
* Margin around loop boxes
* **Default value 10**.
*/
boxMargin: 10,
/**
* margin around the text in loop/alt/opt boxes
* **Default value 5**.
*/
boxTextMargin: 5,
/**
* margin around notes.
* **Default value 10**.
*/
noteMargin: 10,
/**
* Space between messages.
* **Default value 35**.
*/
messageMargin: 35,
/**
* Multiline message alignment. Possible values are:
* * left
* * center **default**
* * right
*/
messageAlign: 'center',
/**
* Depending on css styling this might need adjustment.
* Prolongs the edge of the diagram downwards.
* **Default value 1**.
*/
bottomMarginAdj: 1,
/**
* when this flag is set the height and width is set to 100% and is then scaling with the
* available space if not the absolute space required is used.
* **Default value true**.
*/
useMaxWidth: true,
/**
* This will display arrows that start and begin at the same node as right angles, rather than a curve
* **Default value false**.
*/
rightAngles: false
},
class: {
arrowMarkerAbsolute: false
},
git: {
arrowMarkerAbsolute: false
},
state: {
dividerMargin: 10,
sizeUnit: 5,
padding: 8,
textHeight: 10,
titleShift: -15,
noteMargin: 10,
forkWidth: 70,
forkHeight: 7,
// Used
miniPadding: 2,
// Font size factor, this is used to guess the width of the edges labels before rendering by dagre
// layout. This might need updating if/when switching font
fontSizeFactor: 5.02,
fontSize: 24,
labelHeight: 16,
edgeLengthFactor: '20',
compositTitleSize: 35,
radius: 5
},
/**
* The object containing configurations specific for entity relationship diagrams
*/
er: {
/**
* The amount of padding around the diagram as a whole so that embedded diagrams have margins, expressed in pixels
*/
diagramPadding: 20,
/**
* Directional bias for layout of entities. Can be either 'TB', 'BT', 'LR', or 'RL',
* where T = top, B = bottom, L = left, and R = right.
*/
layoutDirection: 'TB',
/**
* The mimimum width of an entity box, expressed in pixels
*/
minEntityWidth: 100,
/**
* The minimum height of an entity box, expressed in pixels
*/
minEntityHeight: 75,
/**
* The minimum internal padding between the text in an entity box and the enclosing box borders, expressed in pixels
*/
entityPadding: 15,
/**
* Stroke color of box edges and lines
*/
stroke: 'gray',
/**
* Fill color of entity boxes
*/
fill: 'honeydew',
/**
* Font size (expressed as an integer representing a number of pixels)
*/
fontSize: 12
}
};
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
config.git.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
export const defaultConfig = Object.freeze(config);
const siteConfig = assignWithDepth({}, defaultConfig);
const currentConfig = assignWithDepth({}, defaultConfig);
/**
* Sets the siteConfig. The siteConfig is a protected configuration for repeat use. Calls to reset() will reset
* the currentConfig to siteConfig. Calls to reset(configApi.defaultConfig) will reset siteConfig and currentConfig
* to the defaultConfig
* Note: currentConfig is set in this function
* @param conf - the base currentConfig to use as siteConfig
* @returns {*} - the siteConfig
*/
export const setSiteConfig = conf => {
assignWithDepth(currentConfig, conf, { clobber: true });
assignWithDepth(siteConfig, conf);
return getSiteConfig();
};
/**
* Obtains the current siteConfig base configuration
* @returns {*}
*/
export const getSiteConfig = () => {
return assignWithDepth({}, siteConfig);
};
/**
* Sets the currentConfig. The param conf is sanitized based on the siteConfig.secure keys. Any
* values found in conf with key found in siteConfig.secure will be replaced with the corresponding
* siteConfig value.
* @param conf - the potential currentConfig
* @returns {*} - the currentConfig merged with the sanitized conf
*/
export const setConfig = conf => {
assignWithDepth(config, conf);
sanitize(conf);
assignWithDepth(currentConfig, conf);
return getConfig();
};
export const getConfig = () => config;
export const reset = conf => {
Object.keys(config).forEach(key => delete config[key]);
assignWithDepth(config, conf, { clobber: true });
/**
* Obtains the currentConfig
* @returns {*} - the currentConfig
*/
export const getConfig = () => {
return assignWithDepth({}, currentConfig);
};
/**
* Ensures options parameter does not attempt to override siteConfig secure keys
* Note: modifies options in-place
* @param options - the potential setConfig parameter
*/
export const sanitize = options => {
Object.keys(siteConfig.secure).forEach(key => {
if (typeof options[siteConfig.secure[key]] !== 'undefined') {
// DO NOT attempt to print options[siteConfig.secure[key]] within `${}` as a malicious script
// can exploit the logger's attempt to stringify the value and execute arbitrary code
logger.warn(
`Denied attempt to modify a secure key ${siteConfig.secure[key]}`,
options[siteConfig.secure[key]]
);
delete options[siteConfig.secure[key]];
}
});
};
/**
* Resets this currentConfig to conf
* @param conf - the base currentConfig to reset to (default: current siteConfig )
*/
export const reset = (conf = getSiteConfig()) => {
Object.keys(siteConfig).forEach(key => delete siteConfig[key]);
Object.keys(currentConfig).forEach(key => delete currentConfig[key]);
assignWithDepth(siteConfig, conf, { clobber: true });
assignWithDepth(currentConfig, conf, { clobber: true });
};
const configApi = {
const configApi = Object.freeze({
sanitize,
setSiteConfig,
getSiteConfig,
setConfig,
getConfig,
reset
};
reset,
defaultConfig
});
export default configApi;

52
src/config.spec.js Normal file
View File

@@ -0,0 +1,52 @@
/* eslint-env jasmine */
import configApi from './config';
describe('when working with site config', function() {
beforeEach(() => {
configApi.reset(configApi.defaultConfig);
});
it('should set site config and config properly', function() {
let config_0 = { foo: 'bar', bar: 0 };
configApi.setSiteConfig(config_0);
let config_1 = configApi.getSiteConfig();
let config_2 = configApi.getConfig();
expect(config_1.foo).toEqual(config_0.foo);
expect(config_1.bar).toEqual(config_0.bar);
expect(config_1).toEqual(config_2);
});
it('should set config and respect secure keys', function() {
let config_0 = { foo: 'bar', bar: 0, secure: [...configApi.defaultConfig.secure, 'bar'] };
configApi.setSiteConfig(config_0);
let config_1 = { foo: 'baf', bar: 'foo'};
configApi.setConfig(config_1);
let config_2 = configApi.getConfig();
expect(config_2.foo).toEqual(config_1.foo);
expect(config_2.bar).toEqual(0); // Should be siteConfig.bar
});
it('should set reset config properly', function() {
let config_0 = { foo: 'bar', bar: 0};
configApi.setSiteConfig(config_0);
let config_1 = { foo: 'baf'};
configApi.setConfig(config_1);
let config_2 = configApi.getConfig();
expect(config_2.foo).toEqual(config_1.foo);
configApi.reset();
let config_3 = configApi.getConfig();
expect(config_3.foo).toEqual(config_0.foo);
let config_4 = configApi.getSiteConfig();
expect(config_4.foo).toEqual(config_0.foo);
});
it('should set global reset config properly', function() {
let config_0 = { foo: 'bar', bar: 0};
configApi.setSiteConfig(config_0);
let config_1 = configApi.getSiteConfig();
expect(config_1.foo).toEqual(config_0.foo);
let config_2 = configApi.getConfig();
expect(config_2.foo).toEqual(config_0.foo);
configApi.reset(configApi.defaultConfig);
let config_3 = configApi.getSiteConfig();
expect(config_3.foo).toBeUndefined();
let config_4 = configApi.getConfig();
expect(config_4.foo).toBeUndefined();
});
});

View File

@@ -230,7 +230,7 @@ argDirective
;
closeDirective
: close_directive { yy.parseDirective('}%%', 'close_directive'); }
: close_directive { yy.parseDirective('}%%', 'close_directive', 'sequence'); }
;
%%

View File

@@ -1,5 +1,5 @@
import { logger } from '../../logger';
import mermaidAPI from '../../mermaidAPI';
import configApi from '../../config';
let prevActor = undefined;
let actors = {};
@@ -9,63 +9,9 @@ let title = '';
let titleWrapped = false;
let sequenceNumbersEnabled = false;
let wrapEnabled = false;
let currentDirective = {};
export const parseDirective = function(statement, context) {
try {
if (statement !== undefined) {
statement = statement.trim();
switch (context) {
case 'open_directive':
currentDirective = {};
break;
case 'type_directive':
currentDirective.type = statement.toLowerCase();
break;
case 'arg_directive':
currentDirective.args = JSON.parse(statement);
break;
case 'close_directive':
handleDirective(currentDirective);
currentDirective = null;
break;
}
}
} catch (error) {
logger.error(
`Error while rendering sequenceDiagram directive: ${statement} jison context: ${context}`
);
logger.error(error.message);
}
};
const handleDirective = function(directive) {
logger.debug(`Directive type=${directive.type} with args:`, directive.args);
switch (directive.type) {
case 'init':
case 'initialize':
['config'].forEach(prop => {
if (typeof directive.args[prop] !== 'undefined') {
directive.args.sequence = directive.args[prop];
delete directive.args[prop];
}
});
mermaidAPI.initialize(directive.args);
break;
case 'wrap':
case 'nowrap':
wrapEnabled = directive.type === 'wrap';
break;
default:
logger.warn(
`Unhandled directive: source: '%%{${directive.type}: ${JSON.stringify(
directive.args ? directive.args : {}
)}}%%`,
directive
);
break;
}
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(statement, context, type);
};
export const addActor = function(id, name, description) {
@@ -126,22 +72,8 @@ export const addSignal = function(
message = { text: undefined, wrap: undefined },
messageType
) {
logger.debug(
'Adding message from=' +
idFrom +
' to=' +
idTo +
' message=' +
message.text +
' wrap=' +
message.wrap +
' type=' +
messageType
);
if (messageType === LINETYPE.ACTIVE_END) {
const cnt = activationCount(idFrom.actor);
logger.debug('Adding message from=', messages, cnt);
if (cnt < 1) {
// Bail out as there is an activation signal from an inactive participant
let error = new Error('Trying to inactivate an inactive participant (' + idFrom.actor + ')');
@@ -356,6 +288,7 @@ export default {
getActorKeys,
getTitle,
parseDirective,
getConfig: () => configApi.getConfig().sequence,
getTitleWrapped,
clear,
parseMessage,

View File

@@ -23,7 +23,7 @@ Alice->Bob:Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -41,7 +41,7 @@ Alice->Bob:Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`;
parser.parse(str);
mermaidAPI.parse(str);
expect(parser.yy.showSequenceNumbers()).toBe(false);
});
it('it should show sequence numbers when autonumber is enabled', function() {
@@ -52,7 +52,7 @@ Alice->Bob:Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`;
parser.parse(str);
mermaidAPI.parse(str);
expect(parser.yy.showSequenceNumbers()).toBe(true);
});
it('it should handle a sequenceDiagram definition with a title', function() {
@@ -63,7 +63,7 @@ Alice->Bob:Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -82,7 +82,7 @@ sequenceDiagram
Alice->Bob:Hello Bob, how are - you?
Bob-->Alice: I am good thanks!`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -101,7 +101,7 @@ participant B as Bob
A->B:Hello Bob, how are you?
B-->A: I am good thanks!`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(Object.keys(actors)).toEqual(['A', 'B']);
@@ -118,7 +118,7 @@ B-->A: I am good thanks!`;
sequenceDiagram
Alice-xBob:Hello Bob, how are you?`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
expect(actors.Bob.description).toBe('Bob');
@@ -133,7 +133,7 @@ Alice-xBob:Hello Bob, how are you?`;
sequenceDiagram
Alice--xBob:Hello Bob, how are you?`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
expect(actors.Bob.description).toBe('Bob');
@@ -148,7 +148,7 @@ Alice--xBob:Hello Bob, how are you?`;
sequenceDiagram
Alice->>Bob:Hello Bob, how are you?`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
expect(actors.Bob.description).toBe('Bob');
@@ -161,7 +161,7 @@ Alice->>Bob:Hello Bob, how are you?`;
it('it should handle in arrow messages', function() {
const str = 'sequenceDiagram\n' + 'Alice-->>Bob:Hello Bob, how are you?';
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
expect(actors.Bob.description).toBe('Bob');
@@ -179,7 +179,7 @@ activate Bob
Bob-->>Alice:Hello Alice, I'm fine and you?
deactivate Bob`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
expect(actors.Bob.description).toBe('Bob');
@@ -200,7 +200,7 @@ deactivate Bob`;
Alice-->>+Bob:Hello Bob, how are you?
Bob-->>- Alice:Hello Alice, I'm fine and you?`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
expect(actors.Bob.description).toBe('Bob');
@@ -223,7 +223,7 @@ deactivate Bob`;
Bob-->>- Alice:Hello Alice, please meet Carol?
Carol->>- Bob:Oh Bob, I'm so happy to be here!`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
expect(actors.Bob.description).toBe('Bob');
@@ -261,7 +261,7 @@ deactivate Bob`;
let error = false;
try {
parser.parse(str);
mermaidAPI.parse(str);
} catch (e) {
console.log(e.hash);
error = true;
@@ -277,7 +277,7 @@ deactivate Bob`;
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -298,7 +298,7 @@ deactivate Bob`;
Bob-->Alice: I am good thanks!
`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -313,7 +313,7 @@ deactivate Bob`;
const str = `
sequenceDiagram;Alice->Bob: Hello Bob, how are you?;Note right of Bob: Bob thinks;Bob-->Alice: I am good thanks!;`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -333,7 +333,7 @@ sequenceDiagram
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -353,7 +353,7 @@ sequenceDiagram
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -378,7 +378,7 @@ Note right of John: Rational thoughts<br/>prevail...
John->Bob: How about you?
Bob-->John: Jolly good!`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -406,7 +406,7 @@ note right of 4: multiline<br />text
note right of 1: multiline<br \t/>text
`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors['1'].description).toBe('multiline<br>text');
@@ -431,7 +431,7 @@ Alice->Bob: Hello Bob, how are you?
Note over Bob: Bob thinks
`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].from).toBe('Bob');
@@ -445,7 +445,7 @@ Note over Alice,Bob: confusion
Note over Bob,Alice: resolution
`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].from).toBe('Alice');
@@ -465,7 +465,7 @@ loop Multiple happy responses
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -487,7 +487,7 @@ end`;
end
`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -512,7 +512,7 @@ end`;
Bob-->Alice: I am good thanks
end
`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -539,7 +539,7 @@ opt Perhaps a happy response
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
actors.Bob.description = 'Bob';
@@ -564,7 +564,7 @@ else isSick
Bob-->Alice: Feel sick...
end`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
@@ -591,7 +591,7 @@ Bob-->Alice: Feel sick...
else default
Bob-->Alice: :-)
end`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages.length).toBe(9);
expect(messages[1].from).toBe('Bob');
@@ -617,7 +617,7 @@ Alice->>Bob: What do you think about it?
Bob-->>Alice: It's good!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(actors.Alice.description).toBe('Alice');
@@ -633,7 +633,7 @@ end`;
it('it should handle special characters in signals', function() {
const str = 'sequenceDiagram\n' + 'Alice->Bob: -:<>,;# comment';
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[0].message).toBe('-:<>,');
@@ -644,7 +644,7 @@ sequenceDiagram
Alice->Bob: Hello Bob, how are you?
Note right of Bob: -:<>,;# comment`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].message).toBe('-:<>,');
@@ -657,7 +657,7 @@ loop -:<>,;# comment
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].message).toBe('-:<>,');
@@ -670,7 +670,7 @@ opt -:<>,;# comment
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].message).toBe('-:<>,');
@@ -685,7 +685,7 @@ else ,<>:-#; comment
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].message).toBe('-:<>,');
@@ -701,7 +701,7 @@ and ,<>:-#; comment
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].message).toBe('-:<>,');
@@ -715,7 +715,7 @@ loop
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].message).toBe('');
@@ -729,7 +729,7 @@ opt # comment
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].message).toBe('');
@@ -744,7 +744,7 @@ else # comment
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].message).toBe('');
@@ -761,7 +761,7 @@ and # comment
Bob-->Alice: I am good thanks!
end`;
parser.parse(str);
mermaidAPI.parse(str);
const messages = parser.yy.getMessages();
expect(messages[1].message).toBe('');
@@ -772,12 +772,8 @@ end`;
});
describe('when checking the bounds in a sequenceDiagram', function() {
let conf;
beforeEach(function() {
mermaidAPI.reset();
parser.yy = sequenceDb;
parser.yy.clear();
conf = {
beforeAll(() => {
let conf = {
diagramMarginX: 50,
diagramMarginY: 10,
actorMargin: 50,
@@ -791,13 +787,21 @@ describe('when checking the bounds in a sequenceDiagram', function() {
};
mermaidAPI.initialize({ sequence: conf });
});
let conf;
beforeEach(function() {
mermaidAPI.reset();
parser.yy = sequenceDb;
parser.yy.clear();
renderer.bounds.init();
conf = parser.yy.getConfig();
});
it('it should handle a simple bound call', function() {
renderer.bounds.insert(100, 100, 200, 200);
const bounds = renderer.bounds.getBounds();
const { bounds } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(100);
expect(bounds.starty).toBe(100);
expect(bounds.stopx).toBe(200);
@@ -808,7 +812,7 @@ describe('when checking the bounds in a sequenceDiagram', function() {
renderer.bounds.insert(100, 100, 200, 200);
renderer.bounds.insert(25, 50, 300, 400);
const bounds = renderer.bounds.getBounds();
const { bounds } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(25);
expect(bounds.starty).toBe(50);
expect(bounds.stopx).toBe(300);
@@ -820,7 +824,7 @@ describe('when checking the bounds in a sequenceDiagram', function() {
renderer.bounds.insert(25, 50, 300, 400);
renderer.bounds.insert(125, 150, 150, 200);
const bounds = renderer.bounds.getBounds();
const { bounds } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(25);
expect(bounds.starty).toBe(50);
expect(bounds.stopx).toBe(300);
@@ -841,7 +845,7 @@ describe('when checking the bounds in a sequenceDiagram', function() {
expect(loop.stopy).toBe(200 + conf.boxMargin);
// Check bounds of first loop
const bounds = renderer.bounds.getBounds();
const { bounds } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(25);
expect(bounds.starty).toBe(50);
@@ -873,7 +877,7 @@ describe('when checking the bounds in a sequenceDiagram', function() {
expect(loop.stopy).toBe(300 + 2 * conf.boxMargin);
// Check bounds of first loop
const bounds = renderer.bounds.getBounds();
const { bounds } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(100);
expect(bounds.starty).toBe(100);
@@ -895,7 +899,7 @@ describe('when checking the bounds in a sequenceDiagram', function() {
expect(loop.stopy).toBe(300 + conf.boxMargin);
// Check bounds after the loop
const bounds = renderer.bounds.getBounds();
const { bounds } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(loop.startx);
expect(bounds.starty).toBe(loop.starty);
@@ -905,13 +909,8 @@ describe('when checking the bounds in a sequenceDiagram', function() {
});
describe('when rendering a sequenceDiagram', function() {
let conf;
beforeEach(function() {
mermaidAPI.reset();
parser.yy = sequenceDb;
parser.yy.clear();
conf = {
beforeAll(() => {
let conf = {
diagramMarginX: 50,
diagramMarginY: 10,
actorMargin: 50,
@@ -922,10 +921,17 @@ describe('when rendering a sequenceDiagram', function() {
messageMargin: 40,
boxTextMargin: 15,
noteMargin: 25,
wrapEnabled: false,
wrap: false,
mirrorActors: false
};
mermaidAPI.initialize({ sequence: conf });
});
let conf;
beforeEach(function() {
mermaidAPI.reset();
parser.yy = sequenceDb;
parser.yy.clear();
conf = parser.yy.getConfig();
renderer.bounds.init();
});
['tspan', 'fo', 'old', undefined].forEach(function(textPlacement) {
@@ -935,12 +941,12 @@ it should handle one actor, when textPlacement is ${textPlacement}`, function()
sequenceDiagram
participant Alice`;
mermaidAPI.initialize(addConf(conf, 'textPlacement', textPlacement));
renderer.bounds.init();
parser.parse(str);
mermaidAPI.reinitialize({sequence: { textPlacement: textPlacement}});
mermaidAPI.parse(str);
// renderer.setConf(mermaidAPI.getConfig().sequence);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width);
@@ -955,7 +961,7 @@ participant Alice
participant Alice
`;
parser.parse(str);
mermaidAPI.parse(str);
const actors = parser.yy.getActors();
expect(Object.keys(actors)).toEqual(['Alice']);
@@ -967,15 +973,16 @@ participant Alice
Note over Alice: Alice thinks
`;
parser.parse(str);
expect(mermaidAPI.getConfig().sequence.mirrorActors).toBeFalsy();
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width);
// 10 comes from mock of text height
expect(bounds.stopy).toBe(conf.height + conf.boxMargin + 2 * conf.noteMargin + 10);
expect(bounds.stopy).toBe(models.lastNote().stopy);
});
it('it should handle one actor and a note to the left', function() {
const str = `
@@ -983,15 +990,15 @@ sequenceDiagram
participant Alice
Note left of Alice: Alice thinks`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width);
// 10 comes from mock of text height
expect(bounds.stopy).toBe(conf.height + conf.boxMargin + 2 * conf.noteMargin + 10);
expect(bounds.stopy).toBe(models.lastNote().stopy);
});
it('it should handle one actor and a note to the right', function() {
const str = `
@@ -999,29 +1006,29 @@ sequenceDiagram
participant Alice
Note right of Alice: Alice thinks`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width / 2 + conf.actorMargin / 2 + conf.width);
// 10 comes from mock of text height
expect(bounds.stopy).toBe(conf.height + conf.boxMargin + 2 * conf.noteMargin + 10);
expect(bounds.stopy).toBe(models.lastNote().stopy);
});
it('it should handle two actors', function() {
const str = `
sequenceDiagram
Alice->Bob: Hello Bob, how are you?`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(conf.messageMargin + conf.height);
expect(bounds.stopy).toBe(models.lastMessage().stopy);
});
it('it should handle two actors with init directive', function() {
const str = `
@@ -1029,16 +1036,16 @@ Alice->Bob: Hello Bob, how are you?`;
sequenceDiagram
Alice->Bob: Hello Bob, how are you?`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
const mermaid = mermaidAPI.getConfig();
expect(mermaid.logLevel).toBe(0);
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(conf.height + conf.messageMargin + (conf.mirrorActors ? 2 * conf.boxMargin + conf.height : 0));
expect(bounds.stopy).toBe(models.lastMessage().stopy);
});
it('it should handle two actors with init directive with multiline directive', function() {
const str = `
@@ -1049,17 +1056,17 @@ wrap
}%%
Alice->Bob: Hello Bob, how are you?`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const msgs = parser.yy.getMessages();
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
const mermaid = mermaidAPI.getConfig();
expect(mermaid.logLevel).toBe(0);
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(conf.messageMargin + conf.height);
expect(bounds.stopy).toBe(models.lastMessage().stopy);
expect(msgs.every(v => v.wrap)).toBe(true);
});
@@ -1070,17 +1077,15 @@ Alice->Bob: Hello Bob, how are you?
Note over Alice,Bob: Looks
Note over Bob,Alice: Looks back
`;
parser.parse(str);
mermaidAPI.initialize({logLevel:0})
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(
conf.height + conf.messageMargin + 2 * (conf.boxMargin + 2 * conf.noteMargin + 10)
);
expect(bounds.stopy).toBe(models.lastNote().stopy);
});
it('it should draw two actors and two messages', function() {
const str = `
@@ -1088,14 +1093,14 @@ sequenceDiagram
Alice->Bob: Hello Bob, how are you?
Bob->Alice: Fine!`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(0 + conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(0 + 2 * conf.messageMargin + conf.height);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(models.lastMessage().stopy);
});
it('it should draw two actors notes to the right', function() {
const str = `
@@ -1104,19 +1109,17 @@ Alice->Bob: Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob->Alice: Fine!`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
const expStopX = conf.actorMargin + conf.width + conf.width / 2 + conf.noteMargin + conf.width;
expect(bounds.stopx).toBe(expStopX);
expect(bounds.stopy).toBe(
2 * conf.messageMargin + conf.height + conf.boxMargin + 10 + 2 * conf.noteMargin
);
expect(bounds.stopy).toBe(models.lastMessage().stopy);
});
it('it should draw two actors notes to the left', function() {
const str = `
@@ -1125,17 +1128,15 @@ Alice->Bob: Hello Bob, how are you?
Note left of Alice: Bob thinks
Bob->Alice: Fine!`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(
2 * conf.messageMargin + conf.height + conf.boxMargin + 10 + 2 * conf.noteMargin
);
expect(bounds.stopy).toBe(models.lastMessage().stopy);
});
it('it should draw two actors notes to the left with text wrapped (inline)', function() {
const str = `
@@ -1144,19 +1145,17 @@ Alice->>Bob:wrap: Hello Bob, how are you? If you are not available right now, I
Note left of Alice: Bob thinks
Bob->>Alice: Fine!`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
const msgs = parser.yy.getMessages();
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
expect(bounds.starty).toBe(0);
expect(msgs[0].wrap).toBe(true);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(
2 * conf.messageMargin + conf.height + conf.boxMargin + 10 + 2 * conf.noteMargin
);
expect(bounds.stopy).toBe(models.lastMessage().stopy);
});
it('it should draw two actors notes to the left with text wrapped (directive)', function() {
const str = `
@@ -1167,10 +1166,10 @@ Alice->>Bob: Hello Bob, how are you? If you are not available right now, I can l
Note left of Alice: Bob thinks
Bob->>Alice: Fine!`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
const msgs = parser.yy.getMessages();
const mermaid = mermaidAPI.getConfig();
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
@@ -1179,9 +1178,7 @@ Bob->>Alice: Fine!`;
expect(msgs.every(v => v.wrap)).toBe(true);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(
2 * conf.messageMargin + conf.height + conf.boxMargin + 10 + 2 * conf.noteMargin
);
expect(bounds.stopy).toBe(models.lastMessage().stopy);
});
it('it should draw two actors notes to the left with text wrapped and the init directive sets the theme to dark', function() {
const str = `
@@ -1191,10 +1188,11 @@ sequenceDiagram
Alice->>Bob: Hello Bob, how are you? If you are not available right now, I can leave you a message. Please get back to me as soon as you can!
Note left of Alice: Bob thinks
Bob->>Alice: Fine!`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
const msgs = parser.yy.getMessages();
const mermaid = mermaidAPI.getConfig();
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
@@ -1203,22 +1201,19 @@ Bob->>Alice: Fine!`;
expect(msgs.every(v => v.wrap)).toBe(true);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(
2 * conf.messageMargin + conf.height + conf.boxMargin + 10 + 2 * conf.noteMargin
);
expect(bounds.stopy).toBe(models.lastMessage().stopy);
});
it('it should draw two actors, notes to the left with text wrapped and the init directive sets the theme to dark and fontFamily to Menlo, fontSize to 18, and fontWeight to 800', function() {
const str = `
%%{init: { "theme": "dark", 'config': { "fontFamily": "Menlo", "fontSize": 18, "fontWeight": 400, "wrapEnabled": true }}}%%
%%{init: { "theme": "dark", 'config': { "fontFamily": "Menlo", "fontSize": 18, "fontWeight": 400, "wrap": true }}}%%
sequenceDiagram
Alice->>Bob: Hello Bob, how are you? If you are not available right now, I can leave you a message. Please get back to me as soon as you can!
Note left of Alice: Bob thinks
Bob->>Alice: Fine!`;
parser.parse(str);
// renderer.setConf(mermaidAPI.getConfig().sequence);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
const msgs = parser.yy.getMessages();
const mermaid = mermaidAPI.getConfig();
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
@@ -1230,9 +1225,7 @@ Bob->>Alice: Fine!`;
expect(msgs.every(v => v.wrap)).toBe(true);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(
2 * conf.messageMargin + conf.height + conf.boxMargin + 10 + 2 * conf.noteMargin
);
expect(bounds.stopy).toBe(models.lastMessage().stopy);
});
it('it should draw two loops', function() {
const str = `
@@ -1241,17 +1234,15 @@ Alice->Bob: Hello Bob, how are you?
loop Cheers
Bob->Alice: Fine!
end`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(
2 * conf.messageMargin + conf.height + 3 * conf.boxMargin + conf.boxTextMargin
);
expect(bounds.stopy).toBe(models.lastLoop().stopy);
});
it('it should draw background rect', function() {
const str = `
@@ -1261,26 +1252,21 @@ end`;
Bob->Alice: I feel surrounded by darkness
end
`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(0 + conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(0 + 2 * conf.messageMargin + conf.height + 3 * conf.boxMargin);
expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin);
expect(bounds.stopy).toBe(models.lastLoop().stopy);
});
});
describe('when rendering a sequenceDiagram with actor mirror activated', function() {
let conf;
beforeEach(function() {
mermaidAPI.reset();
parser.yy = sequenceDb;
parser.yy.clear();
conf = {
beforeAll(() => {
let conf = {
diagramMarginX: 50,
diagramMarginY: 10,
actorMargin: 50,
@@ -1296,7 +1282,16 @@ describe('when rendering a sequenceDiagram with actor mirror activated', functio
// Prolongs the edge of the diagram downwards
bottomMarginAdj: 1
};
mermaidAPI.initialize({ sequence: conf });
});
let conf;
beforeEach(function() {
mermaidAPI.reset();
parser.yy = sequenceDb;
parser.yy.clear();
conf = parser.yy.getConfig();
renderer.bounds.init();
});
['tspan', 'fo', 'old', undefined].forEach(function(textPlacement) {
@@ -1306,31 +1301,26 @@ describe('when rendering a sequenceDiagram with actor mirror activated', functio
const str = `
sequenceDiagram
participant Alice`;
parser.parse(str);
renderer.bounds.init();
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width);
expect(bounds.stopy).toBe(2 * conf.height + 2 * conf.boxMargin);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height);
});
});
});
describe('when rendering a sequenceDiagram with directives', function() {
let conf;
beforeEach(function() {
mermaidAPI.reset();
parser.yy = sequenceDb;
parser.yy.clear();
conf = {
beforeAll(function() {
let conf = {
diagramMarginX: 50,
diagramMarginY: 10,
actorMargin: 50,
width: 150,
// Height of actor boxes
height: 65,
boxMargin: 10,
messageMargin: 40,
@@ -1338,6 +1328,14 @@ describe('when rendering a sequenceDiagram with directives', function() {
noteMargin: 25
};
mermaidAPI.initialize({ sequence: conf });
});
let conf;
beforeEach(function() {
mermaidAPI.reset();
parser.yy = sequenceDb;
parser.yy.clear();
conf = parser.yy.getConfig();
renderer.bounds.init();
});
@@ -1349,17 +1347,18 @@ sequenceDiagram
participant Alice
`;
parser.parse(str);
renderer.bounds.init();
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
const mermaid = mermaidAPI.getConfig();
expect(mermaid.theme).toBe('dark');
expect(mermaid.logLevel).toBe(1);
expect(bounds.startx).toBe(0);
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopy).toBe(2 * conf.height + 2 * conf.boxMargin);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height);
});
it('it should handle one actor, when logLevel is 3', function() {
const str = `
@@ -1368,15 +1367,15 @@ sequenceDiagram
participant Alice
`;
parser.parse(str);
mermaidAPI.parse(str);
renderer.draw(str, 'tst');
const bounds = renderer.bounds.getBounds();
const { bounds, models } = renderer.bounds.getBounds();
const mermaid = mermaidAPI.getConfig();
expect(mermaid.logLevel).toBe(3);
expect(bounds.startx).toBe(0);
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopy).toBe(2 * conf.height + 2 * conf.boxMargin);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height);
});
});

View File

@@ -8,79 +8,7 @@ import utils, { assignWithDepth } from '../../utils';
parser.yy = sequenceDb;
const conf = {
diagramMarginX: 50,
diagramMarginY: 30,
// Margin between actors
actorMargin: 50,
// Width of actor boxes
width: 150,
// Height of actor boxes
height: 65,
actorFontSize: 14,
actorFontFamily: '"Open-Sans", "sans-serif"',
// 400 = normal
actorFontWeight: 400,
// Note font settings
noteFontSize: 14,
noteFontFamily: '"trebuchet ms", verdana, arial',
noteFontWeight: 400,
noteAlign: 'center',
// Message font settings
messageFontSize: 16,
messageFontFamily: '"trebuchet ms", verdana, arial',
messageFontWeight: 400,
// Margin around loop boxes
boxMargin: 10,
boxTextMargin: 5,
noteMargin: 10,
// Space between messages
messageMargin: 35,
// Multiline message alignment
messageAlign: 'center',
// mirror actors under diagram
mirrorActors: false,
// Depending on css styling this might need adjustment
// Prolongs the edge of the diagram downwards
bottomMarginAdj: 1,
// width of activation box
activationWidth: 10,
labelBoxWidth: 50,
labelBoxHeight: 20,
// text placement as: tspan | fo | old only text as before
textPlacement: 'tspan',
showSequenceNumbers: false,
// wrap text
wrapEnabled: false,
// padding for wrapped text
wrapPadding: 15,
messageFont: () => {
return {
fontFamily: conf.messageFontFamily,
fontSize: conf.messageFontSize,
fontWeight: conf.messageFontWeight
};
},
noteFont: () => {
return {
fontFamily: conf.noteFontFamily,
fontSize: conf.noteFontSize,
fontWeight: conf.noteFontWeight
};
},
actorFont: () => {
return {
fontFamily: conf.actorFontFamily,
fontSize: conf.actorFontSize,
fontWeight: conf.actorFontWeight
};
}
};
const conf = {};
export const bounds = {
data: {
@@ -90,10 +18,57 @@ export const bounds = {
stopy: undefined
},
verticalPos: 0,
sequenceItems: [],
activations: [],
models: {
getHeight: function() {
return (
Math.max.apply(
null,
this.actors.length === 0 ? [0] : this.actors.map(actor => actor.height || 0)
) +
(this.loops.length === 0
? 0
: this.loops.map(it => it.height || 0).reduce((acc, h) => acc + h)) +
(this.messages.length === 0
? 0
: this.messages.map(it => it.height || 0).reduce((acc, h) => acc + h)) +
(this.notes.length === 0
? 0
: this.notes.map(it => it.height || 0).reduce((acc, h) => acc + h))
);
},
clear: function() {
this.actors = [];
this.loops = [];
this.messages = [];
this.notes = [];
},
addActor: function(actorModel) {
this.actors.push(actorModel);
},
addLoop: function(loopModel) {
this.loops.push(loopModel);
},
addMessage: function(msgModel) {
this.messages.push(msgModel);
},
addNote: function(noteModel) {
this.notes.push(noteModel);
},
lastActor: function() {
return this.actors[this.actors.length - 1];
},
lastLoop: function() {
return this.loops[this.loops.length - 1];
},
lastMessage: function() {
return this.messages[this.messages.length - 1];
},
lastNote: function() {
return this.notes[this.notes.length - 1];
},
actors: [],
loops: [],
messages: [],
notes: []
@@ -101,11 +76,7 @@ export const bounds = {
init: function() {
this.sequenceItems = [];
this.activations = [];
this.models = {
loops: [],
messages: [],
notes: []
};
this.models.clear();
this.data = {
startx: undefined,
stopx: undefined,
@@ -113,6 +84,7 @@ export const bounds = {
stopy: undefined
};
this.verticalPos = 0;
setConf(parser.yy.getConfig());
},
updateVal: function(obj, key, val, fun) {
if (typeof obj[key] === 'undefined') {
@@ -219,7 +191,7 @@ export const bounds = {
return this.verticalPos;
},
getBounds: function() {
return this.data;
return { bounds: this.data, models: this.models };
}
};
@@ -229,17 +201,20 @@ export const bounds = {
* @param noteModel:{x: number, y: number, message: string, width: number} - startx: x axis start position, verticalPos: y axis position, messsage: the message to be shown, width: Set this with a custom width to override the default configured width.
*/
const drawNote = function(elem, noteModel) {
bounds.bumpVerticalPos(conf.boxMargin);
noteModel.height = conf.boxMargin;
noteModel.starty = bounds.getVerticalPos();
const rect = svgDraw.getNoteRect();
rect.x = noteModel.x;
rect.y = noteModel.y;
rect.x = noteModel.startx;
rect.y = noteModel.starty;
rect.width = noteModel.width || conf.width;
rect.class = 'note';
let g = elem.append('g');
const rectElem = svgDraw.drawRect(g, rect);
const textObj = svgDraw.getTextObj();
textObj.x = noteModel.x;
textObj.y = noteModel.y;
textObj.x = noteModel.startx;
textObj.y = noteModel.starty;
textObj.width = rect.width;
textObj.dy = '1em';
textObj.text = noteModel.message;
@@ -258,12 +233,13 @@ const drawNote = function(elem, noteModel) {
textElem.map(te => (te._groups || te)[0][0].getBBox().height).reduce((acc, curr) => acc + curr)
);
noteModel.height = textHeight + 2 * conf.noteMargin;
bounds.insert(noteModel.x, noteModel.y, noteModel.x + rect.width, noteModel.y + noteModel.height);
rectElem.attr('height', noteModel.height);
bounds.bumpVerticalPos(noteModel.height);
rectElem.attr('height', textHeight + 2 * conf.noteMargin);
noteModel.height += textHeight + 2 * conf.noteMargin;
bounds.bumpVerticalPos(textHeight + 2 * conf.noteMargin);
noteModel.stopy = noteModel.starty + textHeight + 2 * conf.noteMargin;
noteModel.stopx = noteModel.startx + rect.width;
bounds.insert(noteModel.startx, noteModel.starty, noteModel.stopx, noteModel.stopy);
bounds.models.addNote(noteModel);
};
/**
@@ -272,10 +248,13 @@ const drawNote = function(elem, noteModel) {
* @param msgModel - the model containing fields describing a message
*/
const drawMessage = function(g, msgModel) {
bounds.bumpVerticalPos(conf.messageMargin);
msgModel.height += conf.messageMargin;
msgModel.starty = bounds.getVerticalPos();
const { startx, stopx, starty: verticalPos, message, type, sequenceIndex, wrap } = msgModel;
const textObj = svgDraw.getTextObj();
textObj.x = startx;
textObj.y = verticalPos - (conf.messageFontSize + 7);
textObj.y = verticalPos;
textObj.width = stopx - startx;
textObj.class = 'messageText';
textObj.dy = '1em';
@@ -290,20 +269,18 @@ const drawMessage = function(g, msgModel) {
textObj.wrap = wrap;
let textElem = drawText(g, textObj);
const lineHeight = (textElem[0]._groups || textElem[0])[0][0].getBBox().height;
textElem.forEach(te => te.attr('y', verticalPos - 7 - lineHeight / 2));
const lines = message.split(common.lineBreakRegex).length - 1 > 0 ? 1 : 0;
const lines = message.split(common.lineBreakRegex).length - 1;
let totalOffset = Math.round(
textElem.map(te => (te._groups || te)[0][0].getBBox().height).reduce((acc, curr) => acc + curr)
);
let totalOffset = Math.round(lineHeight + lines * lineHeight);
let textWidth = Math.max.apply(
null,
textElem.map(te => (te._groups || te)[0][0].getBBox().width)
);
bounds.bumpVerticalPos(totalOffset - lines * conf.messageFontSize);
let line;
if (startx === stopx) {
if (conf.rightAngles) {
@@ -342,25 +319,32 @@ const drawMessage = function(g, msgModel) {
}
bounds.bumpVerticalPos(30);
msgModel.height += 30;
const dx = Math.max(textWidth / 2, 100);
bounds.insert(
startx - dx,
bounds.getVerticalPos() - 10 + totalOffset,
stopx + dx,
bounds.getVerticalPos() + totalOffset
bounds.getVerticalPos() + 30 + totalOffset
);
bounds.bumpVerticalPos(10);
msgModel.height += 10;
} else {
line = g.append('line');
line.attr('x1', startx);
line.attr('y1', verticalPos + totalOffset);
line.attr('x2', stopx);
line.attr('y2', verticalPos + totalOffset);
bounds.bumpVerticalPos(10);
msgModel.height += 10;
bounds.insert(
startx,
bounds.getVerticalPos() - 10 + totalOffset,
stopx,
bounds.getVerticalPos() + totalOffset
);
msgModel.height += 10;
bounds.bumpVerticalPos(10);
}
// Make an SVG Container
// Draw the line
@@ -411,6 +395,9 @@ const drawMessage = function(g, msgModel) {
.attr('class', 'sequenceNumber')
.text(sequenceIndex);
}
msgModel.stopy = msgModel.starty + msgModel.height;
bounds.insert(msgModel.fromBounds, msgModel.starty, msgModel.toBounds, msgModel.stopy);
logger.debug(`mm.h:${msgModel.height} vs c.h:${msgModel.stopy - msgModel.starty}`);
};
export const drawActors = function(diagram, actors, actorKeys, verticalPos) {
@@ -435,6 +422,7 @@ export const drawActors = function(diagram, actors, actorKeys, verticalPos) {
prevWidth += actor.width;
prevMargin += actor.margin;
bounds.models.addActor(actor);
}
// Add a margin between the actor boxes and the first arrow
@@ -461,7 +449,7 @@ const actorActivations = function(actor) {
});
};
const actorFlowVerticaBounds = function(actor, actors) {
const activationBounds = function(actor, actors) {
// handle multiple stacked activations for same actor
const actorObj = actors[actor];
const activations = actorActivations(actor);
@@ -498,7 +486,7 @@ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoop
*/
export const draw = function(text, id) {
parser.yy.clear();
parser.yy.setWrap(conf.wrapEnabled);
parser.yy.setWrap(conf.wrap);
parser.parse(text + '\n');
bounds.init();
@@ -545,11 +533,8 @@ export const draw = function(text, id) {
switch (msg.type) {
case parser.yy.LINETYPE.NOTE:
bounds.bumpVerticalPos(conf.boxMargin);
noteModel = msg.noteModel;
noteModel.y = bounds.getVerticalPos();
drawNote(diagram, noteModel);
bounds.models.notes.push(noteModel);
break;
case parser.yy.LINETYPE.ACTIVE_START:
bounds.newActivation(msg, diagram, actors);
@@ -568,9 +553,9 @@ export const draw = function(text, id) {
break;
case parser.yy.LINETYPE.LOOP_END:
loopModel = bounds.endLoop();
svgDraw.drawLoop(diagram, loopModel, 'loop', conf);
bounds.models.loops.push(loopModel);
svgDraw.drawLoop(diagram, loopModel, 'loop', conf, bounds);
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
bounds.models.addLoop(loopModel);
break;
case parser.yy.LINETYPE.RECT_START:
adjustLoopHeightForWrap(loopWidths, msg, conf.boxMargin, conf.boxMargin, message =>
@@ -580,7 +565,7 @@ export const draw = function(text, id) {
case parser.yy.LINETYPE.RECT_END:
loopModel = bounds.endLoop();
svgDraw.drawBackgroundRect(diagram, loopModel);
bounds.models.loops.push(loopModel);
bounds.models.addLoop(loopModel);
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
break;
case parser.yy.LINETYPE.OPT_START:
@@ -595,8 +580,8 @@ export const draw = function(text, id) {
case parser.yy.LINETYPE.OPT_END:
loopModel = bounds.endLoop();
svgDraw.drawLoop(diagram, loopModel, 'opt', conf);
bounds.models.loops.push(loopModel);
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
bounds.models.addLoop(loopModel);
break;
case parser.yy.LINETYPE.ALT_START:
adjustLoopHeightForWrap(
@@ -619,8 +604,8 @@ export const draw = function(text, id) {
case parser.yy.LINETYPE.ALT_END:
loopModel = bounds.endLoop();
svgDraw.drawLoop(diagram, loopModel, 'alt', conf);
bounds.models.loops.push(loopModel);
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
bounds.models.addLoop(loopModel);
break;
case parser.yy.LINETYPE.PAR_START:
adjustLoopHeightForWrap(
@@ -643,19 +628,16 @@ export const draw = function(text, id) {
case parser.yy.LINETYPE.PAR_END:
loopModel = bounds.endLoop();
svgDraw.drawLoop(diagram, loopModel, 'par', conf);
bounds.models.loops.push(loopModel);
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
bounds.models.addLoop(loopModel);
break;
default:
try {
// lastMsg = msg
bounds.bumpVerticalPos(conf.messageMargin);
msgModel = msg.msgModel;
msgModel.starty = bounds.getVerticalPos();
msgModel.sequenceIndex = sequenceIndex;
drawMessage(diagram, msgModel);
bounds.models.messages.push(msgModel);
bounds.insert(msgModel.fromBounds, msgModel.starty, msgModel.toBounds, msgModel.starty);
bounds.models.addMessage(msgModel);
} catch (e) {
logger.error('error while drawing message', e);
}
@@ -681,7 +663,7 @@ export const draw = function(text, id) {
drawActors(diagram, actors, actorKeys, bounds.getVerticalPos());
}
const box = bounds.getBounds();
const { bounds: box } = bounds.getBounds();
// Adjust line height of actor lines now that the height of the diagram is known
logger.debug('For line height fix Querying: #' + id + ' .actor-line');
@@ -724,7 +706,7 @@ export const draw = function(text, id) {
' ' +
(height + extraVertForTitle)
);
logger.debug('bounds', bounds);
logger.debug(`models: ${JSON.stringify(bounds.models, null, 2)}`);
};
/**
@@ -879,13 +861,16 @@ const buildNoteModel = function(msg, actors) {
shouldWrap ? utils.wrapLabel(msg.message, conf.width, conf.noteFont()) : msg.message,
conf.noteFont()
);
logger.debug(`TD:[${textDimensions.width},${textDimensions.height}]`);
let noteModel = {
width: shouldWrap
? conf.width
: Math.max(conf.width, textDimensions.width + 2 * conf.noteMargin),
height: 0,
x: actors[msg.from].x,
y: 0,
startx: actors[msg.from].x,
stopx: 0,
starty: 0,
stopy: 0,
message: msg.message
};
if (msg.placement === parser.yy.PLACEMENT.RIGHTOF) {
@@ -895,7 +880,7 @@ const buildNoteModel = function(msg, actors) {
actors[msg.from].width / 2 + actors[msg.to].width / 2,
textDimensions.width + 2 * conf.noteMargin
);
noteModel.x = startx + (actors[msg.from].width + conf.actorMargin) / 2;
noteModel.startx = startx + (actors[msg.from].width + conf.actorMargin) / 2;
} else if (msg.placement === parser.yy.PLACEMENT.LEFTOF) {
noteModel.width = shouldWrap
? Math.max(conf.width, textDimensions.width + 2 * conf.noteMargin)
@@ -903,7 +888,7 @@ const buildNoteModel = function(msg, actors) {
actors[msg.from].width / 2 + actors[msg.to].width / 2,
textDimensions.width + 2 * conf.noteMargin
);
noteModel.x = startx - noteModel.width + (actors[msg.from].width - conf.actorMargin) / 2;
noteModel.startx = startx - noteModel.width + (actors[msg.from].width - conf.actorMargin) / 2;
} else if (msg.to === msg.from) {
textDimensions = utils.calculateTextDimensions(
shouldWrap
@@ -914,12 +899,12 @@ const buildNoteModel = function(msg, actors) {
noteModel.width = shouldWrap
? Math.max(conf.width, actors[msg.to].width)
: Math.max(actors[msg.to].width, conf.width, textDimensions.width + 2 * conf.noteMargin);
noteModel.x = startx + (actors[msg.to].width - noteModel.width) / 2;
noteModel.startx = startx + (actors[msg.to].width - noteModel.width) / 2;
} else {
noteModel.width =
Math.abs(startx + actors[msg.from].width / 2 - (stopx + actors[msg.to].width / 2)) +
conf.actorMargin;
noteModel.x =
noteModel.startx =
startx < stopx
? startx + actors[msg.from].width / 2 - conf.actorMargin / 2
: stopx + actors[msg.to].width / 2 - conf.actorMargin / 2;
@@ -951,8 +936,8 @@ const buildMessageModel = function(msg, actors) {
if (!process) {
return {};
}
const fromBounds = actorFlowVerticaBounds(msg.from, actors);
const toBounds = actorFlowVerticaBounds(msg.to, actors);
const fromBounds = activationBounds(msg.from, actors);
const toBounds = activationBounds(msg.to, actors);
const fromIdx = fromBounds[0] <= toBounds[0] ? 1 : 0;
const toIdx = fromBounds[0] < toBounds[0] ? 0 : 1;
const allBounds = fromBounds.concat(toBounds);
@@ -985,7 +970,7 @@ const calculateLoopBounds = function(messages, actors) {
let current, noteModel, msgModel;
messages.forEach(function(msg) {
msg.id = utils.generateId();
msg.id = utils.random({ length: 10 });
switch (msg.type) {
case parser.yy.LINETYPE.LOOP_START:
case parser.yy.LINETYPE.ALT_START:
@@ -1046,8 +1031,8 @@ const calculateLoopBounds = function(messages, actors) {
let depth = 0;
stack.forEach(stk => {
current = stk;
current.from = Math.min(current.from, noteModel.x);
current.to = Math.max(current.to, noteModel.x + noteModel.width);
current.from = Math.min(current.from, noteModel.startx);
current.to = Math.max(current.to, noteModel.startx + noteModel.width);
current.width =
Math.max(current.width, Math.abs(current.from - current.to)) -
50 -

View File

@@ -251,11 +251,11 @@ export const drawActivation = function(elem, bounds, verticalPos, conf, actorAct
/**
* Draws a loop in the diagram
* @param elem - elemenet to append the loop to.
* @param bounds - bounds of the given loop.
* @param loopModel - loopModel of the given loop.
* @param labelText - Text within the loop.
* @param conf
* @param conf - diagrom configuration
*/
export const drawLoop = function(elem, bounds, labelText, conf) {
export const drawLoop = function(elem, loopModel, labelText, conf) {
const g = elem.append('g');
const drawLoopLine = function(startx, starty, stopx, stopy) {
return g
@@ -266,20 +266,23 @@ export const drawLoop = function(elem, bounds, labelText, conf) {
.attr('y2', stopy)
.attr('class', 'loopLine');
};
drawLoopLine(bounds.startx, bounds.starty, bounds.stopx, bounds.starty);
drawLoopLine(bounds.stopx, bounds.starty, bounds.stopx, bounds.stopy);
drawLoopLine(bounds.startx, bounds.stopy, bounds.stopx, bounds.stopy);
drawLoopLine(bounds.startx, bounds.starty, bounds.startx, bounds.stopy);
if (typeof bounds.sections !== 'undefined') {
bounds.sections.forEach(function(item) {
drawLoopLine(bounds.startx, item.y, bounds.stopx, item.y).style('stroke-dasharray', '3, 3');
drawLoopLine(loopModel.startx, loopModel.starty, loopModel.stopx, loopModel.starty);
drawLoopLine(loopModel.stopx, loopModel.starty, loopModel.stopx, loopModel.stopy);
drawLoopLine(loopModel.startx, loopModel.stopy, loopModel.stopx, loopModel.stopy);
drawLoopLine(loopModel.startx, loopModel.starty, loopModel.startx, loopModel.stopy);
if (typeof loopModel.sections !== 'undefined') {
loopModel.sections.forEach(function(item) {
drawLoopLine(loopModel.startx, item.y, loopModel.stopx, item.y).style(
'stroke-dasharray',
'3, 3'
);
});
}
let txt = getTextObj();
txt.text = labelText;
txt.x = bounds.startx;
txt.y = bounds.starty;
txt.x = loopModel.startx;
txt.y = loopModel.starty;
const msgFont = conf.messageFont();
txt.fontFamily = msgFont.fontFamily;
txt.fontSize = msgFont.fontSize;
@@ -294,9 +297,9 @@ export const drawLoop = function(elem, bounds, labelText, conf) {
drawLabel(g, txt);
txt = getTextObj();
txt.text = bounds.title;
txt.x = bounds.startx + conf.labelBoxWidth / 2 + (bounds.stopx - bounds.startx) / 2;
txt.y = bounds.starty + conf.boxMargin + conf.boxTextMargin;
txt.text = loopModel.title;
txt.x = loopModel.startx + conf.labelBoxWidth / 2 + (loopModel.stopx - loopModel.startx) / 2;
txt.y = loopModel.starty + conf.boxMargin + conf.boxTextMargin;
txt.anchor = 'middle';
txt.class = 'loopText';
txt.fontFamily = msgFont.fontFamily;
@@ -306,12 +309,12 @@ export const drawLoop = function(elem, bounds, labelText, conf) {
let textElem = drawText(g, txt);
if (typeof bounds.sectionTitles !== 'undefined') {
bounds.sectionTitles.forEach(function(item, idx) {
if (typeof loopModel.sectionTitles !== 'undefined') {
loopModel.sectionTitles.forEach(function(item, idx) {
if (item.message) {
txt.text = item.message;
txt.x = bounds.startx + (bounds.stopx - bounds.startx) / 2;
txt.y = bounds.sections[idx].y + conf.boxMargin + conf.boxTextMargin;
txt.x = loopModel.startx + (loopModel.stopx - loopModel.startx) / 2;
txt.y = loopModel.sections[idx].y + conf.boxMargin + conf.boxTextMargin;
txt.class = 'loopText';
txt.anchor = 'middle';
txt.valign = 'middle';
@@ -319,20 +322,19 @@ export const drawLoop = function(elem, bounds, labelText, conf) {
txt.fontFamily = msgFont.fontFamily;
txt.fontSize = msgFont.fontSize;
txt.fontWeight = msgFont.fontWeight;
txt.wrap = bounds.wrap;
txt.wrap = loopModel.wrap;
textElem = drawText(g, txt);
let sectionHeight = Math.round(
textElem
.map(te => (te._groups || te)[0][0].getBBox().height)
.reduce((acc, curr) => acc + curr)
);
bounds.sections[idx].height += sectionHeight - (conf.boxMargin + conf.boxTextMargin);
loopModel.sections[idx].height += sectionHeight - (conf.boxMargin + conf.boxTextMargin);
}
});
}
bounds.height = Math.round(bounds.stopy - bounds.starty);
loopModel.height = Math.round(loopModel.stopy - loopModel.starty);
return g;
};

View File

@@ -71,11 +71,11 @@ const init = function() {
logger.debug('Start On Load before: ' + mermaid.startOnLoad);
if (typeof mermaid.startOnLoad !== 'undefined') {
logger.debug('Start On Load inner: ' + mermaid.startOnLoad);
mermaidAPI.setConfig({ startOnLoad: mermaid.startOnLoad });
mermaidAPI.initialize({ startOnLoad: mermaid.startOnLoad });
}
if (typeof mermaid.ganttConfig !== 'undefined') {
mermaidAPI.setConfig({ gantt: mermaid.ganttConfig });
mermaidAPI.initialize({ gantt: mermaid.ganttConfig });
}
let txt;

View File

@@ -13,7 +13,7 @@
import { select } from 'd3';
import scope from 'scope-css';
import pkg from '../package.json';
import { setConfig, getConfig } from './config';
import { setConfig, getConfig, setSiteConfig, getSiteConfig } from './config';
import { logger, setLogLevel } from './logger';
import utils, { assignWithDepth } from './utils';
import flowRenderer from './diagrams/flowchart/flowRenderer';
@@ -56,529 +56,11 @@ for (const themeName of ['default', 'forest', 'dark', 'neutral']) {
themes[themeName] = require(`./themes/${themeName}/index.scss`);
}
/**
* These are the default options which can be overridden with the initialization call like so:
* **Example 1:**
* <pre>
* mermaid.initialize({
* flowchart:{
* htmlLabels: false
* }
* });
* </pre>
*
* **Example 2:**
* <pre>
* &lt;script>
* var config = {
* startOnLoad:true,
* flowchart:{
* useMaxWidth:true,
* htmlLabels:true,
* curve:'cardinal',
* },
*
* securityLevel:'loose',
* };
* mermaid.initialize(config);
* &lt;/script>
* </pre>
* A summary of all options and their defaults is found [here](https://github.com/knsv/mermaid/blob/master/docs/mermaidAPI.md#mermaidapi-configuration-defaults). A description of each option follows below.
*
* @name Configuration
*/
const config = {
/** theme , the CSS style sheet
*
* **theme** - Choose one of the built-in themes:
* * default
* * forest
* * dark
* * neutral.
* To disable any pre-defined mermaid theme, use "null".
*
* **themeCSS** - Use your own CSS. This overrides **theme**.
* <pre>
* "theme": "forest",
* "themeCSS": ".node rect { fill: red; }"
* </pre>
*/
theme: 'default',
themeCSS: undefined,
/* **maxTextSize** - The maximum allowed size of the users text diamgram */
maxTextSize: 50000,
/**
* **fontFamily** The font to be used for the rendered diagrams. Default value is \"trebuchet ms\", verdana, arial;
*/
fontFamily: '"trebuchet ms", verdana, arial;',
/**
* This option decides the amount of logging to be used.
* * debug: 1
* * info: 2
* * warn: 3
* * error: 4
* * fatal: (**default**) 5
*/
logLevel: 5,
/**
* Sets the level of trust to be used on the parsed diagrams.
* * **strict**: (**default**) tags in text are encoded, click functionality is disabeled
* * **loose**: tags in text are allowed, click functionality is enabled
*/
securityLevel: 'strict',
/**
* This options controls whether or mermaid starts when the page loads
* **Default value true**.
*/
startOnLoad: true,
/**
* This options controls whether or arrow markers in html code will be absolute paths or
* an anchor, #. This matters if you are using base tag settings.
* **Default value false**.
*/
arrowMarkerAbsolute: false,
/**
* The object containing configurations specific for flowcharts
*/
flowchart: {
/**
* Flag for setting whether or not a html tag should be used for rendering labels
* on the edges.
* **Default value true**.
*/
htmlLabels: true,
/**
* Defines the spacing between nodes on the same level (meaning horizontal spacing for
* TB or BT graphs, and the vertical spacing for LR as well as RL graphs).
* **Default value 50**.
*/
nodeSpacing: 50,
/**
* Defines the spacing between nodes on different levels (meaning vertical spacing for
* TB or BT graphs, and the horizontal spacing for LR as well as RL graphs).
* **Default value 50**.
*/
rankSpacing: 50,
/**
* How mermaid renders curves for flowcharts. Possible values are
* * basis
* * linear **default**
* * cardinal
*/
curve: 'linear',
// Only used in new experimental rendering
// repreesents the padding between the labels and the shape
padding: 15
},
/**
* The object containing configurations specific for sequence diagrams
*/
sequence: {
/**
* margin to the right and left of the sequence diagram.
* **Default value 50**.
*/
diagramMarginX: 50,
/**
* margin to the over and under the sequence diagram.
* **Default value 10**.
*/
diagramMarginY: 10,
/**
* Margin between actors.
* **Default value 50**.
*/
actorMargin: 50,
/**
* Width of actor boxes
* **Default value 150**.
*/
width: 150,
/**
* Height of actor boxes
* **Default value 65**.
*/
height: 65,
/**
* Margin around loop boxes
* **Default value 10**.
*/
boxMargin: 10,
/**
* margin around the text in loop/alt/opt boxes
* **Default value 5**.
*/
boxTextMargin: 5,
/**
* margin around notes.
* **Default value 10**.
*/
noteMargin: 10,
/**
* Space between messages.
* **Default value 35**.
*/
messageMargin: 35,
/**
* Multiline message alignment. Possible values are:
* * left
* * center **default**
* * right
*/
messageAlign: 'center',
/**
* mirror actors under diagram.
* **Default value true**.
*/
mirrorActors: true,
/**
* Depending on css styling this might need adjustment.
* Prolongs the edge of the diagram downwards.
* **Default value 1**.
*/
bottomMarginAdj: 1,
/**
* when this flag is set the height and width is set to 100% and is then scaling with the
* available space if not the absolute space required is used.
* **Default value true**.
*/
useMaxWidth: true,
/**
* This will display arrows that start and begin at the same node as right angles, rather than a curve
* **Default value false**.
*/
rightAngles: false,
/**
* This will show the node numbers
* **Default value false**.
*/
showSequenceNumbers: false,
/**
* This sets the font size of the actor's description
* **Default value 14**.
*/
actorFontSize: 14,
/**
* This sets the font family of the actor's description
* **Default value "Open-Sans", "sans-serif"**.
*/
actorFontFamily: '"Open-Sans", "sans-serif"',
/**
* This sets the font weight of the actor's description
* **Default value 400.
*/
actorFontWeight: 400,
/**
* This sets the font size of actor-attached notes.
* **Default value 14**.
*/
noteFontSize: 14,
/**
* This sets the font family of actor-attached notes.
* **Default value "trebuchet ms", verdana, arial**.
*/
noteFontFamily: '"trebuchet ms", verdana, arial',
/**
* This sets the font weight of the note's description
* **Default value 400.
*/
noteFontWeight: 400,
/**
* This sets the text alignment of actor-attached notes.
* **Default value center**.
*/
noteAlign: 'center',
/**
* This sets the font size of actor messages.
* **Default value 16**.
*/
messageFontSize: 16,
/**
* This sets the font family of actor messages.
* **Default value "trebuchet ms", verdana, arial**.
*/
messageFontFamily: '"trebuchet ms", verdana, arial',
/**
* This sets the font weight of the message's description
* **Default value 400.
*/
messageFontWeight: 400,
/**
* This sets the auto-wrap state for the diagram
* **Default value false.
*/
wrapEnabled: false,
/**
* This sets the auto-wrap padding for the diagram (sides only)
* **Default value 15.
*/
wrapPadding: 15,
/**
* This sets the width of the loop-box (loop, alt, opt, par)
* **Default value 50.
*/
labelBoxWidth: 50,
/**
* This sets the height of the loop-box (loop, alt, opt, par)
* **Default value 20.
*/
labelBoxHeight: 20
},
/**
* The object containing configurations specific for gantt diagrams*
*/
gantt: {
/**
* Margin top for the text over the gantt diagram
* **Default value 25**.
*/
titleTopMargin: 25,
/**
* The height of the bars in the graph
* **Default value 20**.
*/
barHeight: 20,
/**
* The margin between the different activities in the gantt diagram.
* **Default value 4**.
*/
barGap: 4,
/**
* Margin between title and gantt diagram and between axis and gantt diagram.
* **Default value 50**.
*/
topPadding: 50,
/**
* The space allocated for the section name to the left of the activities.
* **Default value 75**.
*/
leftPadding: 75,
/**
* Vertical starting position of the grid lines.
* **Default value 35**.
*/
gridLineStartPadding: 35,
/**
* Font size ...
* **Default value 11**.
*/
fontSize: 11,
/**
* font family ...
* **Default value '"Open-Sans", "sans-serif"'**.
*/
fontFamily: '"Open-Sans", "sans-serif"',
/**
* The number of alternating section styles.
* **Default value 4**.
*/
numberSectionStyles: 4,
/**
* Datetime format of the axis. This might need adjustment to match your locale and preferences
* **Default value '%Y-%m-%d'**.
*/
axisFormat: '%Y-%m-%d'
},
/**
* The object containing configurations specific for sequence diagrams
*/
journey: {
/**
* margin to the right and left of the sequence diagram.
* **Default value 50**.
*/
diagramMarginX: 50,
/**
* margin to the over and under the sequence diagram.
* **Default value 10**.
*/
diagramMarginY: 10,
/**
* Margin between actors.
* **Default value 50**.
*/
actorMargin: 50,
/**
* Width of actor boxes
* **Default value 150**.
*/
width: 150,
/**
* Height of actor boxes
* **Default value 65**.
*/
height: 65,
/**
* Margin around loop boxes
* **Default value 10**.
*/
boxMargin: 10,
/**
* margin around the text in loop/alt/opt boxes
* **Default value 5**.
*/
boxTextMargin: 5,
/**
* margin around notes.
* **Default value 10**.
*/
noteMargin: 10,
/**
* Space between messages.
* **Default value 35**.
*/
messageMargin: 35,
/**
* Multiline message alignment. Possible values are:
* * left
* * center **default**
* * right
*/
messageAlign: 'center',
/**
* Depending on css styling this might need adjustment.
* Prolongs the edge of the diagram downwards.
* **Default value 1**.
*/
bottomMarginAdj: 1,
/**
* when this flag is set the height and width is set to 100% and is then scaling with the
* available space if not the absolute space required is used.
* **Default value true**.
*/
useMaxWidth: true,
/**
* This will display arrows that start and begin at the same node as right angles, rather than a curve
* **Default value false**.
*/
rightAngles: false
},
class: {},
git: {},
state: {
dividerMargin: 10,
sizeUnit: 5,
padding: 8,
textHeight: 10,
titleShift: -15,
noteMargin: 10,
forkWidth: 70,
forkHeight: 7,
// Used
miniPadding: 2,
// Font size factor, this is used to guess the width of the edges labels before rendering by dagre
// layout. This might need updating if/when switching font
fontSizeFactor: 5.02,
fontSize: 24,
labelHeight: 16,
edgeLengthFactor: '20',
compositTitleSize: 35,
radius: 5
},
/**
* The object containing configurations specific for entity relationship diagrams
*/
er: {
/**
* The amount of padding around the diagram as a whole so that embedded diagrams have margins, expressed in pixels
*/
diagramPadding: 20,
/**
* Directional bias for layout of entities. Can be either 'TB', 'BT', 'LR', or 'RL',
* where T = top, B = bottom, L = left, and R = right.
*/
layoutDirection: 'TB',
/**
* The mimimum width of an entity box, expressed in pixels
*/
minEntityWidth: 100,
/**
* The minimum height of an entity box, expressed in pixels
*/
minEntityHeight: 75,
/**
* The minimum internal padding between the text in an entity box and the enclosing box borders, expressed in pixels
*/
entityPadding: 15,
/**
* Stroke color of box edges and lines
*/
stroke: 'gray',
/**
* Fill color of entity boxes
*/
fill: 'honeydew',
/**
* Font size (expressed as an integer representing a number of pixels)
*/
fontSize: 12
}
};
export const defaultConfig = Object.freeze(assignWithDepth({}, config));
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
config.git.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
setLogLevel(config.logLevel);
configApi.reset(config);
function parse(text) {
const graphInit = utils.detectInit(text);
if (graphInit) {
initialize(graphInit);
logger.debug('Init ', graphInit);
reinitialize(graphInit);
logger.debug('reinit ', graphInit);
}
const graphType = utils.detectType(text);
let parser;
@@ -640,7 +122,7 @@ function parse(text) {
parser.parser.yy = journeyDb;
break;
}
parser.parser.yy.graphType = graphType;
parser.parser.yy.parseError = (str, hash) => {
const error = { str, hash };
throw error;
@@ -722,7 +204,7 @@ const render = function(id, _txt, cb, container) {
}
const graphInit = utils.detectInit(txt);
if (graphInit) {
initialize(graphInit);
reinitialize(graphInit);
assignWithDepth(cnf, getConfig());
}
@@ -942,6 +424,79 @@ const render = function(id, _txt, cb, container) {
return svgCode;
};
let currentDirective = {};
const parseDirective = function(statement, context, type) {
try {
if (statement !== undefined) {
statement = statement.trim();
switch (context) {
case 'open_directive':
currentDirective = {};
break;
case 'type_directive':
currentDirective.type = statement.toLowerCase();
break;
case 'arg_directive':
currentDirective.args = JSON.parse(statement);
break;
case 'close_directive':
handleDirective(currentDirective, type);
currentDirective = null;
break;
}
}
} catch (error) {
logger.error(
`Error while rendering sequenceDiagram directive: ${statement} jison context: ${context}`
);
logger.error(error.message);
}
};
const handleDirective = function(directive, type) {
logger.debug(`Directive type=${directive.type} with args:`, directive.args);
switch (directive.type) {
case 'init':
case 'initialize': {
['config'].forEach(prop => {
if (typeof directive.args[prop] !== 'undefined') {
if (type === 'flowchart-v2') {
type = 'flowchart';
}
directive.args[type] = directive.args[prop];
delete directive.args[prop];
}
});
reinitialize(directive.args);
break;
}
case 'wrap':
case 'nowrap':
directive.args = { config: { wrap: directive.type === 'wrap' } };
['config'].forEach(prop => {
if (typeof directive.args[prop] !== 'undefined') {
if (type === 'flowchart-v2') {
type = 'flowchart';
}
directive.args[type] = directive.args[prop];
delete directive.args[prop];
}
});
reinitialize(directive.args);
break;
default:
logger.warn(
`Unhandled directive: source: '%%{${directive.type}: ${JSON.stringify(
directive.args ? directive.args : {}
)}}%%`,
directive
);
break;
}
};
function updateRendererConfigs(conf) {
gitGraphRenderer.setConf(conf.git);
flowRenderer.setConf(conf.flowchart);
@@ -961,14 +516,20 @@ function updateRendererConfigs(conf) {
errorRenderer.setConf(conf.class);
}
function initialize(options) {
console.log(`mermaidAPI.initialize: v${pkg.version}`);
function reinitialize(options) {
console.log(`mermaidAPI.reinitialize: v${pkg.version}`, options);
// Set default options
if (typeof options === 'object') {
assignWithDepth(config, options);
updateRendererConfigs(config);
}
setConfig(config);
const config = typeof options === 'object' ? setConfig(options) : getSiteConfig();
updateRendererConfigs(config);
setLogLevel(config.logLevel);
logger.debug('mermaidAPI.reinitialize: ', config);
}
function initialize(options) {
// console.log(`mermaidAPI.initialize: v${pkg.version}`);
// Set default options
const config = typeof options === 'object' ? setSiteConfig(options) : getSiteConfig();
updateRendererConfigs(config);
setLogLevel(config.logLevel);
logger.debug('mermaidAPI.initialize: ', config);
}
@@ -977,22 +538,30 @@ function initialize(options) {
// console.warn('get config')
// return config
// }
const mermaidAPI = Object.freeze({
render,
parse,
parseDirective,
initialize,
reinitialize,
getConfig,
setConfig,
getSiteConfig,
reset: () => {
// console.warn('reset');
configApi.reset(defaultConfig);
assignWithDepth(config, defaultConfig, { clobber: true });
updateRendererConfigs(config);
configApi.reset();
const siteConfig = getSiteConfig();
updateRendererConfigs(siteConfig);
},
defaultConfig
globalReset: () => {
configApi.reset(configApi.defaultConfig);
updateRendererConfigs(getConfig());
},
defaultConfig: configApi.defaultConfig
});
setLogLevel(getConfig().logLevel);
configApi.reset(getConfig());
export default mermaidAPI;
/**
* ## mermaidAPI configuration defaults

View File

@@ -6,7 +6,7 @@ describe('when using mermaidAPI and ', function() {
describe('doing initialize ', function() {
beforeEach(function() {
document.body.innerHTML = '';
mermaidAPI.reset();
mermaidAPI.globalReset();
});
it('should copy a literal into the configuration', function() {
@@ -30,11 +30,9 @@ describe('when using mermaidAPI and ', function() {
mermaidAPI.initialize({ testObject: object });
let config = mermaidAPI.getConfig();
console.log('1:', config);
expect(config.testObject.test1).toBe(1);
mermaidAPI.initialize({ testObject: { test3: true } });
config = mermaidAPI.getConfig();
console.log(config);
expect(config.testObject.test1).toBe(1);
expect(config.testObject.test2).toBe(false);
@@ -42,13 +40,50 @@ describe('when using mermaidAPI and ', function() {
});
it('should reset mermaid config to global defaults', function() {
let config = {
logLevel: 0
logLevel: 0,
securityLevel: 'loose'
};
mermaidAPI.initialize(config);
expect(mermaidAPI.getConfig().logLevel).toBe(0);
mermaidAPI.reset();
expect(mermaidAPI.getConfig().securityLevel).toBe('loose');
mermaidAPI.globalReset();
expect(mermaidAPI.getConfig()).toEqual(mermaidAPI.defaultConfig);
});
it('should reset mermaid config to site defaults', function() {
let config = {
logLevel: 0
};
mermaidAPI.initialize(config);
const siteConfig = mermaidAPI.getSiteConfig();
expect(mermaidAPI.getConfig().logLevel).toBe(0);
config.logLevel = 3;
config.securityLevel = 'loose';
mermaidAPI.reinitialize(config);
expect(mermaidAPI.getConfig().logLevel).toBe(3);
expect(mermaidAPI.getConfig().securityLevel).toBe('strict');
mermaidAPI.reset();
expect(mermaidAPI.getSiteConfig()).toEqual(siteConfig)
expect(mermaidAPI.getConfig()).toEqual(siteConfig);
});
it('should prevent changes to site defaults (sneaky)', function() {
let config = {
logLevel: 0
};
mermaidAPI.initialize(config);
const siteConfig = mermaidAPI.getSiteConfig();
expect(mermaidAPI.getConfig().logLevel).toBe(0);
config.secure = {
toString: function() {
mermaidAPI.initialize({ securityLevel: 'loose' });
}
};
mermaidAPI.reinitialize(config);
expect(mermaidAPI.getConfig().secure).toEqual(mermaidAPI.getSiteConfig().secure);
expect(mermaidAPI.getConfig().securityLevel).toBe('strict');
mermaidAPI.reset();
expect(mermaidAPI.getSiteConfig()).toEqual(siteConfig)
expect(mermaidAPI.getConfig()).toEqual(siteConfig);
});
it('should prevent clobbering global defaults (direct)', function() {
let config = assignWithDepth({}, mermaidAPI.defaultConfig);
assignWithDepth(config, { logLevel: 0 });
@@ -65,7 +100,7 @@ describe('when using mermaidAPI and ', function() {
it('should prevent changes to global defaults (direct)', function() {
let error = { message: '' };
try {
mermaidAPI.defaultConfig.logLevel = 0;
mermaidAPI.defaultConfig['logLevel'] = 0;
} catch(e) {
error = e;
}

View File

@@ -15,6 +15,7 @@ import {
import { logger } from './logger';
import { sanitizeUrl } from '@braintree/sanitize-url';
import common from './diagrams/common/common';
import cryptoRandomString from 'crypto-random-string';
// Effectively an enum of the supported curve types, accessible by name
const d3CurveTypes = {
@@ -396,6 +397,10 @@ export const generateId = () => {
);
};
export const random = options => {
return cryptoRandomString(options);
};
/**
* @function assignWithDepth
* Extends the functionality of {@link ObjectConstructor.assign} with the ability to merge arbitrary-depth objects
@@ -442,7 +447,7 @@ export const assignWithDepth = function(dst, src, config) {
(dst[key] === undefined || typeof dst[key] === 'object')
) {
if (dst[key] === undefined) {
dst[key] = {};
dst[key] = Array.isArray(src[key]) ? [] : {};
}
dst[key] = assignWithDepth(dst[key], src[key], { depth: depth - 1, clobber });
} else if (clobber || (typeof dst[key] !== 'object' && typeof src[key] !== 'object')) {
@@ -501,6 +506,10 @@ export const wrapLabel = (label, maxWidth, config) => {
{ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', margin: 0, joinWith: '<br/>' },
config
);
const cacheKey = `${label}-${maxWidth}-${JSON.stringify(config)}`;
if (wrapLabel[cacheKey]) {
return wrapLabel[cacheKey];
}
if (common.lineBreakRegex.test(label)) {
return label;
}
@@ -526,11 +535,17 @@ export const wrapLabel = (label, maxWidth, config) => {
completedLines.push(nextLine);
}
});
return completedLines.filter(line => line !== '').join(config.joinWith);
const result = completedLines.filter(line => line !== '').join(config.joinWith);
wrapLabel[cacheKey] = result;
return result;
};
const breakString = (word, maxWidth, hyphenCharacter = '-', config) => {
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', margin: 0 }, config);
const cacheKey = `${word}-${maxWidth}-${hyphenCharacter}-${JSON.stringify(config)}`;
if (breakString[cacheKey]) {
return breakString[cacheKey];
}
const characters = word.split('');
const lines = [];
let currentLine = '';
@@ -547,7 +562,9 @@ const breakString = (word, maxWidth, hyphenCharacter = '-', config) => {
currentLine = nextLine;
}
});
return { hyphenatedStrings: lines, remainingWord: currentLine };
const result = { hyphenatedStrings: lines, remainingWord: currentLine };
breakString[cacheKey] = result;
return result;
};
/**
@@ -598,7 +615,11 @@ export const calculateTextDimensions = function(text, config) {
);
const { fontSize, fontFamily, fontWeight } = config;
if (!text) {
return 0;
return { width: 0, height: 0 };
}
const cacheKey = `${text}-${JSON.stringify(config)}`;
if (calculateTextDimensions[cacheKey]) {
return calculateTextDimensions[cacheKey];
}
// We can't really know if the user supplied font family will render on the user agent;
@@ -638,7 +659,9 @@ export const calculateTextDimensions = function(text, config) {
g.remove();
// Adds some padding, so the text won't sit exactly within the actor's borders
return { width: Math.round(maxWidth), height: Math.round(height) };
const result = { width: Math.round(maxWidth), height: Math.round(height) };
calculateTextDimensions[cacheKey] = result;
return result;
};
export default {
@@ -657,5 +680,6 @@ export default {
formatUrl,
getStylesFromArray,
generateId,
random,
runFunc
};