Merge branch 'develop' into sidv/fixTripleParsing

* develop: (59 commits)
  fix!(deps): fix zenuml style leakage. update @zenuml/core to ^3.0.6 to fix the style leakage.
  Update selectSvgElement.ts
  create `Group` type
  Add specialChars in textNoTagsToken, alphaNumToken
  Return Unicode Text to idStringToken definition
  Add underscore to unit test on special Chars
  Revert to old docs concerning quotations marks in string
  Refactor unit tests and remove repetition
  Correct idStringToken definition to include all individual special tokens
  Add unit tests for node ids with special Chars
  Create lychee.toml
  create `selectSvgElement`
  change `svgElem` to `SVG` in `configureSvgSize`
  add `configureSvgSize` in infoRenderer
  run docs:build
  remove info sandbox test case
  Remove replaceAll method in addLink
  Modify HREF token regex to contain space
  Add unit tests for stange node names
  Remove escaped quotes with backslash feature
  ...
This commit is contained in:
Sidharth Vinod
2023-08-08 13:37:02 +05:30
21 changed files with 702 additions and 513 deletions

44
.github/lychee.toml vendored Normal file
View File

@@ -0,0 +1,44 @@
############################# Display #############################
# Verbose program output
# Accepts log level: "error", "warn", "info", "debug", "trace"
verbose = "debug"
# Don't show interactive progress bar while checking links.
no_progress = true
############################# Cache ###############################
# Enable link caching. This can be helpful to avoid checking the same links on
# multiple runs.
cache = true
# Discard all cached requests older than this duration.
max_cache_age = "1d"
############################# Requests ############################
# Comma-separated list of accepted status codes for valid links.
accept = [200, 429]
############################# Exclusions ##########################
# Exclude URLs and mail addresses from checking (supports regex).
exclude = [
# Network error: Forbidden
"https://codepen.io",
# Timeout error, maybe Twitter has anti-bot defenses against GitHub's CI servers?
"https://twitter.com/mermaidjs_",
# Don't check files that are generated during the build via `pnpm docs:code`
'packages/mermaid/src/docs/config/setup/*',
# Ignore slack invite
"https://join.slack.com/"
]
# Exclude all private IPs from checking.
# Equivalent to setting `exclude_private`, `exclude_link_local`, and
# `exclude_loopback` to true.
exclude_all_private = true

View File

@@ -20,7 +20,7 @@ on:
- cron: '30 8 * * *' - cron: '30 8 * * *'
jobs: jobs:
linkChecker: link-checker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
# lychee only uses the GITHUB_TOKEN to avoid rate-limiting # lychee only uses the GITHUB_TOKEN to avoid rate-limiting
@@ -39,10 +39,7 @@ jobs:
uses: lycheeverse/lychee-action@v1.8.0 uses: lycheeverse/lychee-action@v1.8.0
with: with:
args: >- args: >-
--verbose --config .github/lychee.toml
--no-progress
--cache
--max-cache-age 1d
packages/mermaid/src/docs/**/*.md packages/mermaid/src/docs/**/*.md
README.md README.md
README.zh-CN.md README.zh-CN.md

View File

@@ -1,17 +0,0 @@
# These links are ignored by our link checker https://github.com/lycheeverse/lychee
# The file allows you to list multiple regular expressions for exclusion (one pattern per line).
# Network error: Forbidden
https://codepen.io
# Timeout error, maybe Twitter has anti-bot defenses against GitHub's CI servers?
https://twitter.com/mermaidjs_
# Don't check files that are generated during the build via `pnpm docs:code`
packages/mermaid/src/docs/config/setup/*
# Ignore localhost
http://localhost:3333/
# Ignore slack invite
https://join.slack.com/

View File

@@ -891,4 +891,27 @@ graph TD
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
); );
}); });
it('66: apply class called default on node called default', () => {
imgSnapshotTest(
`
graph TD
classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff
hello --> default
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('67: should be able to style default node independently', () => {
imgSnapshotTest(
`
flowchart TD
classDef default fill:#a34
hello --> default
style default stroke:#000,stroke-width:4px
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
}); });

View File

@@ -96,7 +96,7 @@ mermaid.initialize(config);
#### Defined in #### Defined in
[mermaidAPI.ts:666](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L666) [mermaidAPI.ts:668](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L668)
## Functions ## Functions
@@ -127,7 +127,7 @@ Return the last node appended
#### Defined in #### Defined in
[mermaidAPI.ts:307](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L307) [mermaidAPI.ts:309](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L309)
--- ---
@@ -320,4 +320,4 @@ Remove any existing elements from the given document
#### Defined in #### Defined in
[mermaidAPI.ts:357](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L357) [mermaidAPI.ts:359](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L359)

View File

@@ -608,12 +608,12 @@ It is possible to escape characters using the syntax exemplified here.
```mermaid-example ```mermaid-example
flowchart LR flowchart LR
A["A double quote:#quot;"] -->B["A dec char:#9829;"] A["A double quote:#quot;"] --> B["A dec char:#9829;"]
``` ```
```mermaid ```mermaid
flowchart LR flowchart LR
A["A double quote:#quot;"] -->B["A dec char:#9829;"] A["A double quote:#quot;"] --> B["A dec char:#9829;"]
``` ```
Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names.

View File

@@ -33,7 +33,7 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@zenuml/core": "^3.0.3" "@zenuml/core": "^3.0.6"
}, },
"devDependencies": { "devDependencies": {
"mermaid": "workspace:^" "mermaid": "workspace:^"

View File

@@ -79,8 +79,10 @@ export type DrawDefinition = (
*/ */
export type ParseDirectiveDefinition = (statement: string, context: string, type: string) => void; export type ParseDirectiveDefinition = (statement: string, context: string, type: string) => void;
export type HTML = d3.Selection<HTMLIFrameElement, unknown, Element, unknown>; export type HTML = d3.Selection<HTMLIFrameElement, unknown, Element | null, unknown>;
export type SVG = d3.Selection<SVGSVGElement, unknown, Element, unknown>; export type SVG = d3.Selection<SVGSVGElement, unknown, Element | null, unknown>;
export type Group = d3.Selection<SVGGElement, unknown, Element | null, unknown>;
export type DiagramStylesProvider = (options?: any) => string; export type DiagramStylesProvider = (options?: any) => string;

View File

