Merge branch 'develop' into sidv/splitDiagrams

* develop: (79 commits)
  Minor change
  feat: Add @include support to docs
  feat: Add @include example to docs
  feat: Add @include support to docs
  cleanup
  fix lines
  fix Async rendering
  Revert "sync"
  chore(deps): update pnpm to v7.17.1
  chore(deps): remove dependency on `graphlib`
  test(e2e): make gitgraph snapshots consistent
  chore: Fix lint
  test: Update vitest
  Add official vim plugin to list in integrations
  chore: Cleanup package.json
  chore: Cleanup package.json
  chore: Cleanup package.json
  fix lock
  Docs
  Fix: array concat
  ...
This commit is contained in:
Sidharth Vinod
2022-11-30 19:15:21 +05:30
151 changed files with 2858 additions and 2060 deletions

View File

@@ -21,7 +21,17 @@
"plugin:@cspell/recommended",
"prettier"
],
"plugins": ["@typescript-eslint", "no-only-tests", "html", "jest", "jsdoc", "json", "@cspell"],
"plugins": [
"@typescript-eslint",
"no-only-tests",
"html",
"jest",
"jsdoc",
"json",
"@cspell",
"lodash",
"unicorn"
],
"rules": {
"curly": "error",
"no-console": "error",
@@ -53,7 +63,30 @@
"allowEmptyCatch": true
}
],
"no-only-tests/no-only-tests": "error"
"no-only-tests/no-only-tests": "error",
"lodash/import-scope": ["error", "method"],
"unicorn/better-regex": "error",
"unicorn/no-abusive-eslint-disable": "error",
"unicorn/no-array-push-push": "error",
"unicorn/no-for-loop": "error",
"unicorn/no-instanceof-array": "error",
"unicorn/no-typeof-undefined": "error",
"unicorn/no-unnecessary-await": "error",
"unicorn/no-unsafe-regex": "warn",
"unicorn/no-useless-promise-resolve-reject": "error",
"unicorn/prefer-array-find": "error",
"unicorn/prefer-array-flat-map": "error",
"unicorn/prefer-array-index-of": "error",
"unicorn/prefer-array-some": "error",
"unicorn/prefer-default-parameters": "error",
"unicorn/prefer-includes": "error",
"unicorn/prefer-negative-index": "error",
"unicorn/prefer-object-from-entries": "error",
"unicorn/prefer-string-starts-ends-with": "error",
"unicorn/prefer-string-trim-start-end": "error",
"unicorn/string-content": "error",
"unicorn/prefer-spread": "error",
"unicorn/no-lonely-if": "error"
},
"overrides": [
{

View File

@@ -1,28 +0,0 @@
name: Documentation Checks
on:
push:
branches:
- develop
paths:
- 'packages/mermaid/src/docs/**/*'
pull_request:
branches:
- develop
paths:
- 'packages/mermaid/src/docs/**/*'
jobs:
spellcheck:
name: 'Docs: Spellcheck'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
name: Check out the code
- uses: actions/setup-node@v3
name: Setup node
with:
node-version: '18'
- run: npm install -g cspell
name: Install cSpell
- run: cspell --config ./cSpell.json "packages/mermaid/src/docs/**/*.md" --no-progress
name: Run cSpell

View File

@@ -36,7 +36,7 @@ jobs:
restore-keys: cache-lychee-
- name: Link Checker
uses: lycheeverse/lychee-action@v1.5.2
uses: lycheeverse/lychee-action@v1.5.4
with:
args: --verbose --no-progress --cache --max-cache-age 1d packages/mermaid/src/docs/**/*.md README.md README.zh-CN.md
fail: true

View File

@@ -1,4 +0,0 @@
{
"!(docs/**/*)*.{ts,js,json,html,md,mts}": ["eslint --fix", "prettier --write"],
"cSpell.json": ["ts-node-esm scripts/fixCSpell.ts"]
}

5
.lintstagedrc.mjs Normal file
View File

@@ -0,0 +1,5 @@
export default {
'!(docs/**/*)*.{ts,js,json,html,md,mts}': ['eslint --fix', 'prettier --write'],
'cSpell.json': ['ts-node-esm scripts/fixCSpell.ts'],
'**/*.jison': ['pnpm -w run lint:jison'],
};

View File

@@ -3,4 +3,5 @@ cypress/platform/xss3.html
.cache
coverage
# Autogenerated by PNPM
pnpm-lock.yaml
pnpm-lock.yaml
stats

View File

@@ -16,13 +16,13 @@ type OutputOptions = Exclude<
undefined
>['output'];
const visualizerOptions = (packageName: string): PluginOption[] => {
const visualizerOptions = (packageName: string, core = false): PluginOption[] => {
if (packageName !== 'mermaid' || !visualize) {
return [];
}
return ['network', 'treemap', 'sunburst'].map((chartType) =>
visualizer({
filename: `./stats/${chartType}.html`,
filename: `./stats/${chartType}${core ? '.core' : ''}.html`,
template: chartType as TemplateType,
gzipSize: true,
brotliSize: true,
@@ -56,7 +56,7 @@ interface BuildOptions {
}
export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions): InlineConfig => {
const external = ['require', 'fs', 'path'];
const external: (string | RegExp)[] = ['require', 'fs', 'path'];
console.log(entryName, packageOptions[entryName]);
const { name, file, packageName } = packageOptions[entryName];
let output: OutputOptions = [
@@ -80,7 +80,9 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
);
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
external.push(...Object.keys(dependencies));
// Ignore dependencies and any dependencies of dependencies
// Adapted from the RegEx used by `rollup-plugin-node`
external.push(new RegExp('^(?:' + Object.keys(dependencies).join('|') + ')(?:/.+)?$'));
// This needs to be an array. Otherwise vite will build esm & umd with same name and overwrite esm with umd.
output = [
{
@@ -112,7 +114,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
resolve: {
extensions: ['.jison', '.js', '.ts', '.json'],
},
plugins: [jisonPlugin(), ...visualizerOptions(packageName)],
plugins: [jisonPlugin(), ...visualizerOptions(packageName, core)],
};
if (watch && config.build) {
@@ -149,6 +151,9 @@ if (watch) {
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-mindmap' }));
// build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
}
} else if (visualize) {
await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));
await build(getBuildConfig({ minify: false, core: false, entryName: 'mermaid' }));
} else {
void main();
}

View File

@@ -1,23 +1,6 @@
# mermaid
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
# Whoa, what's going on here?
We are transforming the Mermaid repository to a so called mono-repo. This is a part of the effort to decouple the diagrams from the core of mermaid. This will:
- Make it possible to select which diagrams to include on your site
- Open up for lazy loading
- Make it possible to add diagrams from outside of the Mermaid repository
- Separate the release flow between different diagrams and the Mermaid core
As such be aware of some changes..
# We use pnpm now
# The source code has moved
It is now located in the src folder for each respective package located as subfolders in packages.
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
English | [简体中文](./README.zh-CN.md)

View File

@@ -1,6 +1,6 @@
# mermaid
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
[English](./README.md) | 简体中文

View File

@@ -53,6 +53,18 @@ export const MockD3 = (name, parent) => {
get __parent() {
return parent;
},
node() {
return {
getBBox() {
return {
x: 5,
y: 10,
height: 15,
width: 20,
};
},
};
},
};
elem.append = (name) => {
const mockElem = MockD3(name, elem);

View File

@@ -13,8 +13,10 @@
"bbox",
"bilkent",
"bisheng",
"braintree",
"brolin",
"brotli",
"classdef",
"codedoc",
"colour",
"cpettitt",
@@ -30,9 +32,11 @@
"edgechromium",
"faber",
"flatmap",
"ftplugin",
"gantt",
"gitea",
"gitgraph",
"globby",
"graphlib",
"grav",
"greywolf",
@@ -40,11 +44,13 @@
"jaoude",
"jison",
"kaufmann",
"khroma",
"klemm",
"klink",
"knsv",
"knut",
"laganeckas",
"lintstagedrc",
"lucida",
"matthieu",
"mdbook",
@@ -74,6 +80,7 @@
"treemap",
"ts-nocheck",
"tuleap",
"unist",
"verdana",
"viewports",
"vinod",

View File

@@ -496,4 +496,16 @@ describe('Class diagram V2', () => {
);
cy.get('svg');
});
it('1433: should render a simple class with a title', () => {
imgSnapshotTest(
`---
title: simple class diagram
---
classDiagram-v2
class Class10
`,
{}
);
});
});

View File

@@ -273,4 +273,17 @@ describe('Entity Relationship Diagram', () => {
);
cy.get('svg');
});
it('1433: should render a simple ER diagram with a title', () => {
imgSnapshotTest(
`---
title: simple ER diagram
---
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE-ITEM : contains
`,
{}
);
});
});

View File

@@ -663,4 +663,15 @@ flowchart RL
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('1433: should render a titled flowchart with titleTopMargin set to 0', () => {
imgSnapshotTest(
`---
title: Simple flowchart
---
flowchart TD
A --> B
`,
{ titleTopMargin: 0 }
);
});
});

View File

@@ -326,7 +326,7 @@ describe('Gantt diagram', () => {
);
cy.get('svg').should((svg) => {
const el = svg.get(0);
const children = Array.from(el.children);
const children = [...el.children];
const titleEl = children.find(function (node) {
return node.tagName === 'title';

View File

@@ -322,4 +322,15 @@ describe('Git Graph diagram', () => {
{}
);
});
it('1433: should render a simple gitgraph with a title', () => {
imgSnapshotTest(
`---
title: simple gitGraph
---
gitGraph
commit id: "1-abcdefg"
`,
{}
);
});
});

View File

@@ -1,75 +0,0 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
describe('Mindmap', () => {
it('square shape', () => {
imgSnapshotTest(
`
mindmap
root[
The root
]
`,
{}
);
cy.get('svg');
});
it('rounded rect shape', () => {
imgSnapshotTest(
`
mindmap
root((
The root
))
`,
{}
);
cy.get('svg');
});
it('circle shape', () => {
imgSnapshotTest(
`
mindmap
root(
The root
)
`,
{}
);
cy.get('svg');
});
it('default shape', () => {
imgSnapshotTest(
`
mindmap
The root
`,
{}
);
cy.get('svg');
});
it('adding children', () => {
imgSnapshotTest(
`
mindmap
The root
child1
child2
`,
{}
);
cy.get('svg');
});
it('adding grand children', () => {
imgSnapshotTest(
`
mindmap
The root
child1
child2
child3
`,
{}
);
cy.get('svg');
});
});

View File

@@ -1,115 +0,0 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
describe('Mindmaps', () => {
it('Only a root', () => {
imgSnapshotTest(
`mindmap
root
`,
{}
);
});
it('a root with a shape', () => {
imgSnapshotTest(
`mindmap
root[root]
`,
{}
);
});
it('a root with wrapping text and a shape', () => {
imgSnapshotTest(
`mindmap
root[A root with a long text that wraps to keep the node size in check]
`,
{}
);
});
it('a root with an icon', () => {
imgSnapshotTest(
`mindmap
root[root]
::icon(mdi mdi-fire)
`,
{}
);
});
it('Blang and cloud shape', () => {
imgSnapshotTest(
`mindmap
root))bang((
::icon(mdi mdi-fire)
a))Another bang((
::icon(mdi mdi-fire)
a)A cloud(
::icon(mdi mdi-fire)
`,
{}
);
});
it('Blang and cloud shape with icons', () => {
imgSnapshotTest(
`mindmap
root))bang((
a))Another bang((
a)A cloud(
`,
{}
);
});
it('braches', () => {
imgSnapshotTest(
`mindmap
root
child1
grandchild 1
grandchild 2
child2
grandchild 3
grandchild 4
child3
grandchild 5
grandchild 6
`,
{}
);
});
it('braches with shapes and labels', () => {
imgSnapshotTest(
`mindmap
root
child1((Circle))
grandchild 1
grandchild 2
child2(Round rectangle)
grandchild 3
grandchild 4
child3[Square]
grandchild 5
::icon(mdi mdi-fire)
gc6((grand<br/>child 6))
::icon(mdi mdi-fire)
`,
{}
);
});
it('text shouhld wrap with icon', () => {
imgSnapshotTest(
`mindmap
root
Child3(A node with an icon and with a long text that wraps to keep the node size in check)
`,
{}
);
});
/* The end */
});

View File

@@ -0,0 +1,233 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
/**
* Check whether the SVG Element has a Mindmap root
*
* Sometimes, Cypress takes a snapshot before the mermaid mindmap has finished
* generating the SVG.
*
* @param $p - The element to check.
*/
function shouldHaveRoot($p: JQuery<SVGSVGElement>) {
// get HTML Element from jquery element
const svgElement = $p[0];
expect(svgElement.nodeName).equal('svg');
const sectionRoots = svgElement.getElementsByClassName('mindmap-node section-root');
// mindmap should have at least one root section
expect(sectionRoots).to.have.lengthOf.at.least(1);
}
describe('Mindmaps', () => {
it('Only a root', () => {
imgSnapshotTest(
`mindmap
root
`,
{},
undefined,
shouldHaveRoot
);
});
it('a root with a shape', () => {
imgSnapshotTest(
`mindmap
root[root]
`,
{},
undefined,
shouldHaveRoot
);
});
it('a root with wrapping text and a shape', () => {
imgSnapshotTest(
`mindmap
root[A root with a long text that wraps to keep the node size in check]
`,
{},
undefined,
shouldHaveRoot
);
});
it('a root with an icon', () => {
imgSnapshotTest(
`mindmap
root[root]
::icon(mdi mdi-fire)
`,
{},
undefined,
shouldHaveRoot
);
});
it('Blang and cloud shape', () => {
imgSnapshotTest(
`mindmap
root))bang((
::icon(mdi mdi-fire)
a))Another bang((
::icon(mdi mdi-fire)
a)A cloud(
::icon(mdi mdi-fire)
`,
{},
undefined,
shouldHaveRoot
);
});
it('Blang and cloud shape with icons', () => {
imgSnapshotTest(
`mindmap
root))bang((
a))Another bang((
a)A cloud(
`,
{},
undefined,
shouldHaveRoot
);
});
it('braches', () => {
imgSnapshotTest(
`mindmap
root
child1
grandchild 1
grandchild 2
child2
grandchild 3
grandchild 4
child3
grandchild 5
grandchild 6
`,
{},
undefined,
shouldHaveRoot
);
});
it('braches with shapes and labels', () => {
imgSnapshotTest(
`mindmap
root
child1((Circle))
grandchild 1
grandchild 2
child2(Round rectangle)
grandchild 3
grandchild 4
child3[Square]
grandchild 5
::icon(mdi mdi-fire)
gc6((grand<br/>child 6))
::icon(mdi mdi-fire)
`,
{},
undefined,
shouldHaveRoot
);
});
it('text shouhld wrap with icon', () => {
imgSnapshotTest(
`mindmap
root
Child3(A node with an icon and with a long text that wraps to keep the node size in check)
`,
{},
undefined,
shouldHaveRoot
);
});
it('square shape', () => {
imgSnapshotTest(
`
mindmap
root[
The root
]
`,
{},
undefined,
shouldHaveRoot
);
cy.get('svg');
});
it('rounded rect shape', () => {
imgSnapshotTest(
`
mindmap
root((
The root
))
`,
{},
undefined,
shouldHaveRoot
);
cy.get('svg');
});
it('circle shape', () => {
imgSnapshotTest(
`
mindmap
root(
The root
)
`,
{},
undefined,
shouldHaveRoot
);
cy.get('svg');
});
it('default shape', () => {
imgSnapshotTest(
`
mindmap
The root
`,
{},
undefined,
shouldHaveRoot
);
cy.get('svg');
});
it('adding children', () => {
imgSnapshotTest(
`
mindmap
The root
child1
child2
`,
{},
undefined,
shouldHaveRoot
);
cy.get('svg');
});
it('adding grand children', () => {
imgSnapshotTest(
`
mindmap
The root
child1
child2
child3
`,
{},
undefined,
shouldHaveRoot
);
cy.get('svg');
});
/* The end */
});

View File

@@ -96,7 +96,7 @@ describe('Requirement diagram', () => {
);
cy.get('svg').should((svg) => {
const el = svg.get(0);
const children = Array.from(el.children);
const children = [...el.children];
const titleEl = children.find(function (node) {
return node.tagName === 'title';

View File

@@ -559,4 +559,16 @@ stateDiagram-v2
);
});
});
it('1433: should render a simple state diagram with a title', () => {
imgSnapshotTest(
`---
title: simple state diagram
---
stateDiagram-v2
[*] --> State1
State1 --> [*]
`,
{}
);
});
});

View File

