Merge pull request #1523 from mermaid-js/feature/1483_long_messages

Directives, sequenceDiagram refactor, config normalization, many tweaks
This commit is contained in:
Knut Sveidqvist 2020-07-02 18:39:25 +02:00 committed by GitHub
commit b6d6d3c980
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 30260 additions and 5154 deletions

View File

@ -126,6 +126,29 @@ context('Sequence diagram', () => {
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
Bob-->A: Pasten !
`,
{logLevel: 0}
);
});
it('should wrap (inline) long actor descriptions', () => {
imgSnapshotTest(
`
sequenceDiagram
participant A as wrap: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
Bob-->A: Pasten !
`,
{logLevel: 0}
);
});
it('should wrap (directive) long actor descriptions', () => {
imgSnapshotTest(
`
%%{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
Bob-->A: Pasten !
`,
{}
);
@ -141,6 +164,17 @@ context('Sequence diagram', () => {
{}
);
});
it('should render long notes wrapped (inline) left of actor', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice->>Bob: Hola
Note left of Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render long notes right of actor', () => {
imgSnapshotTest(
`
@ -152,6 +186,17 @@ context('Sequence diagram', () => {
{}
);
});
it('should render long notes wrapped (inline) right of actor', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice->>Bob: Hola
Note right of Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render long notes over actor', () => {
imgSnapshotTest(
`
@ -163,6 +208,17 @@ context('Sequence diagram', () => {
{}
);
});
it('should render long notes wrapped (inline) over actor', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice->>Bob: Hola
Note over Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render long messages from an actor to the left to one to the right', () => {
imgSnapshotTest(
`
@ -173,6 +229,16 @@ context('Sequence diagram', () => {
{}
);
});
it('should render long messages wrapped (inline) from an actor to the left to one to the right', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice->>Bob:wrap:Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render long messages from an actor to the right to one to the left', () => {
imgSnapshotTest(
`
@ -183,6 +249,16 @@ context('Sequence diagram', () => {
{}
);
});
it('should render long messages wrapped (inline) from an actor to the right to one to the left', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice->>Bob: I'm short
Bob->>Alice:wrap: Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
`,
{}
);
});
});
context('background rects', () => {
it('should render a single and nested rects', () => {
@ -216,6 +292,69 @@ context('Sequence diagram', () => {
{}
);
});
it('should render a single and nested opt with long test overflowing', () => {
imgSnapshotTest(
`
sequenceDiagram
participant A
participant B
participant C
participant D
participant E
participant G
A ->>+ B: Task 1
opt this is an opt with a long title that will overflow
B ->>+ C: Task 2
C -->>- B: Return
end
A ->> D: Task 3
opt this is another opt with a long title that will overflow
D ->>+ E: Task 4
opt this is a nested opt with a long title that will overflow
E ->>+ G: Task 5
G -->>- E: Return
end
E ->> E: Task 6
end
D -->> A: Complete
`,
{}
);
});
it('should render a single and nested opt with long test wrapping', () => {
imgSnapshotTest(
`
%%{init: { 'config': { 'wrap': true } } }%%
sequenceDiagram
participant A
participant B
participant C
participant D
participant E
participant G
A ->>+ B: Task 1
opt this is an opt with a long title that will overflow
B ->>+ C: Task 2
C -->>- B: Return
end
A ->> D: Task 3
opt this is another opt with a long title that will overflow
D ->>+ E: Task 4
opt this is a nested opt with a long title that will overflow
E ->>+ G: Task 5
G -->>- E: Return
end
E ->> E: Task 6
end
D -->> A: Complete
`,
{}
);
});
it('should render rect around and inside loops', () => {
imgSnapshotTest(
`
@ -327,5 +466,68 @@ context('Sequence diagram', () => {
{}
);
});
it('should render dark theme from init directive and configure font size 24 font', () => {
imgSnapshotTest(
`
%%{init: {'theme': 'dark', 'config': {'fontSize': 24}}}%%
sequenceDiagram
Alice->>John: Hello John, how are you?
Alice->>John: John, can you hear me?
John-->>Alice: Hi Alice, I can hear you!
John-->>Alice: I feel great!
`,
{}
);
});
it('should render with wrapping enabled', () => {
imgSnapshotTest(
`
%%{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.
A->>John: John, can you hear me? If you are not available, we can talk later.
John-->>A: Hi Alice, I can hear you! I was finishing up an important meeting.
John-->>A: I feel great! I was not ignoring you. I am sorry you had to wait for a response.
`,
{}
);
});
it('should render with an init directive', () => {
imgSnapshotTest(
`%%{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!`,
{}
)
});
});
context('directives', () => {
it('should overide config with directive settings', () => {
imgSnapshotTest(
`
%%{init: { "config": { "mirrorActors": true }}}%%
sequenceDiagram
Alice->>Bob: I'm short
note left of Alice: config set to mirrorActors: false<br/>directive set to mirrorActors: true
Bob->>Alice: Short as well
`,
{ logLevel:0, sequence: { mirrorActors: false, noteFontSize: 18, noteFontFamily: 'Arial' } }
);
});
it('should overide config with directive settings', () => {
imgSnapshotTest(
`
%%{init: { "config": { "mirrorActors": false, "wrap": true }}}%%
sequenceDiagram
Alice->>Bob: I'm short
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' } }
);
});
});
});

3879
dist/mermaid.core.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

27310
dist/mermaid.js vendored

File diff suppressed because one or more lines are too long

2
dist/mermaid.js.map vendored

File diff suppressed because one or more lines are too long

19
dist/mermaid.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -706,14 +706,13 @@ mermaidAPI.initialize({
### Parameters
- `id` the id of the element to be rendered
- `_txt`
- `_txt` the graph definition
- `cb` callback which is called after rendering is finished with the svg code as inparam.
- `container` selector to element in which a div with the graph temporarily will be inserted. In one is
provided a hidden div will be inserted in the body of the page instead. The element will be removed when rendering is
completed.
- `txt` the graph definition
##
##
## mermaidAPI configuration defaults
@ -766,5 +765,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,36 +1,894 @@
let config = {};
import { assignWithDepth } from './utils';
import { logger } from './logger';
const setConf = function(cnf) {
// Top level initially mermaid, gflow, sequenceDiagram and gantt
const lvl1Keys = Object.keys(cnf);
for (let i = 0; i < lvl1Keys.length; i++) {
if (typeof cnf[lvl1Keys[i]] === 'object' && cnf[lvl1Keys[i]] != null) {
const lvl2Keys = Object.keys(cnf[lvl1Keys[i]]);
/**
* 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 , the CSS style sheet
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| Theme |Built in Themes| String | Optional | Values include, default, forest, dark, neutral, null|
***Notes:**To disable any pre-defined mermaid theme, use "null".
* <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,
for (let j = 0; j < lvl2Keys.length; j++) {
// logger.debug('Setting conf ', lvl1Keys[i], '-', lvl2Keys[j])
if (typeof config[lvl1Keys[i]] === 'undefined') {
config[lvl1Keys[i]] = {};
}
// logger.debug('Setting config: ' + lvl1Keys[i] + ' ' + lvl2Keys[j] + ' to ' + cnf[lvl1Keys[i]][lvl2Keys[j]])
config[lvl1Keys[i]][lvl2Keys[j]] = cnf[lvl1Keys[i]][lvl2Keys[j]];
}
} else {
config[lvl1Keys[i]] = cnf[lvl1Keys[i]];
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*|fontFamily | specifies the font to be used in the rendered diagrams| String | Required | Verdana, Arial, Trebuchet MS,|
*
***notes: Default value is \\"trebuchet ms\\".
*/
fontFamily: '"trebuchet ms", verdana, arial;',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| logLevel |This option decides the amount of logging to be used.| String | Required | 1, 2, 3, 4, 5 |
*
*
***Notes:**
*- debug: 1.
*- info: 2.
*- warn: 3.
*- error: 4.
*- fatal: 5(default).
*/
logLevel: 5,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| securitylevel | Level of trust for parsed diagram|String | Required | Strict, Loose |
*
***Notes:
*- **strict**: (**default**) tags in text are encoded, click functionality is disabeled
*- **loose**: tags in text are allowed, click functionality is enabled
*/
securityLevel: 'strict',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| startOnLoad| Dictates whether mermaind starts on Page load | Boolean | Required | True, False |
*
***Notes:**
***Default value: true**
*/
startOnLoad: true,
/**
*| Parameter | Description |Type | Required |Values|
*| --- | --- | --- | --- | --- |
*| arrowMarkerAbsolute | Controls whether or arrow markers in html code are absolute paths or anchors | Boolean | Required | True, False |
*
*
*## Notes**: 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: {
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| htmlLabels | Flag for setting whether or not a html tag should be used for rendering labels on the edges. | Boolean| Required | True, False|
*
***Notes: Default value: true**.
*/
htmlLabels: true,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| nodeSpacing | Defines the spacing between nodes on the same level | Integer| Required | Any positive Numbers |
*
***Notes:
*Pertains to horizontal spacing for TB (top to bottom) or BT (bottom to top) graphs, and the vertical spacing for LR as well as RL graphs.**
***Default value 50**.
*/
nodeSpacing: 50,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| rankSpacing | Defines the spacing between nodes on different levels | Integer | Required| Any Positive Numbers |
*
***Notes: pertains to vertical spacing for TB (top to bottom) or BT (bottom to top), and the horizontal spacing for LR as well as RL graphs.
***Default value 50**.
*/
rankSpacing: 50,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| curve | Defines how mermaid renders curves for flowcharts. | String | Required | Basis, Linear, Cardinal|
*
***Notes:
*Default Vaue: Linear**
*/
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: {
/**
* widt of the activation rect
* **Default value 10**.
*/
activationWidth: 10,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| diagramMarginX | margin to the right and left of the sequence diagram | Integer | Required | Any Positive Values |
*
***Notes:**
***Default value 50**.
*/
diagramMarginX: 50,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| diagramMarginY | Margin to the over and under the sequence diagram | Integer | Required | Any Positive Values|
*
***Notes:**
***Default value 10**.
*/
diagramMarginY: 10,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| actorMargin | Margin between actors. | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 50**.
*/
actorMargin: 50,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| width | Width of actor boxes | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 150**.
*/
width: 150,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| height | Height of actor boxes | Integer | Required | Any Positive Value|
*
***Notes:**
***Default value 65**..
*/
height: 65,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| boxMargin | Margin around loop boxes | Integer | Required | Any Positive Value |
*
***Notes:**
*
***Default value 10**.
*/
boxMargin: 10,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| boxTextMargin| margin around the text in loop/alt/opt boxes | Integer | Required| Any Positive Value|
*
***Notes:**
*
***Default value 5**.
*/
boxTextMargin: 5,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| noteMargin | margin around notes. | Integer | Required | Any Positive Value |
*
***Notes:**
*
***Default value 10**.
*/
noteMargin: 10,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| messageMargin | Space between messages. | Integer | Required | Any Positive Value |
*
***Notes:**
*
*Space between messages.
***Default value 35**.
*/
messageMargin: 35,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| messageAlign | Multiline message alignment | Integer | Required | left, center, right |
*
***Notes:**center **default**
*/
messageAlign: 'center',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| mirrorActors | mirror actors under diagram. | Boolean| Required | True, False |
*
***Notes:**
*
***Default value true**.
*/
mirrorActors: true,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| bottomMarginAdj | Prolongs the edge of the diagram downwards. | Integer | Required | Any Positive Value |
*
***Notes:**Depending on css styling this might need adjustment.
***Default value 1**.
*/
bottomMarginAdj: 1,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| useMaxWidth | See Notes | Boolean | Required | True, False |
*
***Notes:**
*when this flag is set to true, the height and width is set to 100% and is then scaling with the
*available space. If set to false, the absolute space required is used.
***Default value: True**.
*/
useMaxWidth: true,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| rightAngles | display curve arrows as right angles| Boolean | Required | True, False |
*
***Notes:**
*
*This will display arrows that start and begin at the same node as right angles, rather than a curve
***Default value false**.
*/
rightAngles: false,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| showSequenceNumbers | This will show the node numbers | Boolean | Required | True, False |
*
***Notes:**
***Default value false**.
*/
showSequenceNumbers: false,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| actorFontSize| This sets the font size of the actor's description | Integer | Require | Any Positive Value |
*
***Notes:**
***Default value 14**..
*/
actorFontSize: 14,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| actorFontFamily |This sets the font family of the actor's description | 3 | 4 | Open-Sans, Sans-Serif |
*
***Notes:**
***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,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| noteFontSize |This sets the font size of actor-attached notes. | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 14**..
*/
noteFontSize: 14,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| noteFontFamily| This sets the font family of actor-attached notes. | String | Required | trebuchet ms, verdana, arial |
*
***Notes:**
***Default value: trebuchet ms **.
*/
noteFontFamily: '"trebuchet ms", verdana, arial',
/**
* This sets the font weight of the note's description
* **Default value 400.
*/
noteFontWeight: 400,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| noteAlign | This sets the text alignment of actor-attached notes. | string | required | left, center, right|
*
***Notes:**
***Default value center**.
*/
noteAlign: 'center',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| messageFontSize | This sets the font size of actor messages. | Integer | Required | Any Positive Number |
*
***Notes:**
***Default value 16**.
*/
messageFontSize: 16,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| messageFontFamily | This sets the font family of actor messages. | String| Required | trebuchet ms", verdana, aria |
*
***Notes:**
***Default value:"trebuchet ms**.
*/
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 10.
*/
wrapPadding: 10,
/**
* 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: function() {
return {
fontFamily: this.messageFontFamily,
fontSize: this.messageFontSize,
fontWeight: this.messageFontWeight
};
},
noteFont: function() {
return {
fontFamily: this.noteFontFamily,
fontSize: this.noteFontSize,
fontWeight: this.noteFontWeight
};
},
actorFont: function() {
return {
fontFamily: this.actorFontFamily,
fontSize: this.actorFontSize,
fontWeight: this.actorFontWeight
};
}
},
/**
* The object containing configurations specific for gantt diagrams*
*/
gantt: {
/**
*### titleTopMargin
*
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| titleTopMargin | Margin top for the text over the gantt diagram | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 25**.
*/
titleTopMargin: 25,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| barHeight | The height of the bars in the graph | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 20**.
*/
barHeight: 20,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| barGap | The margin between the different activities in the gantt diagram. | Integer | Optional |Any Positive Value |
*
***Notes:**
***Default value 4**.
*/
barGap: 4,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| topPadding | Margin between title and gantt diagram and between axis and gantt diagram. | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 50**.
*/
topPadding: 50,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| leftPadding | The space allocated for the section name to the left of the activities. | Integer| Required | Any Positive Value |
*
***Notes:**
***Default value 75**.
*/
leftPadding: 75,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| gridLineStartPadding | Vertical starting position of the grid lines. | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 35**.
*/
gridLineStartPadding: 35,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| fontSize | Font size| Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 11**.
*/
fontSize: 11,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| fontFamily | font Family | string | required |"Open-Sans", "sans-serif" |
*
***Notes:**
*
***Default value '"Open-Sans", "sans-serif"'**.
*/
fontFamily: '"Open-Sans", "sans-serif"',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| numberSectionStyles | The number of alternating section styles | Integer | 4 | Any Positive Value |
*
***Notes:**
***Default value 4**.
*/
numberSectionStyles: 4,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| axisFormat | Datetime format of the axis. | 3 | Required | Date in yy-mm-dd |
*
***Notes:**
*
* 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: {
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| diagramMarginX | margin to the right and left of the sequence diagram | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 50**.
*/
diagramMarginX: 50,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| diagramMarginY | margin to the over and under the sequence diagram. | Integer | Required | Any Positive Value|
*
***Notes:**
***Default value 10**..
*/
diagramMarginY: 10,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| actorMargin | Margin between actors. | Integer | Required | Any Positive Value|
*
***Notes:**
***Default value 50**.
*/
actorMargin: 50,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| width | Width of actor boxes | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 150**.
*/
width: 150,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| height | Height of actor boxes | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 65**.
*/
height: 65,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| boxMargin | Margin around loop boxes | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 10**.
*/
boxMargin: 10,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| boxTextMargin | margin around the text in loop/alt/opt boxes | Integer | Required | Any Positive Value |
*
***Notes:**
*/
boxTextMargin: 5,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| noteMargin | margin around notes. | Integer | Required | Any Positive Value |
*
***Notes:**
***Default value 10**.
*/
noteMargin: 10,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| messageMargin |Space between messages. | Integer | Required | Any Positive Value |
*
***Notes:**
*
*Space between messages.
***Default value 35**.
*/
messageMargin: 35,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| messageAlign |Multiline message alignment | 3 | 4 | left, center, right |
*
***Notes:**default:center**
*/
messageAlign: 'center',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| bottomMarginAdj | Prolongs the edge of the diagram downwards. | Integer | 4 | Any Positive Value |
*
***Notes:**Depending on css styling this might need adjustment.
***Default value 1**.
*/
bottomMarginAdj: 1,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| useMaxWidth | See notes | Boolean | 4 | True, False |
*
***Notes:**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,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| rightAngles | Curved Arrows become Right Angles, | 3 | 4 | True, False |
*
***Notes:**This will display arrows that start and begin at the same node as right angles, rather than a curves
***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: {
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| diagramPadding | amount of padding around the diagram as a whole | Integer | Required | Any Positive Value |
*
***Notes:**The amount of padding around the diagram as a whole so that embedded diagrams have margins, expressed in pixels
***Default value: 20**.
*/
diagramPadding: 20,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| layoutDirection | Directional bias for layout of entities. | String | Required | "TB", "BT","LR","RL" |
*
***Notes:**
*'TB' for Top-Bottom, 'BT'for Bottom-Top, 'LR' for Left-Right, or 'RL' for Right to Left.
* T = top, B = bottom, L = left, and R = right.
***Default value: TB **.
*/
layoutDirection: 'TB',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| minEntityWidth | The mimimum width of an entity box, | Integer | Required| Any Positive Value |
*
***Notes:**expressed in pixels
***Default value: 100**.
*/
minEntityWidth: 100,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| minEntityHeight| The minimum height of an entity box, | Integer | 4 | Any Positive Value |
*
***Notes:**expressed in pixels
***Default value: 75 **
*/
minEntityHeight: 75,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| entityPadding|minimum internal padding betweentext in box and box borders| Integer | 4 | Any Positive Value |
*
***Notes:**The minimum internal padding betweentext in an entity box and the enclosing box borders, expressed in pixels.
***Default value: 15 **
*/
entityPadding: 15,
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| stroke | Stroke color of box edges and lines | String | 4 | Any recognized color |
***Default value: gray **
*/
stroke: 'gray',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| fill | Fill color of entity boxes | String | 4 | Any recognized color |
*
***Notes:**
***Default value:'honeydew'**
*/
fill: 'honeydew',
/**
*| Parameter | Description |Type | Required | Values|
*| --- | --- | --- | --- | --- |
*| fontSize| Font Size in pixels| Integer | | Any Positive Value |
*
***Notes:**Font size (expressed as an integer representing a number of pixels)
***Default value: 12 **
*/
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 => {
setConf(conf);
sanitize(conf);
assignWithDepth(currentConfig, conf);
return getConfig();
};
/**
* 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 });
};
export const getConfig = () => config;
const configApi = {
const configApi = Object.freeze({
sanitize,
setSiteConfig,
getSiteConfig,
setConfig,
getConfig
// get conf() {
// return config;
// }
};
getConfig,
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

@ -27,6 +27,14 @@ export const sanitizeText = (text, config) => {
export const lineBreakRegex = /<br\s*\/?>/gi;
export const hasBreaks = text => {
return /<br\s*[/]?>/gi.test(text);
};
export const splitBreaks = text => {
return text.split(/<br\s*[/]?>/gi);
};
const breakToPlaceholder = s => {
return s.replace(lineBreakRegex, '#br#');
};
@ -38,5 +46,7 @@ const placeholderToBreak = s => {
export default {
getRows,
sanitizeText,
hasBreaks,
splitBreaks,
lineBreakRegex
};

View File

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

View File

@ -1,6 +1,7 @@
import { logger } from '../../logger';
import { getConfig, setConfig } from '../../config';
import mermaidAPI from '../../mermaidAPI';
import configApi from '../../config';
import common from '../common/common';
import { logger } from '../../logger';
let prevActor = undefined;
let actors = {};
@ -10,58 +11,9 @@ let title = '';
let titleWrapped = false;
let sequenceNumbersEnabled = false;
let wrapEnabled = false;
let configUpdated = 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':
mermaidAPI.initialize(directive.args);
break;
case 'config':
updateConfig(directive.args);
break;
case 'wrap':
case 'nowrap':
wrapEnabled = directive.type === 'wrap';
break;
default:
logger.warn(
`Unrecognized directive: source: '%%{${directive.type}: ${directive.args}}%%`,
directive
);
break;
}
export const parseDirective = function(statement, context, type) {
mermaidAPI.parseDirective(statement, context, type);
};
export const addActor = function(id, name, description) {
@ -77,7 +29,7 @@ export const addActor = function(id, name, description) {
actors[id] = {
name: name,
description: description.text,
wrap: (description.wrap === null && autoWrap()) || !!description.wrap,
wrap: (description.wrap === undefined && autoWrap()) || !!description.wrap,
prevActor: prevActor
};
if (prevActor && actors[prevActor]) {
@ -111,28 +63,19 @@ export const addMessage = function(idFrom, idTo, message, answer) {
from: idFrom,
to: idTo,
message: message.text,
wrap: (message.wrap === null && autoWrap()) || !!message.wrap,
wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap,
answer: answer
});
};
export const addSignal = function(idFrom, idTo, message = { text: null, wrap: null }, messageType) {
logger.debug(
'Adding message from=' +
idFrom +
' to=' +
idTo +
' message=' +
message.text +
' wrap=' +
message.wrap +
' type=' +
messageType
);
export const addSignal = function(
idFrom,
idTo,
message = { text: undefined, wrap: undefined },
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 + ')');
@ -150,7 +93,7 @@ export const addSignal = function(idFrom, idTo, message = { text: null, wrap: nu
from: idFrom,
to: idTo,
message: message.text,
wrap: (message.wrap === null && autoWrap()) || !!message.wrap,
wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap,
type: messageType
});
return true;
@ -180,12 +123,8 @@ export const enableSequenceNumbers = function() {
};
export const showSequenceNumbers = () => sequenceNumbersEnabled;
export const enableWrap = function() {
wrapEnabled = true;
};
export const disableWrap = function() {
wrapEnabled = false;
export const setWrap = function(wrapSetting) {
wrapEnabled = wrapSetting;
};
export const autoWrap = () => wrapEnabled;
@ -193,22 +132,23 @@ export const autoWrap = () => wrapEnabled;
export const clear = function() {
actors = {};
messages = [];
configUpdated = false;
};
export const parseMessage = function(str) {
const _str = str.trim();
return {
const message = {
text: _str.replace(/^[:]?(?:no)?wrap:/, '').trim(),
wrap:
_str.match(/^[:]?(?:no)?wrap:/) === null
? autoWrap()
? common.hasBreaks(_str) || autoWrap()
: _str.match(/^[:]?wrap:/) !== null
? true
: _str.match(/^[:]?nowrap:/) !== null
? false
: autoWrap()
};
logger.debug('parseMessage:', message);
return message;
};
export const LINETYPE = {
@ -251,7 +191,7 @@ export const addNote = function(actor, placement, message) {
actor: actor,
placement: placement,
message: message.text,
wrap: (message.wrap === null && autoWrap()) || !!message.wrap
wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap
};
// Coerce actor into a [to, from, ...] array
@ -262,7 +202,7 @@ export const addNote = function(actor, placement, message) {
from: actors[0],
to: actors[1],
message: message.text,
wrap: (message.wrap === null && autoWrap()) || !!message.wrap,
wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap,
type: LINETYPE.NOTE,
placement: placement
});
@ -270,20 +210,7 @@ export const addNote = function(actor, placement, message) {
export const setTitle = function(titleWrap) {
title = titleWrap.text;
titleWrapped = (titleWrap.wrap === null && autoWrap()) || !!titleWrap.wrap;
};
export const updateConfig = function(config = getConfig()) {
try {
setConfig(config);
configUpdated = true;
} catch (error) {
logger.error('Error: unable to parse config');
}
};
export const hasConfigChange = function() {
return configUpdated;
titleWrapped = (titleWrap.wrap === undefined && autoWrap()) || !!titleWrap.wrap;
};
export const apply = function(param) {
@ -355,20 +282,17 @@ export default {
addActor,
addMessage,
addSignal,
enableWrap,
disableWrap,
autoWrap,
setWrap,
enableSequenceNumbers,
showSequenceNumbers,
autoWrap,
getMessages,
getActors,
getActor,
getActorKeys,
getTitle,
parseDirective,
hasConfigChange,
getConfig,
updateConfig,
getConfig: () => configApi.getConfig().sequence,
getTitleWrapped,
clear,
parseMessage,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -19,27 +19,133 @@ export const drawRect = function(elem, rectData) {
};
export const drawText = function(elem, textData) {
// Remove and ignore br:s
const nText = textData.text.replace(common.lineBreakRegex, ' ');
let prevTextHeight = 0,
textHeight = 0;
const lines = textData.wrap
? textData.text.split(common.lineBreakRegex)
: [textData.text.replace(common.lineBreakRegex, ' ')];
const textElem = elem.append('text');
textElem.attr('x', textData.x);
textElem.attr('y', textData.y);
textElem.style('text-anchor', textData.anchor);
textElem.style('font-family', textData.fontFamily);
textElem.style('font-size', textData.fontSize);
textElem.style('font-weight', textData.fontWeight);
textElem.attr('fill', textData.fill);
if (typeof textData.class !== 'undefined') {
textElem.attr('class', textData.class);
let textElems = [];
let dy = 0;
let yfunc = () => textData.y;
if (
typeof textData.valign !== 'undefined' &&
typeof textData.textMargin !== 'undefined' &&
textData.textMargin > 0
) {
switch (textData.valign) {
case 'top':
case 'start':
yfunc = () => Math.round(textData.y + textData.textMargin);
break;
case 'middle':
case 'center':
yfunc = () =>
Math.round(textData.y + (prevTextHeight + textHeight + textData.textMargin) / 2);
break;
case 'bottom':
case 'end':
yfunc = () =>
Math.round(
textData.y +
(prevTextHeight + textHeight + 2 * textData.textMargin) -
textData.textMargin
);
break;
}
}
if (
typeof textData.anchor !== 'undefined' &&
typeof textData.textMargin !== 'undefined' &&
typeof textData.width !== 'undefined'
) {
switch (textData.anchor) {
case 'left':
case 'start':
textData.x = Math.round(textData.x + textData.textMargin);
textData.anchor = 'start';
textData.dominantBaseline = 'text-after-edge';
textData.alignmentBaseline = 'middle';
break;
case 'middle':
case 'center':
textData.x = Math.round(textData.x + textData.width / 2);
textData.anchor = 'middle';
textData.dominantBaseline = 'middle';
textData.alignmentBaseline = 'middle';
break;
case 'right':
case 'end':
textData.x = Math.round(textData.x + textData.width - textData.textMargin);
textData.anchor = 'end';
textData.dominantBaseline = 'text-before-edge';
textData.alignmentBaseline = 'middle';
break;
}
}
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
if (
typeof textData.textMargin !== 'undefined' &&
textData.textMargin === 0 &&
typeof textData.fontSize !== 'undefined'
) {
dy = i * textData.fontSize;
}
const textElem = elem.append('text');
textElem.attr('x', textData.x);
textElem.attr('y', yfunc());
if (typeof textData.anchor !== 'undefined') {
textElem
.attr('text-anchor', textData.anchor)
.attr('dominant-baseline', textData.dominantBaseline)
.attr('alignment-baseline', textData.alignmentBaseline);
}
if (typeof textData.fontFamily !== 'undefined') {
textElem.style('font-family', textData.fontFamily);
}
if (typeof textData.fontSize !== 'undefined') {
textElem.style('font-size', textData.fontSize);
}
if (typeof textData.fontWeight !== 'undefined') {
textElem.style('font-weight', textData.fontWeight);
}
if (typeof textData.fill !== 'undefined') {
textElem.attr('fill', textData.fill);
}
if (typeof textData.class !== 'undefined') {
textElem.attr('class', textData.class);
}
if (typeof textData.dy !== 'undefined') {
textElem.attr('dy', textData.dy);
} else if (dy !== 0) {
textElem.attr('dy', dy);
}
if (textData.tspan) {
const span = textElem.append('tspan');
span.attr('x', textData.x);
if (typeof textData.fill !== 'undefined') {
span.attr('fill', textData.fill);
}
span.text(line);
} else {
textElem.text(line);
}
if (
typeof textData.valign !== 'undefined' &&
typeof textData.textMargin !== 'undefined' &&
textData.textMargin > 0
) {
textHeight += (textElem._groups || textElem)[0][0].getBBox().height;
prevTextHeight = textHeight;
}
textElems.push(textElem);
}
const span = textElem.append('tspan');
span.attr('x', textData.x + textData.textMargin * 2);
span.attr('fill', textData.fill);
span.text(nText);
return textElem;
return textElems;
};
export const drawLabel = function(elem, txtObject) {
@ -67,17 +173,18 @@ export const drawLabel = function(elem, txtObject) {
);
}
const polygon = elem.append('polygon');
polygon.attr('points', genPoints(txtObject.x, txtObject.y, 50, 20, 7));
polygon.attr('points', genPoints(txtObject.x, txtObject.y, txtObject.width, txtObject.height, 7));
polygon.attr('class', 'labelBox');
txtObject.y = txtObject.y + txtObject.labelMargin;
txtObject.x = txtObject.x + 0.5 * txtObject.labelMargin;
txtObject.y = txtObject.y + txtObject.height / 2;
drawText(elem, txtObject);
return polygon;
};
let actorCnt = -1;
/**
* Draws an actor in the diagram with the attaced line
* Draws an actor in the diagram with the attached line
* @param elem - The diagram we'll draw to.
* @param actor - The actor to draw.
* @param conf - drawText implementation discriminator object
@ -147,11 +254,20 @@ 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 {
boxMargin,
boxTextMargin,
labelBoxHeight,
labelBoxWidth,
messageFontFamily: fontFamily,
messageFontSize: fontSize,
messageFontWeight: fontWeight
} = conf;
const g = elem.append('g');
const drawLoopLine = function(startx, starty, stopx, stopy) {
return g
@ -162,56 +278,77 @@ 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, bounds.stopx, item).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 minSize =
Math.round((3 * conf.fontSize) / 4) < 10 ? conf.fontSize : Math.round((3 * conf.fontSize) / 4);
let txt = getTextObj();
txt.text = labelText;
txt.x = bounds.startx;
txt.y = bounds.starty;
txt.labelMargin = 1.5 * 10; // This is the small box that says "loop"
txt.fontFamily = conf.fontFamily;
txt.fontSize = minSize;
txt.fontWeight = conf.fontWeight;
txt.class = 'labelText'; // Its size & position are fixed.
txt.x = loopModel.startx;
txt.y = loopModel.starty;
txt.fontFamily = fontFamily;
txt.fontSize = fontSize;
txt.fontWeight = fontWeight;
txt.anchor = 'middle';
txt.valign = 'middle';
txt.tspan = false;
txt.width = labelBoxWidth || 50;
txt.height = labelBoxHeight || 20;
txt.textMargin = boxTextMargin;
txt.class = 'labelText';
drawLabel(g, txt);
txt = getTextObj();
txt.text = '[ ' + bounds.title + ' ]';
txt.x = bounds.startx + (bounds.stopx - bounds.startx) / 2;
txt.y = bounds.starty + 1.5 * conf.boxMargin;
txt.text = loopModel.title;
txt.x = loopModel.startx + labelBoxWidth / 2 + (loopModel.stopx - loopModel.startx) / 2;
txt.y = loopModel.starty + boxMargin + boxTextMargin;
txt.anchor = 'middle';
txt.valign = 'middle';
txt.textMargin = boxTextMargin;
txt.class = 'loopText';
txt.fontFamily = conf.fontFamily;
txt.fontSize = minSize;
txt.fontWeight = conf.fontWeight;
txt.fontFamily = fontFamily;
txt.fontSize = fontSize;
txt.fontWeight = fontWeight;
txt.wrap = true;
let textElem = drawText(g, txt);
let textHeight = (textElem._groups || textElem)[0][0].getBBox().height;
if (typeof bounds.sectionTitles !== 'undefined') {
bounds.sectionTitles.forEach(function(item, idx) {
if (item !== '') {
txt.text = '[ ' + item + ' ]';
txt.y = bounds.sections[idx] + 1.5 * conf.boxMargin;
if (typeof loopModel.sectionTitles !== 'undefined') {
loopModel.sectionTitles.forEach(function(item, idx) {
if (item.message) {
txt.text = item.message;
txt.x = loopModel.startx + (loopModel.stopx - loopModel.startx) / 2;
txt.y = loopModel.sections[idx].y + boxMargin + boxTextMargin;
txt.class = 'loopText';
txt.anchor = 'middle';
txt.valign = 'middle';
txt.tspan = false;
txt.fontFamily = fontFamily;
txt.fontSize = fontSize;
txt.fontWeight = fontWeight;
txt.wrap = loopModel.wrap;
textElem = drawText(g, txt);
textHeight += (textElem._groups || textElem)[0][0].getBBox().height;
let sectionHeight = Math.round(
textElem
.map(te => (te._groups || te)[0][0].getBBox().height)
.reduce((acc, curr) => acc + curr)
);
loopModel.sections[idx].height += sectionHeight - (boxMargin + boxTextMargin);
}
});
}
return textHeight + 4;
loopModel.height = Math.round(loopModel.stopy - loopModel.starty);
return g;
};
/**
@ -300,23 +437,24 @@ export const insertArrowCrossHead = function(elem) {
};
export const getTextObj = function() {
const txt = {
return {
x: 0,
y: 0,
fill: undefined,
'text-anchor': 'start',
anchor: undefined,
style: '#666',
width: 100,
height: 100,
width: undefined,
height: undefined,
textMargin: 0,
rx: 0,
ry: 0
ry: 0,
tspan: true,
valign: undefined
};
return txt;
};
export const getNoteRect = function() {
const rect = {
return {
x: 0,
y: 0,
fill: '#EDF2AE',
@ -327,7 +465,6 @@ export const getNoteRect = function() {
rx: 0,
ry: 0
};
return rect;
};
const _drawTextCandidateFunc = (function() {

View File

@ -6,6 +6,8 @@
import decode from 'entity-decode/browser';
import mermaidAPI from './mermaidAPI';
import { logger } from './logger';
import utils from './utils';
/**
* ## init
* Function that goes through the document to find the chart definitions in there and render them.
@ -29,7 +31,7 @@ import { logger } from './logger';
*/
const init = function() {
const conf = mermaidAPI.getConfig();
logger.debug('Starting rendering diagrams');
// console.log('Starting rendering diagrams (init) - mermaid.init');
let nodes;
if (arguments.length >= 2) {
/*! sequence config was passed as #1 */
@ -98,6 +100,11 @@ const init = function() {
.trim()
.replace(/<br\s*\/?>/gi, '<br/>');
const init = utils.detectInit(txt);
if (init) {
logger.debug('Detected early reinit: ', init);
}
try {
mermaidAPI.render(
id,
@ -122,6 +129,7 @@ const init = function() {
};
const initialize = function(config) {
mermaidAPI.reset();
if (typeof config.mermaid !== 'undefined') {
if (typeof config.mermaid.startOnLoad !== 'undefined') {
mermaid.startOnLoad = config.mermaid.startOnLoad;
@ -131,7 +139,6 @@ const initialize = function(config) {
}
}
mermaidAPI.initialize(config);
logger.debug('Initializing mermaid ');
};
/**

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,12 @@
/* eslint-env jasmine */
import mermaidAPI from './mermaidAPI';
import { assignWithDepth } from './utils';
describe('when using mermaidAPI and ', function() {
describe('doing initialize ', function() {
beforeEach(function() {
document.body.innerHTML = '';
mermaidAPI.globalReset();
});
it('should copy a literal into the configuration', function() {
@ -26,13 +28,99 @@ describe('when using mermaidAPI and ', function() {
};
mermaidAPI.initialize({ testObject: object });
let config = mermaidAPI.getConfig();
expect(config.testObject.test1).toBe(1);
mermaidAPI.initialize({ testObject: { test3: true } });
const config = mermaidAPI.getConfig();
config = mermaidAPI.getConfig();
expect(config.testObject.test1).toBe(1);
expect(config.testObject.test2).toBe(false);
expect(config.testObject.test3).toBe(true);
});
it('should reset mermaid config to global defaults', function() {
let config = {
logLevel: 0,
securityLevel: 'loose'
};
mermaidAPI.initialize(config);
expect(mermaidAPI.getConfig().logLevel).toBe(0);
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 });
let error = { message: '' };
try {
mermaidAPI['defaultConfig'] = config;
} catch(e) {
error = e;
}
expect(error.message).toBe('Cannot assign to read only property \'defaultConfig\' of object \'#<Object>\'');
expect(mermaidAPI.defaultConfig['logLevel']).toBe(5);
});
it('should prevent changes to global defaults (direct)', function() {
let error = { message: '' };
try {
mermaidAPI.defaultConfig['logLevel'] = 0;
} catch(e) {
error = e;
}
expect(error.message).toBe('Cannot assign to read only property \'logLevel\' of object \'#<Object>\'');
expect(mermaidAPI.defaultConfig['logLevel']).toBe(5);
});
it('should prevent sneaky changes to global defaults (assignWithDepth)', function() {
let config = {
logLevel: 0
};
let error = { message: '' };
try {
assignWithDepth(mermaidAPI.defaultConfig, config);
} catch(e) {
error = e;
}
expect(error.message).toBe('Cannot assign to read only property \'logLevel\' of object \'#<Object>\'');
expect(mermaidAPI.defaultConfig['logLevel']).toBe(5);
});
});
describe('checking validity of input ', function() {
it('it should throw for an invalid definiton', function() {

View File

@ -10,7 +10,7 @@ $arrowheadColor: $mainContrastColor;
/* Flowchart variables */
$nodeBkg: $mainBkg;
$nodeBorder: purple;
$nodeBorder: $border1;
$clusterBkg: $secondBkg;
$clusterBorder: $border2;
$defaultLinkColor: $lineColor;
@ -34,7 +34,7 @@ $noteBkgColor: #fff5ad;
$noteTextColor: $mainBkg;
$activationBorderColor: $border1;
$activationBkgColor: $secondBkg;
$sequenceNumberColor: white;
$sequenceNumberColor: black;
/* Gantt chart variables */