@@ -49,7 +49,7 @@ describe('diagram detection', () => {
"Parse error on line 2: "Parse error on line 2:
graph TD; A--> graph TD; A-->
--------------^ --------------^
Expecting 'AMP', 'ALPHA', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'MINUS', 'BRKT', 'DOT', 'PUNCTUATION', 'UNICODE_TEXT', 'PLUS', 'EQUALS', 'MULT', 'UNDERSCORE', got 'EOF'" Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'EOF'"
`); `);
await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects
.toThrowErrorMatchingInlineSnapshot(` .toThrowErrorMatchingInlineSnapshot(`

View File

@@ -6,6 +6,40 @@ setConfig({
securityLevel: 'strict', securityLevel: 'strict',
}); });
const keywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'default',
'linkStyle',
'interpolate',
'classDef',
'class',
'href',
'call',
'click',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
'kitty',
];
const doubleEndedEdges = [
{ edgeStart: 'x--', edgeEnd: '--x', stroke: 'normal', type: 'double_arrow_cross' },
{ edgeStart: 'x==', edgeEnd: '==x', stroke: 'thick', type: 'double_arrow_cross' },
{ edgeStart: 'x-.', edgeEnd: '.-x', stroke: 'dotted', type: 'double_arrow_cross' },
{ edgeStart: 'o--', edgeEnd: '--o', stroke: 'normal', type: 'double_arrow_circle' },
{ edgeStart: 'o==', edgeEnd: '==o', stroke: 'thick', type: 'double_arrow_circle' },
{ edgeStart: 'o-.', edgeEnd: '.-o', stroke: 'dotted', type: 'double_arrow_circle' },
{ edgeStart: '<--', edgeEnd: '-->', stroke: 'normal', type: 'double_arrow_point' },
{ edgeStart: '<==', edgeEnd: '==>', stroke: 'thick', type: 'double_arrow_point' },
{ edgeStart: '<-.', edgeEnd: '.->', stroke: 'dotted', type: 'double_arrow_point' },
];
describe('[Edges] when parsing', () => { describe('[Edges] when parsing', () => {
beforeEach(function () { beforeEach(function () {
flow.parser.yy = flowDb; flow.parser.yy = flowDb;
@@ -39,211 +73,62 @@ describe('[Edges] when parsing', () => {
expect(edges[0].type).toBe('arrow_circle'); expect(edges[0].type).toBe('arrow_circle');
}); });
describe('cross', function () { describe('edges', function () {
it('should handle double edged nodes and edges', function () { doubleEndedEdges.forEach((edgeType) => {
const res = flow.parser.parse('graph TD;\nA x--x B;'); it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () {
const res = flow.parser.parse(`graph TD;\nA ${edgeType.edgeStart}${edgeType.edgeEnd} B;`);
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A'); expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B'); expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1); expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A'); expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B'); expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross'); expect(edges[0].type).toBe(`${edgeType.type}`);
expect(edges[0].text).toBe(''); expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('normal'); expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
expect(edges[0].length).toBe(1); });
});
it('should handle double edged nodes with text', function () { it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
const res = flow.parser.parse('graph TD;\nA x-- text --x B;'); const res = flow.parser.parse(
`graph TD;\nA ${edgeType.edgeStart} text ${edgeType.edgeEnd} B;`
);
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A'); expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B'); expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1); expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A'); expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B'); expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross'); expect(edges[0].type).toBe(`${edgeType.type}`);
expect(edges[0].text).toBe('text'); expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('normal'); expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
expect(edges[0].length).toBe(1); });
});
it('should handle double edged nodes and edges on thick arrows', function () { it.each(keywords)(
const res = flow.parser.parse('graph TD;\nA x==x B;'); `should handle ${edgeType.stroke} ${edgeType.type} with %s text`,
function (keyword) {
const res = flow.parser.parse(
`graph TD;\nA ${edgeType.edgeStart} ${keyword} ${edgeType.edgeEnd} B;`
);
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A'); expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B'); expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1); expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A'); expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B'); expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross'); expect(edges[0].type).toBe(`${edgeType.type}`);
expect(edges[0].text).toBe(''); expect(edges[0].text).toBe(`${keyword}`);
expect(edges[0].stroke).toBe('thick'); expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
expect(edges[0].length).toBe(1); }
}); );
it('should handle double edged nodes with text on thick arrows', function () {
const res = flow.parser.parse('graph TD;\nA x== text ==x B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('thick');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes and edges on dotted arrows', function () {
const res = flow.parser.parse('graph TD;\nA x-.-x B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross');
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('dotted');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes with text on dotted arrows', function () {
const res = flow.parser.parse('graph TD;\nA x-. text .-x B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('dotted');
expect(edges[0].length).toBe(1);
});
});
describe('circle', function () {
it('should handle double edged nodes and edges', function () {
const res = flow.parser.parse('graph TD;\nA o--o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('normal');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes with text', function () {
const res = flow.parser.parse('graph TD;\nA o-- text --o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('normal');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes and edges on thick arrows', function () {
const res = flow.parser.parse('graph TD;\nA o==o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('thick');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes with text on thick arrows', function () {
const res = flow.parser.parse('graph TD;\nA o== text ==o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('thick');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes and edges on dotted arrows', function () {
const res = flow.parser.parse('graph TD;\nA o-.-o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('dotted');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes with text on dotted arrows', function () {
const res = flow.parser.parse('graph TD;\nA o-. text .-o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('dotted');
expect(edges[0].length).toBe(1);
}); });
}); });

View File

@@ -24,7 +24,7 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t
expect(vert['A'].labelType).toBe('markdown'); expect(vert['A'].labelType).toBe('markdown');
expect(vert['B'].id).toBe('B'); expect(vert['B'].id).toBe('B');
expect(vert['B'].text).toBe('The dog in the hog'); expect(vert['B'].text).toBe('The dog in the hog');
expect(vert['B'].labelType).toBe('text'); expect(vert['B'].labelType).toBe('string');
expect(edges.length).toBe(2); expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A'); expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B'); expect(edges[0].end).toBe('B');
@@ -35,7 +35,7 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t
expect(edges[1].end).toBe('C'); expect(edges[1].end).toBe('C');
expect(edges[1].type).toBe('arrow_point'); expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('The rat in the mat'); expect(edges[1].text).toBe('The rat in the mat');
expect(edges[1].labelType).toBe('text'); expect(edges[1].labelType).toBe('string');
}); });
it('mardown formatting in subgraphs', function () { it('mardown formatting in subgraphs', function () {
const res = flow.parser.parse(`flowchart LR const res = flow.parser.parse(`flowchart LR

View File

@@ -6,6 +6,29 @@ setConfig({
securityLevel: 'strict', securityLevel: 'strict',
}); });
const keywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'default',
'linkStyle',
'interpolate',
'classDef',
'class',
'href',
'call',
'click',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
];
const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/', '_'];
describe('[Singlenodes] when parsing', () => { describe('[Singlenodes] when parsing', () => {
beforeEach(function () { beforeEach(function () {
flow.parser.yy = flowDb; flow.parser.yy = flowDb;
@@ -259,4 +282,90 @@ describe('[Singlenodes] when parsing', () => {
expect(edges.length).toBe(0); expect(edges.length).toBe(0);
expect(vert['i_d'].styles.length).toBe(0); expect(vert['i_d'].styles.length).toBe(0);
}); });
it.each(keywords)('should handle keywords between dashes "-"', function (keyword) {
const res = flow.parser.parse(`graph TD;a-${keyword}-node;`);
const vert = flow.parser.yy.getVertices();
expect(vert[`a-${keyword}-node`].text).toBe(`a-${keyword}-node`);
});
it.each(keywords)('should handle keywords between periods "."', function (keyword) {
const res = flow.parser.parse(`graph TD;a.${keyword}.node;`);
const vert = flow.parser.yy.getVertices();
expect(vert[`a.${keyword}.node`].text).toBe(`a.${keyword}.node`);
});
it.each(keywords)('should handle keywords between underscores "_"', function (keyword) {
const res = flow.parser.parse(`graph TD;a_${keyword}_node;`);
const vert = flow.parser.yy.getVertices();
expect(vert[`a_${keyword}_node`].text).toBe(`a_${keyword}_node`);
});
it.each(keywords)('should handle nodes ending in %s', function (keyword) {
const res = flow.parser.parse(`graph TD;node_${keyword};node.${keyword};node-${keyword};`);
const vert = flow.parser.yy.getVertices();
expect(vert[`node_${keyword}`].text).toBe(`node_${keyword}`);
expect(vert[`node.${keyword}`].text).toBe(`node.${keyword}`);
expect(vert[`node-${keyword}`].text).toBe(`node-${keyword}`);
});
const errorKeywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'linkStyle',
'interpolate',
'classDef',
'class',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
];
it.each(errorKeywords)('should throw error at nodes beginning with %s', function (keyword) {
const str = `graph TD;${keyword}.node;${keyword}-node;${keyword}/node`;
const vert = flow.parser.yy.getVertices();
expect(() => flow.parser.parse(str)).toThrowError();
});
const workingKeywords = ['default', 'href', 'click', 'call'];
it.each(workingKeywords)('should parse node beginning with %s', function (keyword) {
flow.parser.parse(`graph TD; ${keyword}.node;${keyword}-node;${keyword}/node;`);
const vert = flow.parser.yy.getVertices();
expect(vert[`${keyword}.node`].text).toBe(`${keyword}.node`);
expect(vert[`${keyword}-node`].text).toBe(`${keyword}-node`);
expect(vert[`${keyword}/node`].text).toBe(`${keyword}/node`);
});
it.each(specialChars)(
'should allow node ids of single special characters',
function (specialChar) {
flow.parser.parse(`graph TD; ${specialChar} --> A`);
const vert = flow.parser.yy.getVertices();
expect(vert[`${specialChar}`].text).toBe(`${specialChar}`);
}
);
it.each(specialChars)(
'should allow node ids with special characters at start of id',
function (specialChar) {
flow.parser.parse(`graph TD; ${specialChar}node --> A`);
const vert = flow.parser.yy.getVertices();
expect(vert[`${specialChar}node`].text).toBe(`${specialChar}node`);
}
);
it.each(specialChars)(
'should allow node ids with special characters at end of id',
function (specialChar) {
flow.parser.parse(`graph TD; node${specialChar} --> A`);
const vert = flow.parser.yy.getVertices();
expect(vert[`node${specialChar}`].text).toBe(`node${specialChar}`);
}
);
}); });

View File

@@ -26,15 +26,6 @@ describe('[Style] when parsing', () => {
expect(vert['Q'].styles[0]).toBe('background:#fff'); expect(vert['Q'].styles[0]).toBe('background:#fff');
}); });
// log.debug(flow.parser.parse('graph TD;style Q background:#fff;'));
it('should handle styles for edges', function () {
const res = flow.parser.parse('graph TD;a-->b;\nstyle #0 stroke: #f66;');
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(1);
});
it('should handle multiple styles for a vortex', function () { it('should handle multiple styles for a vortex', function () {
const res = flow.parser.parse('graph TD;style R background:#fff,border:1px solid red;'); const res = flow.parser.parse('graph TD;style R background:#fff,border:1px solid red;');

View File

@@ -305,6 +305,95 @@ describe('[Text] when parsing', () => {
expect(vert['C'].type).toBe('round'); expect(vert['C'].type).toBe('round');
expect(vert['C'].text).toBe('Chimpansen hoppar'); expect(vert['C'].text).toBe('Chimpansen hoppar');
}); });
const keywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'default',
'linkStyle',
'interpolate',
'classDef',
'class',
'href',
'call',
'click',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
'kitty',
];
const shapes = [
{ start: '[', end: ']', name: 'square' },
{ start: '(', end: ')', name: 'round' },
{ start: '{', end: '}', name: 'diamond' },
{ start: '(-', end: '-)', name: 'ellipse' },
{ start: '([', end: '])', name: 'stadium' },
{ start: '>', end: ']', name: 'odd' },
{ start: '[(', end: ')]', name: 'cylinder' },
{ start: '(((', end: ')))', name: 'doublecircle' },
{ start: '[/', end: '\\]', name: 'trapezoid' },
{ start: '[\\', end: '/]', name: 'inv_trapezoid' },
{ start: '[/', end: '/]', name: 'lean_right' },
{ start: '[\\', end: '\\]', name: 'lean_left' },
{ start: '[[', end: ']]', name: 'subroutine' },
{ start: '{{', end: '}}', name: 'hexagon' },
];
shapes.forEach((shape) => {
it.each(keywords)(`should handle %s keyword in ${shape.name} vertex`, function (keyword) {
const rest = flow.parser.parse(
`graph TD;A_${keyword}_node-->B${shape.start}This node has a ${keyword} as text${shape.end};`
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['B'].type).toBe(`${shape.name}`);
expect(vert['B'].text).toBe(`This node has a ${keyword} as text`);
});
});
it.each(keywords)('should handle %s keyword in rect vertex', function (keyword) {
const rest = flow.parser.parse(
`graph TD;A_${keyword}_node-->B[|borders:lt|This node has a ${keyword} as text];`
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['B'].type).toBe('rect');
expect(vert['B'].text).toBe(`This node has a ${keyword} as text`);
});
it('should handle edge case for odd vertex with node id ending with minus', function () {
const res = flow.parser.parse('graph TD;A_node-->odd->Vertex Text];');
const vert = flow.parser.yy.getVertices();
expect(vert['odd-'].type).toBe('odd');
expect(vert['odd-'].text).toBe('Vertex Text');
});
it('should allow forward slashes in lean_right vertices', function () {
const rest = flow.parser.parse(`graph TD;A_node-->B[/This node has a / as text/];`);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['B'].type).toBe('lean_right');
expect(vert['B'].text).toBe(`This node has a / as text`);
});
it('should allow back slashes in lean_left vertices', function () {
const rest = flow.parser.parse(`graph TD;A_node-->B[\\This node has a \\ as text\\];`);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['B'].type).toBe('lean_left');
expect(vert['B'].text).toBe(`This node has a \\ as text`);
});
it('should handle åäö and minus', function () { it('should handle åäö and minus', function () {
const res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};'); const res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};');
@@ -484,4 +573,33 @@ describe('[Text] when parsing', () => {
expect(vert['A'].text).toBe(',.?!+-*'); expect(vert['A'].text).toBe(',.?!+-*');
expect(edges[0].text).toBe(',.?!+-*'); expect(edges[0].text).toBe(',.?!+-*');
}); });
it('should throw error at nested set of brackets', function () {
const str = 'graph TD; A[This is a () in text];';
expect(() => flow.parser.parse(str)).toThrowError("got 'PS'");
});
it('should throw error for strings and text at the same time', function () {
const str = 'graph TD;A(this node has "string" and text)-->|this link has "string" and text|C;';
expect(() => flow.parser.parse(str)).toThrowError("got 'STR'");
});
it('should throw error for escaping quotes in text state', function () {
//prettier-ignore
const str = 'graph TD; A[This is a \"()\" in text];'; //eslint-disable-line no-useless-escape
expect(() => flow.parser.parse(str)).toThrowError("got 'STR'");
});
it('should throw error for nested quoatation marks', function () {
const str = 'graph TD; A["This is a "()" in text"];';
expect(() => flow.parser.parse(str)).toThrowError("Expecting 'SQE'");
});
it('should throw error', function () {
const str = `graph TD; node[hello ) world] --> works`;
expect(() => flow.parser.parse(str)).toThrowError("got 'PE'");
});
}); });