@@ -25,6 +25,7 @@ describe('themeCSS balancing, it', () => {
});
});
// TODO: Delete/Rename this describe, keeping the inner contents.
describe('Pie Chart', () => {
// beforeEach(()=>{
// cy.clock((new Date('2014-06-09')).getTime());

View File

@@ -56,10 +56,11 @@
<body>
<div>Security check</div>
<pre id="diagram" class="mermaid">
flowchart TD
A --> B
B --> C
A --> C
graph LR
subgraph external
inside
end
outside --> external
</pre>
<pre id="diagram" class="mermaid">
mindmap
@@ -91,9 +92,13 @@ mindmap
<!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> -->
<!-- <script src="./mermaid-example-diagram-detector.js"></script> -->
<!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> -->
<script src="./mermaid.js"></script>
<!-- <script src="./mermaid.js"></script> -->
<script>
<script type="module">
import mindmap from '../../packages/mermaid-mindmap/src/detector';
// import example from '../../packages/mermaid-example-diagram/src/detector';
import mermaid from '../../packages/mermaid/src/mermaid';
await mermaid.registerExternalDiagrams([mindmap]);
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};

View File

@@ -54,7 +54,7 @@ function merge(current, update) {
if (
current.hasOwnProperty(key) &&
typeof current[key] === 'object' &&
!(current[key] instanceof Array)
!Array.isArray(current[key])
) {
merge(current[key], update[key]);

8
cypress/tsconfig.json Normal file
View File

@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "es2020",
"lib": ["es2020", "dom"],
"types": ["cypress", "node"]
},
"include": ["**/*.ts"]
}

View File

@@ -17,6 +17,9 @@
<h1>Class diagram demos</h1>
<pre class="mermaid">
---
title: Demo Class Diagram
---
classDiagram
accTitle: Demo Class Diagram
accDescr: This class diagram show the abstract Animal class, and 3 classes that inherit from it: Duck, Fish, and Zebra.

View File

@@ -20,6 +20,9 @@
<body>
<pre class="mermaid">
---
title: This is a title
---
erDiagram
%% title This is a title
%% accDescription Test a description

View File

@@ -17,6 +17,9 @@
<h2>Sample 1</h2>
<h3>graph</h3>
<pre class="mermaid">
---
title: This is a complicated flow
---
graph LR
accTitle: This is a complicated flow
accDescr: This is the descriptoin for the complicated flow.
@@ -221,6 +224,9 @@
<h2>Sample 2</h2>
<h3>graph</h3>
<pre class="mermaid">
---
title: What to buy
---
graph TD
accTitle: What to buy
accDescr: Options of what to buy with Christmas money

View File

@@ -16,6 +16,9 @@
<body>
<h1>Git diagram demo</h1>
<pre class="mermaid">
---
title: Simple Git diagram
---
gitGraph:
options
{

View File

@@ -48,6 +48,9 @@
<li>
<h2><a href="./journey.html">Journey</a></h2>
</li>
<li>
<h2><a href="./mindmap.html">Mindmap</a></h2>
</li>
<li>
<h2><a href="./pie.html">Pie</a></h2>
</li>

View File

@@ -16,8 +16,10 @@
<body>
<h1>Journey diagram demo</h1>
<pre class="mermaid">
journey
title My working day
---
title: My working day
---
journey
accTitle: Very simple journey demo
accDescr: 2 main sections: work and home, each with just a few tasks

108
demos/mindmap.html Normal file
View File

@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Mindmap Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<style>
div.mermaid {
/* font-family: 'trebuchet ms', verdana, arial; */
font-family: 'Courier New', Courier, monospace !important;
}
</style>
</head>
<body>
<h1>Mindmap diagram demo</h1>
<pre class="mermaid">
mindmap
root
child1((Circle))
grandchild 1
grandchild 2
child2(Round rectangle)
grandchild 3
grandchild 4
child3[Square]
grandchild 5
::icon(mdi mdi-fire)
gc6((grand<br/>child 6))
::icon(mdi mdi-fire)
gc7((grand<br/>grand<br/>child 8))
</pre>
<h2>Mindmap with root wrapping text and a shape</h2>
<pre class="mermaid">
mindmap
root[A root with a long text that wraps to keep the node size in check]
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
import mermaidMindmap from './mermaid-mindmap.esm.mjs';
const ALLOWED_TAGS = [
'a',
'b',
'blockquote',
'br',
'dd',
'div',
'dl',
'dt',
'em',
'foreignObject',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'h7',
'h8',
'hr',
'i',
'li',
'ul',
'ol',
'p',
'pre',
'span',
'strike',
'strong',
'table',
'tbody',
'td',
'tfoot',
'th',
'thead',
'tr',
];
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};
await mermaid.registerExternalDiagrams([mermaidMindmap]);
mermaid.initialize({
theme: 'base',
startOnLoad: true,
logLevel: 0,
flowchart: {
useMaxWidth: false,
htmlLabels: true,
},
gantt: {
useMaxWidth: false,
},
useMaxWidth: false,
});
function callback() {
alert('It worked');
}
mermaid.parseError = function (err, hash) {
console.error('In parse error:');
console.error(err);
};
</script>
</body>
</html>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
@@ -17,6 +17,9 @@
<h1>State diagram demos</h1>
<h2>Very simple showing change from State1 to State2</h2>
<pre class="mermaid">
---
title: Very simple diagram
---
stateDiagram
accTitle: This is the accessible title
accDescr:This is an accessible description
@@ -30,8 +33,9 @@
<p>
<code>
classDef notMoving fill:white<br />
classDef movement font-style:italic;<br />
classDef badBadEvent fill:#f00,color:white,font-weight:bold<br />
classDef movement font-style:italic<br />
classDef badBadEvent
fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow<br />
</code>
</p>
<h4>And these are how they are applied:</h4>
@@ -43,15 +47,20 @@
</code>
</p>
<pre class="mermaid">
stateDiagram-v2
---
title: Very simple diagram
---
stateDiagram
direction TB
accTitle: This is the accessible title
accDescr: This is an accessible description
classDef notMoving fill:white
classDef movement font-style:italic;
classDef badBadEvent fill:#f00,color:white,font-weight:bold
classDef movement font-style:italic
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
[*] --> Still
[*]--> Still
Still --> [*]
Still --> Moving
Moving --> Still
@@ -61,10 +70,57 @@
class Still notMoving
class Moving, Crash movement
class Crash badBadEvent
class end badBadEvent
</pre>
<hr />
<h2>Here is a diagram that uses the ::: operator to apply styles to states</h2>
<h4>Here are the <code>classDef</code> statements:</h4>
<p>
<code>
classDef notMoving fill:white<br />
classDef movement font-style:italic<br />
classDef badBadEvent
fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow<br />
</code>
</p>
<h4>And these are how they are applied:</h4>
<p>
<code>
[*] --> Still:::notMoving<br />
...<br />
Still --> Moving:::movement<br />
...<br />
Moving --> Crash:::movement<br />
Crash:::badBadEvent --> [*]<br />
</code>
</p>
<p>
Note that both the starting state and the end state have styles applied:<br />
The start state has the <i>start</i> classDef style<br />and the end state has the
<i>stop</i> classDef style applied.
</p>
<pre class="mermaid">
stateDiagram
direction TB
accTitle: This is the accessible title
accDescr: This is an accessible description
classDef notMoving fill:white
classDef movement font-style:italic
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
[*] --> Still:::notMoving
Still --> [*]
Still --> Moving:::movement
Moving --> Still
Moving --> Crash:::movement
Crash:::badBadEvent --> [*]
</pre>
<hr />
<pre class="mermaid">
stateDiagram-v2
accTitle: very very simple state
@@ -73,6 +129,20 @@
</pre>
<hr />
<h2>States with spaces in them</h2>
<pre class="mermaid">
stateDiagram
classDef yourState font-style:italic,font-weight:bold,fill:white
yswsii: Your state with spaces in it
[*] --> yswsii:::yourState
[*] --> SomeOtherState
SomeOtherState --> YetAnotherState
yswsii --> YetAnotherState
YetAnotherState --> [*]
</pre>
<hr />
<h2>You can label the relationships</h2>
<pre class="mermaid">
stateDiagram-v2
@@ -121,7 +191,7 @@
<pre class="mermaid">
stateDiagram-v2
[*] --> S1
S1 --> S2: This long line uses a br tag<br/>to create multiple<br/>lines.
S1 --> S2: This long line uses a br tag<br />to create multiple<br />lines.
S1 --> S3: This transition descripton uses \na newline character\nto create multiple\nlines.
</pre>
@@ -133,7 +203,7 @@
direction LR
State1: A state with a note
note right of State1
Important information!<br />You can write notes.<br/>And\nthey\ncan\nbe\nmulti-\nline.
Important information!<br />You can write notes.<br />And\nthey\ncan\nbe\nmulti-\nline.
end note
State1 --> State2
note left of State2 : Notes can be to the left of a state\n(like this one).

View File

@@ -36,7 +36,7 @@ Pushes in a directive to the configuration
#### Defined in
[config.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L193)
[config.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L191)
---
@@ -60,7 +60,7 @@ The currentConfig
#### Defined in
[config.ts:138](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L138)
[config.ts:137](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L137)
---
@@ -84,7 +84,7 @@ The siteConfig
#### Defined in
[config.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L97)
[config.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L96)
---
@@ -118,7 +118,7 @@ The siteConfig
#### Defined in
[config.ts:225](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L225)
[config.ts:223](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L223)
---
@@ -147,7 +147,7 @@ options in-place
#### Defined in
[config.ts:153](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L153)
[config.ts:152](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L152)
---
@@ -167,7 +167,7 @@ options in-place
#### Defined in
[config.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L76)
[config.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L75)
---
@@ -199,7 +199,7 @@ The currentConfig merged with the sanitized conf
#### Defined in
[config.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L114)
[config.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L113)
---
@@ -232,7 +232,7 @@ The new siteConfig
#### Defined in
[config.ts:62](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L62)
[config.ts:61](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L61)
---
@@ -273,4 +273,4 @@ The new siteConfig
#### Defined in
[config.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L80)
[config.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L79)

View File

@@ -14,7 +14,7 @@
#### Defined in
[defaultConfig.ts:1881](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L1881)
[defaultConfig.ts:1933](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L1933)
---

View File

@@ -80,7 +80,7 @@ mermaid.initialize(config);
#### Defined in
[mermaidAPI.ts:938](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L938)
[mermaidAPI.ts:939](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L939)
## Functions
@@ -111,7 +111,7 @@ Return the last node appended
#### Defined in
[mermaidAPI.ts:289](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L289)
[mermaidAPI.ts:284](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L284)
---
@@ -137,7 +137,7 @@ the cleaned up svgCode
#### Defined in
[mermaidAPI.ts:240](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L240)
[mermaidAPI.ts:235](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L235)
---
@@ -163,7 +163,7 @@ the string with all the user styles
#### Defined in
[mermaidAPI.ts:167](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L167)
[mermaidAPI.ts:164](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L164)
---
@@ -186,7 +186,7 @@ the string with all the user styles
#### Defined in
[mermaidAPI.ts:217](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L217)
[mermaidAPI.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L212)
---
@@ -213,7 +213,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:151](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L151)
[mermaidAPI.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L148)
---
@@ -279,7 +279,7 @@ Put the svgCode into an iFrame. Return the iFrame code
#### Defined in
[mermaidAPI.ts:268](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L268)
[mermaidAPI.ts:263](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L263)
---
@@ -305,4 +305,4 @@ Remove any existing elements from the given document
#### Defined in
[mermaidAPI.ts:340](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L340)
[mermaidAPI.ts:335](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L335)

View File

@@ -348,7 +348,7 @@ on what kind of integration you use.
## Using the mermaid object
Is it possible to set some configuration via the mermaid object. The two parameters that are supported using this
It is possible to set some configuration via the mermaid object. The two parameters that are supported using this
approach are:
- mermaid.startOnLoad

View File

@@ -1,179 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>
mermaid - Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams,
gantt charts and git graphs.
</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta
name="description"
content="Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs."
/>
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<!-- <link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css"> -->
<link rel="stylesheet" href="theme.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"
/>
<script
defer
data-domain="mermaid-js.github.io"
src="https://plausible.io/js/plausible.js"
></script>
<script>
const require = {
paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.29.1/min/vs' },
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.29.1/min/vs/loader.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.29.1/min/vs/editor/editor.main.nls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.29.1/min/vs/editor/editor.main.js"></script>
<script>
exports = {};
</script>
<script src="https://unpkg.com/monaco-mermaid/browser.js"></script>
<style>
.markdown-section {
max-width: 1200px;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module">
import mermaid from 'https://unpkg.com/mermaid@9/dist/mermaid.esm.min.mjs';
import mindmap from 'https://unpkg.com/@mermaid-js/mermaid-mindmap@9/dist/mermaid-mindmap.esm.min.mjs';
await mermaid.registerExternalDiagrams([mindmap]);
window.mermaid = mermaid;
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
const conf = {
logLevel: 4,
startOnLoad: true,
themeCSS: '.label { font-family: Source Sans Pro,Helvetica Neue,Arial,sans-serif; }',
};
if (isDarkMode) conf.theme = 'dark';
async function loadMermaid() {
mermaid.parseError = (e) => {
console.log('parse error', e); // eslint-disable-line
};
await mermaid.registerExternalDiagrams([mindmap]);
mermaid.initialize(conf);
console.log('mermaid initialized'); // eslint-disable-line
}
await loadMermaid();
</script>
<script>
let initEditor = exports.default;
let parser = new DOMParser();
let currentCodeExample = 0;
let colorize = [];
let num = 0;
function colorizeEverything(html) {
initEditor(monaco);
return new Promise((resolve, reject) => {
monaco.editor.setTheme('mermaid');
const parsed = parser.parseFromString(html, 'text/html').body;
Promise.all(
[...parsed.querySelectorAll('pre[id*="code"]')].map((codeBlock) =>
monaco.editor.colorize(codeBlock.innerText, 'mermaid')
)
).then((result) => {
parsed
.querySelectorAll('pre[id*="code"]')
.forEach((codeBlock, index) => (codeBlock.innerHTML = result[index]));
resolve(parsed.innerHTML);
});
});
}
function escapeHTML(html) {
return html
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&apos;');
}
window.$docsify = {
search: 'auto',
name: 'mermaid',
repo: 'https://github.com/mermaid-js/mermaid',
loadSidebar: true,
mergeNavbar: true,
maxLevel: 4,
subMaxLevel: 2,
markdown: {
renderer: {
code: function (code, lang) {
if (lang === 'mermaid-example') {
console.log('An example'); // eslint-disable-line
currentCodeExample++;
colorize.push(currentCodeExample);
return '<pre id="code' + currentCodeExample + '">' + escapeHTML(code) + '</pre>';
} else if (lang === 'mermaid') {
return '<pre class="mermaid">' + code + '</pre>';
}
return this.origin.code.apply(this, arguments);
},
heading: function (text) {
if (text.includes('THIS IS AN AUTOGENERATED FILE. DO NOT EDIT')) {
return '';
}
return this.origin.heading.apply(this, arguments);
},
},
},
plugins: [
function (hook, vm) {
hook.beforeEach(function (html) {
url = 'https://github.com/mermaid-js/mermaid/blob/develop/src/docs/' + vm.route.file;
const editHtml = '[:memo: Edit this Page](' + url + ')\n';
return editHtml + html;
});
// Invoked on each page load after new HTML has been appended to the DOM
hook.doneEach(async function () {
await mermaid.init();
});
hook.afterEach(function (html, next) {
next(html);
(async () => {
while (!window.hasOwnProperty('monaco'))
await new Promise((resolve) => setTimeout(resolve, 1000));
colorizeEverything(html).then(
(newHTML) =>
(document.querySelector('article.markdown-section').innerHTML = newHTML)
);
})();
});
},
],
};
</script>
<script>
window.onhashchange = function (a) {
// if (location && ga) {
// ga('send', 'pageview', location.hash);
// }
};
</script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
<!-- <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script> -->
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-coffeescript.min.js"></script>
</body>
</html>

View File

@@ -14,7 +14,7 @@ It is a JavaScript based diagramming and charting tool that renders Markdown-ins
<img src="/header.png" alt="" />
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
<!-- Mermaid book banner -->

View File

@@ -39,6 +39,7 @@ They also serve as proof of concept, for the variety of things that can be built
- [markdown-for-mermaid-plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin)
- [JetBrains IDE eg Pycharm](https://www.jetbrains.com/go/guide/tips/mermaid-js-support-in-markdown/)
- [mermerd](https://github.com/KarnerTh/mermerd)
- Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive)
## CRM/ERP/Similar
@@ -121,6 +122,7 @@ They also serve as proof of concept, for the variety of things that can be built
- [Draw.io](https://draw.io) - [Plugin](https://github.com/nopeslide/drawio_mermaid_plugin)
- [Inkdrop](https://www.inkdrop.app) - [Plugin](https://github.com/inkdropapp/inkdrop-mermaid)
- [Vim](https://www.vim.org)
- [Official Vim Syntax and ftplugin](https://github.com/craigmac/vim-mermaid)
- [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram)
- [GNU Emacs](https://www.gnu.org/software/emacs/)
- [Major mode for .mmd files](https://github.com/abrochard/mermaid-mode)

View File

@@ -567,7 +567,7 @@ UpdateRelStyle(customerA, bankA, $offsetY="60")
Container(mobile, "Mobile App", "Xamarin", "Provides a limited subset of the Internet Banking functionality to customers via their mobile device.")
}
Deployment_Node(comp, "Customer's computer", "Mircosoft Windows or Apple macOS"){
Deployment_Node(comp, "Customer's computer", "Microsoft Windows or Apple macOS"){
Deployment_Node(browser, "Web Browser", "Google Chrome, Mozilla Firefox,<br/> Apple Safari or Microsoft Edge"){
Container(spa, "Single Page Application", "JavaScript and Angular", "Provides all of the Internet Banking functionality to customers via their web browser.")
}
@@ -619,7 +619,7 @@ UpdateRelStyle(customerA, bankA, $offsetY="60")
Container(mobile, "Mobile App", "Xamarin", "Provides a limited subset of the Internet Banking functionality to customers via their mobile device.")
}
Deployment_Node(comp, "Customer's computer", "Mircosoft Windows or Apple macOS"){
Deployment_Node(comp, "Customer's computer", "Microsoft Windows or Apple macOS"){
Deployment_Node(browser, "Web Browser", "Google Chrome, Mozilla Firefox,<br/> Apple Safari or Microsoft Edge"){
Container(spa, "Single Page Application", "JavaScript and Angular", "Provides all of the Internet Banking functionality to customers via their web browser.")
}

View File

@@ -14,6 +14,9 @@ The class diagram is the main building block of object-oriented modeling. It is
Mermaid can render class diagrams.
```mermaid-example
---
title: Animal example
---
classDiagram
note "From Duck till Zebra"
Animal <|-- Duck
@@ -40,6 +43,9 @@ classDiagram
```
```mermaid
---
title: Animal example
---
classDiagram
note "From Duck till Zebra"
Animal <|-- Duck
@@ -77,6 +83,9 @@ A single instance of a class in the diagram contains three compartments:
- The bottom compartment contains the operations the class can execute. They are also left-aligned and the first letter is lowercase.
```mermaid-example
---
title: Bank example
---
classDiagram
class BankAccount
BankAccount : +String owner
@@ -87,6 +96,9 @@ classDiagram
```
```mermaid
---
title: Bank example
---
classDiagram
class BankAccount
BankAccount : +String owner
@@ -559,7 +571,7 @@ You would define these actions on a separate line after all classes have been de
## Notes
It is possible to add notes on digram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"`
It is possible to add notes on diagram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"`
### Examples

View File

@@ -13,6 +13,9 @@ Note that practitioners of ER modelling almost always refer to _entity types_ si
Mermaid can render ER diagrams
```mermaid-example
---
title: Order example
---
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE-ITEM : contains
@@ -20,6 +23,9 @@ erDiagram
```
```mermaid
---
title: Order example
---
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE-ITEM : contains

View File

@@ -15,11 +15,17 @@ It can also accommodate different arrow types, multi directional arrows, and lin
### A node (default)
```mermaid-example
---
title: Node
---
flowchart LR
id
```
```mermaid
---
title: Node
---
flowchart LR
id
```
@@ -33,11 +39,17 @@ found for the node that will be used. Also if you define edges for the node late
one previously defined will be used when rendering the box.
```mermaid-example
---
title: Node with text
---
flowchart LR
id1[This is the text in the box]
```
```mermaid
---
title: Node with text
---
flowchart LR
id1[This is the text in the box]
```
@@ -980,7 +992,7 @@ flowchart LR
## Configuration...
Is it possible to adjust the width of the rendered flowchart.
It is possible to adjust the width of the rendered flowchart.
This is done by defining **mermaid.flowchartConfig** or by the CLI to use a JSON file with the configuration. How to use the CLI is described in the mermaidCLI page.
mermaid.flowchartConfig can be set to a JSON string with config parameters or the corresponding object.

View File

@@ -13,31 +13,37 @@ These kind of diagram are particularly helpful to developers and devops teams to
Mermaid can render Git diagrams
```mermaid-example
gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
commit
---
title: Example Git diagram
---
gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
commit
```
```mermaid
gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
commit
---
title: Example Git diagram
---
gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
commit
```
In Mermaid, we support the basic git operations like:

View File

@@ -21,7 +21,7 @@ mindmap
Popularisation
British popular psychology author Tony Buzan
Research
On effectivness<br/>and eatures
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
@@ -42,7 +42,7 @@ mindmap
Popularisation
British popular psychology author Tony Buzan
Research
On effectivness<br/>and eatures
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
@@ -152,6 +152,18 @@ mindmap
id)I am a cloud(
```
### Hexagon
```mermaid-example
mindmap
id{{I am a hexagon}}
```
```mermaid
mindmap
id{{I am a hexagon}}
```
### Default
```mermaid-example

View File

@@ -35,7 +35,7 @@ Drawing a pie chart is really simple in mermaid.
- Followed by dataSet. Pie slices will be ordered clockwise in the same order as the labels.
- `label` for a section in the pie diagram within `" "` quotes.
- Followed by `:` colon as separator
- Followed by `positive numeric value` (supported upto two decimal places)
- Followed by `positive numeric value` (supported up to two decimal places)
\[pie] \[showData] (OPTIONAL)
\[title] \[titlevalue] (OPTIONAL)

View File

@@ -727,7 +727,7 @@ text.actor {
## Configuration
Is it possible to adjust the margins for rendering the sequence diagram.
It is possible to adjust the margins for rendering the sequence diagram.
This is done by defining `mermaid.sequenceConfig` or by the CLI to use a json file with the configuration.
How to use the CLI is described in the [mermaidCLI](../config/mermaidCLI.md) page.

View File

@@ -6,11 +6,17 @@
# State diagrams
> "A state diagram is a type of diagram used in computer science and related fields to describe the behavior of systems. State diagrams require that the system described is composed of a finite number of states; sometimes, this is indeed the case, while at other times this is a reasonable abstraction." Wikipedia
> "A state diagram is a type of diagram used in computer science and related fields to describe the behavior of systems.
> State diagrams require that the system described is composed of a finite number of states; sometimes, this is indeed the
> case, while at other times this is a reasonable abstraction." Wikipedia
Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make it easier for users to share diagrams between mermaid and plantUml.
Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make
it easier for users to share diagrams between mermaid and plantUml.
```mermaid-example
---
title: Simple sample
---
stateDiagram-v2
[*] --> Still
Still --> [*]
@@ -22,6 +28,9 @@ stateDiagram-v2
```
```mermaid
---
title: Simple sample
---
stateDiagram-v2
[*] --> Still
Still --> [*]
@@ -56,20 +65,23 @@ stateDiagram
Crash --> [*]
```
In state diagrams systems are described in terms of its states and how the systems state can change to another state via a transitions. The example diagram above shows three states **Still**, **Moving** and **Crash**. You start in the state of Still. From Still you can change the state to Moving. In Moving you can change the state either back to Still or to Crash. There is no transition from Still to Crash.
In state diagrams systems are described in terms of _states_ and how one _state_ can change to another _state_ via
a _transition._ The example diagram above shows three states: **Still**, **Moving** and **Crash**. You start in the
**Still** state. From **Still** you can change to the **Moving** state. From **Moving** you can change either back to the **Still** state or to
the **Crash** state. There is no transition from **Still** to **Crash**. (You can't crash if you're still.)
## States
A state can be declared in multiple ways. The simplest way is to define a state id as a description.
A state can be declared in multiple ways. The simplest way is to define a state with just an id:
```mermaid-example
stateDiagram-v2
s1
stateId
```
```mermaid
stateDiagram-v2
s1
stateId
```
Another way is by using the state keyword with a description as per below:
@@ -100,7 +112,8 @@ stateDiagram-v2
Transitions are path/edges when one state passes into another. This is represented using text arrow, "-->".
When you define a transition between two states and the states are not already defined the undefined states are defined with the id from the transition. You can later add descriptions to states defined this way.
When you define a transition between two states and the states are not already defined, the undefined states are defined
with the id from the transition. You can later add descriptions to states defined this way.
```mermaid-example
stateDiagram-v2
@@ -112,7 +125,7 @@ stateDiagram-v2
s1 --> s2
```
It is possible to add text to a transition. To describe what it represents.
It is possible to add text to a transition to describe what it represents:
```mermaid-example
stateDiagram-v2
@@ -126,7 +139,8 @@ stateDiagram-v2
## Start and End
There are two special states indicating the start and stop of the diagram. These are written with the \[\*] syntax and the direction of the transition to it defines it either as a start or a stop state.
There are two special states indicating the start and stop of the diagram. These are written with the \[\*] syntax and
the direction of the transition to it defines it either as a start or a stop state.
```mermaid-example
stateDiagram-v2
@@ -142,10 +156,11 @@ stateDiagram-v2
## Composite states
In a real world use of state diagrams you often end up with diagrams that are multi-dimensional as one state can
In a real world use of state diagrams you often end up with diagrams that are multidimensional as one state can
have several internal states. These are called composite states in this terminology.
In order to define a composite state you need to use the state keyword followed by an id and the body of the composite state between {}. See the example below:
In order to define a composite state you need to use the state keyword followed by an id and the body of the composite
state between {}. See the example below:
```mermaid-example
stateDiagram-v2
@@ -305,7 +320,7 @@ It is possible to specify a fork in the diagram using <\<fork>> <\<join>>.
## Notes
Sometimes nothing says it better then a Post-it note. That is also the case in state diagrams.
Sometimes nothing says it better than a Post-it note. That is also the case in state diagrams.
Here you can choose to put the note to the _right of_ or to the _left of_ a node.
@@ -375,7 +390,8 @@ stateDiagram-v2
## Setting the direction of the diagram
With state diagrams you can use the direction statement to set the direction which the diagram will render like in this example.
With state diagrams you can use the direction statement to set the direction which the diagram will render like in this
example.
```mermaid-example
stateDiagram
@@ -405,7 +421,9 @@ stateDiagram
## Comments
Comments can be entered within a state diagram chart, which will be ignored by the parser. Comments need to be on their own line, and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax
Comments can be entered within a state diagram chart, which will be ignored by the parser. Comments need to be on their
own line, and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next
newline will be treated as a comment, including any diagram syntax
```mermaid-example
stateDiagram-v2
@@ -429,22 +447,204 @@ stateDiagram-v2
Crash --> [*]
```
## Styling
## Styling with classDefs
Styling of the a state diagram is done by defining a number of css classes. During rendering these classes are extracted from the file located at src/themes/state.scss
As with other diagrams (like flowcharts), you can define a style in the diagram itself and apply that named style to a
state or states in the diagram.
**These are the current limitations with state diagram classDefs:**
1. Cannot be applied to start or end states
2. Cannot be applied to or within composite states
_These are in development and will be available in a future version._
You define a style using the `classDef` keyword, which is short for "class definition" (where "class" means something
like a _CSS class_)
followed by _a name for the style,_
and then one or more _property-value pairs_. Each _property-value pair_ is
a _[valid CSS property name](https://www.w3.org/TR/CSS/#properties)_ followed by a colon (`:`) and then a _value._
Here is an example of a classDef with just one property-value pair:
classDef movement font-style:italic;
where
- the _name_ of the style is `movement`
- the only _property_ is `font-style` and its _value_ is `italic`
If you want to have more than one _property-value pair_ then you put a comma (`,`) between each _property-value pair._
Here is an example with three property-value pairs:
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
where
- the _name_ of the style is `badBadEvent`
- the first _property_ is `fill` and its _value_ is `#f00`
- the second _property_ is `color` and its _value_ is `white`
- the third _property_ is `font-weight` and its _value_ is `bold`
- the fourth _property_ is `stroke-width` and its _value_ is `2px`
- the fifth _property_ is `stroke` and its _value_ is `yello`
### Apply classDef styles to states
There are two ways to apply a `classDef` style to a state:
1. use the `class` keyword to apply a classDef style to one or more states in a single statement, or
2. use the `:::` operator to apply a classDef style to a state as it is being used in a transition statement (e.g. with an arrow
to/from another state)
#### 1. `class` statement
A `class` statement tells Mermaid to apply the named classDef to one or more classes. The form is:
```text
class [one or more state names, separated by commas] [name of a style defined with classDef]
```
Here is an example applying the `badBadEvent` style to a state named `Crash`:
```text
class Crash badBadEvent
```
Here is an example applying the `movement` style to the two states `Moving` and `Crash`:
```text
class Moving, Crash movement
```
Here is a diagram that shows the examples in use. Note that the `Crash` state has two classDef styles applied: `movement`
and `badBadEvent`
```mermaid-example
stateDiagram
direction TB
accTitle: This is the accessible title
accDescr: This is an accessible description
classDef notMoving fill:white
classDef movement font-style:italic
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
[*]--> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]
class Still notMoving
class Moving, Crash movement
class Crash badBadEvent
class end badBadEvent
```
```mermaid
stateDiagram
direction TB
accTitle: This is the accessible title
accDescr: This is an accessible description
classDef notMoving fill:white
classDef movement font-style:italic
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
[*]--> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]
class Still notMoving
class Moving, Crash movement
class Crash badBadEvent
class end badBadEvent
```
#### 2. `:::` operator to apply a style to a state
You can apply a classDef style to a state using the `:::` (three colons) operator. The syntax is
```text
[state]:::[style name]
```
You can use this in a diagram within a statement using a class. This includes the start and end states. For example:
```mermaid-example
stateDiagram
direction TB
accTitle: This is the accessible title
accDescr: This is an accessible description
classDef notMoving fill:white
classDef movement font-style:italic;
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
[*] --> Still:::notMoving
Still --> [*]
Still --> Moving:::movement
Moving --> Still
Moving --> Crash:::movement
Crash:::badBadEvent --> [*]
```
```mermaid
stateDiagram
direction TB
accTitle: This is the accessible title
accDescr: This is an accessible description
classDef notMoving fill:white
classDef movement font-style:italic;
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
[*] --> Still:::notMoving
Still --> [*]
Still --> Moving:::movement
Moving --> Still
Moving --> Crash:::movement
Crash:::badBadEvent --> [*]
```
## Spaces in state names
Spaces can be added to a state by defining it at the top and referencing the acronym later.
Spaces can be added to a state by first defining the state with an id and then referencing the id later.
In the following example there is a state with the id **yswsii** and description **Your state with spaces in it**.
After it has been defined, **yswsii** is used in the diagram in the first transition (`[*] --> yswsii`)
and also in the transition to **YetAnotherState** (`yswsii --> YetAnotherState`).\
(**yswsii** has been styled so that it is different from the other states.)
```mermaid-example
stateDiagram-v2
Yswsii: Your state with spaces in it
[*] --> Yswsii
stateDiagram
classDef yourState font-style:italic,font-weight:bold,fill:white
yswsii: Your state with spaces in it
[*] --> yswsii:::yourState
[*] --> SomeOtherState
SomeOtherState --> YetAnotherState
yswsii --> YetAnotherState
YetAnotherState --> [*]
```
```mermaid
stateDiagram-v2
Yswsii: Your state with spaces in it
[*] --> Yswsii
stateDiagram
classDef yourState font-style:italic,font-weight:bold,fill:white
yswsii: Your state with spaces in it
[*] --> yswsii:::yourState
[*] --> SomeOtherState
SomeOtherState --> YetAnotherState
yswsii --> YetAnotherState
YetAnotherState --> [*]
```

View File

@@ -4,7 +4,7 @@
"version": "9.2.2",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"packageManager": "pnpm@7.15.0",
"packageManager": "pnpm@7.17.1",
"keywords": [
"diagram",
"markdown",
@@ -60,6 +60,7 @@
"@cspell/eslint-plugin": "^6.14.2",
"@types/eslint": "^8.4.10",
"@types/express": "^4.17.14",
"@types/js-yaml": "^4.0.5",
"@types/jsdom": "^20.0.1",
"@types/lodash": "^4.14.188",
"@types/mdast": "^3.0.10",
@@ -82,15 +83,17 @@
"eslint-plugin-jest": "^27.1.5",
"eslint-plugin-jsdoc": "^39.6.2",
"eslint-plugin-json": "^3.1.0",
"eslint-plugin-lodash": "^7.4.0",
"eslint-plugin-markdown": "^3.0.0",
"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-tsdoc": "^0.2.17",
"eslint-plugin-unicorn": "^45.0.0",
"express": "^4.18.2",
"globby": "^13.1.2",
"husky": "^8.0.2",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.3.1",
"jison": "^0.4.18",
"js-yaml": "^4.1.0",
"jsdom": "^20.0.2",
"lint-staged": "^13.0.3",
"path-browserify": "^1.0.1",
@@ -103,18 +106,8 @@
"ts-node": "^10.9.1",
"typescript": "^4.8.4",
"vite": "^3.2.3",
"vitepress": "^1.0.0-alpha.28",
"vitepress-plugin-mermaid": "^2.0.8",
"vitepress-plugin-search": "^1.0.4-alpha.15",
"vitest": "^0.25.1"
"vitest": "^0.25.3"
},
"resolutions": {
"d3": "^7.6.1"
},
"sideEffects": [
"**/*.css",
"**/*.scss"
],
"volta": {
"node": "18.12.1"
}

View File

@@ -1,6 +1,7 @@
const warning = (s: string) => {
// Todo remove debug code
console.error('Log function was called before initialization', s); // eslint-disable-line
// eslint-disable-next-line no-console
console.error('Log function was called before initialization', s);
};
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';

View File

@@ -172,6 +172,18 @@ root
expect(mm.children.length).toEqual(0);
expect(mm.type).toEqual(mindmap.yy.nodeType.BANG);
});
it('MMP-12-a mutiple types (hexagon)', function () {
let str = `mindmap
root{{the root}}
`;
mindmap.parse(str);
const mm = mindmap.yy.getMindmap();
expect(mm.type).toEqual(mindmap.yy.nodeType.HEXAGON);
expect(mm.descr).toEqual('the root');
expect(mm.children.length).toEqual(0);
});
});
describe('decorations', function () {
it('MMP-13 should be possible to set an icon for the node', function () {

View File

@@ -42,6 +42,9 @@ export const addNode = (level, id, descr, type) => {
case nodeType.RECT:
node.padding = 2 * conf.mindmap.padding;
break;
case nodeType.HEXAGON:
node.padding = 2 * conf.mindmap.padding;
break;
default:
node.padding = conf.mindmap.padding;
}
@@ -79,6 +82,7 @@ export const nodeType = {
CIRCLE: 3,
CLOUD: 4,
BANG: 5,
HEXAGON: 6,
};
export const getType = (startStr, endStr) => {
@@ -94,6 +98,8 @@ export const getType = (startStr, endStr) => {
return nodeType.CLOUD;
case '))':
return nodeType.BANG;
case '{{':
return nodeType.HEXAGON;
default:
return nodeType.DEFAULT;
}
@@ -127,6 +133,8 @@ export const type2Str = (type) => {
return 'cloud';
case nodeType.BANG:
return 'bang';
case nodeType.HEXAGON:
return 'hexgon';
default:
return 'no-border';
}

View File

@@ -92,10 +92,6 @@ function addNodes(mindmap, cy, conf, level) {
*/
function layoutMindmap(node, conf) {
return new Promise((resolve) => {
if (node.children.length === 0) {
return node;
}
// Add temporary render element
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
const cy = cytoscape({

View File

@@ -33,11 +33,12 @@
"))" { yy.getLogger().trace('Explosion Bang'); this.begin('NODE');return 'NODE_DSTART'; }
")" { yy.getLogger().trace('Cloud Bang'); this.begin('NODE');return 'NODE_DSTART'; }
"((" { this.begin('NODE');return 'NODE_DSTART'; }
"{{" { this.begin('NODE');return 'NODE_DSTART'; }
"(" { this.begin('NODE');return 'NODE_DSTART'; }
"[" { this.begin('NODE');return 'NODE_DSTART'; }
[\s]+ return 'SPACELIST' /* skip all whitespace */ ;
// !(-\() return 'NODE_ID';
[^\(\[\n\-\)]+ return 'NODE_ID';
[^\(\[\n\-\)\{\}]+ return 'NODE_ID';
<<EOF>> return 'EOF';
<NODE>["] { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");}
<NSTR>[^"]+ { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";}
@@ -45,11 +46,12 @@
<NODE>[\)]\) {this.popState();yy.getLogger().trace('node end ))');return "NODE_DEND";}
<NODE>[\)] {this.popState();yy.getLogger().trace('node end )');return "NODE_DEND";}
<NODE>[\]] {this.popState();yy.getLogger().trace('node end ...',yytext);return "NODE_DEND";}
<NODE>"}}" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
<NODE>"(-" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";}
<NODE>"-)" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";}
<NODE>"((" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
<NODE>"(" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
<NODE>[^\)\]\(]+ { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';}
<NODE>"(" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
<NODE>[^\)\]\(\}]+ { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';}
<NODE>.+(?!\(\() { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';}
// [\[] return 'NODE_START';
// .+ return 'TXT' ;

View File

@@ -17,7 +17,7 @@ const genSections = (options) => {
sections += `
.section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${
i - 1
} path {
} polygon, .section-${i - 1} path {
fill: ${options['cScale' + i]};
}
.section-${i - 1} text {
@@ -55,7 +55,7 @@ const getStyles = (options) =>
stroke-width: 3;
}
${genSections(options)}
.section-root rect, .section-root path, .section-root circle {
.section-root rect, .section-root path, .section-root circle, .section-root polygon {
fill: ${options.git0};
}
.section-root text {

View File

@@ -145,6 +145,45 @@ const circleBkg = function (elem, node) {
.attr('class', 'node-bkg node-' + db.type2Str(node.type))
.attr('r', node.width / 2);
};
/**
*
* @param parent
* @param w
* @param h
* @param points
* @param node
*/
function insertPolygonShape(parent, w, h, points, node) {
return parent
.insert('polygon', ':first-child')
.attr(
'points',
points
.map(function (d) {
return d.x + ',' + d.y;
})
.join(' ')
)
.attr('transform', 'translate(' + (node.width - w) / 2 + ', ' + h + ')');
}
const hexagonBkg = function (elem, node) {
const h = node.height;
const f = 4;
const m = h / f;
const w = node.width - node.padding + 2 * m;
const points = [
{ x: m, y: 0 },
{ x: w - m, y: 0 },
{ x: w, y: -h / 2 },
{ x: w - m, y: -h },
{ x: m, y: -h },
{ x: 0, y: -h / 2 },
];
const shapeSvg = insertPolygonShape(elem, w, h, points, node);
};
const roundedRectBkg = function (elem, node) {
elem
.append('rect')
@@ -252,6 +291,9 @@ export const drawNode = function (elem, node, fullSection, conf) {
case db.nodeType.BANG:
bangBkg(bkgElem, node, section, conf);
break;
case db.nodeType.HEXAGON:
hexagonBkg(bkgElem, node, section, conf);
break;
}
// Position the node to its coordinate

View File

@@ -1,6 +0,0 @@
{
"src/docs/**": ["pnpm --filter mermaid run docs:build --git"],
"src/docs.mts": ["pnpm --filter mermaid run docs:build --git"],
"src/(defaultConfig|config|mermaidAPI).ts": ["pnpm --filter mermaid run docs:build --git"],
"*.jison": ["pnpm run lint:jison"]
}

View File

@@ -0,0 +1,7 @@
import baseConfig from '../../.lintstagedrc.mjs';
export default {
...baseConfig,
'src/docs/**': ['pnpm --filter mermaid run docs:build --git'],
'src/docs.mts': ['pnpm --filter mermaid run docs:build --git'],
'src/(defaultConfig|config|mermaidAPI).ts': ['pnpm --filter mermaid run docs:build --git'],
};

View File

@@ -1,23 +1,6 @@
# mermaid
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
# Whoa, what's going on here?
We are transforming the Mermaid repository to a so called mono-repo. This is a part of the effort to decouple the diagrams from the core of mermaid. This will:
- Make it possible to select which diagrams to include on your site
- Open up for lazy loading
- Make it possible to add diagrams from outside of the Mermaid repository
- Separate the release flow between different diagrams and the Mermaid core
As such be aware of some changes..
# We use pnpm now
# The source code has moved
It is now located in the src folder for each respective package located as subfolders in packages.
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
English | [简体中文](./README.zh-CN.md)

View File

@@ -1,6 +1,6 @@
# mermaid
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
[English](./README.md) | 简体中文

View File

@@ -1,7 +1,7 @@
{
"name": "mermaid",
"version": "9.2.3-rc.1",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "./dist/mermaid.min.js",
"module": "./dist/mermaid.core.mjs",
"types": "./dist/mermaid.d.ts",
@@ -25,12 +25,13 @@
"scripts": {
"clean": "rimraf dist",
"docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaidAPI.ts && prettier --write ./src/docs/config/setup",
"docs:build": "rimraf ../../docs && pnpm docs:code && ts-node-esm src/docs.mts",
"docs:verify": "pnpm docs:code && ts-node-esm src/docs.mts --verify",
"docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts",
"docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts --verify",
"docs:pre:vitepress": "rimraf src/vitepress && pnpm docs:code && ts-node-esm src/docs.mts --vitepress",
"docs:build:vitepress": "pnpm docs:pre:vitepress && vitepress build src/vitepress",
"docs:dev": "pnpm docs:pre:vitepress && concurrently \"vitepress dev src/vitepress\" \"ts-node-esm src/docs.mts --watch --vitepress\"",
"docs:serve": "pnpm docs:build:vitepress && vitepress serve src/vitepress",
"docs:spellcheck": "cspell --config ../../cSpell.json \"src/docs/**/*.md\"",
"release": "pnpm build",
"prepublishOnly": "pnpm -w run build"
},
@@ -53,13 +54,10 @@
"dependencies": {
"@braintree/sanitize-url": "^6.0.0",
"d3": "^7.0.0",
"dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
"dagre-d3-es": "7.0.4",
"dompurify": "2.4.1",
"fast-clone": "^1.5.13",
"graphlib": "^2.1.8",
"khroma": "^2.0.0",
"lodash": "^4.14.189",
"lodash-es": "^4.17.21",
"moment-mini": "^2.24.0",
"non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.2",
@@ -69,9 +67,9 @@
"@types/d3": "^7.4.0",
"@types/dompurify": "^2.4.0",
"@types/jsdom": "^20.0.1",
"@types/lodash-es": "^4.17.6",
"@types/micromatch": "^4.0.2",
"@types/prettier": "^2.7.1",
"@types/lodash": "^4.14.189",
"@types/stylis": "^4.0.2",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.42.1",
@@ -79,8 +77,8 @@
"chokidar": "^3.5.3",
"concurrently": "^7.5.0",
"coveralls": "^3.1.1",
"cspell": "^6.14.3",
"globby": "^13.1.2",
"identity-obj-proxy": "^3.0.0",
"jison": "^0.4.18",
"js-base64": "^3.7.2",
"jsdom": "^20.0.2",
@@ -94,10 +92,9 @@
"typedoc": "^0.23.18",
"typedoc-plugin-markdown": "^3.13.6",
"typescript": "^4.8.4",
"unist-util-flatmap": "^1.0.0"
},
"resolutions": {
"d3": "^7.0.0"
"unist-util-flatmap": "^1.0.0",
"vitepress": "^1.0.0-alpha.28",
"vitepress-plugin-search": "^1.0.4-alpha.15"
},
"files": [
"dist",

View File

@@ -2,6 +2,7 @@ import * as configApi from './config';
import { log } from './logger';
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI';
import { detectType, getDiagramLoader } from './diagram-api/detectType';
import { extractFrontMatter } from './diagram-api/frontmatter';
import { isDetailedError, type DetailedError } from './utils';
export type ParseErrorFunction = (err: string | DetailedError, hash?: any) => void;
@@ -10,7 +11,7 @@ export class Diagram {
type = 'graph';
parser;
renderer;
db;
db: any;
private detectTypeFailed = false;
constructor(public txt: string, parseError?: ParseErrorFunction) {
const cnf = configApi.getConfig();
@@ -29,6 +30,16 @@ export class Diagram {
this.db.clear?.();
this.renderer = diagram.renderer;
this.parser = diagram.parser;
const originalParse = this.parser.parse.bind(this.parser);
// Wrap the jison parse() method to handle extracting frontmatter.
//
// This can't be done in this.parse() because some code
// directly calls diagram.parser.parse(), bypassing this.parse().
//
// Similarly, we can't do this in getDiagramFromText() because some code
// calls diagram.db.clear(), which would reset anything set by
// extractFrontMatter().
this.parser.parse = (text: string) => originalParse(extractFrontMatter(text, this.db));
this.parser.parser.yy = this.db;
if (diagram.init) {
diagram.init(cnf);
@@ -45,7 +56,7 @@ export class Diagram {
}
try {
text = text + '\n';
this.db.clear();
this.db.clear?.();
this.parser.parse(text);
return true;
} catch (error) {

View File

@@ -11,7 +11,7 @@
* @param id
*/
export default function addSVGAccessibilityFields(yy_parser, svg, id) {
if (typeof svg.insert === 'undefined') {
if (svg.insert === undefined) {
return;
}

View File

@@ -32,20 +32,20 @@ const assignWithDepth = function (dst, src, config) {
return dst;
} else if (Array.isArray(src) && Array.isArray(dst)) {
src.forEach((s) => {
if (dst.indexOf(s) === -1) {
if (!dst.includes(s)) {
dst.push(s);
}
});
return dst;
}
if (typeof dst === 'undefined' || depth <= 0) {
if (dst === undefined || depth <= 0) {
if (dst !== undefined && dst !== null && typeof dst === 'object' && typeof src === 'object') {
return Object.assign(dst, src);
} else {
return src;
}
}
if (typeof src !== 'undefined' && typeof dst === 'object' && typeof src === 'object') {
if (src !== undefined && typeof dst === 'object' && typeof src === 'object') {
Object.keys(src).forEach((key) => {
if (
typeof src[key] === 'object' &&

View File

@@ -18,8 +18,7 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[])
// Join directives
let sumOfDirectives: MermaidConfig = {};
for (let i = 0; i < _directives.length; i++) {
const d = _directives[i];
for (const d of _directives) {
sanitize(d);
// Apply the data from the directive where the the overrides the themeVariables
@@ -153,7 +152,7 @@ export const getConfig = (): MermaidConfig => {
export const sanitize = (options: any) => {
// Checking that options are not in the list of excluded options
['secure', ...(siteConfig.secure ?? [])].forEach((key) => {
if (typeof options[key] !== 'undefined') {
if (options[key] !== undefined) {
// DO NOT attempt to print options[key] within `${}` as a malicious script
// can exploit the logger's attempt to stringify the value and execute arbitrary code
log.debug(`Denied attempt to modify a secure key ${key}`, options[key]);
@@ -170,14 +169,13 @@ export const sanitize = (options: any) => {
// Check that there no attempts of xss, there should be no tags at all in the directive
// blocking data urls as base64 urls can contain svg's with inline script tags
Object.keys(options).forEach((key) => {
if (typeof options[key] === 'string') {
if (
options[key].indexOf('<') > -1 ||
options[key].indexOf('>') > -1 ||
options[key].indexOf('url(data:') > -1
) {
delete options[key];
}
if (
typeof options[key] === 'string' &&
(options[key].includes('<') ||
options[key].includes('>') ||
options[key].includes('url(data:'))
) {
delete options[key];
}
if (typeof options[key] === 'object') {
sanitize(options[key]);

View File

@@ -189,6 +189,7 @@ export interface C4DiagramConfig extends BaseDiagramConfig {
}
export interface GitGraphDiagramConfig extends BaseDiagramConfig {
titleTopMargin?: number;
diagramPadding?: number;
nodeLabel?: NodeLabel;
mainBranchName?: string;
@@ -227,6 +228,7 @@ export interface MindmapDiagramConfig extends BaseDiagramConfig {
export type PieDiagramConfig = BaseDiagramConfig;
export interface ErDiagramConfig extends BaseDiagramConfig {
titleTopMargin?: number;
diagramPadding?: number;
layoutDirection?: string;
minEntityWidth?: number;
@@ -238,6 +240,7 @@ export interface ErDiagramConfig extends BaseDiagramConfig {
}
export interface StateDiagramConfig extends BaseDiagramConfig {
titleTopMargin?: number;
arrowMarkerAbsolute?: boolean;
dividerMargin?: number;
sizeUnit?: number;
@@ -258,6 +261,7 @@ export interface StateDiagramConfig extends BaseDiagramConfig {
}
export interface ClassDiagramConfig extends BaseDiagramConfig {
titleTopMargin?: number;
arrowMarkerAbsolute?: boolean;
dividerMargin?: number;
padding?: number;
@@ -343,6 +347,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
}
export interface FlowchartDiagramConfig extends BaseDiagramConfig {
titleTopMargin?: number;
arrowMarkerAbsolute?: boolean;
diagramPadding?: number;
htmlLabels?: boolean;

View File

@@ -54,7 +54,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
const node = {
isNode,
label: decodeEntities(vertexText).replace(
/fa[lrsb]?:fa-[\w-]+/g,
/fa[blrs]?:fa-[\w-]+/g,
(s) => `<i class='${s.replace(':', ' ')}'></i>`
),
labelStyle: style.replace('fill:', 'color:'),
@@ -74,7 +74,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
rows = [];
}
for (let j = 0; j < rows.length; j++) {
for (const row of rows) {
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
tspan.setAttribute('dy', '1em');
@@ -84,7 +84,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
} else {
tspan.setAttribute('class', 'row');
}
tspan.textContent = rows[j].trim();
tspan.textContent = row.trim();
svgLabel.appendChild(tspan);
}
return svgLabel;

View File

@@ -324,7 +324,7 @@ const cutPathAtIntersect = (_points, boundryNode) => {
pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y);
});
// // if (!pointPresent) {
if (!points.find((e) => e.x === inter.x && e.y === inter.y)) {
if (!points.some((e) => e.x === inter.x && e.y === inter.y)) {
points.push(inter);
} else {
log.warn('abc88 no intersect', inter, points);

View File

@@ -1,5 +1,5 @@
import dagre from 'dagre';
import graphlib from 'graphlib';
import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js';
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json';
import insertMarkers from './markers';
import { updateNodeBounds } from './shapes/util';
import {
@@ -15,7 +15,7 @@ import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } f
import { log } from '../logger';
const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
log.info('Graph in recursive render: XXX', graphlib.json.write(graph), parentCluster);
log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster);
const dir = graph.graph().rankdir;
log.trace('Dir in recursive render - dir:', dir);
@@ -37,7 +37,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
// to the abstract node and is later used by dagre for the layout
graph.nodes().forEach(function (v) {
const node = graph.node(v);
if (typeof parentCluster !== 'undefined') {
if (parentCluster !== undefined) {
const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
// data.clusterPositioning = true;
log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
@@ -95,8 +95,8 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
log.info('### Layout ###');
log.info('#############################################');
log.info(graph);
dagre.layout(graph);
log.info('Graph after layout:', graphlib.json.write(graph));
dagreLayout(graph);
log.info('Graph after layout:', graphlibJson.write(graph));
// Move the nodes to the correct place
let diff = 0;
sortNodesByHierarchy(graph).forEach(function (v) {
@@ -153,10 +153,10 @@ export const render = (elem, graph, markers, diagramtype, id) => {
clearClusters();
clearGraphlib();
log.warn('Graph at first:', graphlib.json.write(graph));
log.warn('Graph at first:', graphlibJson.write(graph));
adjustClustersAndEdges(graph);
log.warn('Graph after:', graphlib.json.write(graph));
// log.warn('Graph ever after:', graphlib.json.write(graph.node('A').graph));
log.warn('Graph after:', graphlibJson.write(graph));
// log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph));
recursiveRender(elem, graph, diagramtype);
};

View File

@@ -1,29 +1,23 @@
/** Decorates with functions required by mermaids dagre-wrapper. */
import { log } from '../logger';
import graphlib from 'graphlib';
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json';
import * as graphlib from 'dagre-d3-es/src/graphlib';
export let clusterDb = {};
let decendants = {};
let descendants = {};
let parents = {};
export const clear = () => {
decendants = {};
descendants = {};
parents = {};
clusterDb = {};
};
const isDecendant = (id, ancenstorId) => {
const isDescendant = (id, ancenstorId) => {
// if (id === ancenstorId) return true;
log.trace(
'In isDecendant',
ancenstorId,
' ',
id,
' = ',
decendants[ancenstorId].indexOf(id) >= 0
);
if (decendants[ancenstorId].indexOf(id) >= 0) {
log.trace('In isDecendant', ancenstorId, ' ', id, ' = ', descendants[ancenstorId].includes(id));
if (descendants[ancenstorId].includes(id)) {
return true;
}
@@ -31,7 +25,7 @@ const isDecendant = (id, ancenstorId) => {
};
const edgeInCluster = (edge, clusterId) => {
log.info('Decendants of ', clusterId, ' is ', decendants[clusterId]);
log.info('Decendants of ', clusterId, ' is ', descendants[clusterId]);
log.info('Edge is ', edge);
// Edges to/from the cluster is not in the cluster, they are in the parent
if (edge.v === clusterId) {
@@ -41,15 +35,15 @@ const edgeInCluster = (edge, clusterId) => {
return false;
}
if (!decendants[clusterId]) {
if (!descendants[clusterId]) {
log.debug('Tilt, ', clusterId, ',not in decendants');
return false;
}
return (
decendants[clusterId].indexOf(edge.v) >= 0 ||
isDecendant(edge.v, clusterId) ||
isDecendant(edge.w, clusterId) ||
decendants[clusterId].indexOf(edge.w) >= 0
descendants[clusterId].includes(edge.v) ||
isDescendant(edge.v, clusterId) ||
isDescendant(edge.w, clusterId) ||
descendants[clusterId].includes(edge.w)
);
};
@@ -131,14 +125,14 @@ const copy = (clusterId, graph, newGraph, rootId) => {
graph.removeNode(node);
});
};
export const extractDecendants = (id, graph) => {
export const extractDescendants = (id, graph) => {
// log.debug('Extracting ', id);
const children = graph.children(id);
let res = [].concat(children);
let res = [...children];
for (let i = 0; i < children.length; i++) {
parents[children[i]] = id;
res = res.concat(extractDecendants(children[i], graph));
for (const child of children) {
parents[child] = id;
res = [...res, ...extractDescendants(child, graph)];
}
return res;
@@ -153,13 +147,13 @@ export const extractDecendants = (id, graph) => {
export const validate = (graph) => {
const edges = graph.edges();
log.trace('Edges: ', edges);
for (let i = 0; i < edges.length; i++) {
if (graph.children(edges[i].v).length > 0) {
log.trace('The node ', edges[i].v, ' is part of and edge even though it has children');
for (const edge of edges) {
if (graph.children(edge.v).length > 0) {
log.trace('The node ', edge.v, ' is part of and edge even though it has children');
return false;
}
if (graph.children(edges[i].w).length > 0) {
log.trace('The node ', edges[i].w, ' is part of and edge even though it has children');
if (graph.children(edge.w).length > 0) {
log.trace('The node ', edge.w, ' is part of and edge even though it has children');
return false;
}
}
@@ -182,8 +176,8 @@ export const findNonClusterChild = (id, graph) => {
log.trace('This is a valid node', id);
return id;
}
for (let i = 0; i < children.length; i++) {
const _id = findNonClusterChild(children[i], graph);
for (const child of children) {
const _id = findNonClusterChild(child, graph);
if (_id) {
log.trace('Found replacement for', id, ' => ', _id);
return _id;
@@ -225,7 +219,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
' Replacement id in edges: ',
findNonClusterChild(id, graph)
);
decendants[id] = extractDecendants(id, graph);
descendants[id] = extractDescendants(id, graph);
clusterDb[id] = { id: findNonClusterChild(id, graph), clusterData: graph.node(id) };
}
});
@@ -235,7 +229,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
const children = graph.children(id);
const edges = graph.edges();
if (children.length > 0) {
log.debug('Cluster identified', id, decendants);
log.debug('Cluster identified', id, descendants);
edges.forEach((edge) => {
// log.debug('Edge, decendants: ', edge, decendants[id]);
@@ -244,19 +238,19 @@ export const adjustClustersAndEdges = (graph, depth) => {
// Any edge where either the one of the nodes is descending to the cluster but not the other
// if (decendants[id].indexOf(edge.v) < 0 && decendants[id].indexOf(edge.w) < 0) {
const d1 = isDecendant(edge.v, id);
const d2 = isDecendant(edge.w, id);
const d1 = isDescendant(edge.v, id);
const d2 = isDescendant(edge.w, id);
// d1 xor d2 - if either d1 is true and d2 is false or the other way around
if (d1 ^ d2) {
log.warn('Edge: ', edge, ' leaves cluster ', id);
log.warn('Decendants of XXX ', id, ': ', decendants[id]);
log.warn('Decendants of XXX ', id, ': ', descendants[id]);
clusterDb[id].externalConnections = true;
}
}
});
} else {
log.debug('Not a cluster ', id, decendants);
log.debug('Not a cluster ', id, descendants);
}
});
@@ -276,7 +270,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
'ids:',
e.v,
e.w,
'Translateing: ',
'Translating: ',
clusterDb[e.v],
' --- ',
clusterDb[e.w]
@@ -322,7 +316,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
graph.setEdge(v, w, edge, e.name);
}
});
log.warn('Adjusted Graph', graphlib.json.write(graph));
log.warn('Adjusted Graph', graphlibJson.write(graph));
extractor(graph, 0);
log.trace(clusterDb);
@@ -336,7 +330,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
};
export const extractor = (graph, depth) => {
log.warn('extractor - ', depth, graphlib.json.write(graph), graph.children('D'));
log.warn('extractor - ', depth, graphlibJson.write(graph), graph.children('D'));
if (depth > 10) {
log.error('Bailing out');
return;
@@ -346,8 +340,7 @@ export const extractor = (graph, depth) => {
// for (let i = 0;)
let nodes = graph.nodes();
let hasChildren = false;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
for (const node of nodes) {
const children = graph.children(node);
hasChildren = hasChildren || children.length > 0;
}
@@ -359,9 +352,7 @@ export const extractor = (graph, depth) => {
// const clusters = Object.keys(clusterDb);
// clusters.forEach(clusterId => {
log.debug('Nodes = ', nodes, depth);
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
for (const node of nodes) {
log.debug(
'Extracting node',
node,
@@ -393,11 +384,9 @@ export const extractor = (graph, depth) => {
const graphSettings = graph.graph();
let dir = graphSettings.rankdir === 'TB' ? 'LR' : 'TB';
if (clusterDb[node]) {
if (clusterDb[node].clusterData && clusterDb[node].clusterData.dir) {
dir = clusterDb[node].clusterData.dir;
log.warn('Fixing dir', clusterDb[node].clusterData.dir, dir);
}
if (clusterDb[node] && clusterDb[node].clusterData && clusterDb[node].clusterData.dir) {
dir = clusterDb[node].clusterData.dir;
log.warn('Fixing dir', clusterDb[node].clusterData.dir, dir);
}
const clusterGraph = new graphlib.Graph({
@@ -415,7 +404,7 @@ export const extractor = (graph, depth) => {
return {};
});
log.warn('Old graph before copy', graphlib.json.write(graph));
log.warn('Old graph before copy', graphlibJson.write(graph));
copy(node, graph, clusterGraph, node);
graph.setNode(node, {
clusterNode: true,
@@ -424,8 +413,8 @@ export const extractor = (graph, depth) => {
labelText: clusterDb[node].labelText,
graph: clusterGraph,
});
log.warn('New graph after copy node: (', node, ')', graphlib.json.write(clusterGraph));
log.debug('Old graph after copy', graphlib.json.write(graph));
log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph));
log.debug('Old graph after copy', graphlibJson.write(graph));
} else {
log.warn(
'Cluster ** ',
@@ -445,8 +434,7 @@ export const extractor = (graph, depth) => {
nodes = graph.nodes();
log.warn('New list of nodes', nodes);
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
for (const node of nodes) {
const data = graph.node(node);
log.warn(' Now next level', node, data);
if (data.clusterNode) {
@@ -463,7 +451,7 @@ const sorter = (graph, nodes) => {
nodes.forEach((node) => {
const children = graph.children(node);
const sorted = sorter(graph, children);
result = result.concat(sorted);
result = [...result, ...sorted];
});
return result;

View File

@@ -1,9 +1,9 @@
import graphlib from 'graphlib';
import dagre from 'dagre';
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json';
import * as graphlib from 'dagre-d3-es/src/graphlib';
import {
validate,
adjustClustersAndEdges,
extractDecendants,
extractDescendants,
sortNodesByHierarchy,
} from './mermaid-graphlib';
import { setLogLevel, log } from '../logger';
@@ -233,9 +233,9 @@ describe('Graphlib decorations', () => {
g.setParent('D', 'C');
// log.info('Graph before', g.node('D'))
// log.info('Graph before', graphlib.json.write(g))
// log.info('Graph before', graphlibJson.write(g))
adjustClustersAndEdges(g);
// log.info('Graph after', graphlib.json.write(g), g.node('C').graph)
// log.info('Graph after', graphlibJson.write(g), g.node('C').graph)
const CGraph = g.node('C').graph;
const DGraph = CGraph.node('D').graph;
@@ -279,9 +279,9 @@ describe('Graphlib decorations', () => {
g.setEdge('A', 'C', { data: 'link2' }, '2');
log.info('Graph before', g.node('D'));
log.info('Graph before', graphlib.json.write(g));
log.info('Graph before', graphlibJson.write(g));
adjustClustersAndEdges(g);
log.trace('Graph after', graphlib.json.write(g));
log.trace('Graph after', graphlibJson.write(g));
expect(g.nodes()).toEqual(['C', 'B', 'A']);
expect(g.nodes().length).toBe(3);
expect(g.edges().length).toBe(2);
@@ -334,11 +334,11 @@ describe('Graphlib decorations', () => {
g.setEdge('c', 'd', { data: 'link2' }, '2');
g.setEdge('d', 'e', { data: 'link2' }, '2');
log.info('Graph before', graphlib.json.write(g));
log.info('Graph before', graphlibJson.write(g));
adjustClustersAndEdges(g);
const bGraph = g.node('b').graph;
// log.trace('Graph after', graphlib.json.write(g))
log.info('Graph after', graphlib.json.write(bGraph));
// log.trace('Graph after', graphlibJson.write(g))
log.info('Graph after', graphlibJson.write(bGraph));
expect(bGraph.nodes().length).toBe(3);
expect(bGraph.edges().length).toBe(2);
});
@@ -360,13 +360,13 @@ describe('Graphlib decorations', () => {
g.setParent('c', 'b');
g.setParent('e', 'c');
log.info('Graph before', graphlib.json.write(g));
log.info('Graph before', graphlibJson.write(g));
adjustClustersAndEdges(g);
const aGraph = g.node('a').graph;
const bGraph = aGraph.node('b').graph;
log.info('Graph after', graphlib.json.write(aGraph));
log.info('Graph after', graphlibJson.write(aGraph));
const cGraph = bGraph.node('c').graph;
// log.trace('Graph after', graphlib.json.write(g))
// log.trace('Graph after', graphlibJson.write(g))
expect(aGraph.nodes().length).toBe(1);
expect(bGraph.nodes().length).toBe(1);
expect(cGraph.nodes().length).toBe(1);
@@ -388,19 +388,19 @@ flowchart TB
const exportedGraph = JSON.parse(
'{"options":{"directed":true,"multigraph":true,"compound":true},"nodes":[{"v":"A","value":{"labelStyle":"","shape":"rect","labelText":"A","rx":0,"ry":0,"class":"default","style":"","id":"A","width":500,"type":"group","padding":15}},{"v":"B","value":{"labelStyle":"","shape":"rect","labelText":"B","rx":0,"ry":0,"class":"default","style":"","id":"B","width":500,"type":"group","padding":15},"parent":"A"},{"v":"b","value":{"labelStyle":"","shape":"rect","labelText":"b","rx":0,"ry":0,"class":"default","style":"","id":"b","padding":15},"parent":"A"},{"v":"c","value":{"labelStyle":"","shape":"rect","labelText":"c","rx":0,"ry":0,"class":"default","style":"","id":"c","padding":15},"parent":"B"},{"v":"a","value":{"labelStyle":"","shape":"rect","labelText":"a","rx":0,"ry":0,"class":"default","style":"","id":"a","padding":15},"parent":"A"}],"edges":[{"v":"b","w":"B","name":"1","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-b-B","classes":"flowchart-link LS-b LE-B"}},{"v":"a","w":"c","name":"2","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-a-c","classes":"flowchart-link LS-a LE-c"}}],"value":{"rankdir":"TB","nodesep":50,"ranksep":50,"marginx":8,"marginy":8}}'
);
const gr = graphlib.json.read(exportedGraph);
const gr = graphlibJson.read(exportedGraph);
log.info('Graph before', graphlib.json.write(gr));
log.info('Graph before', graphlibJson.write(gr));
adjustClustersAndEdges(gr);
const aGraph = gr.node('A').graph;
const bGraph = aGraph.node('B').graph;
log.info('Graph after', graphlib.json.write(aGraph));
// log.trace('Graph after', graphlib.json.write(g))
log.info('Graph after', graphlibJson.write(aGraph));
// log.trace('Graph after', graphlibJson.write(g))
expect(aGraph.parent('c')).toBe('B');
expect(aGraph.parent('B')).toBe(undefined);
});
});
describe('extractDecendants', function () {
describe('extractDescendants', function () {
let g;
beforeEach(function () {
setLogLevel(1);
@@ -443,9 +443,9 @@ describe('extractDecendants', function () {
g.setEdge('A', 'C', { data: 'link2' }, '2');
// log.info(g.edges())
const d1 = extractDecendants('A', g);
const d2 = extractDecendants('B', g);
const d3 = extractDecendants('C', g);
const d1 = extractDescendants('A', g);
const d2 = extractDescendants('B', g);
const d3 = extractDescendants('C', g);
expect(d1).toEqual(['a']);
expect(d2).toEqual(['b']);

View File

@@ -391,12 +391,10 @@ const labelRect = (parent, node) => {
function applyNodePropertyBorders(rect, borders, totalWidth, totalHeight) {
const strokeDashArray = [];
const addBorder = (length) => {
strokeDashArray.push(length);
strokeDashArray.push(0);
strokeDashArray.push(length, 0);
};
const skipBorder = (length) => {
strokeDashArray.push(0);
strokeDashArray.push(length);
strokeDashArray.push(0, length);
};
if (borders.includes('t')) {
log.debug('add top border');

View File

@@ -21,7 +21,7 @@ export const labelHelper = (parent, node, _classes, isNode) => {
// Replace labelText with default value if undefined
let labelText;
if (typeof node.labelText === 'undefined') {
if (node.labelText === undefined) {
labelText = '';
} else {
labelText = typeof node.labelText === 'string' ? node.labelText : node.labelText[0];

View File

@@ -154,6 +154,17 @@ const config: Partial<MermaidConfig> = {
/** The object containing configurations specific for flowcharts */
flowchart: {
/**
* ### titleTopMargin
*
* | Parameter | Description | Type | Required | Values |
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
* | titleTopMargin | Margin top for the text over the flowchart | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 25
*/
titleTopMargin: 25,
/**
* | Parameter | Description | Type | Required | Values |
* | -------------- | ----------------------------------------------- | ------- | -------- | ------------------ |
@@ -851,6 +862,16 @@ const config: Partial<MermaidConfig> = {
sectionColours: ['#fff'],
},
class: {
/**
* ### titleTopMargin
*
* | Parameter | Description | Type | Required | Values |
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
* | titleTopMargin | Margin top for the text over the class diagram | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 25
*/
titleTopMargin: 25,
arrowMarkerAbsolute: false,
dividerMargin: 10,
padding: 5,
@@ -884,6 +905,16 @@ const config: Partial<MermaidConfig> = {
defaultRenderer: 'dagre-wrapper',
},
state: {
/**
* ### titleTopMargin
*
* | Parameter | Description | Type | Required | Values |
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
* | titleTopMargin | Margin top for the text over the state diagram | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 25
*/
titleTopMargin: 25,
dividerMargin: 10,
sizeUnit: 5,
padding: 8,
@@ -932,6 +963,17 @@ const config: Partial<MermaidConfig> = {
/** The object containing configurations specific for entity relationship diagrams */
er: {
/**
* ### titleTopMargin
*
* | Parameter | Description | Type | Required | Values |
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
* | titleTopMargin | Margin top for the text over the diagram | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 25
*/
titleTopMargin: 25,
/**
* | Parameter | Description | Type | Required | Values |
* | -------------- | ----------------------------------------------- | ------- | -------- | ------------------ |
@@ -1085,6 +1127,16 @@ const config: Partial<MermaidConfig> = {
line_height: 20,
},
gitGraph: {
/**
* ### titleTopMargin
*
* | Parameter | Description | Type | Required | Values |
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
* | titleTopMargin | Margin top for the text over the Git diagram | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 25
*/
titleTopMargin: 25,
diagramPadding: 8,
nodeLabel: {
width: 75,

View File

@@ -2,9 +2,9 @@ import { MermaidConfig } from '../config.type';
import { log } from '../logger';
import { DetectorRecord, DiagramDetector, DiagramLoader } from './types';
import { ExternalDiagramDefinition } from '../diagram-api/types';
import { frontMatterRegex } from './frontmatter';
const directive =
/[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi;
const directive = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
const anyComment = /\s*%%.*\n/gm;
const detectors: Record<string, DetectorRecord> = {};
@@ -32,7 +32,7 @@ const detectors: Record<string, DetectorRecord> = {};
* @returns A graph definition key
*/
export const detectType = function (text: string, config?: MermaidConfig): string {
text = text.replace(directive, '').replace(anyComment, '\n');
text = text.replace(frontMatterRegex, '').replace(directive, '').replace(anyComment, '\n');
for (const [key, { detector }] of Object.entries(detectors)) {
const diagram = detector(text, config);
if (diagram) {

View File

@@ -0,0 +1,78 @@
import { vi } from 'vitest';
import { extractFrontMatter } from './frontmatter';
const dbMock = () => ({ setDiagramTitle: vi.fn() });
describe('extractFrontmatter', () => {
it('returns text unchanged if no frontmatter', () => {
expect(extractFrontMatter('diagram', dbMock())).toEqual('diagram');
});
it('returns text unchanged if frontmatter lacks closing delimiter', () => {
const text = `---\ntitle: foo\ndiagram`;
expect(extractFrontMatter(text, dbMock())).toEqual(text);
});
it('handles empty frontmatter', () => {
const db = dbMock();
const text = `---\n\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).not.toHaveBeenCalled();
});
it('handles frontmatter without mappings', () => {
const db = dbMock();
const text = `---\n1\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).not.toHaveBeenCalled();
});
it('does not try to parse frontmatter at the end', () => {
const db = dbMock();
const text = `diagram\n---\ntitle: foo\n---\n`;
expect(extractFrontMatter(text, db)).toEqual(text);
expect(db.setDiagramTitle).not.toHaveBeenCalled();
});
it('handles frontmatter with multiple delimiters', () => {
const db = dbMock();
const text = `---\ntitle: foo---bar\n---\ndiagram\n---\ntest`;
expect(extractFrontMatter(text, db)).toEqual('diagram\n---\ntest');
expect(db.setDiagramTitle).toHaveBeenCalledWith('foo---bar');
});
it('handles frontmatter with multi-line string and multiple delimiters', () => {
const db = dbMock();
const text = `---\ntitle: |\n multi-line string\n ---\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).toHaveBeenCalledWith('multi-line string\n---\n');
});
it('handles frontmatter with title', () => {
const db = dbMock();
const text = `---\ntitle: foo\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).toHaveBeenCalledWith('foo');
});
it('handles booleans in frontmatter properly', () => {
const db = dbMock();
const text = `---\ntitle: true\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).toHaveBeenCalledWith('true');
});
it('ignores unspecified frontmatter keys', () => {
const db = dbMock();
const text = `---\ninvalid: true\ntitle: foo\ntest: bar\n---\ndiagram`;
expect(extractFrontMatter(text, db)).toEqual('diagram');
expect(db.setDiagramTitle).toHaveBeenCalledWith('foo');
});
it('throws exception for invalid YAML syntax', () => {
const text = `---\n!!!\n---\ndiagram`;
expect(() => extractFrontMatter(text, dbMock())).toThrow(
'tag suffix cannot contain exclamation marks'
);
});
});

View File

@@ -0,0 +1,40 @@
import { DiagramDb } from './types';
// The "* as yaml" part is necessary for tree-shaking
import * as yaml from 'js-yaml';
// Match Jekyll-style front matter blocks (https://jekyllrb.com/docs/front-matter/).
// Based on regex used by Jekyll: https://github.com/jekyll/jekyll/blob/6dd3cc21c40b98054851846425af06c64f9fb466/lib/jekyll/document.rb#L10
// Note that JS doesn't support the "\A" anchor, which means we can't use
// multiline mode.
// Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents
export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
type FrontMatterMetadata = {
title?: string;
};
/**
* Extract and parse frontmatter from text, if present, and sets appropriate
* properties in the provided db.
* @param text - The text that may have a YAML frontmatter.
* @param db - Diagram database, could be of any diagram.
* @returns text with frontmatter stripped out
*/
export function extractFrontMatter(text: string, db: DiagramDb): string {
const matches = text.match(frontMatterRegex);
if (matches) {
const parsed: FrontMatterMetadata = yaml.load(matches[1], {
// To keep things simple, only allow strings, arrays, and plain objects.
// https://www.yaml.org/spec/1.2/spec.html#id2802346
schema: yaml.FAILSAFE_SCHEMA,
}) as FrontMatterMetadata;
if (parsed?.title) {
db.setDiagramTitle?.(parsed.title);
}
return text.slice(matches[0].length);
} else {
return text;
}
}

View File

@@ -8,8 +8,16 @@ export interface InjectUtils {
_setupGraphViewbox: any;
}
/**
* Generic Diagram DB that may apply to any diagram type.
*/
export interface DiagramDb {
clear?: () => void;
setDiagramTitle?: (title: string) => void;
}
export interface DiagramDefinition {
db: any;
db: DiagramDb;
renderer: any;
parser: any;
styles: any;

View File

@@ -48,7 +48,7 @@ class Bounds {
}
updateVal(obj, key, val, fun) {
if (typeof obj[key] === 'undefined') {
if (obj[key] === undefined) {
obj[key] = val;
} else {
obj[key] = fun(val, obj[key]);
@@ -177,12 +177,12 @@ function calcC4ShapeTextWH(textType, c4Shape, c4ShapeTextWrap, textConf, textLim
let lineHeight = 0;
c4Shape[textType].height = 0;
c4Shape[textType].width = 0;
for (let i = 0; i < lines.length; i++) {
for (const line of lines) {
c4Shape[textType].width = Math.max(
calculateTextWidth(lines[i], textConf),
calculateTextWidth(line, textConf),
c4Shape[textType].width
);
lineHeight = calculateTextHeight(lines[i], textConf);
lineHeight = calculateTextHeight(line, textConf);
c4Shape[textType].height = c4Shape[textType].height + lineHeight;
}
// c4Shapes[textType].height = c4Shapes[textType].textLines * textConf.fontSize;
@@ -212,9 +212,9 @@ export const drawC4ShapeArray = function (currentBounds, diagram, c4ShapeArray,
// Upper Y is relative point
let Y = 0;
// Draw the c4ShapeArray
for (let i = 0; i < c4ShapeKeys.length; i++) {
for (const c4ShapeKey of c4ShapeKeys) {
Y = 0;
const c4Shape = c4ShapeArray[c4ShapeKeys[i]];
const c4Shape = c4ShapeArray[c4ShapeKey];
// calc c4 shape type width and height
@@ -461,8 +461,7 @@ function drawInsideBoundary(
// conf.width * conf.c4ShapeInRow + conf.c4ShapeMargin * conf.c4ShapeInRow * 2,
// parentBounds.data.widthLimit / Math.min(conf.c4BoundaryInRow, currentBoundaries.length)
// );
for (let i = 0; i < currentBoundaries.length; i++) {
let currentBoundary = currentBoundaries[i];
for (let [i, currentBoundary] of currentBoundaries.entries()) {
let Y = 0;
currentBoundary.image = { width: 0, height: 0, Y: 0 };
if (currentBoundary.sprite) {

View File

@@ -52,8 +52,8 @@ export const drawText = function (elem, textData) {
let dy = 0;
let yfunc = () => textData.y;
if (
typeof textData.valign !== 'undefined' &&
typeof textData.textMargin !== 'undefined' &&
textData.valign !== undefined &&
textData.textMargin !== undefined &&
textData.textMargin > 0
) {
switch (textData.valign) {
@@ -78,9 +78,9 @@ export const drawText = function (elem, textData) {
}
}
if (
typeof textData.anchor !== 'undefined' &&
typeof textData.textMargin !== 'undefined' &&
typeof textData.width !== 'undefined'
textData.anchor !== undefined &&
textData.textMargin !== undefined &&
textData.width !== undefined
) {
switch (textData.anchor) {
case 'left':
@@ -106,12 +106,11 @@ export const drawText = function (elem, textData) {
break;
}
}
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
for (let [i, line] of lines.entries()) {
if (
typeof textData.textMargin !== 'undefined' &&
textData.textMargin !== undefined &&
textData.textMargin === 0 &&
typeof textData.fontSize !== 'undefined'
textData.fontSize !== undefined
) {
dy = i * textData.fontSize;
}
@@ -119,28 +118,28 @@ export const drawText = function (elem, textData) {
const textElem = elem.append('text');
textElem.attr('x', textData.x);
textElem.attr('y', yfunc());
if (typeof textData.anchor !== 'undefined') {
if (textData.anchor !== undefined) {
textElem
.attr('text-anchor', textData.anchor)
.attr('dominant-baseline', textData.dominantBaseline)
.attr('alignment-baseline', textData.alignmentBaseline);
}
if (typeof textData.fontFamily !== 'undefined') {
if (textData.fontFamily !== undefined) {
textElem.style('font-family', textData.fontFamily);
}
if (typeof textData.fontSize !== 'undefined') {
if (textData.fontSize !== undefined) {
textElem.style('font-size', textData.fontSize);
}
if (typeof textData.fontWeight !== 'undefined') {
if (textData.fontWeight !== undefined) {
textElem.style('font-weight', textData.fontWeight);
}
if (typeof textData.fill !== 'undefined') {
if (textData.fill !== undefined) {
textElem.attr('fill', textData.fill);
}
if (typeof textData.class !== 'undefined') {
if (textData.class !== undefined) {
textElem.attr('class', textData.class);
}
if (typeof textData.dy !== 'undefined') {
if (textData.dy !== undefined) {
textElem.attr('dy', textData.dy);
} else if (dy !== 0) {
textElem.attr('dy', dy);
@@ -149,7 +148,7 @@ export const drawText = function (elem, textData) {
if (textData.tspan) {
const span = textElem.append('tspan');
span.attr('x', textData.x);
if (typeof textData.fill !== 'undefined') {
if (textData.fill !== undefined) {
span.attr('fill', textData.fill);
}
span.text(line);
@@ -157,8 +156,8 @@ export const drawText = function (elem, textData) {
textElem.text(line);
}
if (
typeof textData.valign !== 'undefined' &&
typeof textData.textMargin !== 'undefined' &&
textData.valign !== undefined &&
textData.textMargin !== undefined &&
textData.textMargin > 0
) {
textHeight += (textElem._groups || textElem)[0][0].getBBox().height;

View File

@@ -10,6 +10,8 @@ import {
getAccDescription,
setAccDescription,
clear as commonClear,
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb';
const MERMAID_DOM_ID_PREFIX = 'classid-';
@@ -50,7 +52,7 @@ const splitClassNameAndType = function (id) {
export const addClass = function (id) {
let classId = splitClassNameAndType(id);
// Only add class if not exists
if (typeof classes[classId.className] !== 'undefined') {
if (classes[classId.className] !== undefined) {
return;
}
@@ -75,9 +77,9 @@ export const addClass = function (id) {
*/
export const lookUpDomId = function (id) {
const classKeys = Object.keys(classes);
for (let i = 0; i < classKeys.length; i++) {
if (classes[classKeys[i]].id === id) {
return classes[classKeys[i]].domId;
for (const classKey of classKeys) {
if (classes[classKey].id === id) {
return classes[classKey].domId;
}
}
};
@@ -205,7 +207,7 @@ export const setCssClass = function (ids, className) {
if (_id[0].match(/\d/)) {
id = MERMAID_DOM_ID_PREFIX + id;
}
if (typeof classes[id] !== 'undefined') {
if (classes[id] !== undefined) {
classes[id].cssClasses.push(className);
}
});
@@ -220,7 +222,7 @@ export const setCssClass = function (ids, className) {
const setTooltip = function (ids, tooltip) {
const config = configApi.getConfig();
ids.split(',').forEach(function (id) {
if (typeof tooltip !== 'undefined') {
if (tooltip !== undefined) {
classes[id].tooltip = common.sanitizeText(tooltip, config);
}
});
@@ -242,7 +244,7 @@ export const setLink = function (ids, linkStr, target) {
if (_id[0].match(/\d/)) {
id = MERMAID_DOM_ID_PREFIX + id;
}
if (typeof classes[id] !== 'undefined') {
if (classes[id] !== undefined) {
classes[id].link = utils.formatUrl(linkStr, config);
if (config.securityLevel === 'sandbox') {
classes[id].linkTarget = '_top';
@@ -279,10 +281,10 @@ const setClickFunc = function (domId, functionName, functionArgs) {
if (config.securityLevel !== 'loose') {
return;
}
if (typeof functionName === 'undefined') {
if (functionName === undefined) {
return;
}
if (typeof classes[id] !== 'undefined') {
if (classes[id] !== undefined) {
let argList = [];
if (typeof functionArgs === 'string') {
/* Splits functionArgs by ',', ignoring all ',' in double quoted strings */
@@ -408,4 +410,6 @@ export default {
getTooltip,
setTooltip,
lookUpDomId,
setDiagramTitle,
getDiagramTitle,
};

View File

@@ -1,16 +1,15 @@
import { select } from 'd3';
import graphlib from 'graphlib';
import * as graphlib from 'dagre-d3-es/src/graphlib';
import { log } from '../../logger';
import { getConfig } from '../../config';
import { render } from '../../dagre-wrapper/index.js';
import utils from '../../utils';
import { curveLinear } from 'd3';
import { interpolateToCurve, getStylesFromArray } from '../../utils';
import { setupGraphViewbox } from '../../setupGraphViewbox';
import common from '../common/common';
import addSVGAccessibilityFields from '../../accessibility';
let idCache = {};
const sanitizeText = (txt) => common.sanitizeText(txt, getConfig());
let conf = {
@@ -274,16 +273,16 @@ export const addRelations = function (relations, g) {
let style = '';
let labelStyle = '';
if (typeof edge.style !== 'undefined') {
if (edge.style !== undefined) {
const styles = getStylesFromArray(edge.style);
style = styles.style;
labelStyle = styles.labelStyle;
} else {
style = 'fill:none';
if (typeof defaultStyle !== 'undefined') {
if (defaultStyle !== undefined) {
style = defaultStyle;
}
if (typeof defaultLabelStyle !== 'undefined') {
if (defaultLabelStyle !== undefined) {
labelStyle = defaultLabelStyle;
}
}
@@ -291,17 +290,17 @@ export const addRelations = function (relations, g) {
edgeData.style = style;
edgeData.labelStyle = labelStyle;
if (typeof edge.interpolate !== 'undefined') {
if (edge.interpolate !== undefined) {
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
} else if (typeof relations.defaultInterpolate !== 'undefined') {
} else if (relations.defaultInterpolate !== undefined) {
edgeData.curve = interpolateToCurve(relations.defaultInterpolate, curveLinear);
} else {
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
}
edge.text = edge.title;
if (typeof edge.text === 'undefined') {
if (typeof edge.style !== 'undefined') {
if (edge.text === undefined) {
if (edge.style !== undefined) {
edgeData.arrowheadStyle = 'fill: #333';
}
} else {
@@ -315,7 +314,7 @@ export const addRelations = function (relations, g) {
edgeData.labelType = 'text';
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
if (typeof edge.style === 'undefined') {
if (edge.style === undefined) {
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
}
@@ -429,15 +428,15 @@ export const draw = function (text, id, _version, diagObj) {
id
);
utils.insertTitle(svg, 'classTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
// Add label rects for non html labels
if (!conf.htmlLabels) {
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
for (let k = 0; k < labels.length; k++) {
const label = labels[k];
for (const label of labels) {
// Get dimensions of label
const dim = label.getBBox();

View File

@@ -1,6 +1,6 @@
import { select } from 'd3';
import dagre from 'dagre';
import graphlib from 'graphlib';
import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js';
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
import { log } from '../../logger';
import svgDraw from './svgDraw';
import { configureSvgSize } from '../../setupGraphViewbox';
@@ -180,8 +180,8 @@ export const draw = function (text, id, _version, diagObj) {
const classes = diagObj.db.getClasses();
const keys = Object.keys(classes);
for (let i = 0; i < keys.length; i++) {
const classDef = classes[keys[i]];
for (const key of keys) {
const classDef = classes[key];
const node = svgDraw.drawClass(diagram, classDef, conf, diagObj);
idCache[node.id] = node;
@@ -238,9 +238,9 @@ export const draw = function (text, id, _version, diagObj) {
}
});
dagre.layout(g);
dagreLayout(g);
g.nodes().forEach(function (v) {
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
if (v !== undefined && g.node(v) !== undefined) {
log.debug('Node ' + v + ': ' + JSON.stringify(g.node(v)));
root
.select('#' + (diagObj.db.lookUpDomId(v) || v))
@@ -256,7 +256,7 @@ export const draw = function (text, id, _version, diagObj) {
});
g.edges().forEach(function (e) {
if (typeof e !== 'undefined' && typeof g.edge(e) !== 'undefined') {
if (e !== undefined && g.edge(e) !== undefined) {
log.debug('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e)));
svgDraw.drawEdge(diagram, g.edge(e), g.edge(e).relation, conf, diagObj);
}

View File

@@ -148,6 +148,11 @@ g.classGroup line {
font-size: 11px;
}
.classTitleText {
text-anchor: middle;
font-size: 18px;
fill: ${options.textColor};
}
`;
export default getStyles;

View File

@@ -102,7 +102,7 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
p2_card_y = cardinality_2_point.y;
}
if (typeof relation.title !== 'undefined') {
if (relation.title !== undefined) {
const g = elem.append('g').attr('class', 'classLabel');
const label = g
.append('text')
@@ -125,7 +125,7 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
}
log.info('Rendering relation ' + JSON.stringify(relation));
if (typeof relation.relationTitle1 !== 'undefined' && relation.relationTitle1 !== 'none') {
if (relation.relationTitle1 !== undefined && relation.relationTitle1 !== 'none') {
const g = elem.append('g').attr('class', 'cardinality');
g.append('text')
.attr('class', 'type1')
@@ -135,7 +135,7 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
.attr('font-size', '6')
.text(relation.relationTitle1);
}
if (typeof relation.relationTitle2 !== 'undefined' && relation.relationTitle2 !== 'none') {
if (relation.relationTitle2 !== undefined && relation.relationTitle2 !== 'none') {
const g = elem.append('g').attr('class', 'cardinality');
g.append('text')
.attr('class', 'type2')
@@ -355,8 +355,8 @@ export const drawNote = function (elem, note, conf, diagObj) {
};
export const parseMember = function (text) {
const fieldRegEx = /^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+) *(\*|\$)?$/;
const methodRegEx = /^([+|\-|~|#])?(\w+) *\( *(.*)\) *(\*|\$)? *(\w*[~|[\]]*\s*\w*~?)$/;
const fieldRegEx = /^([#+~-])?(\w+)(~\w+~|\[])?\s+(\w+) *([$*])?$/;
const methodRegEx = /^([#+|~-])?(\w+) *\( *(.*)\) *([$*])? *(\w*[[\]|~]*\s*\w*~?)$/;
let fieldMatch = text.match(fieldRegEx);
let methodMatch = text.match(methodRegEx);
@@ -420,7 +420,6 @@ const buildLegacyDisplay = function (text) {
// if for some reason we don't have any match, use old format to parse text
let displayText = '';
let cssStyle = '';
let memberText = '';
let returnType = '';
let methodStart = text.indexOf('(');
let methodEnd = text.indexOf(')');
@@ -433,7 +432,7 @@ const buildLegacyDisplay = function (text) {
if (firstChar.match(/\w/)) {
methodName = text.substring(0, methodStart).trim();
} else {
if (firstChar.match(/\+|-|~|#/)) {
if (firstChar.match(/[#+~-]/)) {
visibility = firstChar;
}

View File

@@ -152,7 +152,7 @@ export const evaluate = (val?: string | boolean): boolean =>
export const parseGenericTypes = function (text: string): string {
let cleanedText = text;
if (text.indexOf('~') !== -1) {
if (text.includes('~')) {
cleanedText = cleanedText.replace(/~([^~].*)/, '<$1');
cleanedText = cleanedText.replace(/~([^~]*)$/, '>$1');

View File

@@ -8,6 +8,8 @@ import {
getAccDescription,
setAccDescription,
clear as commonClear,
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb';
let entities = {};
@@ -30,7 +32,7 @@ export const parseDirective = function (statement, context, type) {
};
const addEntity = function (name) {
if (typeof entities[name] === 'undefined') {
if (entities[name] === undefined) {
entities[name] = { attributes: [] };
log.info('Added new entity :', name);
}
@@ -94,4 +96,6 @@ export default {
getAccTitle,
setAccDescription,
getAccDescription,
setDiagramTitle,
getDiagramTitle,
};

View File

@@ -1,8 +1,9 @@
import graphlib from 'graphlib';
import * as graphlib from 'dagre-d3-es/src/graphlib';
import { line, curveBasis, select } from 'd3';
import dagre from 'dagre';
import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js';
import { getConfig } from '../../config';
import { log } from '../../logger';
import utils from '../../utils';
import erMarkers from './erMarkers';
import { configureSvgSize } from '../../setupGraphViewbox';
import addSVGAccessibilityFields from '../../accessibility';
@@ -10,7 +11,7 @@ import { parseGenericTypes } from '../common/common';
import { v4 as uuid4 } from 'uuid';
/** Regex used to remove chars from the entity name so the result can be used in an id */
const BAD_ID_CHARS_REGEXP = /[^A-Za-z0-9]([\W])*/g;
const BAD_ID_CHARS_REGEXP = /[^\dA-Za-z](\W)*/g;
// Configuration
let conf = {};
@@ -27,8 +28,8 @@ let entityNameIds = new Map();
*/
export const setConf = function (cnf) {
const keys = Object.keys(cnf);
for (let i = 0; i < keys.length; i++) {
conf[keys[i]] = cnf[keys[i]];
for (const key of keys) {
conf[key] = cnf[key];
}
};
@@ -210,9 +211,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
const typeRect = groupNode
.insert('rect', '#' + attributeNode.tn.node().id)
.classed(`er ${attribStyle}`, true)
.style('fill', conf.fill)
.style('fill-opacity', '100%')
.style('stroke', conf.stroke)
.attr('x', 0)
.attr('y', heightOffset)
.attr('width', maxTypeWidth + widthPadding * 2 + spareColumnWidth)
@@ -230,9 +228,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
const nameRect = groupNode
.insert('rect', '#' + attributeNode.nn.node().id)
.classed(`er ${attribStyle}`, true)
.style('fill', conf.fill)
.style('fill-opacity', '100%')
.style('stroke', conf.stroke)
.attr('x', nameXOffset)
.attr('y', heightOffset)
.attr('width', maxNameWidth + widthPadding * 2 + spareColumnWidth)
@@ -252,9 +247,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
const keyTypeRect = groupNode
.insert('rect', '#' + attributeNode.kn.node().id)
.classed(`er ${attribStyle}`, true)
.style('fill', conf.fill)
.style('fill-opacity', '100%')
.style('stroke', conf.stroke)
.attr('x', keyTypeAndCommentXOffset)
.attr('y', heightOffset)
.attr('width', maxKeyWidth + widthPadding * 2 + spareColumnWidth)
@@ -275,9 +267,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
groupNode
.insert('rect', '#' + attributeNode.cn.node().id)
.classed(`er ${attribStyle}`, 'true')
.style('fill', conf.fill)
.style('fill-opacity', '100%')
.style('stroke', conf.stroke)
.attr('x', keyTypeAndCommentXOffset)
.attr('y', heightOffset)
.attr('width', maxCommentWidth + widthPadding * 2 + spareColumnWidth)
@@ -347,9 +336,6 @@ const drawEntities = function (svgNode, entities, graph) {
const rectNode = groupNode
.insert('rect', '#' + textId)
.classed('er entityBox', true)
.style('fill', conf.fill)
.style('fill-opacity', '100%')
.style('stroke', conf.stroke)
.attr('x', 0)
.attr('y', 0)
.attr('width', entityWidth)
@@ -370,7 +356,7 @@ const drawEntities = function (svgNode, entities, graph) {
const adjustEntities = function (svgNode, graph) {
graph.nodes().forEach(function (v) {
if (typeof v !== 'undefined' && typeof graph.node(v) !== 'undefined') {
if (v !== undefined && graph.node(v) !== undefined) {
svgNode
.select('#' + v)
.attr(
@@ -401,7 +387,7 @@ const getEdgeName = function (rel) {
* Add each relationship to the graph
*
* @param relationships The relationships to be added
* @param {Graph} g The graph
* @param g The graph
* @returns {Array} The array of relationships
*/
const addRelationships = function (relationships, g) {
@@ -547,9 +533,7 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
.attr('x', labelPoint.x - labelBBox.width / 2)
.attr('y', labelPoint.y - labelBBox.height / 2)
.attr('width', labelBBox.width)
.attr('height', labelBBox.height)
.style('fill', 'white')
.style('fill-opacity', '85%');
.attr('height', labelBBox.height);
};
/**
@@ -637,7 +621,7 @@ export const draw = function (text, id, _version, diagObj) {
// Add all the relationships to the graph
const relationships = addRelationships(diagObj.db.getRelationships(), g);
dagre.layout(g); // Node and edge positions will be updated
dagreLayout(g); // Node and edge positions will be updated
// Adjust the positions of the entities so that they adhere to the layout
adjustEntities(svg, g);
@@ -649,6 +633,8 @@ export const draw = function (text, id, _version, diagObj) {
const padding = conf.diagramPadding;
utils.insertTitle(svg, 'entityTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
const svgBounds = svg.node().getBBox();
const width = svgBounds.width + padding * 2;
const height = svgBounds.height + padding * 2;
@@ -666,10 +652,8 @@ export const draw = function (text, id, _version, diagObj) {
* Although the official XML standard for ids says that many more characters are valid in the id,
* this keeps things simple by accepting only A-Za-z0-9.
*
* @param {string} [str?=''] Given string to use as the basis for the id. Default is `''`
* @param {string} [prefix?=''] String to put at the start, followed by '-'. Default is `''`
* @param str
* @param prefix
* @param {string} str Given string to use as the basis for the id. Default is `''`
* @param {string} prefix String to put at the start, followed by '-'. Default is `''`
* @returns {string}
* @see https://www.w3.org/TR/xml/#NT-Name
*/

View File

@@ -27,6 +27,12 @@ const getStyles = (options) =>
.relationshipLine {
stroke: ${options.lineColor};
}
.entityTitleText {
text-anchor: middle;
font-size: 18px;
fill: ${options.textColor};
}
`;
export default getStyles;

View File

@@ -1,4 +1,5 @@
import dagreD3 from 'dagre-d3';
import { intersectPolygon } from 'dagre-d3-es/src/dagre-js/intersect/intersect-polygon.js';
import { intersectRect } from 'dagre-d3-es/src/dagre-js/intersect/intersect-rect.js';
/**
* @param parent
@@ -17,7 +18,7 @@ function question(parent, bbox, node) {
];
const shapeSvg = insertPolygonShape(parent, s, s, points);
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point);
return intersectPolygon(node, points, point);
};
return shapeSvg;
}
@@ -42,7 +43,7 @@ function hexagon(parent, bbox, node) {
];
const shapeSvg = insertPolygonShape(parent, w, h, points);
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point);
return intersectPolygon(node, points, point);
};
return shapeSvg;
}
@@ -64,7 +65,7 @@ function rect_left_inv_arrow(parent, bbox, node) {
];
const shapeSvg = insertPolygonShape(parent, w, h, points);
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point);
return intersectPolygon(node, points, point);
};
return shapeSvg;
}
@@ -85,7 +86,7 @@ function lean_right(parent, bbox, node) {
];
const shapeSvg = insertPolygonShape(parent, w, h, points);
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point);
return intersectPolygon(node, points, point);
};
return shapeSvg;
}
@@ -106,7 +107,7 @@ function lean_left(parent, bbox, node) {
];
const shapeSvg = insertPolygonShape(parent, w, h, points);
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point);
return intersectPolygon(node, points, point);
};
return shapeSvg;
}
@@ -127,7 +128,7 @@ function trapezoid(parent, bbox, node) {
];
const shapeSvg = insertPolygonShape(parent, w, h, points);
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point);
return intersectPolygon(node, points, point);
};
return shapeSvg;
}
@@ -148,7 +149,7 @@ function inv_trapezoid(parent, bbox, node) {
];
const shapeSvg = insertPolygonShape(parent, w, h, points);
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point);
return intersectPolygon(node, points, point);
};
return shapeSvg;
}
@@ -170,7 +171,7 @@ function rect_right_inv_arrow(parent, bbox, node) {
];
const shapeSvg = insertPolygonShape(parent, w, h, points);
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point);
return intersectPolygon(node, points, point);
};
return shapeSvg;
}
@@ -194,7 +195,7 @@ function stadium(parent, bbox, node) {
.attr('height', h);
node.intersect = function (point) {
return dagreD3.intersect.rect(node, point);
return intersectRect(node, point);
};
return shapeSvg;
}
@@ -221,7 +222,7 @@ function subroutine(parent, bbox, node) {
];
const shapeSvg = insertPolygonShape(parent, w, h, points);
node.intersect = function (point) {
return dagreD3.intersect.polygon(node, points, point);
return intersectPolygon(node, points, point);
};
return shapeSvg;
}
@@ -270,7 +271,7 @@ function cylinder(parent, bbox, node) {
.attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2 + ry) + ')');
node.intersect = function (point) {
const pos = dagreD3.intersect.rect(node, point);
const pos = intersectRect(node, point);
const x = pos.x - node.x;
if (

View File

@@ -10,6 +10,8 @@ import {
getAccDescription,
setAccDescription,
clear as commonClear,
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb';
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
@@ -44,9 +46,9 @@ export const parseDirective = function (statement, context, type) {
*/
export const lookUpDomId = function (id) {
const veritceKeys = Object.keys(vertices);
for (let i = 0; i < veritceKeys.length; i++) {
if (vertices[veritceKeys[i]].id === id) {
return vertices[veritceKeys[i]].domId;
for (const veritceKey of veritceKeys) {
if (vertices[veritceKey].id === id) {
return vertices[veritceKey].domId;
}
}
return id;
@@ -66,7 +68,7 @@ export const lookUpDomId = function (id) {
export const addVertex = function (_id, text, type, style, classes, dir, props = {}) {
let txt;
let id = _id;
if (typeof id === 'undefined') {
if (id === undefined) {
return;
}
if (id.trim().length === 0) {
@@ -75,7 +77,7 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
// if (id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
if (typeof vertices[id] === 'undefined') {
if (vertices[id] === undefined) {
vertices[id] = {
id: id,
domId: MERMAID_DOM_ID_PREFIX + id + '-' + vertexCounter,
@@ -84,7 +86,7 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
};
}
vertexCounter++;
if (typeof text !== 'undefined') {
if (text !== undefined) {
config = configApi.getConfig();
txt = sanitizeText(text.trim());
@@ -95,33 +97,29 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
vertices[id].text = txt;
} else {
if (typeof vertices[id].text === 'undefined') {
if (vertices[id].text === undefined) {
vertices[id].text = _id;
}
}
if (typeof type !== 'undefined') {
if (type !== undefined) {
vertices[id].type = type;
}
if (typeof style !== 'undefined') {
if (style !== null) {
style.forEach(function (s) {
vertices[id].styles.push(s);
});
}
if (style !== undefined && style !== null) {
style.forEach(function (s) {
vertices[id].styles.push(s);
});
}
if (typeof classes !== 'undefined') {
if (classes !== null) {
classes.forEach(function (s) {
vertices[id].classes.push(s);
});
}
if (classes !== undefined && classes !== null) {
classes.forEach(function (s) {
vertices[id].classes.push(s);
});
}
if (typeof dir !== 'undefined') {
if (dir !== undefined) {
vertices[id].dir = dir;
}
if (typeof vertices[id].props === 'undefined') {
if (vertices[id].props === undefined) {
vertices[id].props = props;
} else if (typeof props !== 'undefined') {
} else if (props !== undefined) {
Object.assign(vertices[id].props, props);
}
};
@@ -132,9 +130,9 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
* @param _start
* @param _end
* @param type
* @param linktext
* @param linkText
*/
export const addSingleLink = function (_start, _end, type, linktext) {
export const addSingleLink = function (_start, _end, type, linkText) {
let start = _start;
let end = _end;
// if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
@@ -142,18 +140,18 @@ export const addSingleLink = function (_start, _end, type, linktext) {
// log.info('Got edge...', start, end);
const edge = { start: start, end: end, type: undefined, text: '' };
linktext = type.text;
linkText = type.text;
if (typeof linktext !== 'undefined') {
edge.text = sanitizeText(linktext.trim());
if (linkText !== undefined) {
edge.text = sanitizeText(linkText.trim());
// strip quotes if string starts and exnds with a quote
// strip quotes if string starts and ends with a quote
if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
edge.text = edge.text.substring(1, edge.text.length - 1);
}
}
if (typeof type !== 'undefined') {
if (type !== undefined) {
edge.type = type.type;
edge.stroke = type.stroke;
edge.length = type.length;
@@ -205,21 +203,19 @@ export const updateLink = function (positions, style) {
};
export const addClass = function (id, style) {
if (typeof classes[id] === 'undefined') {
if (classes[id] === undefined) {
classes[id] = { id: id, styles: [], textStyles: [] };
}
if (typeof style !== 'undefined') {
if (style !== null) {
style.forEach(function (s) {
if (s.match('color')) {
const newStyle1 = s.replace('fill', 'bgFill');
const newStyle2 = newStyle1.replace('color', 'fill');
classes[id].textStyles.push(newStyle2);
}
classes[id].styles.push(s);
});
}
if (style !== undefined && style !== null) {
style.forEach(function (s) {
if (s.match('color')) {
const newStyle1 = s.replace('fill', 'bgFill');
const newStyle2 = newStyle1.replace('color', 'fill');
classes[id].textStyles.push(newStyle2);
}
classes[id].styles.push(s);
});
}
};
@@ -255,11 +251,11 @@ export const setClass = function (ids, className) {
// let id = version === 'gen-2' ? lookUpDomId(_id) : _id;
let id = _id;
// if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
if (typeof vertices[id] !== 'undefined') {
if (vertices[id] !== undefined) {
vertices[id].classes.push(className);
}
if (typeof subGraphLookup[id] !== 'undefined') {
if (subGraphLookup[id] !== undefined) {
subGraphLookup[id].classes.push(className);
}
});
@@ -267,7 +263,7 @@ export const setClass = function (ids, className) {
const setTooltip = function (ids, tooltip) {
ids.split(',').forEach(function (id) {
if (typeof tooltip !== 'undefined') {
if (tooltip !== undefined) {
tooltips[version === 'gen-1' ? lookUpDomId(id) : id] = sanitizeText(tooltip);
}
});
@@ -279,7 +275,7 @@ const setClickFun = function (id, functionName, functionArgs) {
if (configApi.getConfig().securityLevel !== 'loose') {
return;
}
if (typeof functionName === 'undefined') {
if (functionName === undefined) {
return;
}
let argList = [];
@@ -302,7 +298,7 @@ const setClickFun = function (id, functionName, functionArgs) {
argList.push(id);
}
if (typeof vertices[id] !== 'undefined') {
if (vertices[id] !== undefined) {
vertices[id].haveCallback = true;
funs.push(function () {
const elem = document.querySelector(`[id="${domId}"]`);
@@ -328,7 +324,7 @@ const setClickFun = function (id, functionName, functionArgs) {
*/
export const setLink = function (ids, linkStr, target) {
ids.split(',').forEach(function (id) {
if (typeof vertices[id] !== 'undefined') {
if (vertices[id] !== undefined) {
vertices[id].link = utils.formatUrl(linkStr, config);
vertices[id].linkTarget = target;
}
@@ -482,7 +478,7 @@ export const addSubGraph = function (_id, list, _title) {
if (type in prims) {
return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
} else {
return objs.indexOf(item) >= 0 ? false : objs.push(item);
return objs.includes(item) ? false : objs.push(item);
}
});
return { nodeList, dir };
@@ -528,8 +524,8 @@ export const addSubGraph = function (_id, list, _title) {
};
const getPosForId = function (id) {
for (let i = 0; i < subGraphs.length; i++) {
if (subGraphs[i].id === id) {
for (const [i, subGraph] of subGraphs.entries()) {
if (subGraph.id === id) {
return i;
}
}
@@ -620,11 +616,11 @@ const destructStartLink = (_str) => {
let stroke = 'normal';
if (str.indexOf('=') !== -1) {
if (str.includes('=')) {
stroke = 'thick';
}
if (str.indexOf('.') !== -1) {
if (str.includes('.')) {
stroke = 'dotted';
}
@@ -785,4 +781,6 @@ export default {
},
exists,
makeUniq,
setDiagramTitle,
getDiagramTitle,
};

View File

@@ -1,11 +1,12 @@
import graphlib from 'graphlib';
import * as graphlib from 'dagre-d3-es/src/graphlib';
import { select, curveLinear, selectAll } from 'd3';
import flowDb from './flowDb';
import { getConfig } from '../../config';
import utils from '../../utils';
import { render } from '../../dagre-wrapper/index.js';
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
import { log } from '../../logger';
import common, { evaluate } from '../common/common';
import { interpolateToCurve, getStylesFromArray } from '../../utils';
@@ -15,8 +16,8 @@ import addSVGAccessibilityFields from '../../accessibility';
const conf = {};
export const setConf = function (cnf) {
const keys = Object.keys(cnf);
for (let i = 0; i < keys.length; i++) {
conf[keys[i]] = cnf[keys[i]];
for (const key of keys) {
conf[key] = cnf[key];
}
};
@@ -59,7 +60,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
const node = {
label: vertexText.replace(
/fa[lrsb]?:fa-[\w-]+/g,
/fa[blrs]?:fa-[\w-]+/g,
(s) => `<i class='${s.replace(':', ' ')}'></i>`
),
};
@@ -71,12 +72,12 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
const rows = vertexText.split(common.lineBreakRegex);
for (let j = 0; j < rows.length; j++) {
for (const row of rows) {
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
tspan.setAttribute('dy', '1em');
tspan.setAttribute('x', '1');
tspan.textContent = rows[j];
tspan.textContent = row;
svgLabel.appendChild(tspan);
}
vertexNode = svgLabel;
@@ -197,7 +198,7 @@ export const addEdges = function (edges, g, diagObj) {
let defaultStyle;
let defaultLabelStyle;
if (typeof edges.defaultStyle !== 'undefined') {
if (edges.defaultStyle !== undefined) {
const defaultStyles = getStylesFromArray(edges.defaultStyle);
defaultStyle = defaultStyles.style;
defaultLabelStyle = defaultStyles.labelStyle;
@@ -209,7 +210,7 @@ export const addEdges = function (edges, g, diagObj) {
// Identify Link
var linkIdBase = 'L-' + edge.start + '-' + edge.end;
// count the links from+to the same node to give unique id
if (typeof linkIdCnt[linkIdBase] === 'undefined') {
if (linkIdCnt[linkIdBase] === undefined) {
linkIdCnt[linkIdBase] = 0;
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
} else {
@@ -261,10 +262,10 @@ export const addEdges = function (edges, g, diagObj) {
switch (edge.stroke) {
case 'normal':
style = 'fill:none;';
if (typeof defaultStyle !== 'undefined') {
if (defaultStyle !== undefined) {
style = defaultStyle;
}
if (typeof defaultLabelStyle !== 'undefined') {
if (defaultLabelStyle !== undefined) {
labelStyle = defaultLabelStyle;
}
edgeData.thickness = 'normal';
@@ -281,7 +282,7 @@ export const addEdges = function (edges, g, diagObj) {
edgeData.style = 'stroke-width: 3.5px;fill:none;';
break;
}
if (typeof edge.style !== 'undefined') {
if (edge.style !== undefined) {
const styles = getStylesFromArray(edge.style);
style = styles.style;
labelStyle = styles.labelStyle;
@@ -290,16 +291,16 @@ export const addEdges = function (edges, g, diagObj) {
edgeData.style = edgeData.style += style;
edgeData.labelStyle = edgeData.labelStyle += labelStyle;
if (typeof edge.interpolate !== 'undefined') {
if (edge.interpolate !== undefined) {
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
} else if (typeof edges.defaultInterpolate !== 'undefined') {
} else if (edges.defaultInterpolate !== undefined) {
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
} else {
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
}
if (typeof edge.text === 'undefined') {
if (typeof edge.style !== 'undefined') {
if (edge.text === undefined) {
if (edge.style !== undefined) {
edgeData.arrowheadStyle = 'fill: #333';
}
} else {
@@ -310,7 +311,7 @@ export const addEdges = function (edges, g, diagObj) {
edgeData.labelType = 'text';
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
if (typeof edge.style === 'undefined') {
if (edge.style === undefined) {
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
}
@@ -359,7 +360,7 @@ export const draw = function (text, id, _version, diagObj) {
// Fetch the default direction, use TD if none was found
let dir = diagObj.db.getDirection();
if (typeof dir === 'undefined') {
if (dir === undefined) {
dir = 'TD';
}
@@ -437,6 +438,8 @@ export const draw = function (text, id, _version, diagObj) {
const element = root.select('#' + id + ' g');
render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
// Index nodes
@@ -445,9 +448,7 @@ export const draw = function (text, id, _version, diagObj) {
// Add label rects for non html labels
if (!conf.htmlLabels) {
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
for (let k = 0; k < labels.length; k++) {
const label = labels[k];
for (const label of labels) {
// Get dimensions of label
const dim = label.getBBox();

View File

@@ -1,8 +1,9 @@
import graphlib from 'graphlib';
import * as graphlib from 'dagre-d3-es/src/graphlib';
import { select, curveLinear, selectAll } from 'd3';
import { getConfig } from '../../config';
import dagreD3 from 'dagre-d3';
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
import { render as Render } from 'dagre-d3-es';
import { applyStyle } from 'dagre-d3-es/src/dagre-js/util.js';
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
import { log } from '../../logger';
import common, { evaluate } from '../common/common';
import { interpolateToCurve, getStylesFromArray } from '../../utils';
@@ -13,8 +14,8 @@ import addSVGAccessibilityFields from '../../accessibility';
const conf = {};
export const setConf = function (cnf) {
const keys = Object.keys(cnf);
for (let i = 0; i < keys.length; i++) {
conf[keys[i]] = cnf[keys[i]];
for (const key of keys) {
conf[key] = cnf[key];
}
};
@@ -58,7 +59,7 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
const node = {
label: vertexText.replace(
/fa[lrsb]?:fa-[\w-]+/g,
/fa[blrs]?:fa-[\w-]+/g,
(s) => `<i class='${s.replace(':', ' ')}'></i>`
),
};
@@ -70,12 +71,12 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
const rows = vertexText.split(common.lineBreakRegex);
for (let j = 0; j < rows.length; j++) {
for (const row of rows) {
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
tspan.setAttribute('dy', '1em');
tspan.setAttribute('x', '1');
tspan.textContent = rows[j];
tspan.textContent = row;
svgLabel.appendChild(tspan);
}
vertexNode = svgLabel;
@@ -166,7 +167,7 @@ export const addEdges = function (edges, g, diagObj) {
let defaultStyle;
let defaultLabelStyle;
if (typeof edges.defaultStyle !== 'undefined') {
if (edges.defaultStyle !== undefined) {
const defaultStyles = getStylesFromArray(edges.defaultStyle);
defaultStyle = defaultStyles.style;
defaultLabelStyle = defaultStyles.labelStyle;
@@ -192,7 +193,7 @@ export const addEdges = function (edges, g, diagObj) {
let style = '';
let labelStyle = '';
if (typeof edge.style !== 'undefined') {
if (edge.style !== undefined) {
const styles = getStylesFromArray(edge.style);
style = styles.style;
labelStyle = styles.labelStyle;
@@ -200,10 +201,10 @@ export const addEdges = function (edges, g, diagObj) {
switch (edge.stroke) {
case 'normal':
style = 'fill:none';
if (typeof defaultStyle !== 'undefined') {
if (defaultStyle !== undefined) {
style = defaultStyle;
}
if (typeof defaultLabelStyle !== 'undefined') {
if (defaultLabelStyle !== undefined) {
labelStyle = defaultLabelStyle;
}
break;
@@ -219,16 +220,16 @@ export const addEdges = function (edges, g, diagObj) {
edgeData.style = style;
edgeData.labelStyle = labelStyle;
if (typeof edge.interpolate !== 'undefined') {
if (edge.interpolate !== undefined) {
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
} else if (typeof edges.defaultInterpolate !== 'undefined') {
} else if (edges.defaultInterpolate !== undefined) {
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
} else {
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
}
if (typeof edge.text === 'undefined') {
if (typeof edge.style !== 'undefined') {
if (edge.text === undefined) {
if (edge.style !== undefined) {
edgeData.arrowheadStyle = 'fill: #333';
}
} else {
@@ -240,14 +241,14 @@ export const addEdges = function (edges, g, diagObj) {
edgeData.label = `<span id="L-${linkId}" class="edgeLabel L-${linkNameStart}' L-${linkNameEnd}" style="${
edgeData.labelStyle
}">${edge.text.replace(
/fa[lrsb]?:fa-[\w-]+/g,
/fa[blrs]?:fa-[\w-]+/g,
(s) => `<i class='${s.replace(':', ' ')}'></i>`
)}</span>`;
} else {
edgeData.labelType = 'text';
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
if (typeof edge.style === 'undefined') {
if (edge.style === undefined) {
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
}
@@ -315,7 +316,7 @@ export const draw = function (text, id, _version, diagObj) {
// Fetch the default direction, use TD if none was found
let dir = diagObj.db.getDirection();
if (typeof dir === 'undefined') {
if (dir === undefined) {
dir = 'TD';
}
const nodeSpacing = conf.nodeSpacing || 50;
@@ -370,7 +371,6 @@ export const draw = function (text, id, _version, diagObj) {
addEdges(edges, g, diagObj);
// Create the renderer
const Render = dagreD3.render;
const render = new Render();
// Add custom shapes
@@ -390,7 +390,7 @@ export const draw = function (text, id, _version, diagObj) {
.attr('orient', 'auto');
const path = marker.append('path').attr('d', 'M 0 0 L 0 0 L 0 0 z');
dagreD3.util.applyStyle(path, edge[type + 'Style']);
applyStyle(path, edge[type + 'Style']);
};
// Override normal arrowhead defined in d3. Remove style & add class to allow css styling.
@@ -459,9 +459,7 @@ export const draw = function (text, id, _version, diagObj) {
// Add label rects for non html labels
if (!conf.htmlLabels) {
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
for (let k = 0; k < labels.length; k++) {
const label = labels[k];
for (const label of labels) {
// Get dimensions of label
const dim = label.getBBox();

View File

@@ -1,6 +1,6 @@
import flowDb from '../flowDb';
import flow from './flow';
import filter from 'lodash/filter';
import filter from 'lodash-es/filter';
import { setConfig } from '../../../config';
setConfig({
@@ -79,8 +79,8 @@ describe('when parsing directions', function () {
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = filter(subgraphs, (o) => o.id === 'A')[0];
const subgraphB = filter(subgraphs, (o) => o.id === 'B')[0];
const subgraphA = subgraphs.find((o) => o.id === 'A');
const subgraphB = subgraphs.find((o) => o.id === 'B');
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphB.dir).toBe('LR');

View File

@@ -1,6 +1,6 @@
import flowDb from '../flowDb';
import flow from './flow';
import filter from 'lodash/filter';
import filter from 'lodash-es/filter';
import { setConfig } from '../../../config';
setConfig({
@@ -254,8 +254,8 @@ describe('when parsing subgraphs', function () {
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = filter(subgraphs, (o) => o.id === 'A')[0];
const subgraphB = filter(subgraphs, (o) => o.id === 'B')[0];
const subgraphA = subgraphs.find((o) => o.id === 'A');
const subgraphB = subgraphs.find((o) => o.id === 'B');
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphA.nodes).toContain('B');
@@ -279,8 +279,8 @@ describe('when parsing subgraphs', function () {
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = filter(subgraphs, (o) => o.id === 'A')[0];
const subgraphB = filter(subgraphs, (o) => o.id === 'B')[0];
const subgraphA = subgraphs.find((o) => o.id === 'A');
const subgraphB = subgraphs.find((o) => o.id === 'B');
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphA.nodes).toContain('B');
@@ -302,8 +302,8 @@ describe('when parsing subgraphs', function () {
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = filter(subgraphs, (o) => o.id === 'A')[0];
const subgraphB = filter(subgraphs, (o) => o.id === 'B')[0];
const subgraphA = subgraphs.find((o) => o.id === 'A');
const subgraphB = subgraphs.find((o) => o.id === 'B');
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphA.nodes).toContain('B');
expect(subgraphA.nodes).toContain('b');

View File

@@ -103,6 +103,12 @@ const getStyles = (options: FlowChartStyleOptions) =>
pointer-events: none;
z-index: 100;
}
.flowchartTitleText {
text-anchor: middle;
font-size: 18px;
fill: ${options.textColor};
}
`;
export default getStyles;

View File

@@ -150,16 +150,16 @@ export const getTasks = function () {
};
export const isInvalidDate = function (date, dateFormat, excludes, includes) {
if (includes.indexOf(date.format(dateFormat.trim())) >= 0) {
if (includes.includes(date.format(dateFormat.trim()))) {
return false;
}
if (date.isoWeekday() >= 6 && excludes.indexOf('weekends') >= 0) {
if (date.isoWeekday() >= 6 && excludes.includes('weekends')) {
return true;
}
if (excludes.indexOf(date.format('dddd').toLowerCase()) >= 0) {
if (excludes.includes(date.format('dddd').toLowerCase())) {
return true;
}
return excludes.indexOf(date.format(dateFormat.trim())) >= 0;
return excludes.includes(date.format(dateFormat.trim()));
};
const checkTaskDates = function (task, dateFormat, excludes, includes) {
@@ -202,7 +202,7 @@ const getStartDate = function (prevTime, dateFormat, str) {
let latestEndingTask = null;
afterStatement[1].split(' ').forEach(function (id) {
let task = findTaskById(id);
if (typeof task !== 'undefined') {
if (task !== undefined) {
if (!latestEndingTask) {
latestEndingTask = task;
} else {
@@ -230,7 +230,7 @@ const getStartDate = function (prevTime, dateFormat, str) {
log.debug('Invalid date:' + str);
log.debug('With date format:' + dateFormat.trim());
const d = new Date(str);
if (typeof d === 'undefined' || isNaN(d.getTime())) {
if (d === undefined || isNaN(d.getTime())) {
throw new Error('Invalid date:' + str);
}
return d;
@@ -258,15 +258,14 @@ const getStartDate = function (prevTime, dateFormat, str) {
* string.
*/
const parseDuration = function (str) {
const statement = /^(\d+(?:\.\d+)?)([yMwdhms]|ms)$/.exec(str.trim());
const statement = /^(\d+(?:\.\d+)?)([Mdhmswy]|ms)$/.exec(str.trim());
if (statement !== null) {
return moment.duration(Number.parseFloat(statement[1]), statement[2]);
}
return moment.duration.invalid();
};
const getEndDate = function (prevTime, dateFormat, str, inclusive) {
inclusive = inclusive || false;
const getEndDate = function (prevTime, dateFormat, str, inclusive = false) {
str = str.trim();
// Check for actual date
@@ -288,7 +287,7 @@ const getEndDate = function (prevTime, dateFormat, str, inclusive) {
let taskCnt = 0;
const parseId = function (idStr) {
if (typeof idStr === 'undefined') {
if (idStr === undefined) {
taskCnt = taskCnt + 1;
return 'task' + taskCnt;
}
@@ -510,10 +509,10 @@ const compileTasks = function () {
};
let allProcessed = true;
for (let i = 0; i < rawTasks.length; i++) {
for (const [i, rawTask] of rawTasks.entries()) {
compileTask(i);
allProcessed = allProcessed && rawTasks[i].processed;
allProcessed = allProcessed && rawTask.processed;
}
return allProcessed;
};
@@ -531,7 +530,7 @@ export const setLink = function (ids, _linkStr) {
}
ids.split(',').forEach(function (id) {
let rawTask = findTaskById(id);
if (typeof rawTask !== 'undefined') {
if (rawTask !== undefined) {
pushFun(id, () => {
window.open(linkStr, '_self');
});
@@ -550,7 +549,7 @@ export const setLink = function (ids, _linkStr) {
export const setClass = function (ids, className) {
ids.split(',').forEach(function (id) {
let rawTask = findTaskById(id);
if (typeof rawTask !== 'undefined') {
if (rawTask !== undefined) {
rawTask.classes.push(className);
}
});
@@ -560,7 +559,7 @@ const setClickFun = function (id, functionName, functionArgs) {
if (configApi.getConfig().securityLevel !== 'loose') {
return;
}
if (typeof functionName === 'undefined') {
if (functionName === undefined) {
return;
}
@@ -585,7 +584,7 @@ const setClickFun = function (id, functionName, functionArgs) {
}
let rawTask = findTaskById(id);
if (typeof rawTask !== 'undefined') {
if (rawTask !== undefined) {
pushFun(id, () => {
utils.runFunc(functionName, ...argList);
});
@@ -600,24 +599,26 @@ const setClickFun = function (id, functionName, functionArgs) {
* @param callbackFunction A function to be executed when clicked on the task or the task's text
*/
const pushFun = function (id, callbackFunction) {
funs.push(function () {
// const elem = d3.select(element).select(`[id="${id}"]`)
const elem = document.querySelector(`[id="${id}"]`);
if (elem !== null) {
elem.addEventListener('click', function () {
callbackFunction();
});
funs.push(
function () {
// const elem = d3.select(element).select(`[id="${id}"]`)
const elem = document.querySelector(`[id="${id}"]`);
if (elem !== null) {
elem.addEventListener('click', function () {
callbackFunction();
});
}
},
function () {
// const elem = d3.select(element).select(`[id="${id}-text"]`)
const elem = document.querySelector(`[id="${id}-text"]`);
if (elem !== null) {
elem.addEventListener('click', function () {
callbackFunction();
});
}
}
});
funs.push(function () {
// const elem = d3.select(element).select(`[id="${id}-text"]`)
const elem = document.querySelector(`[id="${id}-text"]`);
if (elem !== null) {
elem.addEventListener('click', function () {
callbackFunction();
});
}
});
);
};
/**

View File

@@ -46,11 +46,11 @@ export const draw = function (text, id, version, diagObj) {
const elem = doc.getElementById(id);
w = elem.parentElement.offsetWidth;
if (typeof w === 'undefined') {
if (w === undefined) {
w = 1200;
}
if (typeof conf.useWidth !== 'undefined') {
if (conf.useWidth !== undefined) {
w = conf.useWidth;
}
@@ -77,8 +77,8 @@ export const draw = function (text, id, version, diagObj) {
let categories = [];
for (let i = 0; i < taskArray.length; i++) {
categories.push(taskArray[i].type);
for (const element of taskArray) {
categories.push(element.type);
}
const catsUnfiltered = categories; // for vert labels
@@ -178,8 +178,8 @@ export const draw = function (text, id, version, diagObj) {
})
.attr('height', theGap)
.attr('class', function (d) {
for (let i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
for (const [i, category] of categories.entries()) {
if (d.type === category) {
return 'section section' + (i % conf.numberSectionStyles);
}
}
@@ -247,8 +247,8 @@ export const draw = function (text, id, version, diagObj) {
}
let secNum = 0;
for (let i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
for (const [i, category] of categories.entries()) {
if (d.type === category) {
secNum = i % conf.numberSectionStyles;
}
}
@@ -339,8 +339,8 @@ export const draw = function (text, id, version, diagObj) {
}
let secNum = 0;
for (let i = 0; i < categories.length; i++) {
if (d.type === categories[i]) {
for (const [i, category] of categories.entries()) {
if (d.type === category) {
secNum = i % conf.numberSectionStyles;
}
}
@@ -400,7 +400,7 @@ export const draw = function (text, id, version, diagObj) {
rectangles
.filter(function (d) {
return typeof links[d.id] !== 'undefined';
return links[d.id] !== undefined;
})
.each(function (o) {
var taskRect = doc.querySelector('#' + o.id);
@@ -500,7 +500,7 @@ export const draw = function (text, id, version, diagObj) {
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
.tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
const reTickInterval = /^([1-9][0-9]*)(minute|hour|day|week|month)$/;
const reTickInterval = /^([1-9]\d*)(minute|hour|day|week|month)$/;
const resultTickInterval = reTickInterval.exec(
diagObj.db.getTickInterval() || conf.tickInterval
);
@@ -588,8 +588,8 @@ export const draw = function (text, id, version, diagObj) {
const numOccurances = [];
let prevGap = 0;
for (let i = 0; i < categories.length; i++) {
numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)];
for (const [i, category] of categories.entries()) {
numOccurances[i] = [category, getCount(category, catsUnfiltered)];
}
svg
@@ -604,14 +604,14 @@ export const draw = function (text, id, version, diagObj) {
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
svgLabel.setAttribute('dy', dy + 'em');
for (let j = 0; j < rows.length; j++) {
for (const [j, row] of rows.entries()) {
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttribute('alignment-baseline', 'central');
tspan.setAttribute('x', '10');
if (j > 0) {
tspan.setAttribute('dy', '1em');
}
tspan.textContent = rows[j];
tspan.textContent = row;
svgLabel.appendChild(tspan);
}
return svgLabel;
@@ -630,8 +630,8 @@ export const draw = function (text, id, version, diagObj) {
.attr('font-size', conf.sectionFontSize)
.attr('font-size', conf.sectionFontSize)
.attr('class', function (d) {
for (let i = 0; i < categories.length; i++) {
if (d[0] === categories[i]) {
for (const [i, category] of categories.entries()) {
if (d[0] === category) {
return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles);
}
}

View File

@@ -10,6 +10,8 @@ import {
getAccDescription,
setAccDescription,
clear as commonClear,
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb';
let mainBranchName = getConfig().gitGraph.mainBranchName;
@@ -129,7 +131,7 @@ export const commit = function (msg, id, type, tag) {
export const branch = function (name, order) {
name = common.sanitizeText(name, configApi.getConfig());
if (typeof branches[name] === 'undefined') {
if (branches[name] === undefined) {
branches[name] = head != null ? head.id : null;
branchesConfig[name] = { name, order: order ? parseInt(order, 10) : null };
checkout(name);
@@ -167,7 +169,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
expected: ['branch abc'],
};
throw error;
} else if (typeof currentCommit === 'undefined' || !currentCommit) {
} else if (currentCommit === undefined || !currentCommit) {
let error = new Error(
'Incorrect usage of "merge". Current branch (' + curBranch + ')has no commits'
);
@@ -179,7 +181,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
expected: ['commit'],
};
throw error;
} else if (typeof branches[otherBranch] === 'undefined') {
} else if (branches[otherBranch] === undefined) {
let error = new Error(
'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') does not exist'
);
@@ -191,7 +193,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
expected: ['branch ' + otherBranch],
};
throw error;
} else if (typeof otherCommit === 'undefined' || !otherCommit) {
} else if (otherCommit === undefined || !otherCommit) {
let error = new Error(
'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') has no commits'
);
@@ -213,7 +215,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
expected: ['branch abc'],
};
throw error;
} else if (custom_id && typeof commits[custom_id] !== 'undefined') {
} else if (custom_id && commits[custom_id] !== undefined) {
let error = new Error(
'Incorrect usage of "merge". Commit with id:' +
custom_id +
@@ -265,7 +267,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
targetId = common.sanitizeText(targetId, configApi.getConfig());
tag = common.sanitizeText(tag, configApi.getConfig());
if (!sourceId || typeof commits[sourceId] === 'undefined') {
if (!sourceId || commits[sourceId] === undefined) {
let error = new Error(
'Incorrect usage of "cherryPick". Source commit id should exist and provided'
);
@@ -294,7 +296,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
};
throw error;
}
if (!targetId || typeof commits[targetId] === 'undefined') {
if (!targetId || commits[targetId] === undefined) {
// cherry-pick source commit to current branch
if (sourceCommitBranch === curBranch) {
@@ -311,7 +313,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
throw error;
}
const currentCommit = commits[branches[curBranch]];
if (typeof currentCommit === 'undefined' || !currentCommit) {
if (currentCommit === undefined || !currentCommit) {
let error = new Error(
'Incorrect usage of "cherry-pick". Current branch (' + curBranch + ')has no commits'
);
@@ -342,7 +344,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
};
export const checkout = function (branch) {
branch = common.sanitizeText(branch, configApi.getConfig());
if (typeof branches[branch] === 'undefined') {
if (branches[branch] === undefined) {
let error = new Error(
'Trying to checkout branch which is not yet created. (Help try using "branch ' + branch + '")'
);
@@ -529,5 +531,7 @@ export default {
getAccTitle,
getAccDescription,
setAccDescription,
setDiagramTitle,
getDiagramTitle,
commitType,
};

Some files were not shown because too many files have changed in this diff Show More