View File

@@ -13,6 +13,12 @@
%x acc_descr_multiline %x acc_descr_multiline
%x dir %x dir
%x vertex %x vertex
%x text
%x ellipseText
%x trapText
%x edgeText
%x thickEdgeText
%x dottedEdgeText
%x click %x click
%x href %x href
%x callbackname %x callbackname
@@ -23,41 +29,19 @@
%x close_directive %x close_directive
%% %%
\%\%\{ { this.begin('open_directive'); return 'open_directive'; } \%\%\{ { this.begin('open_directive'); return 'open_directive'; }
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; } <open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; } <type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; } <type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive'; <arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; } accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; } <acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; } accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; } <acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");} accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); } <acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value"; <acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
// <acc_descr_multiline>.*[^\n]* { return "acc_descr_line"} // <acc_descr_multiline>.*[^\n]* { return "acc_descr_line"}
["][`] { this.begin("md_string");}
<md_string>[^`"]+ { return "MD_STR";}
<md_string>[`]["] { this.popState();}
["] this.begin("string");
<string>["] this.popState();
<string>[^"]* return "STR";
"style" return 'STYLE';
"default" return 'DEFAULT';
"linkStyle" return 'LINKSTYLE';
"interpolate" return 'INTERPOLATE';
"classDef" return 'CLASSDEF';
"class" return 'CLASS';
/*
---interactivity command---
'href' adds a link to the specified node. 'href' can only be specified when the
line was introduced with 'click'.
'href "<link>"' attaches the specified link to the node that was specified by 'click'.
*/
"href"[\s]+["] this.begin("href");
<href>["] this.popState();
<href>[^"]* return 'HREF';
/* /*
---interactivity command--- ---interactivity command---
@@ -74,88 +58,128 @@ Function arguments are optional: 'call <callbackname>()' simply executes 'callba
<callbackargs>\) this.popState(); <callbackargs>\) this.popState();
<callbackargs>[^)]* return 'CALLBACKARGS'; <callbackargs>[^)]* return 'CALLBACKARGS';
<md_string>[^`"]+ { return "MD_STR";}
<md_string>[`]["] { this.popState();}
<*>["][`] { this.begin("md_string");}
<string>[^"]+ return "STR";
<string>["] this.popState();
<*>["] this.pushState("string");
"style" return 'STYLE';
"default" return 'DEFAULT';
"linkStyle" return 'LINKSTYLE';
"interpolate" return 'INTERPOLATE';
"classDef" return 'CLASSDEF';
"class" return 'CLASS';
/*
---interactivity command---
'href' adds a link to the specified node. 'href' can only be specified when the
line was introduced with 'click'.
'href "<link>"' attaches the specified link to the node that was specified by 'click'.
*/
"href"[\s] return 'HREF';
/* /*
'click' is the keyword to introduce a line that contains interactivity commands. 'click' is the keyword to introduce a line that contains interactivity commands.
'click' must be followed by an existing node-id. All commands are attached to 'click' must be followed by an existing node-id. All commands are attached to
that id. that id.
'click <id>' can be followed by href or call commands in any desired order 'click <id>' can be followed by href or call commands in any desired order
*/ */
"click"[\s]+ this.begin("click"); "click"[\s]+ this.begin("click");
<click>[\s\n] this.popState(); <click>[\s\n] this.popState();
<click>[^\s\n]* return 'CLICK'; <click>[^\s\n]* return 'CLICK';
"flowchart-elk" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} "flowchart-elk" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"graph" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} "graph" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} "flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"subgraph" return 'subgraph'; "subgraph" return 'subgraph';
"end"\b\s* return 'end'; "end"\b\s* return 'end';
"_self" return 'LINK_TARGET'; "_self" return 'LINK_TARGET';
"_blank" return 'LINK_TARGET'; "_blank" return 'LINK_TARGET';
"_parent" return 'LINK_TARGET'; "_parent" return 'LINK_TARGET';
"_top" return 'LINK_TARGET'; "_top" return 'LINK_TARGET';
<dir>(\r?\n)*\s*\n { this.popState(); return 'NODIR'; } <dir>(\r?\n)*\s*\n { this.popState(); return 'NODIR'; }
<dir>\s*"LR" { this.popState(); return 'DIR'; } <dir>\s*"LR" { this.popState(); return 'DIR'; }
<dir>\s*"RL" { this.popState(); return 'DIR'; } <dir>\s*"RL" { this.popState(); return 'DIR'; }
<dir>\s*"TB" { this.popState(); return 'DIR'; } <dir>\s*"TB" { this.popState(); return 'DIR'; }
<dir>\s*"BT" { this.popState(); return 'DIR'; } <dir>\s*"BT" { this.popState(); return 'DIR'; }
<dir>\s*"TD" { this.popState(); return 'DIR'; } <dir>\s*"TD" { this.popState(); return 'DIR'; }
<dir>\s*"BR" { this.popState(); return 'DIR'; } <dir>\s*"BR" { this.popState(); return 'DIR'; }
<dir>\s*"<" { this.popState(); return 'DIR'; } <dir>\s*"<" { this.popState(); return 'DIR'; }
<dir>\s*">" { this.popState(); return 'DIR'; } <dir>\s*">" { this.popState(); return 'DIR'; }
<dir>\s*"^" { this.popState(); return 'DIR'; } <dir>\s*"^" { this.popState(); return 'DIR'; }
<dir>\s*"v" { this.popState(); return 'DIR'; } <dir>\s*"v" { this.popState(); return 'DIR'; }
.*direction\s+TB[^\n]* return 'direction_tb';
.*direction\s+BT[^\n]* return 'direction_bt';
.*direction\s+RL[^\n]* return 'direction_rl';
.*direction\s+LR[^\n]* return 'direction_lr';
[0-9]+ return 'NUM';
\# return 'BRKT';
":::" return 'STYLE_SEPARATOR';
":" return 'COLON';
"&" return 'AMP';
";" return 'SEMI';
"," return 'COMMA';
"*" return 'MULT';
<INITIAL,edgeText>\s*[xo<]?\-\-+[-xo>]\s* { this.popState(); return 'LINK'; }
<INITIAL>\s*[xo<]?\-\-\s* { this.pushState("edgeText"); return 'START_LINK'; }
<edgeText>[^-]|\-(?!\-)+ return 'EDGE_TEXT';
<INITIAL,thickEdgeText>\s*[xo<]?\=\=+[=xo>]\s* { this.popState(); return 'LINK'; }
<INITIAL>\s*[xo<]?\=\=\s* { this.pushState("thickEdgeText"); return 'START_LINK'; }
<thickEdgeText>[^=]|\=(?!=) return 'EDGE_TEXT';
<INITIAL,dottedEdgeText>\s*[xo<]?\-?\.+\-[xo>]?\s* { this.popState(); return 'LINK'; }
<INITIAL>\s*[xo<]?\-\.\s* { this.pushState("dottedEdgeText"); return 'START_LINK'; }
<dottedEdgeText>[^\.]|\.(?!-) return 'EDGE_TEXT';
<*>\s*\~\~[\~]+\s* return 'LINK';
<ellipseText>[-/\)][\)] { this.popState(); return '-)'; }
<ellipseText>[^\(\)\[\]\{\}]|-/!\)+ return "TEXT"
<*>"(-" { this.pushState("ellipseText"); return '(-'; }
<text>"])" { this.popState(); return 'STADIUMEND'; }
<*>"([" { this.pushState("text"); return 'STADIUMSTART'; }
<text>"]]" { this.popState(); return 'SUBROUTINEEND'; }
<*>"[[" { this.pushState("text"); return 'SUBROUTINESTART'; }
"[|" { return 'VERTEX_WITH_PROPS_START'; }
\> { this.pushState("text"); return 'TAGEND'; }
<text>")]" { this.popState(); return 'CYLINDEREND'; }
<*>"[(" { this.pushState("text") ;return 'CYLINDERSTART'; }
<text>")))" { this.popState(); return 'DOUBLECIRCLEEND'; }
<*>"(((" { this.pushState("text"); return 'DOUBLECIRCLESTART'; }
<trapText>[\\(?=\])][\]] { this.popState(); return 'TRAPEND'; }
<trapText>\/(?=\])\] { this.popState(); return 'INVTRAPEND'; }
<trapText>\/(?!\])|\\(?!\])|[^\\\[\]\(\)\{\}\/]+ return 'TEXT';
<*>"[/" { this.pushState("trapText"); return 'TRAPSTART'; }
<*>"[\\" { this.pushState("trapText"); return 'INVTRAPSTART'; }
.*direction\s+TB[^\n]* return 'direction_tb';
.*direction\s+BT[^\n]* return 'direction_bt';
.*direction\s+RL[^\n]* return 'direction_rl';
.*direction\s+LR[^\n]* return 'direction_lr';
[0-9]+ { return 'NUM';}
\# return 'BRKT';
":::" return 'STYLE_SEPARATOR';
":" return 'COLON';
"&" return 'AMP';
";" return 'SEMI';
"," return 'COMMA';
"*" return 'MULT';
\s*[xo<]?\-\-+[-xo>]\s* return 'LINK';
\s*[xo<]?\=\=+[=xo>]\s* return 'LINK';
\s*[xo<]?\-?\.+\-[xo>]?\s* return 'LINK';
\s*\~\~[\~]+\s* return 'LINK';
\s*[xo<]?\-\-\s* return 'START_LINK';
\s*[xo<]?\=\=\s* return 'START_LINK';
\s*[xo<]?\-\.\s* return 'START_LINK';
"(-" return '(-';
"-)" return '-)';
"([" return 'STADIUMSTART';
"])" return 'STADIUMEND';
"[[" return 'SUBROUTINESTART';
"]]" return 'SUBROUTINEEND';
"[|" return 'VERTEX_WITH_PROPS_START';
"[(" return 'CYLINDERSTART';
")]" return 'CYLINDEREND';
"(((" return 'DOUBLECIRCLESTART';
")))" return 'DOUBLECIRCLEEND';
\- return 'MINUS';
"." return 'DOT';
[\_] return 'UNDERSCORE';
\+ return 'PLUS';
\% return 'PCT';
"=" return 'EQUALS';
\= return 'EQUALS';
"<" return 'TAGSTART'; "<" return 'TAGSTART';
">" return 'TAGEND'; ">" return 'TAGEND';
"^" return 'UP'; "^" return 'UP';
"\|" return 'SEP'; "\|" return 'SEP';
"v" return 'DOWN'; "v" return 'DOWN';
[A-Za-z]+ return 'ALPHA'; "*" return 'MULT';
"\\]" return 'TRAPEND'; "#" return 'BRKT';
"[/" return 'TRAPSTART'; "&" return 'AMP';
"/]" return 'INVTRAPEND'; ([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.])|=(?!=))+ return 'NODE_STRING';
"[\\" return 'INVTRAPSTART'; "-" return 'MINUS'
[!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION';
[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| [\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]| [\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|
@@ -218,13 +242,20 @@ that id.
[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]| [\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|
[\uFFD2-\uFFD7\uFFDA-\uFFDC] [\uFFD2-\uFFD7\uFFDA-\uFFDC]
return 'UNICODE_TEXT'; return 'UNICODE_TEXT';
"|" return 'PIPE';
"(" return 'PS'; <text>"|" { this.popState(); return 'PIPE'; }
")" return 'PE'; <*>"|" { this.pushState("text"); return 'PIPE'; }
"[" return 'SQS';
"]" return 'SQE'; <text>")" { this.popState(); return 'PE'; }
"{" return 'DIAMOND_START' <*>"(" { this.pushState("text"); return 'PS'; }
"}" return 'DIAMOND_STOP'
<text>"]" { this.popState(); return 'SQE'; }
<*>"[" { this.pushState("text"); return 'SQS'; }
<text>(\}) { this.popState(); return 'DIAMOND_STOP' }
<*>"{" { this.pushState("text"); return 'DIAMOND_START' }
<text>[^\[\]\(\)\{\}\|\"]+ return "TEXT";
"\"" return 'QUOTE'; "\"" return 'QUOTE';
(\r?\n)+ return 'NEWLINE'; (\r?\n)+ return 'NEWLINE';
\s return 'SPACE'; \s return 'SPACE';
@@ -255,11 +286,11 @@ openDirective
; ;
typeDirective typeDirective
: type_directive { yy.parseDirective($1, 'type_directive'); } : type_directive { yy.parseDirective($type_directive, 'type_directive'); }
; ;
argDirective argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); } : arg_directive { $arg_directive = $arg_directive.trim().replace(/'/g, '"'); yy.parseDirective($arg_directive, 'arg_directive'); }
; ;
closeDirective closeDirective
@@ -275,15 +306,15 @@ document
{ $$ = [];} { $$ = [];}
| document line | document line
{ {
if(!Array.isArray($2) || $2.length > 0){ if(!Array.isArray($line) || $line.length > 0){
$1.push($2); $document.push($line);
} }
$$=$1;} $$=$document;}
; ;
line line
: statement : statement
{$$=$1;} {$$=$statement;}
| SEMI | SEMI
| NEWLINE | NEWLINE
| SPACE | SPACE
@@ -296,15 +327,15 @@ graphConfig
| GRAPH NODIR | GRAPH NODIR
{ yy.setDirection('TB');$$ = 'TB';} { yy.setDirection('TB');$$ = 'TB';}
| GRAPH DIR FirstStmtSeperator | GRAPH DIR FirstStmtSeperator
{ yy.setDirection($2);$$ = $2;} { yy.setDirection($DIR);$$ = $DIR;}
// | GRAPH SPACE TAGEND FirstStmtSeperator // | GRAPH SPACE TAGEND FirstStmtSeperator
// { yy.setDirection("LR");$$ = $3;} // { yy.setDirection("LR");$$ = $TAGEND;}
// | GRAPH SPACE TAGSTART FirstStmtSeperator // | GRAPH SPACE TAGSTART FirstStmtSeperator
// { yy.setDirection("RL");$$ = $3;} // { yy.setDirection("RL");$$ = $TAGSTART;}
// | GRAPH SPACE UP FirstStmtSeperator // | GRAPH SPACE UP FirstStmtSeperator
// { yy.setDirection("BT");$$ = $3;} // { yy.setDirection("BT");$$ = $UP;}
// | GRAPH SPACE DOWN FirstStmtSeperator // | GRAPH SPACE DOWN FirstStmtSeperator
// { yy.setDirection("TB");$$ = $3;} // { yy.setDirection("TB");$$ = $DOWN;}
; ;
ending: endToken ending ending: endToken ending
@@ -332,7 +363,7 @@ spaceList
statement statement
: verticeStatement separator : verticeStatement separator
{ /* console.warn('finat vs', $1.nodes); */ $$=$1.nodes} { /* console.warn('finat vs', $verticeStatement.nodes); */ $$=$verticeStatement.nodes}
| styleStatement separator | styleStatement separator
{$$=[];} {$$=[];}
| linkStyleStatement separator | linkStyleStatement separator
@@ -343,110 +374,121 @@ statement
{$$=[];} {$$=[];}
| clickStatement separator | clickStatement separator
{$$=[];} {$$=[];}
| subgraph SPACE text SQS text SQE separator document end | subgraph SPACE textNoTags SQS text SQE separator document end
{$$=yy.addSubGraph($3,$8,$5);} {$$=yy.addSubGraph($textNoTags,$document,$text);}
| subgraph SPACE text separator document end | subgraph SPACE textNoTags separator document end
{$$=yy.addSubGraph($3,$5,$3);} {$$=yy.addSubGraph($textNoTags,$document,$textNoTags);}
// | subgraph SPACE text separator document end // | subgraph SPACE textNoTags separator document end
// {$$=yy.addSubGraph($3,$5,$3);} // {$$=yy.addSubGraph($textNoTags,$document,$textNoTags);}
| subgraph separator document end | subgraph separator document end
{$$=yy.addSubGraph(undefined,$3,undefined);} {$$=yy.addSubGraph(undefined,$document,undefined);}
| direction | direction
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); } | acc_title acc_title_value { $$=$acc_title_value.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); } | acc_descr acc_descr_value { $$=$acc_descr_value.trim();yy.setAccDescription($$); }
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | acc_descr_multiline_value { $$=$acc_descr_multiline_value.trim();yy.setAccDescription($$); }
; ;
separator: NEWLINE | SEMI | EOF ; separator: NEWLINE | SEMI | EOF ;
verticeStatement: verticeStatement link node verticeStatement: verticeStatement link node
{ /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } } { /* console.warn('vs',$verticeStatement.stmt,$node); */ yy.addLink($verticeStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($verticeStatement.nodes) } }
| verticeStatement link node spaceList | verticeStatement link node spaceList
{ /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } } { /* console.warn('vs',$verticeStatement.stmt,$node); */ yy.addLink($verticeStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($verticeStatement.nodes) } }
|node spaceList {/*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }} |node spaceList {/*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }}
|node { /*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }} |node { /*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }}
; ;
node: styledVertex node: styledVertex
{ /* console.warn('nod', $1); */ $$ = [$1];} { /* console.warn('nod', $styledVertex); */ $$ = [$styledVertex];}
| node spaceList AMP spaceList styledVertex | node spaceList AMP spaceList styledVertex
{ $$ = $1.concat($5); /* console.warn('pip', $1[0], $5, $$); */ } { $$ = $node.concat($styledVertex); /* console.warn('pip', $node[0], $styledVertex, $$); */ }
; ;
styledVertex: vertex styledVertex: vertex
{ /* console.warn('nod', $1); */ $$ = $1;} { /* console.warn('nod', $vertex); */ $$ = $vertex;}
| vertex STYLE_SEPARATOR idString | vertex STYLE_SEPARATOR idString
{$$ = $1;yy.setClass($1,$3)} {$$ = $vertex;yy.setClass($vertex,$idString)}
; ;
vertex: idString SQS text SQE vertex: idString SQS text SQE
{$$ = $1;yy.addVertex($1,$3,'square');} {$$ = $idString;yy.addVertex($idString,$text,'square');}
| idString DOUBLECIRCLESTART text DOUBLECIRCLEEND | idString DOUBLECIRCLESTART text DOUBLECIRCLEEND
{$$ = $1;yy.addVertex($1,$3,'doublecircle');} {$$ = $idString;yy.addVertex($idString,$text,'doublecircle');}
| idString PS PS text PE PE | idString PS PS text PE PE
{$$ = $1;yy.addVertex($1,$4,'circle');} {$$ = $idString;yy.addVertex($idString,$text,'circle');}
| idString '(-' text '-)' | idString '(-' text '-)'
{$$ = $1;yy.addVertex($1,$3,'ellipse');} {$$ = $idString;yy.addVertex($idString,$text,'ellipse');}
| idString STADIUMSTART text STADIUMEND | idString STADIUMSTART text STADIUMEND
{$$ = $1;yy.addVertex($1,$3,'stadium');} {$$ = $idString;yy.addVertex($idString,$text,'stadium');}
| idString SUBROUTINESTART text SUBROUTINEEND | idString SUBROUTINESTART text SUBROUTINEEND
{$$ = $1;yy.addVertex($1,$3,'subroutine');} {$$ = $idString;yy.addVertex($idString,$text,'subroutine');}
| idString VERTEX_WITH_PROPS_START ALPHA COLON ALPHA PIPE text SQE | idString VERTEX_WITH_PROPS_START NODE_STRING\[field] COLON NODE_STRING\[value] PIPE text SQE
{$$ = $1;yy.addVertex($1,$7,'rect',undefined,undefined,undefined, Object.fromEntries([[$3, $5]]));} {$$ = $idString;yy.addVertex($idString,$text,'rect',undefined,undefined,undefined, Object.fromEntries([[$field, $value]]));}
| idString CYLINDERSTART text CYLINDEREND | idString CYLINDERSTART text CYLINDEREND
{$$ = $1;yy.addVertex($1,$3,'cylinder');} {$$ = $idString;yy.addVertex($idString,$text,'cylinder');}
| idString PS text PE | idString PS text PE
{$$ = $1;yy.addVertex($1,$3,'round');} {$$ = $idString;yy.addVertex($idString,$text,'round');}
| idString DIAMOND_START text DIAMOND_STOP | idString DIAMOND_START text DIAMOND_STOP
{$$ = $1;yy.addVertex($1,$3,'diamond');} {$$ = $idString;yy.addVertex($idString,$text,'diamond');}
| idString DIAMOND_START DIAMOND_START text DIAMOND_STOP DIAMOND_STOP | idString DIAMOND_START DIAMOND_START text DIAMOND_STOP DIAMOND_STOP
{$$ = $1;yy.addVertex($1,$4,'hexagon');} {$$ = $idString;yy.addVertex($idString,$text,'hexagon');}
| idString TAGEND text SQE | idString TAGEND text SQE
{$$ = $1;yy.addVertex($1,$3,'odd');} {$$ = $idString;yy.addVertex($idString,$text,'odd');}
| idString TRAPSTART text TRAPEND | idString TRAPSTART text TRAPEND
{$$ = $1;yy.addVertex($1,$3,'trapezoid');} {$$ = $idString;yy.addVertex($idString,$text,'trapezoid');}
| idString INVTRAPSTART text INVTRAPEND | idString INVTRAPSTART text INVTRAPEND
{$$ = $1;yy.addVertex($1,$3,'inv_trapezoid');} {$$ = $idString;yy.addVertex($idString,$text,'inv_trapezoid');}
| idString TRAPSTART text INVTRAPEND | idString TRAPSTART text INVTRAPEND
{$$ = $1;yy.addVertex($1,$3,'lean_right');} {$$ = $idString;yy.addVertex($idString,$text,'lean_right');}
| idString INVTRAPSTART text TRAPEND | idString INVTRAPSTART text TRAPEND
{$$ = $1;yy.addVertex($1,$3,'lean_left');} {$$ = $idString;yy.addVertex($idString,$text,'lean_left');}
| idString | idString
{ /*console.warn('h: ', $1);*/$$ = $1;yy.addVertex($1);} { /*console.warn('h: ', $idString);*/$$ = $idString;yy.addVertex($idString);}
; ;
link: linkStatement arrowText link: linkStatement arrowText
{$1.text = $2;$$ = $1;} {$linkStatement.text = $arrowText;$$ = $linkStatement;}
| linkStatement TESTSTR SPACE | linkStatement TESTSTR SPACE
{$1.text = $2;$$ = $1;} {$linkStatement.text = $TESTSTR;$$ = $linkStatement;}
| linkStatement arrowText SPACE | linkStatement arrowText SPACE
{$1.text = $2;$$ = $1;} {$linkStatement.text = $arrowText;$$ = $linkStatement;}
| linkStatement | linkStatement
{$$ = $1;} {$$ = $linkStatement;}
| START_LINK text LINK | START_LINK edgeText LINK
{var inf = yy.destructLink($3, $1); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$2};} {var inf = yy.destructLink($LINK, $START_LINK); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$edgeText};}
; ;
edgeText: edgeTextToken
{$$={text:$edgeTextToken, type:'text'};}
| edgeText edgeTextToken
{$$={text:$edgeText.text+''+$edgeTextToken, type:$edgeText.type};}
|STR
{$$={text: $STR, type: 'string'};}
| MD_STR
{$$={text:$MD_STR, type:'markdown'};}
;
linkStatement: LINK linkStatement: LINK
{var inf = yy.destructLink($1);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};} {var inf = yy.destructLink($LINK);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};}
; ;
arrowText: arrowText:
PIPE text PIPE PIPE text PIPE
{$$ = $2;} {$$ = $text;}
; ;
text: textToken text: textToken
{ $$={text:$1, type: 'text'};} { $$={text:$textToken, type: 'text'};}
| text textToken | text textToken
{ $$={text:$1.text+''+$2, type: $1.type};} { $$={text:$text.text+''+$textToken, type: $text.type};}
| STR | STR
{ $$={text: $1, type: 'text'};} { $$ = {text: $STR, type: 'string'};}
| MD_STR | MD_STR
{ $$={text: $1, type: 'markdown'};} { $$={text: $MD_STR, type: 'markdown'};}
; ;
@@ -456,109 +498,104 @@ keywords
textNoTags: textNoTagsToken textNoTags: textNoTagsToken
{$$=$1;} {$$={text:$textNoTagsToken, type: 'text'};}
| textNoTags textNoTagsToken | textNoTags textNoTagsToken
{$$=$1+''+$2;} {$$={text:$textNoTags.text+''+$textNoTagsToken, type: $textNoTags.type};}
| STR
{ $$={text: $STR, type: 'text'};}
| MD_STR
{ $$={text: $MD_STR, type: 'markdown'};}
; ;
classDefStatement:CLASSDEF SPACE DEFAULT SPACE stylesOpt classDefStatement:CLASSDEF SPACE idString SPACE stylesOpt
{$$ = $1;yy.addClass($3,$5);} {$$ = $CLASSDEF;yy.addClass($idString,$stylesOpt);}
| CLASSDEF SPACE alphaNum SPACE stylesOpt
{$$ = $1;yy.addClass($3,$5);}
; ;
classStatement:CLASS SPACE alphaNum SPACE alphaNum classStatement:CLASS SPACE idString\[vertex] SPACE idString\[class]
{$$ = $1;yy.setClass($3, $5);} {$$ = $CLASS;yy.setClass($vertex, $class);}
; ;
clickStatement clickStatement
: CLICK CALLBACKNAME {$$ = $1;yy.setClickEvent($1, $2);} : CLICK CALLBACKNAME {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME);}
| CLICK CALLBACKNAME SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} | CLICK CALLBACKNAME SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME);yy.setTooltip($CLICK, $STR);}
| CLICK CALLBACKNAME CALLBACKARGS {$$ = $1;yy.setClickEvent($1, $2, $3);} | CLICK CALLBACKNAME CALLBACKARGS {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);}
| CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setTooltip($1, $5);} | CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);yy.setTooltip($CLICK, $STR);}
| CLICK HREF {$$ = $1;yy.setLink($1, $2);} | CLICK HREF STR {$$ = $CLICK;yy.setLink($CLICK, $STR);}
| CLICK HREF SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);} | CLICK HREF STR SPACE STR {$$ = $CLICK;yy.setLink($CLICK, $STR1);yy.setTooltip($CLICK, $STR2);}
| CLICK HREF SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);} | CLICK HREF STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);}
| CLICK HREF SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);} | CLICK HREF STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);}
| CLICK alphaNum {$$ = $1;yy.setClickEvent($1, $2);} | CLICK alphaNum {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);}
| CLICK alphaNum SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} | CLICK alphaNum SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);yy.setTooltip($CLICK, $STR);}
| CLICK STR {$$ = $1;yy.setLink($1, $2);} | CLICK STR {$$ = $CLICK;yy.setLink($CLICK, $STR);}
| CLICK STR SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);} | CLICK STR\[link] SPACE STR\[tooltip] {$$ = $CLICK;yy.setLink($CLICK, $link);yy.setTooltip($CLICK, $tooltip);}
| CLICK STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);} | CLICK STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);}
| CLICK STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);} | CLICK STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);}
; ;
styleStatement:STYLE SPACE alphaNum SPACE stylesOpt styleStatement:STYLE SPACE idString SPACE stylesOpt
{$$ = $1;yy.addVertex($3,undefined,undefined,$5);} {$$ = $STYLE;yy.addVertex($idString,undefined,undefined,$stylesOpt);}
| STYLE SPACE HEX SPACE stylesOpt
{$$ = $1;yy.updateLink($3,$5);}
; ;
linkStyleStatement linkStyleStatement
: LINKSTYLE SPACE DEFAULT SPACE stylesOpt : LINKSTYLE SPACE DEFAULT SPACE stylesOpt
{$$ = $1;yy.updateLink([$3],$5);} {$$ = $LINKSTYLE;yy.updateLink([$DEFAULT],$stylesOpt);}
| LINKSTYLE SPACE numList SPACE stylesOpt | LINKSTYLE SPACE numList SPACE stylesOpt
{$$ = $1;yy.updateLink($3,$5);} {$$ = $LINKSTYLE;yy.updateLink($numList,$stylesOpt);}
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt | LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
{$$ = $1;yy.updateLinkInterpolate([$3],$7);yy.updateLink([$3],$9);} {$$ = $LINKSTYLE;yy.updateLinkInterpolate([$DEFAULT],$alphaNum);yy.updateLink([$DEFAULT],$stylesOpt);}
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt | LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
{$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);} {$$ = $LINKSTYLE;yy.updateLinkInterpolate($numList,$alphaNum);yy.updateLink($numList,$stylesOpt);}
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum | LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum
{$$ = $1;yy.updateLinkInterpolate([$3],$7);} {$$ = $LINKSTYLE;yy.updateLinkInterpolate([$DEFAULT],$alphaNum);}
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum | LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum
{$$ = $1;yy.updateLinkInterpolate($3,$7);} {$$ = $LINKSTYLE;yy.updateLinkInterpolate($numList,$alphaNum);}
; ;
numList: NUM numList: NUM
{$$ = [$1]} {$$ = [$NUM]}
| numList COMMA NUM | numList COMMA NUM
{$1.push($3);$$ = $1;} {$numList.push($NUM);$$ = $numList;}
; ;
stylesOpt: style stylesOpt: style
{$$ = [$1]} {$$ = [$style]}
| stylesOpt COMMA style | stylesOpt COMMA style
{$1.push($3);$$ = $1;} {$stylesOpt.push($style);$$ = $stylesOpt;}
; ;
style: styleComponent style: styleComponent
|style styleComponent |style styleComponent
{$$ = $1 + $2;} {$$ = $style + $styleComponent;}
; ;
styleComponent: ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ; styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | BRKT | STYLE | PCT ;
/* Token lists */ /* Token lists */
idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON | AMP | BRKT | MULT | UNICODE_TEXT;
textToken : textNoTagsToken | TAGSTART | TAGEND | START_LINK | PCT | DEFAULT; textToken : TEXT | TAGSTART | TAGEND | UNICODE_TEXT;
textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ; textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | AMP | UNICODE_TEXT | COLON | MULT | BRKT | keywords | START_LINK ;
edgeTextToken : EDGE_TEXT | UNICODE_TEXT ;
alphaNumToken : NUM | UNICODE_TEXT | NODE_STRING | DIR | DOWN | MINUS | COMMA | COLON | AMP | BRKT | MULT;
idString idString
:idStringToken :idStringToken
{$$=$1} {$$=$idStringToken}
| idString idStringToken | idString idStringToken
{$$=$1+''+$2} {$$=$idString+''+$idStringToken}
; ;
alphaNum alphaNum
: alphaNumStatement : alphaNumToken
{$$=$1;} {$$=$alphaNumToken;}
| alphaNum alphaNumStatement | alphaNum alphaNumToken
{$$=$1+''+$2;} {$$=$alphaNum+''+$alphaNumToken;}
; ;
alphaNumStatement
: DIR
{$$=$1;}
| alphaNumToken
{$$=$1;}
| DOWN
{$$='v';}
| MINUS
{$$='-';}
;
direction direction
: direction_tb : direction_tb
@@ -571,9 +608,4 @@ direction
{ $$={stmt:'dir', value:'LR'};} { $$={stmt:'dir', value:'LR'};}
; ;
alphaNumToken : PUNCTUATION | AMP | UNICODE_TEXT | NUM| ALPHA | COLON | COMMA | PLUS | EQUALS | MULT | DOT | BRKT| UNDERSCORE ;
idStringToken : ALPHA|UNDERSCORE |UNICODE_TEXT | NUM| COLON | COMMA | PLUS | MINUS | DOWN |EQUALS | MULT | BRKT | DOT | PUNCTUATION | AMP | DEFAULT;
graphCodeTokens: STADIUMSTART | STADIUMEND | SUBROUTINESTART | SUBROUTINEEND | VERTEX_WITH_PROPS_START | CYLINDERSTART | CYLINDEREND | TRAPSTART | TRAPEND | INVTRAPSTART | INVTRAPEND | PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAGSTART | TAGEND | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI;
%% %%

View File

@@ -1,7 +1,7 @@
import { select } from 'd3';
import { log } from '../../logger.js'; import { log } from '../../logger.js';
import { getConfig } from '../../config.js'; import { configureSvgSize } from '../../setupGraphViewbox.js';
import type { DrawDefinition, HTML, SVG } from '../../diagram-api/types.js'; import type { DrawDefinition, Group, SVG } from '../../diagram-api/types.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
/** /**
* Draws a an info picture in the tag with id: id based on the graph definition in text. * Draws a an info picture in the tag with id: id based on the graph definition in text.
@@ -11,40 +11,20 @@ import type { DrawDefinition, HTML, SVG } from '../../diagram-api/types.js';
* @param version - MermaidJS version. * @param version - MermaidJS version.
*/ */
const draw: DrawDefinition = (text, id, version) => { const draw: DrawDefinition = (text, id, version) => {
try { log.debug('rendering info diagram\n' + text);
log.debug('rendering info diagram\n' + text);
const { securityLevel } = getConfig(); const svg: SVG = selectSvgElement(id);
// handle root and document for when rendering in sandbox mode configureSvgSize(svg, 100, 400, true);
let sandboxElement: HTML | undefined;
let document: Document | null | undefined;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
document = sandboxElement.nodes()[0].contentDocument;
}
// @ts-ignore - figure out how to assign HTML to document type const group: Group = svg.append('g');
const root: HTML = group
sandboxElement !== undefined && document !== undefined && document !== null .append('text')
? select(document) .attr('x', 100)
: select('body'); .attr('y', 40)
.attr('class', 'version')
const svg: SVG = root.select('#' + id); .attr('font-size', 32)
svg.attr('height', 100); .style('text-anchor', 'middle')
svg.attr('width', 400); .text(`v${version}`);
const g = svg.append('g');
g.append('text') // text label for the x axis
.attr('x', 100)
.attr('y', 40)
.attr('class', 'version')
.attr('font-size', '32px')
.style('text-anchor', 'middle')
.text('v ' + version);
} catch (e) {
log.error('error while rendering info diagram', e);
}
}; };
export const renderer = { draw }; export const renderer = { draw };

View File

@@ -390,7 +390,7 @@ It is possible to escape characters using the syntax exemplified here.
```mermaid-example ```mermaid-example
flowchart LR flowchart LR
A["A double quote:#quot;"] -->B["A dec char:#9829;"] A["A double quote:#quot;"] --> B["A dec char:#9829;"]
``` ```
Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names.

View File

@@ -284,7 +284,9 @@ export const cleanUpSvgCode = (
* TODO replace btoa(). Replace with buf.toString('base64')? * TODO replace btoa(). Replace with buf.toString('base64')?
*/ */
export const putIntoIFrame = (svgCode = '', svgElement?: D3Element): string => { export const putIntoIFrame = (svgCode = '', svgElement?: D3Element): string => {
const height = svgElement ? svgElement.viewBox.baseVal.height + 'px' : IFRAME_HEIGHT; const height = svgElement?.viewBox?.baseVal?.height
? svgElement.viewBox.baseVal.height + 'px'
: IFRAME_HEIGHT;
const base64encodedSrc = btoa('<body style="' + IFRAME_BODY_STYLE + '">' + svgCode + '</body>'); const base64encodedSrc = btoa('<body style="' + IFRAME_BODY_STYLE + '">' + svgCode + '</body>');
return `<iframe style="width:${IFRAME_WIDTH};height:${height};${IFRAME_STYLES}" src="data:text/html;base64,${base64encodedSrc}" sandbox="${IFRAME_SANDBOX_OPTS}"> return `<iframe style="width:${IFRAME_WIDTH};height:${height};${IFRAME_STYLES}" src="data:text/html;base64,${base64encodedSrc}" sandbox="${IFRAME_SANDBOX_OPTS}">
${IFRAME_NOT_SUPPORTED_MSG} ${IFRAME_NOT_SUPPORTED_MSG}

View File

@@ -0,0 +1,22 @@
import { select } from 'd3';
import { getConfig } from '../config.js';
import type { HTML, SVG } from '../diagram-api/types.js';
/**
* Selects the SVG element using {@link id}.
*
* @param id - The diagram ID.
* @returns The selected {@link SVG} element using {@link id}.
*/
export const selectSvgElement = (id: string): SVG => {
const { securityLevel } = getConfig();
// handle root and document for when rendering in sandbox mode
let root: HTML = select('body');
if (securityLevel === 'sandbox') {
const sandboxElement: HTML = select(`#i${id}`);
const doc: Document = sandboxElement.node()?.contentDocument ?? document;
root = select(doc.body as HTMLIFrameElement);
}
const svg: SVG = root.select(`#${id}`);
return svg;
};

View File

@@ -1,4 +1,5 @@
import { log } from './logger.js'; import { log } from './logger.js';
import { SVG } from './diagram-api/types.js';
/** /**
* Applies d3 attributes * Applies d3 attributes
@@ -35,7 +36,7 @@ export const calculateSvgSizeAttrs = function (height, width, useMaxWidth) {
/** /**
* Applies attributes from `calculateSvgSizeAttrs` * Applies attributes from `calculateSvgSizeAttrs`
* *
* @param {SVGSVGElement} svgElem The SVG Element to configure * @param {SVG} svgElem The SVG Element to configure
* @param {number} height The height of the SVG * @param {number} height The height of the SVG
* @param {number} width The width of the SVG * @param {number} width The width of the SVG
* @param {boolean} useMaxWidth Whether or not to use max-width and set width to 100% * @param {boolean} useMaxWidth Whether or not to use max-width and set width to 100%

16
pnpm-lock.yaml generated
View File

@@ -413,8 +413,8 @@ importers:
packages/mermaid-zenuml: packages/mermaid-zenuml:
dependencies: dependencies:
'@zenuml/core': '@zenuml/core':
specifier: ^3.0.3 specifier: ^3.0.6
version: 3.0.3(ts-node@10.9.1) version: 3.0.6(ts-node@10.9.1)
devDependencies: devDependencies:
mermaid: mermaid:
specifier: workspace:^ specifier: workspace:^
@@ -5738,14 +5738,14 @@ packages:
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
dev: true dev: true
/@zenuml/core@3.0.3(ts-node@10.9.1): /@zenuml/core@3.0.6(ts-node@10.9.1):
resolution: {integrity: sha512-Wp6yF5iERvGXrR3z/mNhQYP2uI54Bd7RQ2ZwA26Lca+tufj4X8pfqGVSORLqbUl4wjjdcwHc2RNT8AU+cz2NCg==} resolution: {integrity: sha512-azEBVrl+ClCPhII92TbzBUFcWhIjlOPdEHVzF6eZXs5Oy4JlrfldS5pAZBHCFL4riOBsjZ5sHHmQLQg9V07T4Q==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
dependencies: dependencies:
'@types/assert': 1.5.6 '@types/assert': 1.5.6
'@types/ramda': 0.28.25 '@types/ramda': 0.28.25
'@vue/compat': 3.3.4(vue@3.3.4) '@vue/compat': 3.3.4(vue@3.3.4)
antlr4: 4.13.0 antlr4: 4.11.0
color-string: 1.9.1 color-string: 1.9.1
dom-to-image-more: 2.16.0 dom-to-image-more: 2.16.0
file-saver: 2.0.5 file-saver: 2.0.5
@@ -6026,9 +6026,9 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: true dev: true
/antlr4@4.13.0: /antlr4@4.11.0:
resolution: {integrity: sha512-zooUbt+UscjnWyOrsuY/tVFL4rwrAGwOivpQmvmUDE22hy/lUA467Rc1rcixyRwcRUIXFYBwv7+dClDSHdmmew==} resolution: {integrity: sha512-GUGlpE2JUjAN+G8G5vY+nOoeyNhHsXoIJwP1XF1oRw89vifA1K46T6SEkwLwr7drihN7I/lf0DIjKc4OZvBX8w==}
engines: {node: '>=16'} engines: {node: '>=14'}
dev: false dev: false
/any-promise@1.3.0: /any-promise@1.3.0: